]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/synth/arch/v2_0/src/synth_intr.c
Initial revision
[karo-tx-redboot.git] / packages / hal / synth / arch / v2_0 / src / synth_intr.c
1 //=============================================================================
2 //
3 //      synth_intr.c
4 //
5 //      Interrupt and clock code for the Linux synthetic target.
6 //
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2005 eCosCentric Ltd
12 // Copyright (C) 2002 Bart Veer
13 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
14 //
15 // eCos is free software; you can redistribute it and/or modify it under
16 // the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 or (at your option) any later version.
18 //
19 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22 // for more details.
23 //
24 // You should have received a copy of the GNU General Public License along
25 // with eCos; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 //
28 // As a special exception, if other files instantiate templates or use macros
29 // or inline functions from this file, or you compile this file and link it
30 // with other works to produce a work based on this file, this file does not
31 // by itself cause the resulting work to be covered by the GNU General Public
32 // License. However the source code for this file must still be made available
33 // in accordance with section (3) of the GNU General Public License.
34 //
35 // This exception does not invalidate any other reasons why a work based on
36 // this file might be covered by the GNU General Public License.
37 //
38 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 // at http://sources.redhat.com/ecos/ecos-license/
40 // -------------------------------------------
41 //####ECOSGPLCOPYRIGHTEND####
42 //=============================================================================
43 //#####DESCRIPTIONBEGIN####
44 //
45 // Author(s):    bartv
46 // Contributors: bartv, asl
47 // Date:         2001-03-30
48 // Purpose:      Implement the interrupt subsystem for the synthetic target
49 //####DESCRIPTIONEND####
50 //=============================================================================
51
52 // sigprocmask handling.
53 //
54 // In the synthetic target interrupts and exceptions are based around
55 // POSIX sighandlers. When the clock ticks a SIGALRM signal is raised.
56 // When the I/O auxiliary wants to raise some other interrupt, it
57 // sends a SIGIO signal. When an exception occurs this results in
58 // signals like SIGILL and SIGSEGV. This implies an implementation
59 // where the VSR is the signal handler. Disabling interrupts would
60 // then mean using sigprocmask() to block certain signals, and
61 // enabling interrupts means unblocking those signals.
62 //
63 // However there are a few problems. One of these is performance: some
64 // bits of the system such as buffered tracing make very extensive use
65 // of enabling and disabling interrupts, so making a sigprocmask
66 // system call each time adds a lot of overhead. More seriously, there
67 // is a subtle discrepancy between POSIX signal handling and hardware
68 // interrupts. Signal handlers are expected to return, and then the
69 // system automatically passes control back to the foreground thread.
70 // In the process, the sigprocmask is manipulated before invoking the
71 // signal handler and restored afterwards. Interrupt handlers are
72 // different: it is quite likely that an interrupt results in another
73 // eCos thread being activated, so the signal handler does not
74 // actually return until the interrupted thread gets another chance to
75 // run. 
76 //
77 // The second problem can be addressed by making the sigprocmask part
78 // of the thread state, saving and restoring it as part of a context
79 // switch (c.f. siglongjmp()). This matches quite nicely onto typical
80 // real hardware, where there might be a flag inside some control
81 // register that controls whether or not interrupts are enabled.
82 // However this adds more system calls to context switch overhead.
83 //
84 // The alternative approach is to implement interrupt enabling and
85 // disabling in software. The sigprocmask is manipulated only once,
86 // during initialization, such that certain signals are allowed
87 // through and others are blocked. When a signal is raised the signal
88 // handler will always be invoked, but it will decide in software
89 // whether or not the signal should be processed immediately. This
90 // alternative approach does not correspond particularly well with
91 // real hardware: effectively the VSR is always allowed to run.
92 // However for typical applications this will not really matter, and
93 // the performance gains outweigh the discrepancy.
94 //
95 // Nested interrupts and interrupt priorities can be implemented in
96 // software, specifically by manipulating the current mask of blocked
97 // interrupts. This is not currently implemented.
98 //
99 // At first glance it might seem that an interrupt stack could be
100 // implemented trivially using sigaltstack. This does not quite work:
101 // signal handlers do not always return immediately, so the system
102 // does not get a chance to clean up the signal handling stack. A
103 // separate interrupt stack is possible but would have to be
104 // implemented here, in software, e.g. by having the signal handler
105 // invoke the VSR on that stack. Unfortunately the system may have
106 // pushed quite a lot of state on to the current stack already when
107 // raising the signal, so things could get messy.
108
109 // ----------------------------------------------------------------------------
110 #include <pkgconf/hal.h>
111 #include <pkgconf/hal_synth.h>
112
113 // There are various dependencies on the kernel, e.g. how exceptions
114 // should be handled.
115 #include <pkgconf/system.h>
116 #ifdef CYGPKG_KERNEL
117 # include <pkgconf/kernel.h>
118 #endif
119
120 #include <cyg/infra/cyg_type.h>         // base types
121 #include <cyg/infra/diag.h>
122 #include <cyg/hal/hal_arch.h>
123 #include <cyg/hal/hal_intr.h>
124 #include <cyg/hal/hal_io.h>
125 #include <cyg/infra/cyg_ass.h>          // Assertions are safe in the synthetic target
126
127 #include "synth_protocol.h"
128
129 // ----------------------------------------------------------------------------
130 // Statics.
131
132 // The bogomips rating, used by HAL_DELAY_US()
133 int hal_bogomips;
134
135 // Are interrupts currently enabled?
136 volatile cyg_bool_t hal_interrupts_enabled = false;
137
138 // These flags are updated by the signal handler when a signal comes in
139 // and interrupts are disabled.
140 static volatile cyg_bool_t  synth_sigio_pending         = false;
141 static volatile cyg_bool_t  synth_sigalrm_pending       = false;
142
143 // The current VSR, to be invoked by the signal handler. This allows
144 // application code to install an alternative VSR, without that VSR
145 // having to check for interrupts being disabled and updating the
146 // pending flags. Effectively, the VSR is only invoked when interrupts
147 // are enabled.
148 static void (*synth_VSR)(void)                          = (void (*)(void)) 0;
149
150 // The current ISR status and mask registers, or rather software
151 // emulations thereof. These are not static since application-specific
152 // VSRs may want to examine/manipulate these. They are also not
153 // exported in any header file, forcing people writing such VSRs to
154 // know what they are doing.
155 volatile cyg_uint32 synth_pending_isrs      = 0;
156 volatile cyg_uint32 synth_masked_isrs       = 0xFFFFFFFF;
157
158 // The vector of interrupt handlers.
159 typedef struct synth_isr_handler {
160     cyg_ISR_t*          isr;
161     CYG_ADDRWORD        data;
162     CYG_ADDRESS         obj;
163     cyg_priority_t      pri;
164 } synth_isr_handler;
165 static synth_isr_handler synth_isr_handlers[CYGNUM_HAL_ISR_COUNT];
166
167 static void  synth_alrm_sighandler(int);
168 static void  synth_io_sighandler(int);
169
170 // ----------------------------------------------------------------------------
171 // Basic ISR and VSR handling.
172
173 // The default ISR handler. The system should never receive an interrupt it
174 // does not know how to handle.
175 static cyg_uint32
176 synth_default_isr(cyg_vector_t vector, cyg_addrword_t data)
177 {
178     CYG_UNUSED_PARAM(cyg_vector_t, vector);
179     CYG_UNUSED_PARAM(cyg_addrword_t, data);
180     CYG_FAIL("Default isr handler should never get invoked");
181     return CYG_ISR_HANDLED;
182 }
183
184 // The VSR is invoked
185 //  1) directly by a SIGALRM or SIGIO signal handler, if interrupts
186 //     were enabled.
187 //  2) indirectly by hal_enable_interrupts(), if a signal happened
188 //     while interrupts were disabled. hal_enable_interrupts()
189 //     will have re-invoked the signal handler.
190 //
191 // On entry interrupts are disabled, and there should be one or more
192 // pending ISRs which are not masked off.
193 //
194 // The implementation is as per the HAL specification, where
195 // applicable.
196
197 static void
198 synth_default_vsr(void)
199 {
200     int         isr_vector;
201     cyg_uint32  isr_result;
202
203     CYG_ASSERT(!hal_interrupts_enabled, "VSRs should only be invoked when interrupts are disabled");
204     CYG_ASSERT(0 != (synth_pending_isrs & ~synth_masked_isrs), "VSRs should only be invoked when an interrupt is pending");
205
206     // No need to save the cpu state. Either we are in a signal
207     // handler and the system has done that for us, or we are called
208     // synchronously via enable_interrupts.
209
210     // Increment the kernel scheduler lock, if the kernel is present.
211     // This prevents context switching while interrupt handling is in
212     // progress.
213 #ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT
214     cyg_scheduler_lock();
215 #endif
216
217     // Do not switch to an interrupt stack - functionality is not
218     // implemented
219
220     // Do not allow nested interrupts - functionality is not
221     // implemented.
222
223     // Decode the actual external interrupt being delivered. This is
224     // determined from the pending and masked variables. Only one isr
225     // source can be handled here, since interrupt_end must be invoked
226     // with details of that interrupt. Multiple pending interrupts
227     // will be handled by a recursive call 
228     HAL_LSBIT_INDEX(isr_vector, (synth_pending_isrs & ~synth_masked_isrs));
229     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= isr_vector) && (isr_vector <= CYGNUM_HAL_ISR_MAX), "ISR vector must be valid");
230
231     isr_result = (*synth_isr_handlers[isr_vector].isr)(isr_vector, synth_isr_handlers[isr_vector].data);
232
233     // Do not switch back from the interrupt stack, there isn't one.
234     
235     // Interrupts were not enabled before, so they must be enabled
236     // now. This may result in a recursive invocation if other IRQs
237     // are still pending. The ISR should have either acknowledged or
238     // masked the current interrupt source, to prevent a recursive
239     // call for the current interrupt.
240     hal_enable_interrupts();
241
242     // Now call interrupt_end() with the result of the isr and the
243     // ISR's object This may return straightaway, or it may result in
244     // a context switch to another thread. In the latter case, when
245     // the current thread is reactivated we end up back here. The
246     // third argument should be a pointer to the saved state, but that
247     // is only relevant for thread-aware debugging which is not yet
248     // supported by the synthetic target.
249     {
250         extern void interrupt_end(cyg_uint32, CYG_ADDRESS, HAL_SavedRegisters*);
251         interrupt_end(isr_result, synth_isr_handlers[isr_vector].obj, (HAL_SavedRegisters*) 0);
252     }
253
254     // Restore machine state and return to the interrupted thread.
255     // That requires no effort here.
256 }
257
258 // Enabling interrupts. If a SIGALRM or SIGIO arrived at an inconvenient
259 // time, e.g. when already interacting with the auxiliary, then these
260 // will have been left pending and must be serviced now. Next, enabling
261 // interrupts means checking the interrupt pending and mask registers
262 // and seeing if the VSR should be invoked.
263 void
264 hal_enable_interrupts(void)
265 {
266     hal_interrupts_enabled = true;
267     if (synth_sigalrm_pending) {
268         synth_sigalrm_pending = false;
269         synth_alrm_sighandler(CYG_HAL_SYS_SIGALRM);
270     }
271     if (synth_sigio_pending) {
272         synth_sigio_pending = false;
273         synth_io_sighandler(CYG_HAL_SYS_SIGIO);
274     }
275
276     // The interrupt mask "register" may have been modified while
277     // interrupts were disabled. If there are pending interrupts,
278     // invoke the VSR. The VSR must be invoked with interrupts
279     // disabled, and will return with interrupts enabled.
280     // An alternative implementation that might be more accurate
281     // is to raise a signal, e.g. SIGUSR1. That way all interrupts
282     // come in via the system's signal handling mechanism, and
283     // it might be possible to do something useful with saved contexts
284     // etc., facilitating thread-aware debugging.
285     if (0 != (synth_pending_isrs & ~synth_masked_isrs)) {
286         hal_interrupts_enabled = false;
287         (*synth_VSR)();
288         CYG_ASSERT( hal_interrupts_enabled, "Interrupts should still be enabled on return from the VSR");
289     }
290 }
291
292 // ----------------------------------------------------------------------------
293 // Other interrupt-related routines. Mostly these just involve
294 // updating some of the statics, but they may be called while
295 // interrupts are still enabled so care has to be taken.
296
297 cyg_bool_t
298 hal_interrupt_in_use(cyg_vector_t vec)
299 {
300     CYG_ASSERT( (CYGNUM_HAL_ISR_MIN <= vec) && (vec <= CYGNUM_HAL_ISR_MAX), "Can only attach to valid ISR vectors");
301     return synth_default_isr != synth_isr_handlers[vec].isr;
302 }
303
304 void
305 hal_interrupt_attach(cyg_vector_t vec, cyg_ISR_t* isr, CYG_ADDRWORD data, CYG_ADDRESS obj)
306 {
307     CYG_ASSERT( (CYGNUM_HAL_ISR_MIN <= vec) && (vec <= CYGNUM_HAL_ISR_MAX), "Can only attach to valid ISR vectors");
308     CYG_CHECK_FUNC_PTR( isr, "A valid ISR must be supplied");
309     // The object cannot be validated, it may be NULL if chained
310     // interrupts are enabled.
311     CYG_ASSERT( synth_isr_handlers[vec].isr == &synth_default_isr, "Only one ISR can be attached to a vector at the HAL level");
312     CYG_ASSERT( (false == hal_interrupts_enabled) || (0 != (synth_masked_isrs & (0x01 << vec))), "ISRs should only be attached when it is safe");
313
314     // The priority will have been installed shortly before this call.
315     synth_isr_handlers[vec].isr     = isr;
316     synth_isr_handlers[vec].data    = data;
317     synth_isr_handlers[vec].obj     = obj;
318 }
319
320 void
321 hal_interrupt_detach(cyg_vector_t vec, cyg_ISR_t* isr)
322 {
323     CYG_ASSERT( (CYGNUM_HAL_ISR_MIN <= vec) && (vec <= CYGNUM_HAL_ISR_MAX), "Can only detach from valid ISR vectors");
324     CYG_CHECK_FUNC_PTR( isr, "A valid ISR must be supplied");
325     CYG_ASSERT( isr != &synth_default_isr, "An ISR must be attached before it can be detached");
326     CYG_ASSERT( (false == hal_interrupts_enabled) || (0 != (synth_masked_isrs & (0x01 << vec))), "ISRs should only be detached when it is safe");
327
328     // The Cyg_Interrupt destructor does an unconditional detach, even if the
329     // isr is not currently attached.
330     if (isr == synth_isr_handlers[vec].isr) {
331         synth_isr_handlers[vec].isr     = &synth_default_isr;
332         synth_isr_handlers[vec].data    = (CYG_ADDRWORD) 0;
333         synth_isr_handlers[vec].obj     = (CYG_ADDRESS) 0;
334     }
335     
336     // The priority is not updated here. This should be ok, if another
337     // isr is attached then the appropriate priority will be installed
338     // first.
339 }
340
341 void (*hal_vsr_get(cyg_vector_t vec))(void)
342 {
343     CYG_ASSERT( (CYGNUM_HAL_VSR_MIN <= vec) && (vec <= CYGNUM_HAL_VSR_MAX), "Can only get valid VSR vectors");
344     return synth_VSR;
345 }
346
347 void
348 hal_vsr_set(cyg_vector_t vec, void (*new_vsr)(void), void (**old_vsrp)(void))
349 {
350     cyg_bool_t  old;
351
352     CYG_ASSERT( (CYGNUM_HAL_VSR_MIN <= vec) && (vec <= CYGNUM_HAL_VSR_MAX), "Can only get valid VSR vectors");
353     CYG_CHECK_FUNC_PTR( new_vsr, "A valid VSR must be supplied");
354
355     // There is a theoretical possibility of two hal_vsr_set calls at
356     // the same time. The old and new VSRs must be kept in synch.
357     HAL_DISABLE_INTERRUPTS(old);
358     if (0 != old_vsrp) {
359         *old_vsrp = synth_VSR;
360     }
361     synth_VSR = new_vsr;
362     HAL_RESTORE_INTERRUPTS(old);
363 }
364
365 void
366 hal_interrupt_mask(cyg_vector_t which)
367 {
368     CYG_PRECONDITION( !hal_interrupts_enabled, "Interrupts should be disabled on entry to hal_interrupt_mask");
369     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= which) && (which <= CYGNUM_HAL_ISR_MAX), "A valid ISR vector must be supplied");
370     synth_masked_isrs |= (0x01 << which);
371 }
372
373 void
374 hal_interrupt_unmask(cyg_vector_t which)
375 {
376     CYG_PRECONDITION( !hal_interrupts_enabled, "Interrupts should be disabled on entry to hal_interrupt_unmask");
377     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= which) && (which <= CYGNUM_HAL_ISR_MAX), "A valid ISR vector must be supplied");
378     synth_masked_isrs &= ~(0x01 << which);
379 }
380
381 void
382 hal_interrupt_acknowledge(cyg_vector_t which)
383 {
384     cyg_bool_t old;
385     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= which) && (which <= CYGNUM_HAL_ISR_MAX), "A valid ISR vector must be supplied");
386
387     // Acknowledging an interrupt means clearing the bit in the
388     // interrupt pending "register".
389     // NOTE: does the auxiliary need to keep track of this? Probably
390     // not, the auxiliary can just raise SIGIO whenever a device wants
391     // attention. There may be a trade off here between additional
392     // communication and unnecessary SIGIOs.
393     HAL_DISABLE_INTERRUPTS(old);
394     synth_pending_isrs &= ~(0x01 << which);
395     HAL_RESTORE_INTERRUPTS(old);
396 }
397
398 void
399 hal_interrupt_configure(cyg_vector_t which, cyg_bool_t level, cyg_bool_t up)
400 {
401     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= which) && (which <= CYGNUM_HAL_ISR_MAX), "A valid ISR vector must be supplied");
402     // The synthetic target does not currently distinguish between
403     // level and edge interrupts. Possibly this information will have
404     // to be passed on to the auxiliary in future.
405     CYG_UNUSED_PARAM(cyg_vector_t, which);
406     CYG_UNUSED_PARAM(cyg_bool_t, level);
407     CYG_UNUSED_PARAM(cyg_bool_t, up);
408 }
409
410 void
411 hal_interrupt_set_level(cyg_vector_t which, cyg_priority_t level)
412 {
413     CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= which) && (which <= CYGNUM_HAL_ISR_MAX), "A valid ISR vector must be supplied");
414     // The legal values for priorities are not defined at this time.
415     // Manipulating the interrupt priority level currently has no
416     // effect. The information is stored anyway, for future use.
417     synth_isr_handlers[which].pri = level;
418 }
419
420 // ----------------------------------------------------------------------------
421 // Exception handling. Typically this involves calling into the kernel,
422 // translating the POSIX signal number into a HAL exception number. In
423 // practice these signals will generally be caught in the debugger and
424 // will not have to be handled by eCos itself.
425
426 static void
427 synth_exception_sighandler(int sig)
428 {
429     CYG_WORD    ecos_exception_number = 0;
430     cyg_bool_t  old;
431
432     // There is no need to save state, that will have been done by the
433     // system as part of the signal delivery process.
434     
435     // Disable interrupts. Performing e.g. an interaction with the
436     // auxiliary after a SIGSEGV is dubious.
437     HAL_DISABLE_INTERRUPTS(old);
438
439     // Now decode the signal and turn it into an eCos exception.
440     switch(sig) {
441       case CYG_HAL_SYS_SIGILL:
442         ecos_exception_number = CYGNUM_HAL_EXCEPTION_ILLEGAL_INSTRUCTION;
443         break;
444       case CYG_HAL_SYS_SIGBUS:
445       case CYG_HAL_SYS_SIGSEGV:
446         ecos_exception_number = CYGNUM_HAL_EXCEPTION_DATA_ACCESS;
447         break;
448       case CYG_HAL_SYS_SIGFPE:
449         ecos_exception_number = CYGNUM_HAL_EXCEPTION_FPU;
450         break;
451       default:
452         CYG_FAIL("Unknown signal");
453         break;
454     }
455
456 #ifdef CYGPKG_KERNEL_EXCEPTIONS
457     // Deliver the signal, usually to the kernel, possibly to the
458     // common HAL. The second argument should be the current
459     // savestate, but that is not readily accessible.
460     cyg_hal_deliver_exception(ecos_exception_number, (CYG_ADDRWORD) 0);
461
462     // It is now necessary to restore the machine state, including
463     // interrupts. In theory higher level code may have manipulated
464     // the machine state to prevent any recurrence of the exception.
465     // In practice the machine state is not readily accessible.
466     HAL_RESTORE_INTERRUPTS(old);
467 #else
468     CYG_FAIL("Exception!!!");
469     for (;;);
470 #endif    
471 }
472
473 // ----------------------------------------------------------------------------
474 // The clock support. This can be implemented using the setitimer()
475 // and getitimer() calls. The kernel will install a suitable interrupt
476 // handler for CYGNUM_HAL_INTERRUPT_RTC, but it depends on the HAL
477 // for low-level manipulation of the clock hardware.
478 //
479 // There is a problem with HAL_CLOCK_READ(). The obvious
480 // implementation would use getitimer(), but that has the wrong
481 // behaviour: it is intended for fairly coarse intervals and works in
482 // terms of system clock ticks, as opposed to a fine-grained
483 // implementation that actually examines the system clock. Instead use
484 // gettimeofday().
485
486 static struct cyg_hal_sys_timeval synth_clock   = { 0, 0 };
487
488 void
489 hal_clock_initialize(cyg_uint32 period)
490 {
491     struct cyg_hal_sys_itimerval    timer;
492
493     // Needed for hal_clock_read(), if HAL_CLOCK_READ() is used before
494     // the first clock interrupt.
495     cyg_hal_sys_gettimeofday(&synth_clock, (struct cyg_hal_sys_timezone*) 0);
496     
497     // The synthetic target clock resolution is in microseconds. A typical
498     // value for the period will be 10000, corresponding to one timer
499     // interrupt every 10ms. Set up a timer to interrupt in period us,
500     // and again every period us after that.
501     CYG_ASSERT( period < 1000000, "Clock interrupts should happen at least once per second");
502     timer.hal_it_interval.hal_tv_sec    = 0;
503     timer.hal_it_interval.hal_tv_usec   = period;
504     timer.hal_it_value.hal_tv_sec       = 0;
505     timer.hal_it_value.hal_tv_usec      = period;
506     
507     if (0 != cyg_hal_sys_setitimer(CYG_HAL_SYS_ITIMER_REAL, &timer, (struct cyg_hal_sys_itimerval*) 0)) {
508         CYG_FAIL("Failed to initialize the clock itimer");
509     }
510 }
511
512 static void
513 synth_alrm_sighandler(int sig)
514 {
515     CYG_PRECONDITION((CYG_HAL_SYS_SIGALRM == sig), "Only SIGALRM should be handled here");
516     
517     if (!hal_interrupts_enabled) {
518         synth_sigalrm_pending = true;
519         return;
520     }
521
522     // Interrupts were enabled, but must be blocked before any further processing.
523     hal_interrupts_enabled = false;
524
525     // Update the cached value of the clock for hal_clock_read()
526     cyg_hal_sys_gettimeofday(&synth_clock, (struct cyg_hal_sys_timezone*) 0);
527
528     // Update the interrupt status "register" to match pending interrupts
529     // A timer signal means that IRQ 0 needs attention.
530     synth_pending_isrs |= 0x01;
531
532     // If any of the pending interrupts are not masked, invoke the
533     // VSR. That will reenable interrupts.
534     if (0 != (synth_pending_isrs & ~synth_masked_isrs)) {
535         (*synth_VSR)();
536     } else {
537         hal_interrupts_enabled = true;
538     }
539
540     // The VSR will have invoked interrupt_end() with interrupts
541     // enabled, and they should still be enabled.
542     CYG_ASSERT( hal_interrupts_enabled, "Interrupts should still be enabled on return from the VSR");
543 }
544
545 // Implementing hal_clock_read(). gettimeofday() in conjunction with
546 // synth_clock gives the time since the last clock tick in
547 // microseconds, the correct unit for the synthetic target.
548 cyg_uint32
549 hal_clock_read(void)
550 {
551     int elapsed;
552     struct cyg_hal_sys_timeval  now;
553     cyg_hal_sys_gettimeofday(&now, (struct cyg_hal_sys_timezone*) 0);
554
555     elapsed = (1000000 * (now.hal_tv_sec - synth_clock.hal_tv_sec)) + (now.hal_tv_usec - synth_clock.hal_tv_usec);
556     return elapsed;
557 }
558
559 // ----------------------------------------------------------------------------
560 // The signal handler for SIGIO. This can also be invoked by
561 // hal_enable_interrupts() to catch up with any signals that arrived
562 // while interrupts were disabled. SIGIO is raised by the auxiliary
563 // when it requires attention, i.e. when one or more of the devices
564 // want to raise an interrupt. Finding out exactly which interrupt(s)
565 // are currently pending in the auxiliary requires communication with
566 // the auxiliary.
567 //
568 // If interrupts are currently disabled then the signal cannot be
569 // handled immediately. In particular SIGIO cannot be handled because
570 // there may already be ongoing communication with the auxiliary.
571 // Instead some volatile flags are used to keep track of which signals
572 // were raised while interrupts were disabled. 
573 //
574 // It might be better to perform the interaction with the auxiliary
575 // as soon as possible, i.e. either in the SIGIO handler or when the
576 // current communication completes. That way the mask of pending
577 // interrupts would remain up to date even when interrupts are
578 // disabled, thus allowing applications to run in polled mode.
579
580 // A little utility called when the auxiliary has been asked to exit,
581 // implicitly affecting this application as well. The sole purpose
582 // of this function is to put a suitably-named function on the stack
583 // to make it more obvious from inside gdb what is happening.
584 static void
585 synth_io_handle_shutdown_request_from_auxiliary(void)
586 {
587     cyg_hal_sys_exit(0);
588 }
589
590 static void
591 synth_io_sighandler(int sig)
592 {
593     CYG_PRECONDITION((CYG_HAL_SYS_SIGIO == sig), "Only SIGIO should be handled here");
594     
595     if (!hal_interrupts_enabled) {
596         synth_sigio_pending = true;
597         return;
598     }
599     
600     // Interrupts were enabled, but must be blocked before any further processing.
601     hal_interrupts_enabled = false;
602
603     // Update the interrupt status "register" to match pending interrupts
604     // Contact the auxiliary to find out what interrupts are currently pending there.
605     // If there is no auxiliary at present, e.g. because it has just terminated
606     // and things are generally somewhat messy, ignore it.
607     //
608     // This code also deals with the case where the user has requested program
609     // termination. It would be wrong for the auxiliary to just exit, since the
610     // application could not distinguish that case from a crash. Instead the
611     // auxiliary can optionally return an additional byte of data, and if that
612     // byte actually gets sent then that indicates pending termination.
613     if (synth_auxiliary_running) {
614         int             result;
615         int             actual_len;
616         unsigned char   dummy[1];
617         synth_auxiliary_xchgmsg(SYNTH_DEV_AUXILIARY, SYNTH_AUXREQ_GET_IRQPENDING, 0, 0,
618                                 (const unsigned char*) 0, 0,        // The auxiliary does not need any additional data
619                                 &result, dummy, &actual_len, 1);
620         synth_pending_isrs |= result;
621         if (actual_len) {
622             // The auxiliary has been asked to terminate by the user. This
623             // request has now been passed on to the eCos application.
624             synth_io_handle_shutdown_request_from_auxiliary();
625         }
626     }
627
628     // If any of the pending interrupts are not masked, invoke the VSR
629     if (0 != (synth_pending_isrs & ~synth_masked_isrs)) {
630         (*synth_VSR)();
631     } else {
632         hal_interrupts_enabled = true;
633     }
634
635     // The VSR will have invoked interrupt_end() with interrupts
636     // enabled, and they should still be enabled.
637     CYG_ASSERT( hal_interrupts_enabled, "Interrupts should still be enabled on return from the VSR");
638 }
639
640 // ----------------------------------------------------------------------------
641 // Here we define an action to do in the idle thread. For the
642 // synthetic target it makes no sense to spin eating processor time
643 // that other processes could make use of. Instead we call select. The
644 // itimer will still go off and kick the scheduler back into life,
645 // giving us an escape path from the select. There is one problem: in
646 // some configurations, e.g. when preemption is disabled, the idle
647 // thread must yield continuously rather than blocking.
648 void
649 hal_idle_thread_action(cyg_uint32 loop_count)
650 {
651 #ifndef CYGIMP_HAL_IDLE_THREAD_SPIN
652     cyg_hal_sys__newselect(0,
653                            (struct cyg_hal_sys_fd_set*) 0,
654                            (struct cyg_hal_sys_fd_set*) 0,
655                            (struct cyg_hal_sys_fd_set*) 0,
656                            (struct cyg_hal_sys_timeval*) 0);
657 #endif
658     CYG_UNUSED_PARAM(cyg_uint32, loop_count);
659 }
660
661 // ----------------------------------------------------------------------------
662 // The I/O auxiliary.
663 //
664 // I/O happens via an auxiliary process. During startup this code attempts
665 // to locate and execute a program ecosynth which should be installed in
666 // ../libexec/ecosynth relative to some directory on the search path.
667 // Subsequent I/O operations involve interacting with this auxiliary.
668
669 #define MAKESTRING1(a) #a
670 #define MAKESTRING2(a) MAKESTRING1(a)
671 #define AUXILIARY       "../libexec/ecos/hal/synth/arch/" MAKESTRING2(CYGPKG_HAL_SYNTH) "/ecosynth"
672
673 // Is the auxiliary up and running?
674 cyg_bool    synth_auxiliary_running   = false;
675
676 // The pipes to and from the auxiliary.
677 static int  to_aux      = -1;
678 static int  from_aux    = -1;
679
680 // Attempt to start up the auxiliary. Note that this happens early on
681 // during system initialization so it is "known" that the world is
682 // still simple, e.g. that no other files have been opened.
683 static void
684 synth_start_auxiliary(void)
685 {
686 #define BUFSIZE 256
687     char        filename[BUFSIZE];
688     const char* path = 0;
689     int         i, j;
690     cyg_bool    found   = false;
691     int         to_aux_pipe[2];
692     int         from_aux_pipe[2];
693     int         child;
694     int         aux_version;
695 #if 1
696     // Check for a command line argument -io. Only run the auxiliary if this
697     // argument is provided, i.e. default to traditional behaviour.
698     for (i = 1; i < cyg_hal_sys_argc; i++) {
699         const char* tmp = cyg_hal_sys_argv[i];
700         if ('-' == *tmp) {
701             // Arguments beyond -- are reserved for use by the application,
702             // and should not be interpreted by the HAL itself or by ecosynth.
703             if (('-' == tmp[1]) && ('\0' == tmp[2])) {
704                 break;
705             }
706             tmp++;
707             if ('-' == *tmp) {
708                 // Do not distinguish between -io and --io
709                 tmp++;
710             }
711             if (('i' == tmp[0]) && ('o' == tmp[1]) && ('\0' == tmp[2])) {
712                 found = 1;
713                 break;
714             }
715         }
716     }
717     if (!found) {
718         return;
719     }
720 #else
721     // Check for a command line argument -ni or -nio. Always run the
722     // auxiliary unless this argument is given, i.e. default to full
723     // I/O support.
724     for (i = 1; i < cyg_hal_sys_argc; i++) {
725         const char* tmp = cyg_hal_sys_argv[i];
726         if ('-' == *tmp) {
727             if (('-' == tmp[1]) && ('\0' == tmp[2])) {
728                 break;
729             }
730             tmp++;
731             if ('-' == *tmp) {
732                 tmp++;
733             }
734             if ('n' == *tmp) {
735                 tmp++;
736                 if ('i' == *tmp) {
737                     tmp++;
738                     if ('\0' == *tmp) {
739                         found = 1;  // -ni or --ni
740                         break;
741                     }
742                     if (('o' == *tmp) && ('\0' == tmp[1])) {
743                         found = 1;  // -nio or --nio
744                         break;
745                     }
746                 }
747             }
748         }
749     }
750     if (found) {
751         return;
752     }
753 #endif
754     
755     // The auxiliary must be found relative to the current search path,
756     // so look for a PATH= environment variable.
757     for (i = 0; (0 == path) && (0 != cyg_hal_sys_environ[i]); i++) {
758         const char *var = cyg_hal_sys_environ[i];
759         if (('P' == var[0]) && ('A' == var[1]) && ('T' == var[2]) && ('H' == var[3]) && ('=' == var[4])) {
760             path = var + 5;
761         }
762     }
763     if (0 == path) {
764         // Very unlikely, but just in case.
765         path = ".:/bin:/usr/bin";
766     }
767
768     found = 0;
769     while (!found && ('\0' != *path)) {         // for every entry in the path
770         char *tmp = AUXILIARY;
771         
772         j = 0;
773
774         // As a special case, an empty string in the path corresponds to the
775         // current directory.
776         if (':' == *path) {
777             filename[j++] = '.';
778             path++;
779         } else {
780             while ((j < BUFSIZE) && ('\0' != *path) && (':' != *path)) {
781                 filename[j++] = *path++;
782             }
783             // If not at the end of the search path, move on to the next entry.
784             if ('\0' != *path) {
785                 while ((':' != *path) && ('\0' != *path)) {
786                     path++;
787                 }
788                 if (':' == *path) {
789                     path++;
790                 }
791             }
792         }
793         // Now append a directory separator, and then the name of the executable.
794         if (j < BUFSIZE) {
795             filename[j++] = '/';
796         }
797         while ((j < BUFSIZE) && ('\0' != *tmp)) {
798             filename[j++] = *tmp++;
799         }
800         // If there has been a buffer overflow, skip this entry.
801         if (j == BUFSIZE) {
802             filename[BUFSIZE-1] = '\0';
803             diag_printf("Warning: buffer limit reached while searching PATH for the I/O auxiliary.\n");
804             diag_printf("       : skipping current entry.\n");
805         } else {
806             // filename now contains a possible match for the auxiliary.
807             filename[j++]    = '\0';
808             if (0 == cyg_hal_sys_access(filename, CYG_HAL_SYS_X_OK)) {
809                 found = true;
810             }
811         }
812     }
813 #undef BUFSIZE
814
815     if (!found) {
816         diag_printf("Error: unable to find the I/O auxiliary program on the current search PATH\n");
817         diag_printf("     : please install the appropriate host-side tools.\n");
818         cyg_hal_sys_exit(1);
819     }
820
821     // An apparently valid executable exists (or at the very least it existed...),
822     // so create the pipes that will be used for communication.
823     if ((0 != cyg_hal_sys_pipe(to_aux_pipe)) ||
824         (0 != cyg_hal_sys_pipe(from_aux_pipe))) {
825         diag_printf("Error: unable to set up pipes for communicating with the I/O auxiliary.\n");
826         cyg_hal_sys_exit(1);
827     }
828     
829     // Time to fork().
830     child = cyg_hal_sys_fork();
831     if (child < 0) {
832         diag_printf("Error: failed to fork() process for the I/O auxiliary.\n");
833         cyg_hal_sys_exit(1);
834     } else if (child == 0) {
835         cyg_bool    found_dotdot;
836         // There should not be any problems rearranging the file descriptors as desired...
837         cyg_bool    unexpected_error = 0;
838         
839         // In the child process. Close unwanted file descriptors, then some dup2'ing,
840         // and execve() the I/O auxiliary. The auxiliary will inherit stdin,
841         // stdout and stderr from the eCos application, so that functions like
842         // printf() work as expected. In addition fd 3 will be the pipe from
843         // the eCos application and fd 4 the pipe to the application. It is possible
844         // that the eCos application was run from some process other than a shell
845         // and hence that file descriptors 3 and 4 are already in use, but that is not
846         // supported. One possible workaround would be to close all file descriptors
847         // >= 3, another would be to munge the argument vector passing the file
848         // descriptors actually being used.
849         unexpected_error |= (0 != cyg_hal_sys_close(to_aux_pipe[1]));
850         unexpected_error |= (0 != cyg_hal_sys_close(from_aux_pipe[0]));
851         
852         if (3 != to_aux_pipe[0]) {
853             if (3 == from_aux_pipe[1]) {
854                 // Because to_aux_pipe[] was set up first it should have received file descriptors 3 and 4.
855                 diag_printf("Internal error: file descriptors have been allocated in an unusual order.\n");
856                 cyg_hal_sys_exit(1);
857             } else {
858                 unexpected_error |= (3 != cyg_hal_sys_dup2(to_aux_pipe[0], 3));
859                 unexpected_error |= (0 != cyg_hal_sys_close(to_aux_pipe[0]));
860             }
861         }
862         if (4 != from_aux_pipe[1]) {
863             unexpected_error |= (4 != cyg_hal_sys_dup2(from_aux_pipe[1], 4));
864             unexpected_error |= (0 != cyg_hal_sys_close(from_aux_pipe[1]));
865         }
866         if (unexpected_error) {
867             diag_printf("Error: internal error in auxiliary process, failed to manipulate pipes.\n");
868             cyg_hal_sys_exit(1);
869         }
870         // The arguments passed to the auxiliary are mostly those for
871         // the synthetic target application, except for argv[0] which
872         // is replaced with the auxiliary's pathname. The latter
873         // currently holds at least one ../, and cleaning this up is
874         // useful.
875         //
876         // If the argument vector contains -- then that and subsequent
877         // arguments are not passed to the auxiliary. Instead such
878         // arguments are reserved for use by the application.
879         do {
880             int len;
881             for (len = 0; '\0' != filename[len]; len++)
882                 ;
883             found_dotdot = false;
884             for (i = 0; i < (len - 4); i++) {
885                 if (('/' == filename[i]) && ('.' == filename[i+1]) && ('.' == filename[i+2]) && ('/' == filename[i+3])) {
886                     j = i + 3;
887                     for ( --i; (i >= 0) && ('/' != filename[i]); i--) {
888                         CYG_EMPTY_STATEMENT;
889                     }
890                     if (i >= 0) {
891                         found_dotdot = true;
892                         do {
893                             i++; j++;
894                             filename[i] = filename[j];
895                         } while ('\0' != filename[i]);
896                     }
897                 }
898             }
899         } while(found_dotdot);
900         
901         cyg_hal_sys_argv[0] = filename;
902
903         for (i = 1; i < cyg_hal_sys_argc; i++) {
904             const char* tmp = cyg_hal_sys_argv[i];
905             if (('-' == tmp[0]) && ('-' == tmp[1]) && ('\0' == tmp[2])) {
906                 cyg_hal_sys_argv[i] = (const char*) 0;
907                 break;
908             }
909         }
910         cyg_hal_sys_execve(filename, cyg_hal_sys_argv, cyg_hal_sys_environ);
911
912         // An execute error has occurred. Report this here, then exit. The
913         // parent will detect a close on the pipe without having received
914         // any data, and it will assume that a suitable diagnostic will have
915         // been displayed already.
916         diag_printf("Error: failed to execute the I/O auxiliary.\n");
917         cyg_hal_sys_exit(1);
918     } else {
919         int     rc;
920         char    buf[1];
921         
922         // Still executing the eCos application.
923         // Do some cleaning-up.
924         to_aux      = to_aux_pipe[1];
925         from_aux    = from_aux_pipe[0];
926         if ((0 != cyg_hal_sys_close(to_aux_pipe[0]))  ||
927             (0 != cyg_hal_sys_close(from_aux_pipe[1]))) {
928             diag_printf("Error: internal error in main process, failed to manipulate pipes.\n");
929             cyg_hal_sys_exit(1);
930         }
931
932         // It is now a good idea to block until the auxiliary is
933         // ready, i.e. give it a chance to read in its configuration
934         // files, load appropriate scripts, pop up windows, ... This
935         // may take a couple of seconds or so. Once the auxiliary is
936         // ready it will write a single byte down the pipe. This is
937         // the only time that the auxiliary will write other than when
938         // responding to a request.
939         do {
940             rc = cyg_hal_sys_read(from_aux, buf, 1);
941         } while (-CYG_HAL_SYS_EINTR == rc);
942
943         if (1 != rc) {
944             // The auxiliary has not started up successfully, so exit
945             // immediately. It should have generated appropriate
946             // diagnostics.
947             cyg_hal_sys_exit(1);
948         }
949     }
950
951     // At this point the auxiliary is up and running. It should not
952     // generate any interrupts just yet since none of the devices have
953     // been initialized. Remember that the auxiliary is now running,
954     // so that the initialization routines for those devices can
955     // figure out that they should interact with the auxiliary rather
956     // than attempt anything manually.
957     synth_auxiliary_running   = true;
958
959     // Make sure that the auxiliary is the right version.
960     synth_auxiliary_xchgmsg(SYNTH_DEV_AUXILIARY, SYNTH_AUXREQ_GET_VERSION, 0, 0,
961                             (const unsigned char*) 0, 0,
962                             &aux_version, (unsigned char*) 0, (int*) 0, 0);
963     if (SYNTH_AUXILIARY_PROTOCOL_VERSION != aux_version) {
964         synth_auxiliary_running = false;
965         diag_printf("Error: an incorrect version of the I/O auxiliary is installed\n"
966                     "    Expected version %d, actual version %d\n"
967                     "    Installed binary is %s\n",
968                     SYNTH_AUXILIARY_PROTOCOL_VERSION, aux_version, filename);
969         cyg_hal_sys_exit(1);
970     }
971 }
972
973 // Write a request to the I/O auxiliary, and optionally get back a
974 // reply. The dev_id is 0 for messages intended for the auxiliary
975 // itself, for example a device instantiation or a request for the
976 // current interrupt sate. Otherwise it identifies a specific device.
977 // The request code is specific to the device, and the two optional
978 // arguments are specific to the request.
979 void
980 synth_auxiliary_xchgmsg(int devid, int reqcode, int arg1, int arg2,
981                         const unsigned char* txdata, int txlen,
982                         int* result, 
983                         unsigned char* rxdata, int* actual_rxlen, int rxlen)
984 {
985     unsigned char   request[SYNTH_REQUEST_LENGTH];
986     unsigned char   reply[SYNTH_REPLY_LENGTH];
987     int             rc;
988     int             reply_rxlen;
989     cyg_bool_t      old_isrstate;
990
991     CYG_ASSERT(devid >= 0, "A valid device id should be supplied");
992     CYG_ASSERT((0 == txlen) || ((const unsigned char*)0 != txdata), "Data transmits require a transmit buffer");
993     CYG_ASSERT((0 == rxlen) || ((unsigned char*)0 != rxdata), "Data receives require a receive buffer");
994     CYG_ASSERT((0 == rxlen) || ((int*)0 != result), "If a reply is expected then space must be allocated");
995
996     // I/O interactions with the auxiliary must be atomic: a context switch in
997     // between sending the header and the actual data would be bad.
998     HAL_DISABLE_INTERRUPTS(old_isrstate);
999
1000     // The auxiliary should be running for the duration of this
1001     // exchange. However the auxiliary can disappear asynchronously,
1002     // so it is not possible for higher-level code to be sure that the
1003     // auxiliary is still running.
1004     //
1005     // If the auxiliary disappears during this call then usually this
1006     // will cause a SIGCHILD or SIGPIPE, both of which result in
1007     // termination. The exception is when the auxiliary decides to
1008     // shut down stdout for some reason without exiting - that has to
1009     // be detected in the read loop.
1010     if (synth_auxiliary_running) {
1011         request[SYNTH_REQUEST_DEVID_OFFSET + 0]     = (devid >>  0) & 0x0FF;
1012         request[SYNTH_REQUEST_DEVID_OFFSET + 1]     = (devid >>  8) & 0x0FF;
1013         request[SYNTH_REQUEST_DEVID_OFFSET + 2]     = (devid >> 16) & 0x0FF;
1014         request[SYNTH_REQUEST_DEVID_OFFSET + 3]     = (devid >> 24) & 0x0FF;
1015         request[SYNTH_REQUEST_REQUEST_OFFSET + 0]   = (reqcode >>  0) & 0x0FF;
1016         request[SYNTH_REQUEST_REQUEST_OFFSET + 1]   = (reqcode >>  8) & 0x0FF;
1017         request[SYNTH_REQUEST_REQUEST_OFFSET + 2]   = (reqcode >> 16) & 0x0FF;
1018         request[SYNTH_REQUEST_REQUEST_OFFSET + 3]   = (reqcode >> 24) & 0x0FF;
1019         request[SYNTH_REQUEST_ARG1_OFFSET + 0]      = (arg1 >>  0) & 0x0FF;
1020         request[SYNTH_REQUEST_ARG1_OFFSET + 1]      = (arg1 >>  8) & 0x0FF;
1021         request[SYNTH_REQUEST_ARG1_OFFSET + 2]      = (arg1 >> 16) & 0x0FF;
1022         request[SYNTH_REQUEST_ARG1_OFFSET + 3]      = (arg1 >> 24) & 0x0FF;
1023         request[SYNTH_REQUEST_ARG2_OFFSET + 0]      = (arg2 >>  0) & 0x0FF;
1024         request[SYNTH_REQUEST_ARG2_OFFSET + 1]      = (arg2 >>  8) & 0x0FF;
1025         request[SYNTH_REQUEST_ARG2_OFFSET + 2]      = (arg2 >> 16) & 0x0FF;
1026         request[SYNTH_REQUEST_ARG2_OFFSET + 3]      = (arg2 >> 24) & 0x0FF;
1027         request[SYNTH_REQUEST_TXLEN_OFFSET + 0]     = (txlen >>  0) & 0x0FF;
1028         request[SYNTH_REQUEST_TXLEN_OFFSET + 1]     = (txlen >>  8) & 0x0FF;
1029         request[SYNTH_REQUEST_TXLEN_OFFSET + 2]     = (txlen >> 16) & 0x0FF;
1030         request[SYNTH_REQUEST_TXLEN_OFFSET + 3]     = (txlen >> 24) & 0x0FF;
1031         request[SYNTH_REQUEST_RXLEN_OFFSET + 0]     = (rxlen >>  0) & 0x0FF;
1032         request[SYNTH_REQUEST_RXLEN_OFFSET + 1]     = (rxlen >>  8) & 0x0FF;
1033         request[SYNTH_REQUEST_RXLEN_OFFSET + 2]     = (rxlen >> 16) & 0x0FF;
1034         request[SYNTH_REQUEST_RXLEN_OFFSET + 3]     = ((rxlen >> 24) & 0x0FF) | (((int*)0 != result) ? 0x080 : 0);
1035
1036         // sizeof(synth_auxiliary_request) < PIPE_BUF (4096) so a single write should be atomic,
1037         // subject only to incoming clock or SIGIO or child-related signals.
1038         do {
1039             rc = cyg_hal_sys_write(to_aux, (const void*) &request, SYNTH_REQUEST_LENGTH);
1040         } while (-CYG_HAL_SYS_EINTR == rc);
1041
1042         // Is there any more data to be sent?
1043         if (0 < txlen) {
1044             int sent    = 0;
1045             CYG_LOOP_INVARIANT(synth_auxiliary_running, "The auxiliary cannot just disappear");
1046         
1047             while (sent < txlen) {
1048                 rc = cyg_hal_sys_write(to_aux, (const void*) &(txdata[sent]), txlen - sent);
1049                 if (-CYG_HAL_SYS_EINTR == rc) {
1050                     continue;
1051                 } else if (rc < 0) {
1052                     diag_printf("Internal error: unexpected result %d when sending buffer to auxiliary.\n", rc);
1053                     diag_printf("              : this application is exiting immediately.\n");
1054                     cyg_hal_sys_exit(1);
1055                 } else {
1056                     sent += rc;
1057                 }
1058             }
1059             CYG_ASSERT(sent <= txlen, "Amount of data sent should not exceed requested size");
1060         }
1061
1062         // The auxiliary can now process this entire request. Is a reply expected?
1063         if ((int*)0 != result) {
1064             // The basic reply is also only a small number of bytes, so should be atomic.
1065             do {
1066                 rc = cyg_hal_sys_read(from_aux, (void*) &reply, SYNTH_REPLY_LENGTH);
1067             } while (-CYG_HAL_SYS_EINTR == rc);
1068             if (rc <= 0) {
1069                 if (rc < 0) {
1070                     diag_printf("Internal error: unexpected result %d when receiving data from auxiliary.\n", rc);
1071                 } else {
1072                     diag_printf("Internal error: EOF detected on pipe from auxiliary.\n");
1073                 }
1074                 diag_printf("              : this application is exiting immediately.\n");
1075                 cyg_hal_sys_exit(1);
1076             }
1077             CYG_ASSERT(SYNTH_REPLY_LENGTH == rc, "The correct amount of data should have been read");
1078
1079             // Replies are packed in Tcl and assumed to be two 32-bit
1080             // little-endian integers.
1081             *result   = (reply[SYNTH_REPLY_RESULT_OFFSET + 3] << 24) |
1082                 (reply[SYNTH_REPLY_RESULT_OFFSET + 2] << 16) |
1083                 (reply[SYNTH_REPLY_RESULT_OFFSET + 1] <<  8) |
1084                 (reply[SYNTH_REPLY_RESULT_OFFSET + 0] <<  0);
1085             reply_rxlen = (reply[SYNTH_REPLY_RXLEN_OFFSET + 3] << 24) |
1086                 (reply[SYNTH_REPLY_RXLEN_OFFSET + 2] << 16) |
1087                 (reply[SYNTH_REPLY_RXLEN_OFFSET + 1] <<  8) |
1088                 (reply[SYNTH_REPLY_RXLEN_OFFSET + 0] <<  0);
1089         
1090             CYG_ASSERT(reply_rxlen <= rxlen, "The auxiliary should not be sending more data than was requested.");
1091         
1092             if ((int*)0 != actual_rxlen) {
1093                 *actual_rxlen  = reply_rxlen;
1094             }
1095             if (reply_rxlen) {
1096                 int received = 0;
1097             
1098                 while (received < reply_rxlen) {
1099                     rc = cyg_hal_sys_read(from_aux, (void*) &(rxdata[received]), reply_rxlen - received);
1100                     if (-CYG_HAL_SYS_EINTR == rc) {
1101                         continue;
1102                     } else if (rc <= 0) {
1103                         if (rc < 0) {
1104                             diag_printf("Internal error: unexpected result %d when receiving data from auxiliary.\n", rc);
1105                         } else {
1106                             diag_printf("Internal error: EOF detected on pipe from auxiliary.\n");
1107                         }
1108                         diag_printf("              : this application is exiting immediately.\n");
1109                     } else {
1110                         received += rc;
1111                     }
1112                 }
1113                 CYG_ASSERT(received == reply_rxlen, "Amount received should be exact");
1114             }
1115         }
1116     }
1117
1118     HAL_RESTORE_INTERRUPTS(old_isrstate);
1119 }
1120
1121 // Instantiate a device. This takes arguments such as
1122 // devs/eth/synth/ecosynth, current, ethernet, eth0, and 200x100 If
1123 // the package and version are NULL strings then the device being
1124 // initialized is application-specific and does not belong to any
1125 // particular package.
1126 int
1127 synth_auxiliary_instantiate(const char* pkg, const char* version, const char* devtype, const char* devinst, const char* devdata)
1128 {
1129     int           result = -1;
1130     unsigned char buf[512 + 1];
1131     const char*   str;
1132     int           index;
1133
1134     CYG_ASSERT((const char*)0 != devtype, "Device instantiations must specify a valid device type");
1135     CYG_ASSERT((((const char*)0 != pkg) && ((const char*)0 != version)) || \
1136                (((const char*)0 == pkg) && ((const char*)0 == version)), "If a package is specified then the version must be supplied as well");
1137     
1138     index = 0;
1139     str = pkg;
1140     if ((const char*)0 == str) {
1141         str = "";
1142     }
1143     while ( (index < 512) && ('\0' != *str) ) {
1144         buf[index++] = *str++;
1145     }
1146     if (index < 512) buf[index++] = '\0';
1147     str = version;
1148     if ((const char*)0 == str) {
1149         str = "";
1150     }
1151     while ((index < 512) && ('\0' != *str) ) {
1152         buf[index++] = *str++;
1153     }
1154     if (index < 512) buf[index++] = '\0';
1155     for (str = devtype; (index < 512) && ('\0' != *str); ) {
1156         buf[index++] = *str++;
1157     }
1158     if (index < 512) buf[index++] = '\0';
1159     if ((const char*)0 != devinst) {
1160         for (str = devinst; (index < 512) && ('\0' != *str); ) {
1161             buf[index++] = *str++;
1162         }
1163     }
1164     if (index < 512) buf[index++] = '\0';
1165     if ((const char*)0 != devdata) {
1166         for (str = devdata; (index < 512) && ('\0' != *str); ) {
1167             buf[index++] = *str++;
1168         }
1169     }
1170     if (index < 512) {
1171         buf[index++] = '\0';
1172     } else {
1173         diag_printf("Internal error: buffer overflow constructing instantiate request for auxiliary.\n");
1174         diag_printf("              : this application is exiting immediately.\n");
1175         cyg_hal_sys_exit(1);
1176     }
1177
1178     if (synth_auxiliary_running) {
1179         synth_auxiliary_xchgmsg(SYNTH_DEV_AUXILIARY, SYNTH_AUXREQ_INSTANTIATE, 0, 0,
1180                                 buf, index,
1181                                 &result, 
1182                                 (unsigned char*) 0, (int *) 0, 0);
1183     }
1184     return result;
1185 }
1186
1187 // ----------------------------------------------------------------------------
1188 // SIGPIPE and SIGCHLD are special, related to the auxiliary process.
1189 //
1190 // A SIGPIPE can only happen when the application is writing to the
1191 // auxiliary, which only happens inside synth_auxiliary_xchgmsg() (this
1192 // assumes that no other code is writing down a pipe, e.g. to interact
1193 // with a process other than the standard I/O auxiliary). Either the
1194 // auxiliary has explicitly closed the pipe, which it is not supposed
1195 // to do, or more likely it has exited. Either way, there is little
1196 // point in continuing - unless we already know that the system is
1197 // shutting down.
1198 static void
1199 synth_pipe_sighandler(int sig)
1200 {
1201     CYG_ASSERT(CYG_HAL_SYS_SIGPIPE == sig, "The right signal handler should be invoked");
1202     if (synth_auxiliary_running) {
1203         synth_auxiliary_running   = false;
1204         diag_printf("Internal error: communication with the I/O auxiliary has been lost.\n");
1205         diag_printf("              : this application is exiting immediately.\n");
1206         cyg_hal_sys_exit(1);
1207     }
1208 }
1209
1210 // Similarly it is assumed that there will be no child processes other than
1211 // the auxiliary. Therefore a SIGCHLD indicates that the auxiliary has
1212 // terminated unexpectedly. This is bad: normal termination involves
1213 // the application exiting and the auxiliary terminating in response,
1214 // not the other way around.
1215 //
1216 // As a special case, if it is known that the auxiliary is not currently
1217 // usable then the signal is ignored. This copes with the situation where
1218 // the auxiliary has just been fork()'d but has failed to initialize, or
1219 // alternatively where the whole system is in the process of shutting down
1220 // cleanly and it happens that the auxiliary got there first.
1221 static void
1222 synth_chld_sighandler(int sig)
1223 {
1224     CYG_ASSERT(CYG_HAL_SYS_SIGCHLD == sig, "The right signal handler should be invoked");
1225     if (synth_auxiliary_running) {
1226         synth_auxiliary_running   = false;
1227         diag_printf("Internal error: the I/O auxiliary has terminated unexpectedly.\n");
1228         diag_printf("              : this application is exiting immediately.\n");
1229         cyg_hal_sys_exit(1);
1230     }
1231 }
1232
1233 // ----------------------------------------------------------------------------
1234 // Initialization
1235
1236 void
1237 synth_hardware_init(void)
1238 {
1239     struct cyg_hal_sys_sigaction action;
1240     struct cyg_hal_sys_sigset_t  blocked;
1241     int i;
1242
1243     // Set up a sigprocmask to block all signals except the ones we
1244     // particularly want to handle. However do not block the tty
1245     // signals - the ability to ctrl-C a program is important.
1246     CYG_HAL_SYS_SIGFILLSET(&blocked);
1247     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGILL);
1248     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGBUS);
1249     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGFPE);
1250     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGSEGV);
1251     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGPIPE);
1252     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGCHLD);
1253     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGALRM);
1254     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGIO);
1255     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGHUP);
1256     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGINT);
1257     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGQUIT);
1258     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGTERM);
1259     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGCONT);
1260     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGSTOP);
1261     CYG_HAL_SYS_SIGDELSET(&blocked, CYG_HAL_SYS_SIGTSTP);
1262         
1263     if (0 != cyg_hal_sys_sigprocmask(CYG_HAL_SYS_SIG_SETMASK, &blocked, (cyg_hal_sys_sigset_t*) 0)) {
1264         CYG_FAIL("Failed to initialize sigprocmask");
1265     }
1266
1267     // Now set up the VSR and ISR statics
1268     synth_VSR = &synth_default_vsr;
1269     for (i = 0; i < CYGNUM_HAL_ISR_COUNT; i++) {
1270         synth_isr_handlers[i].isr       = &synth_default_isr;
1271         synth_isr_handlers[i].data      = (CYG_ADDRWORD) 0;
1272         synth_isr_handlers[i].obj       = (CYG_ADDRESS) 0;
1273         synth_isr_handlers[i].pri       = CYGNUM_HAL_ISR_COUNT;
1274     }
1275
1276     // Install signal handlers for SIGIO and SIGALRM, the two signals
1277     // that may cause the VSR to run. SA_NODEFER is important: it
1278     // means that the current signal will not be blocked while the
1279     // signal handler is running. Combined with a mask of 0, it means
1280     // that the sigprocmask does not change when a signal handler is
1281     // invoked, giving eCos the flexibility to switch to other threads
1282     // instead of having the signal handler return immediately.
1283     action.hal_mask     = 0;
1284     action.hal_flags    = CYG_HAL_SYS_SA_NODEFER;
1285     action.hal_handler  = &synth_alrm_sighandler;
1286     action.hal_restorer = (void (*)(void)) 0;
1287 #ifdef CYG_HAL_SYS_SIGACTION_ADJUST
1288     CYG_HAL_SYS_SIGACTION_ADJUST(CYG_HAL_SYS_SIGALRM, &action);
1289 #endif    
1290     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGALRM, &action, (struct cyg_hal_sys_sigaction*) 0)) {
1291         CYG_FAIL("Failed to install signal handler for SIGALRM");
1292     }
1293     action.hal_handler  = &synth_io_sighandler;
1294     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGIO, &action, (struct cyg_hal_sys_sigaction*) 0)) {
1295         CYG_FAIL("Failed to install signal handler for SIGIO");
1296     }
1297
1298     // Install handlers for the various exceptions. For now these also
1299     // operate with unchanged sigprocmasks, allowing nested
1300     // exceptions. It is not clear that this is entirely a good idea,
1301     // but in practice these exceptions will usually be handled by gdb
1302     // anyway.
1303     action.hal_handler  = &synth_exception_sighandler;
1304     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGILL,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1305         CYG_FAIL("Failed to install signal handler for SIGILL");
1306     }
1307     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGBUS,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1308         CYG_FAIL("Failed to install signal handler for SIGBUS");
1309     }
1310     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGFPE,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1311         CYG_FAIL("Failed to install signal handler for SIGFPE");
1312     }
1313     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGSEGV,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1314         CYG_FAIL("Failed to install signal handler for SIGSEGV");
1315     }
1316
1317     // Also cope with SIGCHLD and SIGPIPE. SIGCHLD indicates that the
1318     // auxiliary has terminated, which is a bad thing. SIGPIPE
1319     // indicates that a write to the auxiliary has terminated, but
1320     // the error condition was caught at a different stage.
1321     action.hal_handler = &synth_pipe_sighandler;
1322     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGPIPE,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1323         CYG_FAIL("Failed to install signal handler for SIGPIPE");
1324     }
1325     action.hal_handler = &synth_chld_sighandler;
1326     action.hal_flags  |= CYG_HAL_SYS_SA_NOCLDSTOP | CYG_HAL_SYS_SA_NOCLDWAIT;
1327     if (0 != cyg_hal_sys_sigaction(CYG_HAL_SYS_SIGCHLD,  &action, (struct cyg_hal_sys_sigaction*) 0)) {
1328         CYG_FAIL("Failed to install signal handler for SIGCHLD");
1329     }
1330
1331     // Determine the processor's bogomips rating. This adds some
1332     // start-up overhead to all applications, even if HAL_DELAY_US()
1333     // is not used. However doing it on demand in the first call
1334     // to HAL_DELAY_US() would risk running out of file descriptors.
1335     {
1336         int     fd;
1337         char    buf[4096];      // much larger than current /proc/cpuinfo, but still small enough for synthetic target stacks
1338         int     read;
1339         int     i;
1340
1341         fd  = cyg_hal_sys_open("/proc/cpuinfo", CYG_HAL_SYS_O_RDONLY, 0);
1342         if (fd < 0) {
1343             CYG_FAIL("Failed to open /proc/cpuinfo, needed for BogoMips rating");
1344         }
1345         read    = cyg_hal_sys_read(fd, buf, 4096);
1346         cyg_hal_sys_close(fd);
1347
1348         for (i = 0; i < read; i++) {
1349             if ((buf[i  ] == 'b') && (buf[i+1] == 'o') && (buf[i+2] == 'g') && (buf[i+3] == 'o') &&
1350                 (buf[i+4] == 'm') && (buf[i+5] == 'i') && (buf[i+6] == 'p') && (buf[i+7] == 's')) {
1351
1352                 for ( i += 8; (i < read) && ((buf[i] < '1') || (buf[i] > '9')); i++) {
1353                     ;
1354                 }
1355                 // Only bother with the integer part of the rating
1356                 for ( ; (i < read) && (buf[i] >= '0') && (buf[i] <= '9'); i++) {
1357                     hal_bogomips = (10 * hal_bogomips) + (buf[i] - '0');
1358                 }
1359                 break;
1360             }
1361         }
1362         if (0 == hal_bogomips) {
1363             CYG_FAIL("Failed to find bogomips entry in /proc/cpuinfo");
1364         }
1365     }
1366     
1367     // Start up the auxiliary process.
1368     synth_start_auxiliary();
1369     
1370     // All done. At this stage interrupts are still disabled, no ISRs
1371     // have been installed, and the clock is not yet ticking.
1372     // Exceptions can come in and will be processed normally. SIGIO
1373     // and SIGALRM could come in, but nothing has yet been done
1374     // to make that happen.
1375 }
1376
1377 // Second-stage hardware init. This is called after all C++ static
1378 // constructors have been run, which should mean that all device
1379 // drivers have been initialized and will have performed appropriate
1380 // interactions with the I/O auxiliary. There should now be a
1381 // message exchange with the auxiliary to let it know that there will
1382 // not be any more devices, allowing it to remove unwanted frames,
1383 // run the user's mainrc.tcl script, and so on. Also this is the
1384 // time that the various toplevels get mapped on to the display.
1385 //
1386 // This request blocks until the auxiliary is ready. The return value
1387 // indicates whether or not any errors occurred on the auxiliary side,
1388 // and that those errors have not been suppressed using --keep-going
1389
1390 void
1391 synth_hardware_init2(void)
1392 {
1393     if (synth_auxiliary_running) {
1394         int result;
1395         synth_auxiliary_xchgmsg(SYNTH_DEV_AUXILIARY, SYNTH_AUXREQ_CONSTRUCTORS_DONE,
1396                                0, 0, (const unsigned char*) 0, 0,
1397                                &result,
1398                                (unsigned char*) 0, (int*) 0, 0);
1399         if ( !result ) {
1400             cyg_hal_sys_exit(1);
1401         }
1402     }
1403 }