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

* new-exceptions/bspsupport/, new-exceptions/bspsupport/ppc_exc.S,
	new-exceptions/bspsupport/ppc_exc_test.c,
	new-exceptions/bspsupport/vectors.h,
	new-exceptions/bspsupport/vectors_init.c,
	new-exceptions/bspsupport/irq.c,
	new-exceptions/bspsupport/ppc_exc_bspsupp.h,
	new-exceptions/bspsupport/ppc_exc_hdl.c,
	new-exceptions/bspsupport/ppc_exc_asm_macros.h,
	new-exceptions/bspsupport/nested_irq_test.c:
	New files. Added 'middleware' code for helping BSPs implement
	exception and interrupt handling and implementing the 'new'
	RTEMS IRQ API (which I personally dislike).
This commit is contained in:
Till Straumann
2007-12-08 23:43:24 +00:00
parent 4be2812f5b
commit 94e1931c5b
10 changed files with 2182 additions and 0 deletions

View File

@@ -1,3 +1,18 @@
2007-12-08 Till Straumann <strauman@slac.stanford.edu>
* new-exceptions/bspsupport/, new-exceptions/bspsupport/ppc_exc.S,
new-exceptions/bspsupport/ppc_exc_test.c,
new-exceptions/bspsupport/vectors.h,
new-exceptions/bspsupport/vectors_init.c,
new-exceptions/bspsupport/irq.c,
new-exceptions/bspsupport/ppc_exc_bspsupp.h,
new-exceptions/bspsupport/ppc_exc_hdl.c,
new-exceptions/bspsupport/ppc_exc_asm_macros.h,
new-exceptions/bspsupport/nested_irq_test.c:
New files. Added 'middleware' code for helping BSPs implement
exception and interrupt handling and implementing the 'new'
RTEMS IRQ API (which I personally dislike).
2007-12-08 Till Straumann <strauman@slac.stanford.edu>
* new-exceptions/e500_raw_exc_init.c, new-exceptions/raw_exception.c,

View File

@@ -0,0 +1,374 @@
/*
*
* This file contains the PIC-independent implementation of the functions described in irq.h
*
* Copyright (C) 1998, 1999 valette@crf.canon.fr
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#include <stdlib.h>
#include <rtems.h>
#include "irq_supp.h"
#include <rtems/score/apiext.h> /* for post ISR signal processing */
#include <libcpu/raw_exception.h>
#include <libcpu/cpuIdent.h>
#include "vectors.h"
#include <stdlib.h>
#include <rtems/bspIo.h> /* for printk */
#include <libcpu/spr.h>
/*
* default handler connected on each irq after bsp initialization
*/
static rtems_irq_connect_data default_rtems_entry;
/*
* location used to store initial tables used for interrupt
* management.
*/
static rtems_irq_global_settings* internal_config;
static rtems_irq_connect_data* rtems_hdl_tbl;
SPR_RW(BOOKE_TSR)
/* legacy mode for bookE DEC exception;
* to avoid the double layer of function calls
* (dec_handler_bookE -> C_dispatch_irq_handler -> user handler)
* it is preferrable for the user to hook the DEC
* exception directly.
* However, the legacy mode works with less modifications
* of user code.
*/
void C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum)
{
/* clear interrupt; we must do this
* before C_dispatch_irq_handler()
* re-enables MSR_EE.
*/
_write_BOOKE_TSR( BOOKE_TSR_DIS );
C_dispatch_irq_handler(frame, ASM_DEC_VECTOR);
}
/*
* ------------------------ RTEMS Irq helper functions ----------------
*/
/*
* This function check that the value given for the irq line
* is valid.
*/
static int isValidInterrupt(int irq)
{
if ( (irq < internal_config->irqBase) || (irq >= internal_config->irqBase + internal_config->irqNb))
return 0;
return 1;
}
/*
* ------------------------ RTEMS Shared Irq Handler Mngt Routines ----------------
*/
int BSP_install_rtems_shared_irq_handler (const rtems_irq_connect_data* irq)
{
rtems_interrupt_level level;
rtems_irq_connect_data* vchain;
if (!isValidInterrupt(irq->name)) {
printk("Invalid interrupt vector %d\n",irq->name);
return 0;
}
rtems_interrupt_disable(level);
if ( (int)rtems_hdl_tbl[irq->name].next_handler == -1 ) {
rtems_interrupt_enable(level);
printk("IRQ vector %d already connected to an unshared handler\n",irq->name);
return 0;
}
vchain = (rtems_irq_connect_data*)malloc(sizeof(rtems_irq_connect_data));
/* save off topmost handler */
vchain[0]= rtems_hdl_tbl[irq->name];
/*
* store the data provided by user
*/
rtems_hdl_tbl[irq->name] = *irq;
/* link chain to new topmost handler */
rtems_hdl_tbl[irq->name].next_handler = (void *)vchain;
/*
* enable_irq_at_pic is supposed to ignore
* requests to disable interrupts outside
* of the range handled by the PIC
*/
BSP_enable_irq_at_pic(irq->name);
/*
* Enable interrupt on device
*/
if (irq->on)
irq->on(irq);
rtems_interrupt_enable(level);
return 1;
}
/*
* ------------------------ RTEMS Single Irq Handler Mngt Routines ----------------
*/
int BSP_install_rtems_irq_handler (const rtems_irq_connect_data* irq)
{
rtems_interrupt_level level;
if (!isValidInterrupt(irq->name)) {
printk("Invalid interrupt vector %d\n",irq->name);
return 0;
}
/*
* Check if default handler is actually connected. If not issue an error.
* You must first get the current handler via i386_get_current_idt_entry
* and then disconnect it using i386_delete_idt_entry.
* RATIONALE : to always have the same transition by forcing the user
* to get the previous handler before accepting to disconnect.
*/
rtems_interrupt_disable(level);
if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
rtems_interrupt_enable(level);
printk("IRQ vector %d already connected\n",irq->name);
return 0;
}
/*
* store the data provided by user
*/
rtems_hdl_tbl[irq->name] = *irq;
rtems_hdl_tbl[irq->name].next_handler = (void *)-1;
/*
* enable_irq_at_pic is supposed to ignore
* requests to disable interrupts outside
* of the range handled by the PIC
*/
BSP_enable_irq_at_pic(irq->name);
/*
* Enable interrupt on device
*/
if (irq->on)
irq->on(irq);
rtems_interrupt_enable(level);
return 1;
}
int BSP_get_current_rtems_irq_handler (rtems_irq_connect_data* irq)
{
rtems_interrupt_level level;
if (!isValidInterrupt(irq->name)) {
return 0;
}
rtems_interrupt_disable(level);
*irq = rtems_hdl_tbl[irq->name];
rtems_interrupt_enable(level);
return 1;
}
int BSP_remove_rtems_irq_handler (const rtems_irq_connect_data* irq)
{
rtems_irq_connect_data *pchain= NULL, *vchain = NULL;
rtems_interrupt_level level;
if (!isValidInterrupt(irq->name)) {
return 0;
}
/*
* Check if default handler is actually connected. If not issue an error.
* You must first get the current handler via i386_get_current_idt_entry
* and then disconnect it using i386_delete_idt_entry.
* RATIONALE : to always have the same transition by forcing the user
* to get the previous handler before accepting to disconnect.
*/
rtems_interrupt_disable(level);
if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
rtems_interrupt_enable(level);
return 0;
}
if( (int)rtems_hdl_tbl[irq->name].next_handler != -1 )
{
int found = 0;
for( (pchain= NULL, vchain = &rtems_hdl_tbl[irq->name]);
(vchain->hdl != default_rtems_entry.hdl);
(pchain= vchain, vchain = (rtems_irq_connect_data*)vchain->next_handler) )
{
if( vchain->hdl == irq->hdl )
{
found= -1; break;
}
}
if( !found )
{
rtems_interrupt_enable(level);
return 0;
}
}
else
{
if (rtems_hdl_tbl[irq->name].hdl != irq->hdl)
{
rtems_interrupt_enable(level);
return 0;
}
}
/*
* disable_irq_at_pic is supposed to ignore
* requests to disable interrupts outside
* of the range handled by the PIC
*/
BSP_disable_irq_at_pic(irq->name);
/*
* Disable interrupt on device
*/
if (irq->off)
irq->off(irq);
/*
* restore the default irq value
*/
if( !vchain )
{
/* single handler vector... */
rtems_hdl_tbl[irq->name] = default_rtems_entry;
}
else
{
if( pchain )
{
/* non-first handler being removed */
pchain->next_handler = vchain->next_handler;
}
else
{
/* first handler isn't malloc'ed, so just overwrite it. Since
the contents of vchain are being struct copied, vchain itself
goes away */
vchain = vchain->next_handler;
rtems_hdl_tbl[irq->name]= *vchain;
}
free(vchain);
}
rtems_interrupt_enable(level);
return 1;
}
/*
* Less cumbersome, alternate entry points;
* RETURNS: more traditional, 0 on success, nonzero on error
*/
static int doit(
int (*p)(const rtems_irq_connect_data*),
rtems_irq_number n,
rtems_irq_hdl hdl,
rtems_irq_hdl_param prm)
{
rtems_irq_connect_data xx;
xx.name = n;
xx.hdl = hdl;
xx.handle = prm;
xx.on = 0;
xx.off = 0;
xx.isOn = 0;
return ! p(&xx);
}
int BSP_rtems_int_connect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
{
return doit(BSP_install_rtems_shared_irq_handler, n, hdl, p);
}
int BSP_rtems_int_disconnect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
{
return doit(BSP_remove_rtems_irq_handler, n, hdl, p);
}
/*
* RTEMS Global Interrupt Handler Management Routines
*/
int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config)
{
int i;
rtems_interrupt_level level;
rtems_irq_connect_data* vchain;
/*
* Store various code accelerators
*/
internal_config = config;
default_rtems_entry = config->defaultEntry;
rtems_hdl_tbl = config->irqHdlTbl;
rtems_interrupt_disable(level);
if ( !BSP_setup_the_pic(config) ) {
printk("PIC setup failed; leaving IRQs OFF\n");
return 0;
}
for ( i = config->irqBase; i < config->irqBase + config->irqNb; i++ ) {
for( vchain = &rtems_hdl_tbl[i];
((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl);
vchain = (rtems_irq_connect_data*)vchain->next_handler )
{
if (vchain->on)
vchain->on(vchain);
}
}
rtems_interrupt_enable(level);
{
ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler);
if ( ppc_cpu_is_bookE() ) {
/* bookE decrementer interrupt needs to be cleared BEFORE
* dispatching the user ISR (because the user ISR is called
* with EE enabled)
* We do this so that existing DEC handlers can be used
* with minor modifications.
*/
ppc_exc_set_handler(ASM_BOOKE_PIT_VECTOR, C_dispatch_dec_handler_bookE);
} else {
ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler);
}
}
return 1;
}
int BSP_rtems_irq_mngt_get(rtems_irq_global_settings** config)
{
*config = internal_config;
return 0;
}

View File

@@ -0,0 +1,107 @@
/*
* Test nested interrupts.
*
* Author: Till Straumann <strauman@slac.stanford.edu>, 2007
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
/*
* Needs board with 2 available openpic timers
*
* 'timer_instdis(timer, install, period)'
*
* installs 'timer_isr' to openpic timer # 'timer'.
* The interrupt priority is set to 8 + timer#
*
* The timer_isr prints a message then polls
* the variable 'timer_poll' while it has the value
* of the timer # then sets it to -1 and prints
* the 'leave' message.
*
* To test nested interrupts:
*
* timer_instdis(0, 1, period)
* wait_a_bit()
* timer_instdis(1, 1, period)
* timer_poll = 0;
*
* As soon as timer 0's IRQ fires the
* isr prints
* TIMER ISR (0) ...
* then starts polling (since timer_poll == 0 )
* eventually, timer 1 goes off, interrupts (because
* it's priority is 9 (i.e., higher than timer 0's priority)
* and prints
* TIMER ISR (1)
* it skips polling since timer_poll is 0, not 1 but
* resets timer_poll -1 and prints
* Leaving ISR (1)
* timer 0 isr resumes polling and finds timer_poll == -1
* so it also writes -1 to timer_poll and exits, printing
* Leaving ISR (0)
*
* The timer IRQs can be unhooked with
* timer_instdis( 0, 0, period );
* timer_instdis( 1, 0, period );
*/
#include <rtems.h>
#include <rtems/bspIo.h>
#include <bsp/openpic.h>
#include <bsp/irq.h>
#include <inttypes.h>
#include <stdio.h>
volatile int timer_poll=-1;
static void timer_isr(rtems_irq_hdl_param p)
{
uint32_t top;
uint32_t r1;
uint32_t lat = (OpenPIC->Global.Timer[(int)p].Current_Count & 0x7fffffff);
lat = OpenPIC->Global.Timer[(int)p].Base_Count - lat;
asm volatile("mfspr %0, %2; mr %1, 1":"=r"(top),"=r"(r1):"i"(SPRG1));
printk("Timer ISR (%i): LAT: 0x%08x, TOP 0x%08x, BOT 0x%08x, SP 0x%08x\n",
(int)p, lat, top, top-rtems_configuration_get_interrupt_stack_size(), r1);
printk("_ISR_Nest_level %i\n", _ISR_Nest_level);
while ( timer_poll == (int)p )
;
timer_poll = -1;
printk("Leaving ISR (%i)\n",(int)p);
}
int timer_instdis(int t, int inst, unsigned period)
{
rtems_irq_connect_data xx;
xx.name = BSP_MISC_IRQ_LOWEST_OFFSET + t;
xx.hdl = timer_isr;
xx.handle = (rtems_irq_hdl_param)t;
xx.on = 0;
xx.off = 0;
xx.isOn = 0;
if ( !inst ) {
openpic_maptimer(t, 0);
openpic_inittimer(t, 0, 0);
}
if ( ! ( inst ? BSP_install_rtems_irq_handler(&xx) : BSP_remove_rtems_irq_handler(&xx) ) ) {
openpic_maptimer(t, 0);
openpic_inittimer(t, 0, 0);
fprintf(stderr,"unable to %s timer ISR #%i\n", inst ? "install" : "remove", t);
return -1;
}
if ( inst ) {
openpic_maptimer( t, 1 );
openpic_inittimer( t, 8 + t, OPENPIC_VEC_SOURCE + xx.name );
openpic_settimer( t, period, 1 );
}
return 0;
}

View File

@@ -0,0 +1,368 @@
/*
* (c) 1999, Eric Valette valette@crf.canon.fr
*
* Modified and partially rewritten by Till Straumann, 2007
*
* Low-level assembly code for PPC exceptions.
*
* This file was written with the goal to eliminate
* ALL #ifdef <cpu_flavor> conditionals -- please do not
* reintroduce such statements.
*/
/* Load macro definitions */
#include "ppc_exc_asm_macros.h"
/******************************************************/
/* PROLOGUES */
/******************************************************/
/*
* Expand prologue snippets for classic, ppc405-critical, bookE-critical
* and E500 machine-check, synchronous and asynchronous exceptions
*/
PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_std _VEC=0 _PRI=std _FLVR=std
PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_p405_crit _VEC=0 _PRI=crit _FLVR=p405_crit
PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_bookE_crit _VEC=0 _PRI=crit _FLVR=bookE_crit
PPC_EXC_MIN_PROLOG_SYNC _NAME=tmpl_e500_mchk _VEC=0 _PRI=mchk _FLVR=e500_mchk
PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_std _VEC=0 _PRI=std _FLVR=std
PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_p405_crit _VEC=0 _PRI=crit _FLVR=p405_crit
PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_bookE_crit _VEC=0 _PRI=crit _FLVR=bookE_crit
PPC_EXC_MIN_PROLOG_ASYNC _NAME=tmpl_e500_mchk _VEC=0 _PRI=mchk _FLVR=e500_mchk
.global ppc_exc_min_prolog_size
ppc_exc_min_prolog_size = 4 * 4
/* Special prologue for 603e-style CPUs.
*
* 603e shadows GPR0..GPR3 for certain exceptions. We must switch
* that off before we can use the stack pointer. Note that this is
* ONLY safe if the shadowing is actually active -- otherwise, r1
* is destroyed. We deliberately use r1 so problems become obvious
* if this is abused!
*/
.global ppc_exc_tgpr_clr_prolog
ppc_exc_tgpr_clr_prolog:
mfmsr r1
rlwinm r1,r1,0,15,13
mtmsr r1
isync
/* FALL THRU TO 'auto' PROLOG */
/* Determine vector dynamically/automatically
*
* BUT: - only standard exceptions (no critical ones)
* - vector offset must be on 256 Byte boundary.
*/
.global ppc_exc_min_prolog_auto
ppc_exc_min_prolog_auto:
stwu r1, -EXCEPTION_FRAME_END(r1)
stw r3, GPR3_OFFSET(r1)
mflr r3
bla wrap_auto
.global ppc_exc_tgpr_clr_prolog_size
ppc_exc_tgpr_clr_prolog_size = . - ppc_exc_tgpr_clr_prolog
/*
* Automatic vector, asynchronous exception; however,
* automatic vector calculation is less efficient than
* using an explicit vector in a minimal prolog snippet.
* The latter method is preferable since there usually
* are few asynchronous exceptions.
*
* For generic exceptions (which are the bulk) using
* the 'auto' prologue is OK since performance is not
* really an issue.
*/
.global ppc_exc_min_prolog_auto_async
ppc_exc_min_prolog_auto_async:
stw r1, ppc_exc_lock_std@sdarel(r13)
stw r3, ppc_exc_gpr3_std@sdarel(r13)
mflr r3
bla wrap_auto_async
/******************************************************/
/* WRAPPERS */
/******************************************************/
/* Tag start and end of the wrappers.
* If exceptions are installed farther removed
* from the text area than 32M then the wrappers
* must be moved to an area that is reachable
* from where the prologues reside. Branches into
* C-code are far.
*/
.global __ppc_exc_wrappers_start
__ppc_exc_wrappers_start = .
/* Expand wrappers for different exception flavors */
/* Standard/classic powerpc */
WRAP _FLVR=std _PRI=std _SRR0=srr0 _SRR1=srr1 _RFI=rfi
/* ppc405 has a critical exception using srr2/srr3 */
WRAP _FLVR=p405_crit _PRI=crit _SRR0=srr2 _SRR1=srr3 _RFI=rfci
/* bookE has critical exception using csrr0 cssr1 */
WRAP _FLVR=bookE_crit _PRI=crit _SRR0=csrr0 _SRR1=csrr1 _RFI=rfci
/* e500 has machine-check exception using mcsrr0 mcssr1 */
WRAP _FLVR=e500_mchk _PRI=mchk _SRR0=mcsrr0 _SRR1=mcsrr1 _RFI=rfmci
/* LR holds vector, r3 holds orig. LR */
wrap_auto:
stw r14, GPR14_OFFSET(r1)
/* find address where we jumped from */
mflr r14
/* restore LR */
mtlr r3
/* compute vector into R3 */
rlwinm r3, r14, 24, 26, 31
/* we're now in almost the same state as if called by
* min_prolog_std but we must skip saving r14
* since that's done already
*/
b wrap_no_save_r14_std
wrap_auto_async:
stwu r1, -EXCEPTION_FRAME_END(r1)
stw r14, GPR14_OFFSET(r1)
/* find address where we jumped from */
mflr r14
/* restore LR */
mtlr r3
/* set upper bits to indicate that non-volatile
* registers should not be saved/restored.
*/
li r3, 0xffff8000
/* compute vector into R3 */
rlwimi r3, r14, 24, 26, 31
/* we're now in almost the same state as if called by
* min_prolog_std but we must skip saving r14
* since that's done already
*/
b wrap_no_save_r14_std
/*
* Common code for all flavors of exception and whether
* they are synchronous or asynchronous.
*
* Call with
* r3 : vector
* r4 : srr0
* r5 : srr1
* r14: exception frame
* cr4: OR of lower-priority locks
* cr2: exception type (asyn/isr [<0] or synchronous [>=0])
* lr : is updated by 'bl'
* all others: original state
*
* If this is an asynchronous exception ( cr2 < 0 ):
* - save volatile registers only,
* - disable thread dispatching,
* - switch to interrupt stack (if necessary),
* - call the C-dispatcher,
* - switch back the stack,
* - decrement the dispatch-disable level
* - check if it is safe to dispatch (disable-level must be 0
* AND no lower-priority asynchronous exception must be under
* way (as indicated by the lock variables).
* - If it would be OK to dispatch, call the C-wrapup code.
* - restore volatile registers
*
* Otherwise, i.e., if we are dealing with a synchronous exception
* then:
* - save all registers
* - call the C-dispatcher
* - restore registers
*/
wrap_common:
stw r4, SRR0_FRAME_OFFSET(r14)
stw r5, SRR1_FRAME_OFFSET(r14)
/* prepare for calling C code; */
/* use non-volatile r15 for remembering lr */
stw r15, GPR15_OFFSET(r14)
/* save vector; negative if only scratch regs. are valid */
stw r3, EXCEPTION_NUMBER_OFFSET(r14)
/* save scratch registers */
/* r2 should be unused or fixed anyways (eabi sdata2) */
stw r0, GPR0_OFFSET(r14)
stw r2, GPR2_OFFSET(r14)
stw r6, GPR6_OFFSET(r14)
stw r7, GPR7_OFFSET(r14)
stw r8, GPR8_OFFSET(r14)
stw r9, GPR9_OFFSET(r14)
stw r10, GPR10_OFFSET(r14)
stw r11, GPR11_OFFSET(r14)
stw r12, GPR12_OFFSET(r14)
/* r13 must be fixed anyways (sysv sdata) */
/* save LR */
mflr r15
mfctr r4
mfxer r5
stw r4, EXC_CTR_OFFSET(r14)
stw r5, EXC_XER_OFFSET(r14)
/*
* Switch MMU / RI on if necessary;
* remember decision in cr3
*/
lwz r4, ppc_exc_msr_bits@sdarel(r13)
cmpwi cr3, r4, 0
beq cr3, 1f
mfmsr r5
or r5, r5, r4
mtmsr r5
sync
isync
1:
/* If this is a asynchronous exception we skip ahead */
blt cr2, skip_save_nonvolatile_regs
/* YES; they want everything ('normal exception') */
/* save original stack pointer */
lwz r5, EXC_MIN_GPR1(r14)
stw r5, GPR1_OFFSET(r14)
stw r13, GPR13_OFFSET(r14)
/* store r16..r31 into the exception frame */
stmw r16, GPR16_OFFSET(r14)
skip_save_nonvolatile_regs:
/* store address of exception frame in r4; vector is in r3 */
addi r4, r14, FRAME_LINK_SPACE
/* clear CR[6] to make sure no vararg callee assumes that
* there are any valid FP regs
*/
crxor 6,6,6
/* Far branch to ppc_C_wrapper */
lis r5, ppc_exc_C_wrapper@h
addi r4, r14, FRAME_LINK_SPACE
ori r5, r5, ppc_exc_C_wrapper@l
mtlr r5
blrl
/* do not clobber r3 since we pass the return value
* of ppc_exc_C_wrapper on to ppc_exc_wrapup
*/
/* skip decrementing the thread-dispatch disable level
* and calling ppc_exc_wrapup if this is a synchronous
* exception.
*/
bge cr2, restore_nonvolatile_regs
/* decrement ISR nest level;
* disable all interrupts.
*/
lwz r4, ppc_exc_msr_irq_mask@sdarel(r13)
mfmsr r5
andc r4, r5, r4
mtmsr r4
lwz r4, _ISR_Nest_level@sdarel(r13)
addi r4, r4, -1
stw r4, _ISR_Nest_level@sdarel(r13)
/* switch back to original stack */
mr r1, r14
/* restore interrupt mask */
mtmsr r5
/* decrement thread_dispatch level and check
* if we have to run the dispatcher.
*/
lwz r5, _Thread_Dispatch_disable_level@sdarel(r13)
addic. r5, r5, -1
stw r5, _Thread_Dispatch_disable_level@sdarel(r13)
/* test _Thread_Dispatch_disable nesting level AND
* lower priority locks (in cr4); ONLY if
* _Thread_Dispatch_disable_level == 0 AND no lock is set
* then call ppc_exc_wrapup which may do a context switch.
*/
crand EQ(cr0), EQ(cr0), EQ(cr4)
bne 2f
crxor 6,6,6
/* Far branch to ppc_exc_wrapup */
lis r5, ppc_exc_wrapup@h
addi r4, r14, FRAME_LINK_SPACE
ori r5, r5, ppc_exc_wrapup@l
mtlr r5
blrl
2:
lwz r14, GPR14_OFFSET(r1)
/* we can skip restoring r16..r31 */
b skip_restore_nonvolatile_regs
restore_nonvolatile_regs:
/* synchronous exc: restore everything from the exception frame */
lwz r14, GPR14_OFFSET(r1)
/* restore stack pointer */
lwz r5, GPR1_OFFSET(r1)
stw r5, EXC_MIN_GPR1(r1)
/* restore non-volatile regs */
lwz r13, GPR13_OFFSET(r1)
lmw r16, GPR16_OFFSET(r1)
skip_restore_nonvolatile_regs:
lwz r3, EXC_XER_OFFSET(r1)
lwz r4, EXC_CTR_OFFSET(r1)
mtxer r3
mtctr r4
/* restore lr, r15 */
mtlr r15
lwz r15, GPR15_OFFSET(r1)
/* restore scratch regs */
lwz r12, GPR12_OFFSET(r1)
lwz r11, GPR11_OFFSET(r1)
lwz r10, GPR10_OFFSET(r1)
lwz r9, GPR9_OFFSET(r1)
lwz r8, GPR8_OFFSET(r1)
lwz r7, GPR7_OFFSET(r1)
lwz r6, GPR6_OFFSET(r1)
lwz r3, GPR3_OFFSET(r1)
lwz r2, GPR2_OFFSET(r1)
lwz r0, GPR0_OFFSET(r1)
beq cr3, 2f
/* restore MSR settings */
lwz r5, ppc_exc_msr_bits@sdarel(r13)
mfmsr r4
andc r4, r4, r5
mtmsr r4
sync
isync
2:
lwz r4, EXC_CR_OFFSET(r1)
mtcr r4
/* restore SRR and stack */
lwz r4, SRR0_FRAME_OFFSET(r1)
lwz r5, SRR1_FRAME_OFFSET(r1)
blr
.global __ppc_exc_wrappers_end
__ppc_exc_wrappers_end = .

View File

@@ -0,0 +1,278 @@
/*
* (c) 1999, Eric Valette valette@crf.canon.fr
*
* Modified and partially rewritten by Till Straumann, 2007
*
* Low-level assembly code for PPC exceptions (macros).
*
* This file was written with the goal to eliminate
* ALL #ifdef <cpu_flavor> conditionals -- please do not
* reintroduce such statements.
*/
#include <rtems/score/cpu.h>
#include <bsp/vectors.h>
#include <libcpu/raw_exception.h>
#define EXC_MIN_GPR1 0
#define FRAME_LINK_SPACE 8
#define r0 0
#define r1 1
#define r2 2
#define r3 3
#define r4 4
#define r5 5
#define r6 6
#define r7 7
#define r8 8
#define r9 9
#define r10 10
#define r11 11
#define r12 12
#define r13 13
#define r14 14
#define r15 15
#define r16 16
#define r17 17
#define r18 18
#define r19 19
#define r20 20
#define r21 21
#define r22 22
#define r23 23
#define r24 24
#define r25 25
#define r26 26
#define r27 27
#define r28 28
#define r29 29
#define r30 30
#define r31 31
#define cr0 0
#define cr1 1
#define cr4 4
#define LT(cr) ((cr)*4+0)
#define GT(cr) ((cr)*4+1)
#define EQ(cr) ((cr)*4+2)
#define NOFRAME 0xffff8000
/* Switch r1 to interrupt stack if not already there.
*
* USES: RA, RB
* ON EXIT: RA, RB available, r1 points into interrupt
* stack.
*
* NOTES:
* - NEVER store stuff in a frame before
* reserving it (stwu r1) - otherwise
* higher-priority exception may overwrite.
* - algorithm should allow nesting of higher
* priority exceptions (HPE) (by disabling
* them while the stack is switched).
*/
#if 0
.macro SWITCH_STACK RA RB FLVR
mfspr \RB, SPRG1
cmplw cr0, r1, \RB
bgt do_r1_reload_\FLVR
lwz \RA, ppc_exc_intr_stack_size@sdarel(r13)
subf \RB, \RB, \RA
cmplw cr0, r1, \RB
bge no_r1_reload_\FLVR
do_r1_reload_\FLVR:
mfspr r1, SPRG1
no_r1_reload_\FLVR:
.endm
#else
.macro SWITCH_STACK RA RB FLVR
/* disable interrupts */
lwz \RA, ppc_exc_msr_irq_mask@sdarel(r13)
mfmsr \RB
and \RA, \RB, \RA
mtmsr \RA
/* increment nest level */
lwz \RA, _ISR_Nest_level@sdarel(r13)
cmplwi cr0, \RA, 0
bne no_r1_reload_\FLVR
/* reload r1 */
mfspr r1, SPRG1
no_r1_reload_\FLVR:
addi \RA, \RA, 1
stw \RA, _ISR_Nest_level@sdarel(r13)
/* restore IRQ mask */
mtmsr \RB
.endm
#endif
/*
* Minimal prologue snippets:
*
* Rationale: on some PPCs the vector offsets are spaced
* as closely as 16 bytes.
*
* If we deal with asynchronous exceptions ('interrupts')
* then we can use 4 instructions to
* 1. atomically write lock to indicate ISR is in progress
* (we cannot atomically increase the Thread_Dispatch_disable_level,
* see README)
* 2. save a register in special area
* 3. load register with vector info
* 4. branch
*
* If we deal with a synchronous exception (no stack switch
* nor dispatch-disabling necessary) then it's easier:
* 1. push stack frame
* 2. save register on stack
* 3. load register with vector info
* 4. branch
*
*/
.macro PPC_EXC_MIN_PROLOG_ASYNC _NAME _VEC _PRI _FLVR
.global ppc_exc_min_prolog_async_\_NAME
ppc_exc_min_prolog_async_\_NAME:
/* Atomically write lock variable in 1st instruction with non-zero value
* (r1 is always nonzero; r13 could also be used)
*/
stw r1, ppc_exc_lock_\_PRI@sdarel(r13)
/* We have no stack frame yet; store r3 in special area;
* a higher-priority (critical) interrupt uses a different area
* (hence the different prologue snippets) (\PRI)
*/
stw r3, ppc_exc_gpr3_\_PRI@sdarel(r13)
/* Load vector.
*/
li r3, ( \_VEC | 0xffff8000 )
/* Branch (must be within 32MB)
*/
ba wrap_\_FLVR
.endm
.macro PPC_EXC_MIN_PROLOG_SYNC _NAME _VEC _PRI _FLVR
.global ppc_exc_min_prolog_sync_\_NAME
ppc_exc_min_prolog_sync_\_NAME:
stwu r1, -EXCEPTION_FRAME_END(r1)
stw r3, GPR3_OFFSET(r1)
li r3, \_VEC
ba wrap_nopush_\_FLVR
.endm
.macro TEST_LOCK_std
/* 'std' is lowest level, i.e., can not be locked -> EQ(cr4) = 1 */
creqv EQ(cr4), EQ(cr4), EQ(cr4)
.endm
/* critical-exception wrapper has to check 'std' lock: */
.macro TEST_LOCK_crit
lwz r5, ppc_exc_lock_std@sdarel(r13)
cmpli cr4, r5, 0
.endm
/* machine-check wrapper has to check 'std' and 'crit' locks */
.macro TEST_LOCK_mchk
lwz r5, ppc_exc_lock_std@sdarel(r13)
cmpli cr4, r5, 0
lwz r5, ppc_exc_lock_crit@sdarel(r13)
cmpli cr0, r5, 0
cror EQ(cr4), EQ(cr4), EQ(cr0)
.endm
/* Minimal prologue snippets jump into WRAP
* which prepares calling code common to all
* flavors of exceptions.
* We must have this macro instantiated for
* each possible flavor of exception so that
* we use the proper lock variable, SRR register pair and
* RFI instruction.
*/
.macro WRAP _FLVR _PRI _SRR0 _SRR1 _RFI
wrap_\_FLVR:
stwu r1, -EXCEPTION_FRAME_END(r1)
wrap_nopush_\_FLVR:
stw r14, GPR14_OFFSET(r1)
wrap_no_save_r14_\_FLVR:
/* Save r4 r5 and CR; we want CR soon */
mfcr r14
stw r4, GPR4_OFFSET(r1)
stw r5, GPR5_OFFSET(r1)
stw r14, EXC_CR_OFFSET(r1)
/* Check if this is an 'interrupt-type' exception
* (MSB vector is set).
* 'interrupt-type' exceptions disable thread dispatching
* and switch to a private stack.
* The type of exception is kept in (non-volatile) cr2
* < 0 -> interrupt-type
* > 0 -> 'normal' exception; always on task stack,
* may switch context at any time.
*/
cmpwi cr2, r3, 0
/*
* Save frame address in r14
*/
mr r14, r1
bge cr2, no_thread_dispatch_disable_\_FLVR
/* first thing we need to
* increment the thread-dispatch disable level
* in case a higher priority exception occurs
* we don't want it to run the scheduler.
*/
lwz r5, _Thread_Dispatch_disable_level@sdarel(r13)
addi r5, r5, 1
stw r5, _Thread_Dispatch_disable_level@sdarel(r13)
/* clear lock; no higher-priority interrupt occurring after
* this point can cause a context switch.
*/
li r5, 0
stw r5, ppc_exc_lock_\_PRI@sdarel(r13)
/* test lower-priority locks; result in (non-volatile) cr4 */
TEST_LOCK_\_PRI
/* Peform stack switch if necessary */
SWITCH_STACK RA=r4 RB=r5 FLVR=\_FLVR
/* save r3, in exception frame */
lwz r5, ppc_exc_gpr3_\_PRI@sdarel(r13)
stw r5, GPR3_OFFSET(r14)
no_thread_dispatch_disable_\_FLVR:
/* save lr into exception frame */
mflr r4
stw r4, EXC_LR_OFFSET(r14)
/* we now have r4,r5,lr,cr available;
* r3 still holds the vector,
* r14 a pointer to the exception frame (always on
* task stack)
* r1 is the stack pointer, either on the task stack
* or on the IRQ stack
*/
/* retrieve SRR0/SRR1 */
mf\_SRR0 r4
mf\_SRR1 r5
/* branch to common routine */
bl wrap_common
/* restore SRR, r4, r5, r1 (stack pointer) and lr */
mt\_SRR0 r4
mt\_SRR1 r5
/* restore lr */
lwz r5, EXC_LR_OFFSET(r1)
lwz r4, GPR4_OFFSET(r1)
mtlr r5
lwz r5, GPR5_OFFSET(r1)
lwz r1, EXC_MIN_GPR1(r1)
\_RFI
.endm

View File

@@ -0,0 +1,128 @@
/* PowerPC exception handling middleware; consult README for more
* information.
*
* Author: Till Straumann <strauman@slac.stanford.edu>, 2007
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#ifndef PPC_EXC_SHARED_H
#define PPC_EXC_SHARED_H
#ifdef __cplusplus
extern "C" {
#endif
/********* C-Exception Handlers *********************/
/* API to be used by middleware, */
/* BSP and application code (if necessary */
/****************************************************/
typedef int (*ppc_exc_handler_t)(BSP_Exception_frame *f, int vector);
/*
* Bits in MSR that are enabled during execution of exception handlers / ISRs
* (on classic PPC these are DR/IR/RI [default], on bookE-style CPUs they should
* be set to 0 during initialization)
*
* By default, the setting of these bits that is in effect when exception
* handling is initialized is used.
*/
extern uint32_t ppc_exc_msr_bits;
/*
* Set of MSR bits required to disable all
* asynchronous exceptions (depends on CPU type;
* must be set during initialization).
* Interrupt are disabled by writing the
* one's complement of this mask to msr:
* msr &= ~ppc_exc_msr_irq_mask;
*/
extern uint32_t ppc_exc_msr_irq_mask;
/*
* Hook C exception handlers.
* - handlers for asynchronous exceptions run on the ISR stack
* with thread-dispatching disabled.
* - handlers for synchronous exceptions run on the task stack
* with thread-dispatching enabled.
*
* If a particular slot is NULL then the traditional 'globalExcHdl' is used.
*
* ppc_exc_set_handler() registers a handler (returning 0 on success,
* -1 if the vector argument is too big).
*
* It is legal to set a NULL handler. This leads to the globalExcHdl
* being called if an exception for 'vector' occurs.
*/
int
ppc_exc_set_handler(unsigned vector, ppc_exc_handler_t hdl);
/* ppc_exc_get_handler() retrieves the currently active handler.
*/
ppc_exc_handler_t
ppc_exc_get_handler(unsigned vector);
/********* Low-level Exception Handlers *************/
/* This API is used by middleware code */
/****************************************************/
typedef uint32_t ppc_exc_min_prolog_t[4];
/* Templates are ppc_raw_except_func BUT they must be exactly 16 bytes */
typedef rtems_raw_except_func ppc_exc_min_prolog_template_t;
/*
* Expand a prolog template into 'buf' using vector 'vec'
*/
void
ppc_exc_min_prolog_expand(ppc_exc_min_prolog_t buf, ppc_exc_min_prolog_template_t templ, uint16_t vec);
extern unsigned ppc_exc_min_prolog_size[];
/* Symbols are defined by the linker; declare as an array so
* that gcc doesn't attempt to emit a relocation looking for
* it in the SDA section
*/
extern unsigned ppc_exc_tgpr_clr_prolog_size[];
/* Templates for ppc_exc_min_prolog_expand() which fills-in the vector information */
extern void ppc_exc_min_prolog_async_tmpl_std();
extern void ppc_exc_min_prolog_sync_tmpl_std();
extern void ppc_exc_min_prolog_async_tmpl_p405_crit();
extern void ppc_exc_min_prolog_sync_tmpl_p405_crit();
extern void ppc_exc_min_prolog_async_tmpl_bookE_crit();
extern void ppc_exc_min_prolog_sync_tmpl_bookE_crit();
extern void ppc_exc_min_prolog_sync_tmpl_e500_mchk();
extern void ppc_exc_min_prolog_async_tmpl_e500_mchk();
/* Special prologue for handling register shadowing on 603-style CPUs */
extern void ppc_exc_tgpr_clr_prolog();
/* Classic prologue which determines the vector dynamically from
* the offset address. This must only be used for classic, synchronous
* exceptions with a vector offset aligned on a 256-byte boundary.
*/
extern void ppc_exc_min_prolog_auto();
/* CPU support may store the address of a function here
* that can be used by the default exception handler to
* obtain fault-address info which is helpful. Unfortunately,
* the SPR holding this information is not uniform
* across PPC families so we need assistance from
* CPU support
*/
extern uint32_t (*ppc_exc_get_DAR)();
#ifdef __cplusplus
};
#endif
#endif

View File

@@ -0,0 +1,172 @@
/* PowerPC exception handling middleware; consult README for more
* information.
*
* Author: Till Straumann <strauman@slac.stanford.edu>, 2007
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#include <stdint.h>
#include <string.h>
#include <rtems.h>
#include <rtems/score/cpu.h>
#include <libcpu/raw_exception.h>
#include <libcpu/spr.h>
#include <rtems/score/apiext.h>
#include "vectors.h"
#include "ppc_exc_bspsupp.h"
/* offset into min-prolog where vector # is hardcoded */
#define PPC_EXC_PROLOG_VEC_OFFSET 2
/* Provide temp. storage space for a few registers.
* This is used by the assembly code prior to setting up
* the stack.
* One set is needed for each exception type with its
* own SRR0/SRR1 pair since such exceptions may nest.
*
* NOTE: The assembly code needs these variables to
* be in the .sdata section and accesses them
* via R13.
*/
uint32_t ppc_exc_lock_std = 0;
uint32_t ppc_exc_lock_crit = 0;
uint32_t ppc_exc_lock_mchk = 0;
uint32_t ppc_exc_gpr3_std = 0;
uint32_t ppc_exc_gpr3_crit = 0;
uint32_t ppc_exc_gpr3_mchk = 0;
uint32_t ppc_exc_msr_irq_mask = MSR_EE;
/* MSR bits to enable once critical status info is saved and the stack
* is switched; must be set depending on CPU type
*
* Default is set here for classic PPC CPUs with a MMU
* but is overridden from vectors_init.c
*/
uint32_t ppc_exc_msr_bits = MSR_IR | MSR_DR | MSR_RI;
/* Table of C-handlers */
static ppc_exc_handler_t ppc_exc_handlers[LAST_VALID_EXC + 1] = {0, };
ppc_exc_handler_t
ppc_exc_get_handler(unsigned vector)
{
if ( vector > LAST_VALID_EXC )
return 0;
return ppc_exc_handlers[vector];
}
int
ppc_exc_set_handler(unsigned vector, ppc_exc_handler_t hdl)
{
if ( vector > LAST_VALID_EXC )
return -1;
ppc_exc_handlers[vector] = hdl;
return 0;
}
/* This routine executes on the interrupt stack (if vect < 0) */
int
ppc_exc_C_wrapper(int vect, BSP_Exception_frame *f)
{
int i = vect & 0x3f;
int rval = 1;
if ( i <= LAST_VALID_EXC && ppc_exc_handlers[i] ) {
rval = ppc_exc_handlers[i](f, i);
}
if ( rval ) {
/* not handled, so far ... */
if ( globalExceptHdl ) {
/*
* global handler must be prepared to
* deal with asynchronous exceptions!
*/
globalExceptHdl(f);
}
rval = 0;
}
return rval;
}
void
ppc_exc_wrapup(int ll_rval, BSP_Exception_frame *f)
{
/* Check if we need to run the global handler now */
if ( ll_rval ) {
/* We get here if ppc_exc_C_wrapper() returned nonzero.
* This could be useful if we need to do something
* with thread-dispatching enabled (at this point it is)
* after handling an asynchronous exception.
*/
}
/* dispatch_disable level is decremented from assembly code. */
if ( _Context_Switch_necessary )
_Thread_Dispatch();
else if ( _ISR_Signals_to_thread_executing ) {
_ISR_Signals_to_thread_executing = 0;
/*
* Process pending signals that have not already been
* processed by _Thread_Dispatch. This happens quite
* unfrequently : the ISR must have posted an action
* to the current running thread.
*/
if ( _Thread_Do_post_task_switch_extension ||
_Thread_Executing->do_post_task_switch_extension ) {
_Thread_Executing->do_post_task_switch_extension = FALSE;
_API_extensions_Run_postswitch();
}
}
}
void
ppc_exc_min_prolog_expand(ppc_exc_min_prolog_t buf, ppc_exc_min_prolog_template_t templ, uint16_t vec)
{
memcpy(&buf[0], templ, sizeof(ppc_exc_min_prolog_t));
/* fixup the vector */
buf[PPC_EXC_PROLOG_VEC_OFFSET] = (buf[PPC_EXC_PROLOG_VEC_OFFSET] & 0xffff8000) | (vec & 0x7fff);
}
#undef TESTING
#ifdef TESTING
static void noop(const struct __rtems_raw_except_connect_data__*x) {}
rtems_raw_except_connect_data exc_conn = {
exceptIndex: ASM_SYS_VECTOR,
hdl : {
vector: ASM_SYS_VECTOR,
raw_hdl: 0,
raw_hdl_size: 0
},
on : noop,
off : noop,
isOn : 0 /* never used AFAIK */
};
void
ppc_exc_raise()
{
asm volatile("li 3, 0xffffdead; sc");
}
int
exc_conn_do()
{
exc_conn.hdl.raw_hdl = ppc_exc_min_prolog_auto;
exc_conn.hdl.raw_hdl_size = 16;
return ppc_set_exception(&exc_conn);
}
#endif

View File

@@ -0,0 +1,237 @@
/*
* Test low-level exception handling code:
*
* - hook an exception handler
* - clobber (almost) all registers with a known value
* - raise exception
* - from exception handler, increment all saved register
* contents by one (to ensure registers are not only
* saved properly but also restored properly).
* - resume execution
* - verify registers are now 'clobber_value + 1'
*
* NOTE: cannot be used on PSIM because SYS exception is used
* internally by simulator (but we could use a trap or
* something else).
*
* Author: Till Straumann <strauman@slac.stanford.edu>, 2007
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vectors.h"
#include "ppc_exc_bspsupp.h"
typedef struct regs_ {
uint32_t cr, xer, lr, ctr;
uint32_t gpr0;
uint32_t gpr1;
uint32_t gpr2;
uint32_t gpr3;
uint32_t gpr4;
uint32_t gpr5;
uint32_t gpr6;
uint32_t gpr7;
uint32_t gpr8;
uint32_t gpr9;
uint32_t gpr10;
uint32_t gpr11;
uint32_t gpr12;
uint32_t gpr13;
uint32_t gpr14;
uint32_t gpr15;
uint32_t gpr16;
uint32_t gpr17;
uint32_t gpr18;
uint32_t gpr19;
uint32_t gpr20;
uint32_t gpr21;
uint32_t gpr22;
uint32_t gpr23;
uint32_t gpr24;
uint32_t gpr25;
uint32_t gpr26;
uint32_t gpr27;
uint32_t gpr28;
uint32_t gpr29;
uint32_t gpr30;
uint32_t gpr31;
} ppc_exc_int_regs;
#define OFF(x) (uintptr_t)(&((ppc_exc_int_regs*)0)->x)
void
storegs(ppc_exc_int_regs *p0, ppc_exc_int_regs *p1)
{
asm volatile(
" stmw 0, %6(%0) ;"
" mfcr 0 ;"
" stw 0, %2(%0) ;"
" mflr 0 ;"
" stw 0, %3(%0) ;"
" mfxer 0 ;"
" stw 0, %4(%0) ;"
" mfctr 0 ;"
" stw 0, %5(%0) ;"
" lwz 0, %6(%0) ;"
" sc ;"
" stmw 0, %6(%1) ;"
" mfcr 0 ;"
" stw 0, %2(%1) ;"
" mflr 0 ;"
" stw 0, %3(%1) ;"
" mfxer 0 ;"
" stw 0, %4(%1) ;"
" mfctr 0 ;"
" stw 0, %5(%1) ;"
:
:"b"(p0),"b"(p1),
"i"(OFF(cr)), "i"(OFF(lr)), "i"(OFF(xer)), "i"(OFF(ctr)),
"i"(OFF(gpr0))
:"r0");
}
/* Load up all registers from 'pre' issue system call and store
* registers in 'post'
*/
ppc_exc_int_regs pre;
ppc_exc_int_regs pst;
void
clobber()
{
asm volatile(
" lis 2, pre@h ;"
" ori 2, 2, pre@l ;"
" lwz 3, %0(2) ;"
" mtcr 3 ;"
" lwz 3, %1(2) ;"
" mtlr 3 ;"
" lwz 3, %2(2) ;"
" mtxer 3 ;"
/* don't know which ones stick */
" mfxer 3 ;"
" stw 3, %2(2) ;"
" lwz 3, %3(2) ;"
" mtctr 3 ;"
" lwz 0, %4(2) ;"
/* must not clobber R13, R1, R2 */
" stw 13, %6(2) ;"
" lmw 3, %5(2) ;"
" sc ;"
" stmw 0, %4(2) ;"
" mfcr 0 ;"
" stw 0, %0(2) ;"
" mflr 0 ;"
" stw 0, %1(2) ;"
" mfxer 0 ;"
" stw 0, %2(2) ;"
" mfctr 0 ;"
" stw 0, %3(2) ;"
:
:"i"(OFF(cr)), "i"(OFF(lr)), "i"(OFF(xer)), "i"(OFF(ctr)),
"i"(OFF(gpr0)), "i"(OFF(gpr3)), "i"(OFF(gpr13))
:"r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
"r10", "r11", "r12", "r14", "r15", "r16",
"r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
"xer","lr","ctr",
"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7",
"memory");
}
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
handle_clobber_exc(BSP_Exception_frame *f, int vector)
{
int i;
u32_a_t *p = (u32_a_t*)&f->GPR0;
for ( i=0; i<32; i++ ) {
switch (i) {
case 1: case 2: case 13: break;
default:
p[i].u++;
break;
}
}
f->GPR2 = (uint32_t)&pst;
f->EXC_CR++;
f->EXC_CTR++;
f->EXC_XER++;
f->EXC_LR++;
}
/* This routine tests the raw exception code;
* - hook 'handle_clobber_exc' to SYS exception handler
* - 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
* - exception handler increments all reg. contents by 1,
* stores address of 'pst' area in R2 and returns control
* to ppc_exc_clobber().
* - save all register contents to *R2 (should be &pst).
* - test for mismatches (except R1, R2, R13 and parts of xer)
*/
void
ppc_exc_clobber()
{
u32_a_t *a, *b;
int i;
a = (u32_a_t*)&pre;
b = (u32_a_t*)&pst;
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);
clobber();
ppc_exc_set_handler(ASM_SYS_VECTOR, 0);
for ( i=0; i< sizeof(pre)/sizeof(uint32_t); i++ ) {
switch (i) {
case OFF(gpr1)/sizeof(uint32_t):
case OFF(gpr2)/sizeof(uint32_t):
case OFF(gpr13)/sizeof(uint32_t):
break;
default:
if ( a[i].u != b[i].u - 1 ) {
printf("MISMATCH at %i: 0x%08"PRIx32" -- 0x%08"PRIx32"\n",
i, a[i].u, b[i].u);
}
}
}
}
#if 0
void
ppc_exc_test()
{
ppc_exc_int_regs a, b;
int i;
memset(&a, 0xaa, sizeof(a));
memset(&b, 0x55, sizeof(b));
storegs(&a, &b);
if ( memcmp(&a, &b, sizeof(a)) ) {
printf("FAILURE: context prior and after exception don't match!\n");
}
for ( i=0; i< sizeof(a)/sizeof(uint32_t); i++ ) {
printf("0x%08"PRIx32" -- 0x%08"PRIx32"\n",
((uint32_t __attribute__((may_alias)) *)&a)[i],
((uint32_t __attribute__((may_alias)) *)&b)[i]);
}
}
#endif

View File

@@ -0,0 +1,145 @@
/*
* vectors.h Exception frame related contant and API.
*
* This include file describe the data structure and the functions implemented
* by rtems to handle exceptions.
*
* CopyRight (C) 1999 valette@crf.canon.fr
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#ifndef LIBCPU_POWERPC_BSPSUPP_VECTORS_H
#define LIBCPU_POWERPC_BSPSUPP_VECTORS_H
#include <libcpu/raw_exception.h>
/*
* The callee (high level exception code written in C)
* will store the Link Registers (return address) at entry r1 + 4 !!!.
* So let room for it!!!.
*/
#define LINK_REGISTER_CALLEE_UPDATE_ROOM 4
#define SRR0_FRAME_OFFSET 8
#define SRR1_FRAME_OFFSET 12
#define EXCEPTION_NUMBER_OFFSET 16
#define GPR0_OFFSET 20
#define GPR1_OFFSET 24
#define GPR2_OFFSET 28
#define GPR3_OFFSET 32
#define GPR4_OFFSET 36
#define GPR5_OFFSET 40
#define GPR6_OFFSET 44
#define GPR7_OFFSET 48
#define GPR8_OFFSET 52
#define GPR9_OFFSET 56
#define GPR10_OFFSET 60
#define GPR11_OFFSET 64
#define GPR12_OFFSET 68
#define GPR13_OFFSET 72
#define GPR14_OFFSET 76
#define GPR15_OFFSET 80
#define GPR16_OFFSET 84
#define GPR17_OFFSET 88
#define GPR18_OFFSET 92
#define GPR19_OFFSET 96
#define GPR20_OFFSET 100
#define GPR21_OFFSET 104
#define GPR22_OFFSET 108
#define GPR23_OFFSET 112
#define GPR24_OFFSET 116
#define GPR25_OFFSET 120
#define GPR26_OFFSET 124
#define GPR27_OFFSET 128
#define GPR28_OFFSET 132
#define GPR29_OFFSET 136
#define GPR30_OFFSET 140
#define GPR31_OFFSET 144
#define EXC_CR_OFFSET 148
#define EXC_CTR_OFFSET 152
#define EXC_XER_OFFSET 156
#define EXC_LR_OFFSET 160
/*
* maintain the EABI requested 8 bytes aligment
* As SVR4 ABI requires 16, make it 16 (as some
* exception may need more registers to be processed...)
*/
#define EXCEPTION_FRAME_END 176
#ifndef ASM
/* codemove is like memmove, but it also gets the cache line size
* as 4th parameter to synchronize them. If this last parameter is
* zero, it performs more or less like memmove. No copy is performed if
* source and destination addresses are equal. However the caches
* are synchronized. Note that the size is always rounded up to the
* next mutiple of 4.
*/
extern void * codemove(void *, const void *, unsigned int, unsigned long);
extern void exception_nop_enable(const rtems_raw_except_connect_data* ptr);
extern int exception_always_enabled(const rtems_raw_except_connect_data* ptr);
extern void initialize_exceptions();
typedef struct _BSP_Exception_frame {
unsigned EXC_SRR0;
unsigned EXC_SRR1;
unsigned _EXC_number;
unsigned GPR0;
unsigned GPR1;
unsigned GPR2;
unsigned GPR3;
unsigned GPR4;
unsigned GPR5;
unsigned GPR6;
unsigned GPR7;
unsigned GPR8;
unsigned GPR9;
unsigned GPR10;
unsigned GPR11;
unsigned GPR12;
unsigned GPR13;
unsigned GPR14;
unsigned GPR15;
unsigned GPR16;
unsigned GPR17;
unsigned GPR18;
unsigned GPR19;
unsigned GPR20;
unsigned GPR21;
unsigned GPR22;
unsigned GPR23;
unsigned GPR24;
unsigned GPR25;
unsigned GPR26;
unsigned GPR27;
unsigned GPR28;
unsigned GPR29;
unsigned GPR30;
unsigned GPR31;
unsigned EXC_CR;
unsigned EXC_CTR;
unsigned EXC_XER;
unsigned EXC_LR;
unsigned EXC_MSR;
unsigned EXC_DAR;
} BSP_Exception_frame;
typedef void (*exception_handler_t) (BSP_Exception_frame* excPtr);
extern exception_handler_t globalExceptHdl;
/*
* Compatibility with pc386
*/
typedef BSP_Exception_frame CPU_Exception_frame;
typedef exception_handler_t cpuExcHandlerType;
/*
* dummy functions for exception interface
*/
void exception_nop_enable(const rtems_raw_except_connect_data* ptr);
int exception_always_enabled(const rtems_raw_except_connect_data* ptr);
#endif /* ASM */
#endif /* LIBCPU_POWERPC_BSPSUPP_VECTORS_H */

View File

@@ -0,0 +1,358 @@
/*
* vectors_init.c Exception hanlding initialisation (and generic handler).
*
* This include file describe the data structure and the functions implemented
* by rtems to handle exceptions.
*
* CopyRight (C) 1999 valette@crf.canon.fr
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*/
#include <rtems.h>
#include <rtems/bspIo.h>
#include <rtems/error.h>
#include <libcpu/raw_exception.h>
#include <libcpu/spr.h>
#include <libcpu/cpuIdent.h>
#include "vectors.h"
#include "ppc_exc_bspsupp.h"
static rtems_raw_except_global_settings exception_config;
static rtems_raw_except_connect_data exception_table[LAST_VALID_EXC + 1];
#if 0
typedef struct ppc_exc_connect_data_ {
rtems_raw_except_connect_data raw;
ppc_exc_handler_t c_hdl;
} ppc_exc_connect_data;
#endif
exception_handler_t globalExceptHdl;
/* T. Straumann: provide a stack trace
* <strauman@slac.stanford.edu>, 6/26/2001
*/
typedef struct LRFrameRec_ {
struct LRFrameRec_ *frameLink;
unsigned long *lr;
} LRFrameRec, *LRFrame;
#define STACK_CLAMP 50 /* in case we have a corrupted bottom */
SPR_RO(LR)
SPR_RO(DAR)
#define DEAR_BOOKE 61
#define DEAR_405 0x3d5
SPR_RO(DEAR_BOOKE)
SPR_RO(DEAR_405)
uint32_t ppc_exc_get_DAR_dflt()
{
if ( ppc_cpu_is_60x() )
return _read_DAR();
else switch ( ppc_cpu_is_bookE() ) {
default: break;
case PPC_BOOKE_STD:
case PPC_BOOKE_E500:
return _read_DEAR_BOOKE();
case PPC_BOOKE_405:
return _read_DEAR_405();
}
return 0xdeadbeef;
}
uint32_t (*ppc_exc_get_DAR)() = ppc_exc_get_DAR_dflt;
void
BSP_printStackTrace(BSP_Exception_frame* excPtr)
{
LRFrame f;
int i;
LRFrame sp;
void *lr;
printk("Stack Trace: \n ");
if (excPtr) {
printk("IP: 0x%08x, ",excPtr->EXC_SRR0);
sp=(LRFrame)excPtr->GPR1;
lr=(void*)excPtr->EXC_LR;
} else {
/* there's no macro for this */
__asm__ __volatile__("mr %0, 1":"=r"(sp));
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))
printk("\n");
}
if (i>=STACK_CLAMP) {
printk("Too many stack frames (stack possibly corrupted), giving up...\n");
} else {
if (i%5)
printk("\n");
}
}
void C_exception_handler(BSP_Exception_frame* excPtr)
{
int recoverable = 0;
int synch = (int)excPtr->_EXC_number >= 0 ;
printk("Exception handler called for exception %d\n", excPtr->_EXC_number & 0x7fff);
printk("\t Next PC or Address of fault = %x\n", excPtr->EXC_SRR0);
printk("\t Saved MSR = %x\n", excPtr->EXC_SRR1);
printk("\t R0 = %08x", excPtr->GPR0);
if ( synch ) {
printk(" R1 = %08x", excPtr->GPR1);
printk(" R2 = %08x", excPtr->GPR2);
} else {
printk(" ");
printk(" ");
}
printk(" R3 = %08x\n", excPtr->GPR3);
printk("\t R4 = %08x", excPtr->GPR4);
printk(" R5 = %08x", excPtr->GPR5);
printk(" R6 = %08x", excPtr->GPR6);
printk(" R7 = %08x\n", excPtr->GPR7);
printk("\t R8 = %08x", excPtr->GPR8);
printk(" R9 = %08x", excPtr->GPR9);
printk(" R10 = %08x", excPtr->GPR10);
printk(" R11 = %08x\n", excPtr->GPR11);
printk("\t R12 = %08x", excPtr->GPR12);
if ( synch ) {
printk(" R13 = %08x", excPtr->GPR13);
printk(" R14 = %08x", excPtr->GPR14);
printk(" R15 = %08x\n", excPtr->GPR15);
printk("\t R16 = %08x", excPtr->GPR16);
printk(" R17 = %08x", excPtr->GPR17);
printk(" R18 = %08x", excPtr->GPR18);
printk(" R19 = %08x\n", excPtr->GPR19);
printk("\t R20 = %08x", excPtr->GPR20);
printk(" R21 = %08x", excPtr->GPR21);
printk(" R22 = %08x", excPtr->GPR22);
printk(" R23 = %08x\n", excPtr->GPR23);
printk("\t R24 = %08x", excPtr->GPR24);
printk(" R25 = %08x", excPtr->GPR25);
printk(" R26 = %08x", excPtr->GPR26);
printk(" R27 = %08x\n", excPtr->GPR27);
printk("\t R28 = %08x", excPtr->GPR28);
printk(" R29 = %08x", excPtr->GPR29);
printk(" R30 = %08x", excPtr->GPR30);
printk(" R31 = %08x\n", excPtr->GPR31);
} else {
printk("\n");
}
printk("\t CR = %08x\n", excPtr->EXC_CR);
printk("\t CTR = %08x\n", excPtr->EXC_CTR);
printk("\t XER = %08x\n", excPtr->EXC_XER);
printk("\t LR = %08x\n", excPtr->EXC_LR);
/* Would be great to print DAR but unfortunately,
* that is not portable across different CPUs.
* AFAIK on classic PPC DAR is SPR 19, on the
* 405 we have DEAR = SPR 0x3d5 and booE says
* DEAR = SPR 61 :-(
*/
if ( ppc_exc_get_DAR ) {
printk("\t DAR = %08x\n", ppc_exc_get_DAR());
}
BSP_printStackTrace(excPtr);
if (excPtr->_EXC_number == ASM_DEC_VECTOR)
recoverable = 1;
if (excPtr->_EXC_number == ASM_SYS_VECTOR)
#ifdef TEST_RAW_EXCEPTION_CODE
recoverable = 1;
#else
recoverable = 0;
#endif
if (!recoverable) {
printk("unrecoverable exception!!! Push reset button\n");
while(1);
}
}
/***********************************************************
* dummy functions for on/off/isOn calls
* these functions just do nothing fulfill the semantic
* requirements to enable/disable a certain exception
*/
void exception_nop_enable(const rtems_raw_except_connect_data* ptr)
{
}
int exception_always_enabled(const rtems_raw_except_connect_data* ptr)
{
return 1;
}
/* Raw exception framework wants to keep a pointer to
* the prologue so we must keep the ones we generate
* from templates around...
*/
#define NUM_PROLOG 8 /* just a reasonable limit */
static int n_prolog = 0;
static ppc_exc_min_prolog_t prologues[NUM_PROLOG];
static ppc_exc_min_prolog_template_t prolog_templates[][2] = {
[ PPC_EXC_CLASSIC ] =
{
ppc_exc_min_prolog_sync_tmpl_std,
ppc_exc_min_prolog_async_tmpl_std,
},
[ PPC_EXC_405_CRITICAL ] =
{
ppc_exc_min_prolog_sync_tmpl_p405_crit,
ppc_exc_min_prolog_async_tmpl_p405_crit,
},
[ PPC_EXC_BOOKE_CRITICAL ] =
{
ppc_exc_min_prolog_sync_tmpl_bookE_crit,
ppc_exc_min_prolog_async_tmpl_bookE_crit,
},
[ PPC_EXC_E500_MACHCHK ] =
{
ppc_exc_min_prolog_sync_tmpl_e500_mchk,
ppc_exc_min_prolog_async_tmpl_e500_mchk,
},
};
static rtems_raw_except_func
make_prologue(int vector, ppc_raw_exception_category cat)
{
int async = (cat & PPC_EXC_ASYNC) ? 1 : 0 ;
ppc_exc_min_prolog_template_t tmpl;
cat &= ~PPC_EXC_ASYNC;
if ( n_prolog >= NUM_PROLOG ) {
rtems_panic("Not enough exception prologue slots; increase NUM_PROLOG (%s)\n",__FILE__);
}
if ( ! (tmpl = prolog_templates[cat][async]) ) {
rtems_panic("No exception prologue template for category 0x%02x found\n", cat);
}
ppc_exc_min_prolog_expand(prologues[n_prolog], tmpl, vector);
return (rtems_raw_except_func)prologues[n_prolog++];
}
void ppc_exc_init(
rtems_raw_except_connect_data *exception_table,
int nEntries)
{
int i,v;
ppc_raw_exception_category cat;
uintptr_t vaddr;
/*
* Initialize pointer used by low level execption handling
*/
globalExceptHdl = C_exception_handler;
/*
* Put default_exception_vector_code_prolog at relevant exception
* code entry addresses
*/
exception_config.exceptSize = nEntries;
exception_config.rawExceptHdlTbl = exception_table;
exception_config.defaultRawEntry.exceptIndex = 0;
exception_config.defaultRawEntry.hdl.vector = 0;
/* Note that the 'auto' handler cannot be used for everything; in particular,
* it assumes classic exceptions with a vector offset aligned on a 256-byte
* boundary.
*/
exception_config.defaultRawEntry.hdl.raw_hdl = ppc_exc_min_prolog_auto;
/*
* Note that the cast of an array address to an unsigned
* is not a bug as it is defined by a .set directly in asm...
*/
exception_config.defaultRawEntry.hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size;
for (i=0; i < exception_config.exceptSize; i++) {
if ( PPC_EXC_INVALID == (cat = ppc_vector_is_valid ((v=exception_table[i].hdl.vector))) ) {
continue;
}
exception_table[i].exceptIndex = i;
exception_table[v].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size;
/* special cases */
if ( ppc_cpu_has_shadowed_gprs()
&& ( ASM_60X_IMISS_VECTOR == v
|| ASM_60X_DLMISS_VECTOR == v
|| ASM_60X_DSMISS_VECTOR == v ) ) {
exception_table[i].hdl.raw_hdl = ppc_exc_tgpr_clr_prolog;
exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_tgpr_clr_prolog_size;
} else {
vaddr = (uintptr_t)ppc_get_vector_addr( v );
/*
* default prolog can handle classic, synchronous exceptions
* with a vector offset aligned on a 256-byte boundary.
*/
if ( PPC_EXC_CLASSIC == cat && 0 == ( vaddr & 0xff ) ) {
exception_table[i].hdl.raw_hdl_size = exception_config.defaultRawEntry.hdl.raw_hdl_size;
exception_table[i].hdl.raw_hdl = exception_config.defaultRawEntry.hdl.raw_hdl;
} else {
exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size;
exception_table[i].hdl.raw_hdl = make_prologue( v, cat );
}
}
exception_table[i].on = exception_nop_enable;
exception_table[i].off = exception_nop_enable;
exception_table[i].isOn = exception_always_enabled;
}
if (!ppc_init_exceptions(&exception_config)) {
BSP_panic("Exception handling initialization failed\n");
}
#ifdef RTEMS_DEBUG
else {
printk("Exception handling initialization done\n");
}
#endif
}
void initialize_exceptions()
{
int i;
int n = sizeof(exception_table)/sizeof(exception_table[0]);
/* Use current MMU / RI settings when running C exception handlers */
ppc_exc_msr_bits = _read_MSR() & ( MSR_DR | MSR_IR | MSR_RI );
/* Copy into a SDA variable that is easy to access from
* assembly code
*/
if ( ppc_cpu_is_bookE() ) {
ppc_exc_msr_irq_mask = MSR_EE | MSR_CE | MSR_DE ;
} else {
ppc_exc_msr_irq_mask = MSR_EE ;
}
for ( i=0; i<n; i++ )
exception_table[i].hdl.vector = i;
ppc_exc_init(exception_table, n);
}