mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-05 15:15:44 +00:00
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:
@@ -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,
|
||||
|
||||
374
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
Normal file
374
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
368
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/ppc_exc.S
Normal file
368
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/ppc_exc.S
Normal 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 = .
|
||||
@@ -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
|
||||
@@ -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
|
||||
172
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/ppc_exc_hdl.c
Normal file
172
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/ppc_exc_hdl.c
Normal 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
|
||||
@@ -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*)⪯
|
||||
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
|
||||
145
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/vectors.h
Normal file
145
c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/vectors.h
Normal 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 */
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user