Back to home page

LXR

 
 

    


Warning, /bsps/powerpc/shared/exceptions/README.md is written in an unsupported language. File is not indexed.

0001 Exceptions
0002 ==========
0003 
0004 BSP support middleware for 'new-exception' style PPC.
0005 
0006 T. Straumann, 12/2007
0007 
0008 
0009 EXPLANATION OF SOME TERMS
0010 -------------------------
0011 
0012 In this README we refer to exceptions and sometimes
0013 to 'interrupts'. Interrupts simply are asynchronous
0014 exceptions such as 'external' exceptions or 'decrementer'
0015 /'timer' exceptions.
0016 
0017 Traditionally (in the libbsp/powerpc/shared implementation),
0018 synchronous exceptions are handled entirely in the context
0019 of the interrupted task, i.e., the exception handlers use
0020 the task's stack and leave thread-dispatching enabled,
0021 i.e., scheduling is allowed to happen 'in the middle'
0022 of an exception handler.
0023 
0024 Asynchronous exceptions/interrupts, OTOH, use a dedicated
0025 interrupt stack and defer scheduling until after the last
0026 nested ISR has finished.
0027 
0028 
0029 RATIONALE
0030 ---------
0031 The 'new-exception' processing API works at a rather
0032 low level. It provides functions for
0033 installing low-level code (which must be written in
0034 assembly code) directly into the PPC vector area.
0035 It is entirely left to the BSP to implement low-level
0036 exception handlers and to implement an API for
0037 C-level exception handlers and to implement the
0038 RTEMS interrupt API defined in cpukit/include/rtems/irq.h.
0039 
0040 The result has been a Darwinian evolution of variants
0041 of this code which is very hard to maintain. Mostly,
0042 the four files
0043 
0044 libbsp/powerpc/shared/vectors/vectors.S
0045   (low-level handlers for 'normal' or 'synchronous'
0046   exceptions. This code saves all registers on
0047   the interrupted task's stack and calls a
0048   'global' C (high-level) exception handler.
0049 
0050 libbsp/powerpc/shared/vectors/vectors_init.c
0051   (default implementation of the 'global' C
0052   exception handler and initialization of the
0053   vector table with trampoline code that ends up
0054   calling the 'global' handler.
0055 
0056 libbsp/powerpc/shared/irq/irq_asm.S
0057   (low-level handlers for 'IRQ'-type or 'asynchronous'
0058   exceptions. This code is very similar to vectors.S
0059   but does slightly more: after saving (only
0060   the minimal set of) registers on the interrupted
0061   task's stack it disables thread-dispatching, switches
0062   to a dedicated ISR stack (if not already there which is
0063   possible for nested interrupts) and then executes the high
0064   level (C) interrupt dispatcher 'C_dispatch_irq_handler()'.
0065   After 'C_dispatch_irq_handler()' returns the stack
0066   is switched back (if not a nested IRQ), thread-dispatching
0067   is re-enabled, signals are delivered and a context
0068   switch is initiated if necessary.
0069 
0070 libbsp/powerpc/shared/irq/irq.c
0071   implementation of the RTEMS ('new') IRQ API defined
0072   in cpukit/include/rtems/irq.h.
0073 
0074 have been copied and modified by a myriad of BSPs leading
0075 to many slightly different variants.
0076 
0077 THE BSP-SUPORT MIDDLEWARE
0078 =========================
0079 
0080 The code in this directory is an attempt to provide the
0081 functionality implemented by the aforementioned files
0082 in a more generic way so that it can be shared by more
0083 BSPs rather than being copied and modified.
0084 
0085 Another important goal was eliminating all conditional
0086 compilation which tested for specific CPU models by means
0087 of C-preprocessor symbols (#ifdef ppcXYZ).
0088 Instead, appropriate run-time checks for features defined
0089 in cpuIdent.h are used.
0090 
0091 The assembly code has been (almost completely) rewritten
0092 and it tries to address a few problems while deliberately
0093 trying to live with the existing APIs and semantics
0094 (how these could be improved is beyond the scope but
0095 that they could is beyond doubt...):
0096 
0097  - some PPCs don't fit into the classic scheme where
0098    the exception vector addresses all were multiples of
0099    0x100 (some vectors are spaced as closely as 0x10).
0100    The API should not expose vector offsets but only
0101    vector numbers which can be considered an abstract
0102    entity. The mapping from vector numbers to actual
0103    address offsets is performed inside 'raw_exception.c'
0104  - having to provide assembly prologue code in order to
0105    hook an exception is cumbersome. The middleware
0106    tries to free users and BSP writers from this issue
0107    by dealing with assembly prologues entirely inside
0108    the middleware. The user can hook ordinary C routines.
0109  - the advent of BookE CPUs brought interrupts with
0110    multiple priorities: non-critical and critical 
0111    interrupts. Unfortunately, these are not entirely
0112    trivial to deal with (unless critical interrupts
0113    are permanently disabled [which is still the case:
0114    ATM rtems_interrupt_enable()/rtems_interrupt_disable()
0115    only deal with EE]). See separate section titled
0116    'race condition...' below for a detailed explanation.
0117 
0118 
0119 STRUCTURE
0120 ---------
0121 The middleware uses exception 'categories' or
0122 'flavors' as defined in raw_exception.h.
0123 
0124 The middleware consists of the following parts:
0125 
0126    1 small 'prologue' snippets that encode the
0127      vector information and jump to appropriate
0128          'flavored-wrapper' code for further handling.
0129          Some PPC exceptions are spaced only
0130          16-bytes apart, so the generic
0131          prologue snippets are only 16-bytes long.
0132          Prologues for synchronuos and asynchronous
0133          exceptions differ.
0134 
0135    2 flavored-wrappers which sets up a stack frame
0136      and do things that are specific for
0137          different 'flavors' of exceptions which
0138          currently are
0139            - classic PPC exception
0140            - ppc405 critical exception
0141            - bookE critical exception
0142            - e500 machine check exception
0143 
0144    Assembler macros are provided and they can be
0145    expanded to generate prologue templates and
0146    flavored-wrappers for different flavors
0147    of exceptions. Currently, there are two prologues
0148    for all aforementioned flavors. One for synchronous
0149    exceptions, the other for interrupts.
0150 
0151    3 generic assembly-level code that does the bulk
0152      of saving register context and calling C-code.
0153 
0154    4 C-code (ppc_exc_hdl.c) for dispatching BSP/user
0155      handlers.
0156 
0157    5 Initialization code (vectors_init.c). All valid
0158      exceptions for the detected CPU are determined
0159          and a fitting prologue snippet for the exception
0160          category (classic, critical, synchronous or IRQ, ...)
0161          is generated from a template and the vector number
0162          and then installed in the vector area.
0163 
0164          The user/BSP only has to deal with installing
0165          high-level handlers but by default, the standard
0166          'C_dispatch_irq_handler' routine is hooked to
0167          the external and 'decrementer' exceptions.
0168 
0169    6 RTEMS IRQ API is implemented by 'irq.c'. It
0170      relies on a few routines to be provided by
0171          the BSP.
0172 
0173 USAGE
0174 -----
0175         BSP writers must provide the following routines 
0176         (declared in irq_supp.h):
0177         Interrupt controller (PIC) support:
0178 ```c
0179                 BSP_setup_the_pic()        - initialize PIC hardware
0180                 BSP_enable_irq_at_pic()    - enable/disable given irq at PIC; IGNORE if
0181                 BSP_disable_irq_at_pic()     irq number out of range!
0182                 C_dispatch_irq_handler()   - handle irqs and dispatch user handlers
0183                                              this routine SHOULD use the inline
0184                                                                          fragment
0185 
0186                                                                            bsp_irq_dispatch_list()
0187 
0188                                                                          provided by irq_supp.h
0189                                                                          for calling user handlers.
0190 
0191         BSP initialization; call
0192 
0193         rtems_status_code sc = ppc_exc_initialize(
0194           PPC_INTERRUPT_DISABLE_MASK_DEFAULT,
0195           interrupt_stack_begin,
0196           interrupt_stack_size
0197         );
0198         if (sc != RTEMS_SUCCESSFUL) {
0199           BSP_panic("cannot initialize exceptions");
0200         }
0201         BSP_rtems_irq_mngt_set();
0202 ```
0203 
0204         Note that BSP_rtems_irq_mngt_set() hooks the C_dispatch_irq_handler()
0205         to the external and decrementer (PIT exception for bookE; a decrementer
0206         emulation is activated) exceptions for backwards compatibility reasons.
0207         C_dispatch_irq_handler() must therefore be able to support these two
0208         exceptions.
0209         However, the BSP implementor is free to either disconnect
0210         C_dispatch_irq_handler() from either of these exceptions, to connect
0211         other handlers (e.g., for SYSMGMT exceptions) or to hook
0212         C_dispatch_irq_handler() to yet more exceptions etc. *after*
0213         BSP_rtems_irq_mngt_set() executed.
0214 
0215         Hooking exceptions:
0216 
0217         The API defined in vectors.h declares routines for connecting
0218         a C-handler to any exception. Note that the execution environment
0219         of the C-handler depends on the exception being synchronous or
0220         asynchronous:
0221 
0222                 - synchronous exceptions use the task stack and do not
0223                   disable thread dispatching scheduling.
0224                 - asynchronous exceptions use a dedicated stack and do
0225                   defer thread dispatching until handling has (almost) finished.
0226 
0227         By inspecting the vector number stored in the exception frame
0228         the nature of the exception can be determined: asynchronous 
0229         exceptions have the most significant bit(s) set.
0230 
0231         Any exception for which no dedicated handler is registered
0232         ends up being handled by the routine addressed by the
0233         (traditional) 'globalExcHdl' function pointer.
0234 
0235         Makefile.am:
0236                 - make sure the Makefile.am does NOT use any of the files
0237                         vectors.S, vectors.h, vectors_init.c, irq_asm.S, irq.c
0238                   from 'libbsp/powerpc/shared' NOR must the BSP implement
0239                   any functionality that is provided by those files (and
0240                   now the middleware).
0241 
0242                 - (probably) remove 'vectors.rel' and anything related
0243 
0244                 - add
0245 
0246 ```shell
0247                     ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/vectors.h
0248                     ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/irq_supp.h
0249 ```
0250                   to 'include_bsp_HEADERS'
0251 
0252                 - add
0253 ```shell
0254                     ../../../libcpu/@RTEMS_CPU@/@exceptions@/exc_bspsupport.rel
0255                     ../../../libcpu/@RTEMS_CPU@/@exceptions@/irq_bspsupport.rel
0256 ```
0257                   to 'libbsp_a_LIBADD'
0258 
0259                   (irq.c is in a separate '.rel' so that you can get support
0260                   for exceptions only).
0261 
0262 CAVEATS
0263 -------
0264 On classic PPCs, early (and late) parts of the low-level
0265 exception handling code run with the MMU disabled which mean
0266 that the default caching attributes (write-back) are in effect
0267 (thanks to Thomas Doerfler for bringing this up).
0268 The code currently assumes that the MMU translations
0269 for the task and interrupt stacks as well as some
0270 variables in the data-area MATCH THE DEFAULT CACHING
0271 ATTRIBUTES (this assumption also holds for the old code
0272 in libbsp/powepc/shared/vectors ../irq).
0273 
0274 During initialization of exception handling, a crude test
0275 is performed to check if memory seems to have the write-back
0276 attribute. The 'dcbz' instruction should - on most PPCs - cause
0277 an alignment exception if the tested cache-line does not
0278 have this attribute.
0279 
0280 BSPs which entirely disable caching (e.g., by physically
0281 disabling the cache(s)) should set the variable
0282   ppc_exc_cache_wb_check = 0
0283 prior to calling initialize_exceptions(). 
0284 Note that this check does not catch all possible
0285 misconfigurations (e.g., on the 860, the default attribute
0286 is AFAIK [libcpu/powerpc/mpc8xx/mmu/mmu_init.c] set to
0287 'caching-disabled' which is potentially harmful but
0288 this situation is not detected).
0289 
0290 
0291 RACE CONDITION WHEN DEALING WITH CRITICAL INTERRUPTS
0292 ----------------------------------------------------
0293 
0294    The problematic race condition is as follows:
0295 
0296    Usually, ISRs are allowed to use certain OS
0297    primitives such as e.g., releasing a semaphore.
0298    In order to prevent a context switch from happening
0299    immediately (this would result in the ISR being
0300    suspended), thread-dispatching must be disabled
0301    around execution of the ISR. However, on the
0302    PPC architecture it is neither possible to 
0303    atomically disable ALL interrupts nor is it
0304    possible to atomically increment a variable
0305    (the thread-dispatch-disable level).
0306    Hence, the following sequence of events could
0307    occur:
0308     1) low-priority interrupt (LPI) is taken
0309     2) before the LPI can increase the 
0310            thread-dispatch-disable level or disable
0311            high-priority interupts, a high-priority
0312            interrupt (HPI) happens
0313         3) HPI increases dispatch-disable level
0314         4) HPI executes high-priority ISR which e.g.,
0315            posts a semaphore
0316         5) HPI decreases dispatch-disable level and
0317            realizes that a context switch is necessary
0318         6) context switch is performed since LPI had
0319            not gotten to the point where it could
0320            increase the dispatch-disable level.
0321    At this point, the LPI has been effectively
0322    suspended which means that the low-priority
0323    ISR will not be executed until the task 
0324    interupted in 1) is scheduled again!
0325 
0326    The solution to this problem is letting the
0327    first machine instruction of the low-priority
0328    exception handler write a non-zero value to 
0329    a variable in memory:
0330 
0331         ee_vector_offset:  
0332 
0333          stw r1, ee_lock@sdarel(r13)    
0334          .. save some registers etc..
0335                  .. increase thread-dispatch-disable-level
0336                  .. clear 'ee_lock' variable
0337 
0338         After the HPI decrements the dispatch-disable level
0339         it checks 'ee_lock' and refrains from performing
0340         a context switch if 'ee_lock' is nonzero. Since
0341         the LPI will complete execution subsequently it
0342         will eventually do the context switch.
0343 
0344         For the single-instruction write operation we must
0345           a) write a register that is guaranteed to be
0346              non-zero (e.g., R1 (stack pointer) or R13
0347                  (SVR4 short-data area).
0348           b) use an addressing mode that doesn't require
0349              loading any registers. The short-data area
0350                  pointer R13 is appropriate.
0351 
0352     CAVEAT: unfortunately, this method by itself
0353         is *NOT* enough because raising a low-priority
0354         exception and executing the first instruction
0355         of the handler is *NOT* atomic. Hence, the following
0356         could occur:
0357 
0358          1) LPI is taken
0359          2) PC is saved in SRR0, PC is loaded with 
0360             address of 'locking instruction'
0361                   stw r1, ee_lock@sdarel(r13)
0362      3) ==> critical interrupt happens
0363          4) PC (containing address of locking instruction)
0364             is saved in CSRR0
0365      5) HPI is dispatched
0366 
0367         For the HPI to correctly handle this situation
0368         it does the following:
0369 
0370         
0371                 a) increase thread-dispatch disable level
0372                 b) do interrupt work
0373                 c) decrease thread-dispatch disable level
0374             d) if ( dispatch-disable level == 0 )
0375                  d1) check ee_lock
0376                  d2) check instruction at *CSRR0
0377                  d3) do a context switch if necessary ONLY IF
0378                      ee_lock is NOT set AND *CSRR0 is NOT the
0379                          'locking instruction'
0380 
0381         this works because the address of 'ee_lock'
0382         is embedded in the locking instruction
0383         'stw r1, ee_lock@sdarel(r13)' and because the
0384         registers r1/r13 have a special purpose
0385         (stack-pointer, SDA-pointer). Hence it is safe
0386         to assume that the particular instruction
0387         'stw r1,ee_lock&sdarel(r13)' never occurs
0388         anywhere else.
0389 
0390         Another note: this algorithm also makes sure
0391         that ONLY nested ASYNCHRONOUS interrupts which
0392         enable/disable thread-dispatching and check if
0393         thread-dispatching is required before returning
0394         control engage in this locking protocol. It is
0395         important that when a critical, asynchronous
0396         interrupt interrupts a 'synchronous' exception
0397         (which does not disable thread-dispatching)
0398         the thread-dispatching operation upon return of
0399         the HPI is NOT deferred (because the synchronous
0400         handler would not, eventually, check for a
0401         dispatch requirement).
0402 
0403         And one more note: We never want to disable
0404         machine-check exceptions to avoid a checkstop.
0405         This means that we cannot use enabling/disabling
0406         this type of exception for protection of critical
0407         OS data structures.
0408         Therefore, calling OS primitives from a asynchronous
0409         machine-check handler is ILLEGAL and not supported.
0410         Since machine-checks can happen anytime it is not
0411         legal to test if a deferred context switch should
0412         be performed when the asynchronous machine-check
0413         handler returns (since _Context_Switch_is_necessary
0414         could have been set by a IRQ-protected section of
0415         code that was hit by the machine-check).
0416         Note that synchronous machine-checks can legally
0417         use OS primitives and currently there are no
0418         asynchronous machine-checks defined.
0419 
0420 Epilogue
0421 --------
0422    You have to disable all asynchronous exceptions which may cause a context
0423    switch before the restoring of the SRRs and the RFI.  Reason:
0424    
0425       Suppose we are in the epilogue code of an EE between the move to SRRs and
0426       the RFI. Here EE is disabled but CE is enabled. Now a CE happens.  The
0427       handler decides that a thread dispatch is necessary. The CE checks if
0428       this is possible:
0429    
0430          o The thread dispatch disable level is 0, because the EE has already
0431            decremented it.
0432          o The EE lock variable is cleared.
0433          o The EE executes not the first instruction.
0434    
0435       Hence a thread dispatch is allowed. The CE issues a context switch to a
0436       task with EE enabled (for example a task waiting for a semaphore). Now a
0437       EE happens and the current content of the SRRs is lost.