From 9b099d935c477b47f1e05d153e511e183ee69621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Fr=C3=B6schle?= Date: Mon, 13 Jan 2014 15:13:29 +0000 Subject: [PATCH] implemented safe stack for access exception handler --- include/firebee.h | 2 +- include/mmu.h | 2 +- net/am79c874.c | 3 ++ net/fec.c | 9 ++-- sys/exceptions.S | 59 ++++++++++++-------------- sys/mmu.c | 105 ++++++++++++++++++++++++++++++++-------------- 6 files changed, 111 insertions(+), 69 deletions(-) diff --git a/include/firebee.h b/include/firebee.h index a0c3509..c02f92e 100644 --- a/include/firebee.h +++ b/include/firebee.h @@ -27,7 +27,7 @@ * Author: Markus Fröschle */ -#define SYSCLK 132000 +#define SYSCLK 132000 /* NOTE: 132 _is_ correct. 133 _is_ wrong. Do not change! */ #define BOOTFLASH_BASE_ADDRESS 0xE0000000 #define BOOTFLASH_SIZE 0x800000 /* FireBee has 8 MByte Flash */ diff --git a/include/mmu.h b/include/mmu.h index ac8d5b4..ba3a4e1 100644 --- a/include/mmu.h +++ b/include/mmu.h @@ -35,6 +35,6 @@ extern long video_tlb; extern long video_sbt; extern void mmu_init(void); -extern void mmutr_miss(uint32_t addresss); +extern void mmutr_miss(uint32_t address); #endif /* _MMU_H_ */ diff --git a/net/am79c874.c b/net/am79c874.c index 7c742e1..d2f90f1 100644 --- a/net/am79c874.c +++ b/net/am79c874.c @@ -93,7 +93,10 @@ int am79c874_init(uint8_t fec_ch, uint8_t phy_addr, uint8_t speed, uint8_t duple /* Set the default mode (Full duplex, 100 Mbps) */ if (!fec_mii_write(fec_ch, phy_addr, MII_AM79C874_CR, MII_AM79C874_CR_100MB | MII_AM79C874_CR_DPLX)) + { + dbg("%s: forced setting 100Mbps/full failed.\r\n", __FUNCTION__); return 0; + } } #ifdef DBG_AM79 diff --git a/net/fec.c b/net/fec.c index c4e8b1f..2ec484a 100644 --- a/net/fec.c +++ b/net/fec.c @@ -17,6 +17,7 @@ #include "bas_string.h" #include "bas_printf.h" #include "util.h" +#include "wait.h" #include "am79c874.h" //#include "bcm5222.h" #include @@ -91,11 +92,12 @@ int fec_mii_write(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t data) */ for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++) { + wait(1); if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII) break; } - if(timeout == FEC_MII_TIMEOUT) + if (timeout == FEC_MII_TIMEOUT) return 0; /* @@ -156,11 +158,12 @@ int fec_mii_read(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data) */ for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++) { + wait(1); if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII) break; } - if(timeout == FEC_MII_TIMEOUT) + if (timeout == FEC_MII_TIMEOUT) return 0; /* @@ -481,9 +484,7 @@ void fec_init(uint8_t ch, uint8_t mode, const uint8_t *pa) */ MCF_FEC_RCR(ch) = 0 | MCF_FEC_RCR_MAX_FL(ETH_MAX_FRM) -//#ifdef FEC_PROMISCUOUS | MCF_FEC_RCR_PROM -//#endif | MCF_FEC_RCR_FCE; if (mode == FEC_MODE_MII) diff --git a/sys/exceptions.S b/sys/exceptions.S index d9cf410..575db53 100644 --- a/sys/exceptions.S +++ b/sys/exceptions.S @@ -391,51 +391,41 @@ reset_vector: // cannot use the supervisor stack in here. Therefore we first switch to a safe stack // (RAMBAR0, the processor internal SRAM) access_exception: - move.w #0x2700,sr // disable interrupt + // no point in disabling interrupts within an exception handler + // move.w #0x2700,sr // disable interrupt + + // save current SSP move.l sp,__SUP_SP // defined in linker script: top of SRAM0 lea __SUP_SP - 4,sp // now we can savely use the stack - move.l d0,-(sp) // ++ vr - move.l a0,-(sp) + // save gcc scratch registers, others will be handled by called function + lea -4*4(sp),sp + movem.l d0-d1/a0-a1,(sp) - move.l __SUP_SP,a0 // original stack frame - move.w (a0),d0 // get format_status word from stack - andi.l #0x0c03,d0 // mask out fault status bits - cmpi.l #0x0401,d0 // TLB miss on opword of instruction fetch? - beq access_exception_mmu // yes - cmpi.l #0x0402,d0 // TLB miss on extension word of instruction fetch? - beq access_exception_mmu // yes - cmpi.l #0x0802,d0 // TLB miss on data write? - beq access_exception_mmu // yes - cmpi.l #0x0c02,d0 // TLB miss on data read, or read-modify-write? - beq access_exception_mmu // yes + + move.l __SUP_SP,a0 // original stack pointer - // revert stack trickery + move.l 4(a0),-(sp) // format status word + move.l (a0),-(sp) // program counter at access error + jsr _access_exception + lea 2*4(sp),sp // adjust stack + + tst.l d0 // handled? + bne bus_error + + movem.l (sp),d0-d1/a0-a1 // restore scratch registers + lea 4*4(sp),sp + + move.l (sp)+,a0 move.l (sp)+,d0 move.l __SUP_SP,sp bra bus_error // everything else is a classic bus error -access_exception_mmu: - move.l MCF_MMU_MMUSR,d0 // did the last fault hit in TLB? - btst #1,d0 // yes, it did. So we already mapped that page - bne bus_error // and this must be a real bus error - - move.l MCF_MMU_MMUAR,d0 - cmp.l #__FASTRAM_END,d0 // above max User RAM area? - bge bus_error // -> bus error - - lea -5*4(sp),sp // save gcc scratch registers - movem.l d0-d1/a0-a2,(sp) - - move.l d0,-(sp) // fault address - jsr _mmutr_miss // else we have an MMU TLB miss - addq.l #4,sp - movem.l (sp),d0-d1/a0-a2 // restore gcc scratch registers lea 5*4(sp),sp @@ -449,11 +439,16 @@ access_exception_mmu: rte bus_error: + + // revert stack trickery + + move.l (sp)+,a0 move.l (sp)+,d0 // restore register + move.l __SUP_SP,sp // restore original stack + bra std_exc_vec zero_divide: - move.w #0x2700,sr // disable interrupt move.l a0,-(a7) move.l d0,-(a7) move.l 12(a7),a0 // pc diff --git a/sys/mmu.c b/sys/mmu.c index 1ac23a2..d141348 100644 --- a/sys/mmu.c +++ b/sys/mmu.c @@ -394,45 +394,88 @@ void mmu_init(void) MCF_MMU_MMUOR_UAA; /* update allocation address field */ } + +/* + * handle an access error + * upper level routine called from access_exception inside exceptions.S + */ +bool access_exception(uint32_t pc, uint32_t format_status) +{ + int fault_status; + uint32_t fault_address; + bool is_tlb_miss = false; /* assume access error is not a TLB miss */ + extern uint8_t __FASTRAM_END[]; + uint32_t FASTRAM_END = (uint32_t) &__FASTRAM_END[0]; + + fault_status = (((format_status & 0xc000000) >> 26) | + ((format_status & 0x30000) >> 16)); + + /* + * determine if access fault was caused by a TLB miss + */ + switch (fault_status) + { + case 0x5: /* TLB miss on opword of instruction fetch */ + case 0x6: /* TLB miss on extension word of instruction fetch */ + case 0xa: /* TLB miss on data write */ + case 0xe: /* TLB miss on data read or read-modify-write */ + is_tlb_miss = true; + break; + + default: + break; + } + + if (is_tlb_miss) + { + if (MCF_MMU_MMUSR & 1) /* did the last fault hit in TLB? */ + { + /* + * if yes, then we already mapped that page during a previous turn and this is in fact a bus error + */ + is_tlb_miss = false; + } + else + { + fault_address = MCF_MMU_MMUAR; /* retrieve fault access address from MMU */ + if (fault_address > FASTRAM_END) + { + is_tlb_miss = false; /* this is a bus error */ + } + else /* map this page */ + { + mmutr_miss(fault_address); + return true; + } + } + } + return is_tlb_miss; +} + + void mmutr_miss(uint32_t address) { dbg("MMU TLB MISS at 0x%08x\r\n", address); flush_and_invalidate_caches(); - switch (address) - { - case keyctl: - case keybd: - /* do something to emulate the IKBD access */ - dbg("IKBD access\r\n"); - break; + /* add missed page to TLB */ + MCF_MMU_MMUTR = (address & 0xfff00000) | /* virtual aligned to 1M */ + MCF_MMU_MMUTR_SG | /* shared global */ + MCF_MMU_MMUTR_V; /* valid */ - case midictl: - case midi: - /* do something to emulate MIDI access */ - dbg("MIDI ACIA access\r\n"); - break; + MCF_MMU_MMUDR = (address & 0xfff00000) | /* physical aligned to 1M */ + MCF_MMU_MMUDR_SZ(0) | /* 1 MB page size */ + MCF_MMU_MMUDR_CM(0x1) | /* cacheable copyback */ + MCF_MMU_MMUDR_R | /* read access enable */ + MCF_MMU_MMUDR_W | /* write access enable */ + MCF_MMU_MMUDR_X; /* execute access enable */ - default: - /* add missed page to TLB */ - MCF_MMU_MMUTR = (address & 0xfff00000) | /* virtual aligned to 1M */ - MCF_MMU_MMUTR_SG | /* shared global */ - MCF_MMU_MMUTR_V; /* valid */ + MCF_MMU_MMUOR = MCF_MMU_MMUOR_ACC | /* access TLB, data */ + MCF_MMU_MMUOR_UAA; /* update allocation address field */ - MCF_MMU_MMUDR = (address & 0xfff00000) | /* physical aligned to 1M */ - MCF_MMU_MMUDR_SZ(0) | /* 1 MB page size */ - MCF_MMU_MMUDR_CM(0x1) | /* cacheable copyback */ - MCF_MMU_MMUDR_R | /* read access enable */ - MCF_MMU_MMUDR_W | /* write access enable */ - MCF_MMU_MMUDR_X; /* execute access enable */ - - MCF_MMU_MMUOR = MCF_MMU_MMUOR_ACC | /* access TLB, data */ - MCF_MMU_MMUOR_UAA; /* update allocation address field */ - - MCF_MMU_MMUOR = MCF_MMU_MMUOR_ITLB | /* instruction */ - MCF_MMU_MMUOR_ACC | /* access TLB */ - MCF_MMU_MMUOR_UAA; /* update allocation address field */ - } + MCF_MMU_MMUOR = MCF_MMU_MMUOR_ITLB | /* instruction */ + MCF_MMU_MMUOR_ACC | /* access TLB */ + MCF_MMU_MMUOR_UAA; /* update allocation address field */ }