2007-12-09 Till Straumann <strauman@slac.stanford.edu>

* new-exceptions/bspsupport/ppc_exc_test.c,
	new-exceptions/bspsupport/vectors_init.c,
	new-exceptions/bspsupport/ppc_exc_bspsupp.h,
	new-exceptions/bspsupport/README,
	new-exceptions/bspsupport/irq_supp.h:
	Added README and some comments; now use TRAP exception
	in ppc_exc_test.c so that it works on PSIM.
This commit is contained in:
Till Straumann
2007-12-10 07:06:53 +00:00
parent 9b4c770e88
commit bf5742a630
6 changed files with 334 additions and 19 deletions

View File

@@ -1,3 +1,13 @@
2007-12-09 Till Straumann <strauman@slac.stanford.edu>
* new-exceptions/bspsupport/ppc_exc_test.c,
new-exceptions/bspsupport/vectors_init.c,
new-exceptions/bspsupport/ppc_exc_bspsupp.h,
new-exceptions/bspsupport/README,
new-exceptions/bspsupport/irq_supp.h:
Added README and some comments; now use TRAP exception
in ppc_exc_test.c so that it works on PSIM.
2007-12-08 Till Straumann <strauman@slac.stanford.edu>
* irq_supp.h: was moved from libbsp/powerpc/shared/irq to

View File

@@ -0,0 +1,311 @@
$Id$
BSP support middleware for 'new-exception' style PPC.
T. Straumann, 12/2007
EXPLANATION OF SOME TERMS
=========================
In this README we refer to exceptions and sometimes
to 'interrupts'. Interrupts simply are asynchronous
exceptions such as 'external' exceptions or 'decrementer'
/'timer' exceptions.
Traditionally (in the libbsp/powerpc/shared implementation),
synchronous exceptions are handled entirely in the context
of the interrupted task, i.e., the exception handlers use
the task's stack and leave thread-dispatching enabled,
i.e., scheduling is allowed to happen 'in the middle'
of an exception handler.
Asynchronous exceptions/interrupts, OTOH, use a dedicated
interrupt stack and defer scheduling until after the last
nested ISR has finished.
RATIONALE
=========
The 'new-exception' processing API works at a rather
low level. It provides functions for
installing low-level code (which must be written in
assembly code) directly into the PPC vector area.
It is entirely left to the BSP to implement low-level
exception handlers and to implement an API for
C-level exception handlers and to implement the
RTEMS interrupt API defined in cpukit/include/rtems/irq.h.
The result has been a Darwinian evolution of variants
of this code which is very hard to maintain. Mostly,
the four files
libbsp/powerpc/shared/vectors/vectors.S
(low-level handlers for 'normal' or 'synchronous'
exceptions. This code saves all registers on
the interrupted task's stack and calls a
'global' C (high-level) exception handler.
libbsp/powerpc/shared/vectors/vectors_init.c
(default implementation of the 'global' C
exception handler and initialization of the
vector table with trampoline code that ends up
calling the 'global' handler.
libbsp/powerpc/shared/irq/irq_asm.S
(low-level handlers for 'IRQ'-type or 'asynchronous'
exceptions. This code is very similar to vectors.S
but does slightly more: after saving (only
the minimal set of) registers on the interrupted
task's stack it disables thread-dispatching, switches
to a dedicated ISR stack (if not already there which is
possible for nested interrupts) and then executes the high
level (C) interrupt dispatcher 'C_dispatch_irq_handler()'.
After 'C_dispatch_irq_handler()' returns the stack
is switched back (if not a nested IRQ), thread-dispatching
is re-enabled, signals are delivered and a context
switch is initiated if necessary.
libbsp/powerpc/shared/irq/irq.c
implementation of the RTEMS ('new') IRQ API defined
in cpukit/include/rtems/irq.h.
have been copied and modified by a myriad of BSPs leading
to many slightly different variants.
THE BSP-SUPORT MIDDLEWARE
=========================
The code in this directory is an attempt to provide the
functionality implemented by the aforementioned files
in a more generic way so that it can be shared by more
BSPs rather than being copied and modified.
Another important goal was eliminating all conditional
compilation which tested for specific CPU models by means
of C-preprocessor symbols (#ifdef ppcXYZ).
Instead, appropriate run-time checks for features defined
in cpuIdent.h are used.
The assembly code has been (almost completely) rewritten
and it tries to address a few problems while deliberately
trying to live with the existing APIs and semantics
(how these could be improved is beyond the scope but
that they could is beyond doubt...):
- some PPCs don't fit into the classic scheme where
the exception vector addresses all were multiples of
0x100 (some are spaced as closely as 0x10).
The API should not expose vector offsets but only
vector numbers which can be considered an abstract
entity. The mapping from vector numbers to actual
address offsets is performed inside 'raw_exception.c'
- having to provide assembly prologue code in order to
hook an exception is cumbersome. The middleware
tries to free users and BSP writers from this issue
by dealing with assembly prologues entirely inside
the middleware. The user can hook ordinary C routines.
- the advent of BookE CPUs brought interrupts with
multiple priorities: non-critical and critical
interrupts. Unfortunately, these are not entirely
trivial to deal with (unless critical interrupts
are permanently disabled [which is still the case:
ATM rtems_interrupt_enable()/rtems_interrupt_disable()
only deal with EE]). See separate section titled
'race condition...' below for a detailed explanation.
STRUCTURE
=========
The middleware uses exception 'categories' or
'flavors' as defined in raw_exception.h.
The middleware consists of the following parts:
1 small 'prologue' snippets that encode the
vector information and jump to appropriate
'flavored-wrapper' code for further handling.
Some PPC exceptions are spaced only
16-bytes apart, so the generic
prologue snippets are only 16-bytes long.
Prologues for synchronuos and asynchronous
exceptions differ.
2 flavored-wrappers which sets up a stack frame
and do things that are specific for
different 'flavors' of exceptions which
currently are
- classic PPC exception
- ppc405 critical exception
- bookE critical exception
- e500 machine check exception
Assembler macros are provided and they can be
expanded to generate prologue templates and
flavored-wrappers for different flavors
of exceptions. Currently, there are two prologues
for all aforementioned flavors. One for synchronous
exceptions, the other for interrupts.
3 generic assembly-level code that does the bulk
of saving register context and calling C-code.
4 C-code (ppc_exc_hdl.c) for dispatching BSP/user
handlers.
5 Initialization code (vectors_init.c). All valid
exceptions for the detected CPU are determined
and a fitting prologue snippet for the exception
category (classic, critical, synchronous or IRQ, ...)
is generated from a template and the vector number
and then installed in the vector area.
The user/BSP only has to deal with installing
high-level handlers but by default, the standard
'C_dispatch_irq_handler' routine is hooked to
the external and 'decrementer' exceptions.
6 RTEMS IRQ API is implemented by 'irq.c'. It
relies on a few routines to be provided by
the BSP.
USAGE
=====
BSP writers must provide the following routines
(declared in irq_supp.h):
Interrupt controller (PIC) support:
BSP_setup_the_pic() - initialize PIC hardware
BSP_enable_irq_at_pic() - enable/disable given irq at PIC; IGNORE if
BSP_disable_irq_at_pic() irq number out of range!
C_dispatch_irq_handler() - handle irqs and dispatch user handlers
this routine SHOULD use the inline
fragment
bsp_irq_dispatch_list()
provided by irq_supp.h
for calling user handlers.
BSP initialization; call
initialize_exceptions();
BSP_rtems_irq_mngt_set();
Note that BSP_rtems_irq_mngt_set() hooks the C_dispatch_irq_handler()
to the external and decrementer (PIT exception for bookE; a decrementer
emulation is activated) exceptions for backwards compatibility reasons.
C_dispatch_irq_handler() must therefore be able to support these two
exceptions.
However, the BSP implementor is free to either disconnect
C_dispatch_irq_handler() from either of these exceptions, to connect
other handlers (e.g., for SYSMGMT exceptions) or to hook
C_dispatch_irq_handler() to yet more exceptions etc. *after*
BSP_rtems_irq_mngt_set() executed.
Hooking exceptions:
The API defined in ppc_exc_bspsupp.h declares routines for connecting
a C-handler to any exception. Note that the execution environment
of the C-handler depends on the exception being synchronous or
asynchronous:
- synchronous exceptions use the task stack and do not
disable thread dispatching scheduling.
- asynchronous exceptions use a dedicated stack and do
defer thread dispatching until handling has (almost) finished.
By inspecting the vector number stored in the exception frame
the nature of the exception can be determined: asynchronous
exceptions have the most significant bit(s) set.
Any exception for which no dedicated handler is registered
ends up being handled by the routine addressed by the
(traditional) 'globalExcHdl' function pointer.
Makefile.am:
- make sure the Makefile.am does NOT use any of the files
vectors.S, vectors.h, vectors_init.c, irq_asm.S, irq.c
from 'libbsp/powerpc/shared' NOR must the BSP implement
any functionality that is provided by those files (and
now the middleware).
- (probably) remove 'vectors.rel' and anything related
- add
include_bsp_HEADERS += \
../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/vectors.h \
../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/irq_supp.h \
../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/ppc_exc_bspsupp.h
- add
../../../libcpu/@RTEMS_CPU@/@exceptions@/exc_bspsupport.rel \
to 'libbsp_a_LIBADD'
RACE CONDITION WHEN DEALING WITH CRITICAL INTERRUPTS
====================================================
The problematic race condition is as follows:
Usually, ISRs are allowed to use certain OS
primitives such as e.g., releasing a semaphore.
In order to prevent a context switch from happening
immediately (this would result in the ISR being
suspended), thread-dispatching must be disabled
around execution of the ISR. However, on the
PPC architecture it is neither possible to
atomically disable ALL interrupts nor is it
possible to atomically increment a variable
(the thread-dispatch-disable level).
Hence, the following sequence of events could
occur:
1) low-priority interrupt (LPI) is taken
2) before the LPI can increase the
thread-dispatch-disable level or disable
high-priority interupts, a high-priority
interrupt (HPI) happens
3) HPI increases dispatch-disable level
4) HPI executes high-priority ISR which e.g.,
posts a semaphore
5) HPI decreases dispatch-disable level and
realizes that a context switch is necessary
6) context switch is performed since LPI had
not gotten to the point where it could
increase the dispatch-disable level.
At this point, the LPI has been effectively
suspended which means that the low-priority
ISR will not be executed until the task
interupted in 1) is scheduled again!
The solution to this problem is letting the
first machine instruction of the low-priority
exception handler write a non-zero value to
a variable in memory:
ee_vector_offset:
stw r1, ee_lock@sdarel(r13)
.. save some registers etc..
.. increase thread-dispatch-disable-level
.. clear 'ee_lock' variable
The earliest a critical exception could interrupt
the 'external' exception handler is after the
'stw r1, ee_lock@sdarel(r13)' instruction.
After the HPI decrements the dispatch-disable level
it checks 'ee_lock' and refrains from performing
a context switch if 'ee_lock' is nonzero. Since
the LPI will complete execution subsequently it
will eventually do the context switch.
For the single-instruction write operation we must
a) write a register that is guaranteed to be
non-zero (e.g., R1 (stack pointer) or R13
(SVR4 short-data area).
b) use an addressing mode that doesn't require
loading any registers. The short-data area
pointer R13 is appropriate.

View File

@@ -48,8 +48,6 @@ extern int BSP_disable_irq_at_pic(const rtems_irq_number irqLine);
/*
* Initialize the PIC.
* Return nonzero on success, zero on failure (which will be treated
* as fatal by the manager).
*/
extern int BSP_setup_the_pic(rtems_irq_global_settings* config);
@@ -73,6 +71,9 @@ int C_dispatch_irq_handler (struct _BSP_Exception_frame *frame, unsigned int exc
* enables interrupts, traverses list of
* shared handlers for a given interrupt
* and restores original irq level
*
* Note that _ISR_Get_level() & friends are preferable to
* manipulating MSR directly.
*/
static inline void

View File

@@ -20,7 +20,7 @@ extern "C" {
/********* C-Exception Handlers *********************/
/* API to be used by middleware, */
/* BSP and application code (if necessary */
/* BSP and application code (if necessary) */
/****************************************************/
@@ -71,7 +71,7 @@ ppc_exc_get_handler(unsigned vector);
/********* Low-level Exception Handlers *************/
/* This API is used by middleware code */
/* This part of the API is used by middleware code */
/****************************************************/

View File

@@ -84,7 +84,7 @@ storegs(ppc_exc_int_regs *p0, ppc_exc_int_regs *p1)
" mfctr 0 ;"
" stw 0, %5(%0) ;"
" lwz 0, %6(%0) ;"
" sc ;"
" trap ;"
" stmw 0, %6(%1) ;"
" mfcr 0 ;"
" stw 0, %2(%1) ;"
@@ -129,7 +129,7 @@ clobber()
/* must not clobber R13, R1, R2 */
" stw 13, %6(2) ;"
" lmw 3, %5(2) ;"
" sc ;"
" trap ;"
" stmw 0, %4(2) ;"
" mfcr 0 ;"
" stw 0, %0(2) ;"
@@ -154,7 +154,7 @@ clobber()
typedef union { uint32_t u; uint8_t c[4]; } u32_a_t;
/* exception handler; adds 1 to all register contents (except r1,r2,r13) */
void
int
handle_clobber_exc(BSP_Exception_frame *f, int vector)
{
int i;
@@ -172,6 +172,8 @@ u32_a_t *p = (u32_a_t*)&f->GPR0;
f->EXC_CTR++;
f->EXC_XER++;
f->EXC_LR++;
f->EXC_SRR0 += 4;
return 0;
}
@@ -180,7 +182,7 @@ u32_a_t *p = (u32_a_t*)&f->GPR0;
* - clobber all registers with 0xaffe0000 + <index>
* (except: r1, r2, r13, non-sticky bits in xer)
* R2 is clobbered with the address of the pre area.
* - issue 'sc' -> SYS exception
* - issue 'trap' -> PROG exception
* - exception handler increments all reg. contents by 1,
* stores address of 'pst' area in R2 and returns control
* to ppc_exc_clobber().
@@ -197,9 +199,9 @@ u32_a_t *a, *b;
for ( i=0; i< sizeof(pre)/sizeof(uint32_t); i++ ) {
a[i].u = 0xaffe0000 + i;
}
ppc_exc_set_handler(ASM_SYS_VECTOR, handle_clobber_exc);
ppc_exc_set_handler(ASM_PROG_VECTOR, handle_clobber_exc);
clobber();
ppc_exc_set_handler(ASM_SYS_VECTOR, 0);
ppc_exc_set_handler(ASM_PROG_VECTOR, 0);
for ( i=0; i< sizeof(pre)/sizeof(uint32_t); i++ ) {
switch (i) {
case OFF(gpr1)/sizeof(uint32_t):

View File

@@ -88,15 +88,6 @@ void *lr;
lr=(LRFrame)_read_LR();
}
printk("LR: 0x%08x\n",lr);
{
uint32_t *x = (uint32_t*)sp;
uint32_t top;
asm volatile("mfspr %0, %1":"=r"(top):"i"(SPRG1));
printk("TOS: 0x%08x\n",top);
while ( x < (uint32_t*)sp->frameLink ) {
printk(" 0x%08x\n",*x++);
}
}
for (f=(LRFrame)sp, i=0; f->frameLink && i<STACK_CLAMP; f=f->frameLink) {
printk("--^ 0x%08x", (long)(f->frameLink->lr));
if (!(++i%5))