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.