From d8886c7dbfcb544049136a3a3d44104da267adbd Mon Sep 17 00:00:00 2001 From: Lephe Date: Sun, 14 Jun 2020 18:22:20 +0200 Subject: [PATCH] core: answer TLB misses and remove startup mapping (UNSTABLE) This change adds a TLB miss handler that calls __TLB_LoadPTEH() and removes the startu mapping of add-in pages in the explore() routine of src/core/start.c. Note that calling __TLB_LoadPTEH() manually might unexpectedly lead to a TLB multihit problem as the requested page might be accidentally loaded by a TLB miss in the code that loads it. A TLB multihit is a platform reset, so this function should always be considered unsafe to call (unless the calling code is in a combination of P1 space and ILRAM). This change also moves a lot of functions out of the .pretext section, notably topti, as this was designed to allow panic messages when the add-in couldn't be mapped entirely. By contrast, a GMAPPED macro has been defined to mark crucial kernel code and data that must remain mapped at all times. This currently puts the data in ILRAM because static RAM is not executable. An alternative will have to be found for SH3-based fx9860g machines. This version still does not allow TLB misses in timer callbacks and breaks return-to-menu in a severe way! It is not suitable for any stable application! --- fxcg50.ld | 10 ++++-- include/core/setup.h | 6 ++-- include/gint/defs/attributes.h | 2 ++ include/gint/mpu/mmu.h | 2 +- src/core/exch.c | 6 ++-- src/core/exch.s | 6 ++-- src/core/setup.c | 34 +++++++++--------- src/core/start.c | 31 ---------------- src/core/syscalls.S | 36 ++++++++++++------- src/core/tlbh.S | 65 ++++++++++++++++++++++++++++++++++ src/keysc/keysc.c | 12 +++---- src/render-cg/topti-asm.s | 2 +- src/render-cg/topti.c | 3 -- src/render-fx/dtext.c | 1 - src/render-fx/topti-asm.s | 3 +- src/render-fx/topti.c | 2 -- src/render/topti.c | 4 --- src/tmu/tmu.c | 6 ++-- 18 files changed, 138 insertions(+), 93 deletions(-) create mode 100644 src/core/tlbh.S diff --git a/fxcg50.ld b/fxcg50.ld index 9c0cc47..a2d4989 100644 --- a/fxcg50.ld +++ b/fxcg50.ld @@ -54,9 +54,13 @@ SECTIONS *(.dtors .dtors.*) _etors = . ; - _gint_exch_tlbh_start = . ; - *(.gint.exch_tlbh); - _gint_exch_tlbh_size = ABSOLUTE(. - _gint_exch_tlbh_start); + _gint_exch_start = . ; + *(.gint.exch) + _gint_exch_size = ABSOLUTE(. - _gint_exch_start); + + _gint_tlbh_start = . ; + *(.gint.tlbh) + _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); *(.text .text.*) } > rom diff --git a/include/core/setup.h b/include/core/setup.h index 4118968..7e13026 100644 --- a/include/core/setup.h +++ b/include/core/setup.h @@ -20,8 +20,10 @@ Returns the previous VBR address. */ uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void)); -/* gint_exch_tlbh(): Exception and TLB miss handler */ -void gint_exch_tlbh(void); +/* gint_exch(): Exception handler */ +void gint_exch(void); +/* gint_tlbh(): TLB miss handler */ +void gint_tlbh(void); /* gint_inth_7705(): SH7705 exception handler */ void gint_inth_7705(void); diff --git a/include/gint/defs/attributes.h b/include/gint/defs/attributes.h index c5f19d1..fac700c 100644 --- a/include/gint/defs/attributes.h +++ b/include/gint/defs/attributes.h @@ -17,6 +17,8 @@ #define GILRAM __attribute__((section(".ilram"))) #define GXRAM __attribute__((section(".xram"))) #define GYRAM __attribute__((section(".yram"))) +/* Objects that must remain mapped */ +#define GMAPPED GILRAM /* Unused parameters or variables */ #define GUNUSED __attribute__((unused)) diff --git a/include/gint/mpu/mmu.h b/include/gint/mpu/mmu.h index b81aa9b..9b60333 100644 --- a/include/gint/mpu/mmu.h +++ b/include/gint/mpu/mmu.h @@ -14,7 +14,7 @@ //--- // SH7705 TLB. Refer to: // "Renesas SH7705 Group Hardware Manual" -// Section 3: "Memory Manaegement Unit (MMU)" +// Section 3: "Memory Management Unit (MMU)" //--- /* tlb_addr_t - address part of a TLB entry */ diff --git a/src/core/exch.c b/src/core/exch.c index a1e9871..536fd3c 100644 --- a/src/core/exch.c +++ b/src/core/exch.c @@ -9,7 +9,7 @@ #define dtext(x, y, str) dtext (x, y, str, C_BLACK, C_NONE) /* gint_panic_default(): Default panic handler */ -GNORETURN static void gint_default_panic(GUNUSED uint32_t code) +GNORETURN GMAPPED static void gint_default_panic(GUNUSED uint32_t code) { uint32_t TEA, TRA; @@ -124,7 +124,7 @@ GDATA GNORETURN void (*gint_exc_panic)(uint32_t code) = gint_default_panic; GDATA int (*gint_exc_catcher)(uint32_t code) = NULL; /* gint_panic(): Panic handler function */ -void gint_panic(uint32_t code) +GMAPPED void gint_panic(uint32_t code) { gint_exc_panic(code); } @@ -142,7 +142,7 @@ void gint_exc_catch(int (*handler)(uint32_t code)) } /* gint_exc_skip(): Skip pending exception instructions */ -void gint_exc_skip(int instructions) +GMAPPED void gint_exc_skip(int instructions) { uint32_t spc; diff --git a/src/core/exch.s b/src/core/exch.s index c5539db..d7937ee 100644 --- a/src/core/exch.s +++ b/src/core/exch.s @@ -1,8 +1,8 @@ -.global _gint_exch_tlbh -.section .gint.exch_tlbh +.global _gint_exch +.section .gint.exch .align 4 -_gint_exch_tlbh: +_gint_exch: sts.l pr, @-r15 stc.l gbr, @-r15 sts.l mach, @-r15 diff --git a/src/core/setup.c b/src/core/setup.c index dadc3af..edd61a8 100644 --- a/src/core/setup.c +++ b/src/core/setup.c @@ -16,7 +16,8 @@ extern char gint_vbr[]; /* System's VBR address */ GBSS uint32_t system_vbr; /* Size of exception and TLB handler */ -extern char gint_exch_tlbh_size; +extern char gint_exch_size; +extern char gint_tlbh_size; /* Driver table */ extern gint_driver_t bdrv, edrv; @@ -33,7 +34,7 @@ typedef struct /* gint_ctx_save() - save interrupt controller configuration @arg ctx gint core context object */ -static void gint_ctx_save(gint_core_ctx *ctx) +GMAPPED static void gint_ctx_save(gint_core_ctx *ctx) { if(isSH3()) { @@ -53,7 +54,7 @@ static void gint_ctx_save(gint_core_ctx *ctx) /* gint_ctx_restore() - restore interrupt controller configuration @arg ctx gint core context object */ -static void gint_ctx_restore(gint_core_ctx *ctx) +GMAPPED static void gint_ctx_restore(gint_core_ctx *ctx) { if(isSH3()) { @@ -84,7 +85,7 @@ static void gint_ctx_restore(gint_core_ctx *ctx) GBSS static gint_core_ctx sys_ctx; /* lock() - lock interrupts to avoid receiving unsupported signals */ -static void lock(void) +GMAPPED static void lock(void) { /* Just disable everything, drivers will enable what they support */ if(isSH3()) for(int i = 0; i < 8; i++) @@ -94,7 +95,7 @@ static void lock(void) } /* gint_install() - install and start gint */ -void gint_install(void) +GMAPPED void gint_install(void) { /* VBR address, provided by the linker script */ void *vbr = (void *)&gint_vbr; @@ -103,7 +104,8 @@ void gint_install(void) void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305; /* Size of the exception and TLB handlers */ - uint32_t exch_tlbh_size = (uint32_t)&gint_exch_tlbh_size; + uint32_t exch_size = (uint32_t)&gint_exch_size; + uint32_t tlbh_size = (uint32_t)&gint_tlbh_size; /* First save the hardware configuration. This step is crucial because we don't want the system to find out about us directly manipulating @@ -111,8 +113,8 @@ void gint_install(void) gint_ctx_save(&sys_ctx); /* Load the event handler entry points into memory */ - memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size); - memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size); + memcpy(vbr + 0x100, gint_exch, exch_size); + memcpy(vbr + 0x400, gint_tlbh, tlbh_size); memcpy(vbr + 0x600, inth_entry, 64); /* Time to switch VBR and roll! */ @@ -120,7 +122,7 @@ void gint_install(void) } /* unlock() - unlock interrupts, restoring system settings */ -static void unlock(void) +GMAPPED static void unlock(void) { /* Restore all driver settings, but do it in reverse order of loading to honor the dependency system */ @@ -133,7 +135,7 @@ static void unlock(void) } /* gint_unload() - unload gint and give back control to the system */ -void gint_unload(void) +GMAPPED void gint_unload(void) { /* First wait for all the drivers to finish their current jobs */ for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;) @@ -151,7 +153,7 @@ void gint_unload(void) static gint_core_ctx gint_ctx; extern gint_driver_t bdrv, edrv; -static void gint_switch_out(void) +GMAPPED static void gint_switch_out(void) { /* Save all drivers in reverse order */ for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;) @@ -170,7 +172,7 @@ static void gint_switch_out(void) } } -static void gint_switch_in(void) +GMAPPED static void gint_switch_in(void) { /* Save system state again */ for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;) @@ -190,7 +192,7 @@ static void gint_switch_in(void) } /* gint_switch() - temporarily switch out of gint */ -void gint_switch(void (*function)(void)) +GMAPPED void gint_switch(void (*function)(void)) { /* Wait for all the drivers to finish their current jobs */ for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;) @@ -233,7 +235,7 @@ void __ConfigureStatusArea(int mode); static int __osmenu_id; -static void __osmenu_handler(void) +GMAPPED static void __osmenu_handler(void) { __PutKeyCode(0x04, 0x09, 0); @@ -241,7 +243,7 @@ static void __osmenu_handler(void) __Timer_Deinstall(__osmenu_id); } -static void __osmenu(void) +GMAPPED static void __osmenu(void) { __ClearKeyBuffer(); @@ -286,7 +288,7 @@ static void __osmenu(void) } /* gint_osmenu() - switch out of gint and call the calculator's main menu */ -void gint_osmenu(void) +GMAPPED void gint_osmenu(void) { gint_switch(__osmenu); } diff --git a/src/core/start.c b/src/core/start.c index 0f9cf21..ed14906 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -66,25 +66,6 @@ static void regclr(uint32_t *r, int32_t s) } #define regclr(r, s) regclr(&r, (int32_t)&s) -/* explore() - read data from a memory region to get it mapped by the system - This function accesses all the 1k-blocks between b and b+s. This corresponds - to the region [b; b+s) when b and s are 1k-aligned. - @b Base pointer (first address checked) - @s Size of region */ -GSECTION(".pretext") -static void explore(volatile void *b, int32_t s) -{ - GUNUSED uint8_t x; - - while(s > 0) - { - x = *(volatile uint8_t *)b; - b += 1024; - s -= 1024; - } -} -#define explore(b, s) explore(&b, (int32_t)&s) - /* acall() - call an array of functions (constructors or destructors) @f First element of array @l First element outside of the array */ @@ -130,18 +111,6 @@ int start(int isappli, int optnum) regclr(rbss, sbss); bootlog_loaded(); - /* Traverse all ROM pages */ - explore(brom, srom); - - /* Count how much memory got mapped from this process */ - uint32_t rom, ram; - isSH3() ? tlb_mapped_memory(&rom, &ram) - : utlb_mapped_memory(&rom, &ram); - bootlog_mapped(rom, ram); - - /* Cancel add-in execution if not all pages are mapped */ -// if(rom < (uint32_t)&srom) gint_panic(0x1040); - /* Install gint and switch VBR */ gint_install(); bootlog_kernel(); diff --git a/src/core/syscalls.S b/src/core/syscalls.S index 0efb627..18f71c8 100644 --- a/src/core/syscalls.S +++ b/src/core/syscalls.S @@ -41,9 +41,7 @@ .global ___GetVRAMAddress .global ___ConfigureStatusArea -.section ".pretext" - -#define syscall(id) \ +#define syscall_(id, syscall_table) \ mov.l syscall_table, r2 ;\ mov.l 1f, r0 ;\ jmp @r2 ;\ @@ -51,12 +49,29 @@ .align 4 ;\ 1: .long id +#define syscall(id) syscall_(id, syscall_table) + +/*** Special syscalls in ILRAM ***/ + +.section .ilram + #ifdef FX9860G - -/* TLB Management */ - ___TLB_LoadPTEH: - syscall(0x0003) + syscall_(0x0003, 2f) +2: .long 0x80010070 +#endif + +#ifdef FXCG50 +___TLB_LoadPTEH: + syscall_(0x000c, 2f) +2: .long 0x80020070 +#endif + +/*** Normal syscalls in ROM ***/ + +.section .pretext + +#ifdef FX9860G /* Dynamic allocation */ @@ -118,13 +133,10 @@ syscall_table: #ifdef FXCG50 -/* TLB Management */ - -___TLB_LoadPTEH: - syscall(0x000c) - /* Dynamic allocation */ +.section .pretext + _malloc: syscall(0x1f44) _free: diff --git a/src/core/tlbh.S b/src/core/tlbh.S new file mode 100644 index 0000000..4f60cd8 --- /dev/null +++ b/src/core/tlbh.S @@ -0,0 +1,65 @@ +.global _gint_tlbh +.section .gint.tlbh +.align 4 + +_gint_tlbh: + sts.l pr, @-r15 + stc.l gbr, @-r15 + sts.l mach, @-r15 + sts.l macl, @-r15 + + /* Get HWMPU in gint's hardware info. If last bit is set, we're SH3 */ + mov.l .gint, r0 + mov.l @r0, r0 + tst #1, r0 + mov.l .tea_sh4, r0 + bt test_tea + mov.l .tea_sh3, r0 + +test_tea: + # Check TEA to see if we want to map a page or raise a SysERROR + mov.l @r0, r0 + mov.l .max_mapped_rom, r1 + cmp/ge r1, r0 + bt panic + +map: + # If TEA is mappable, map a page and return + mov.l .TLB_LoadPTEH, r0 + jsr @r0 + nop + + lds.l @r15+, macl + lds.l @r15+, mach + ldc.l @r15+, gbr + lds.l @r15+, pr + rte + nop + +panic: + # Otherwise, panic by defaulting to the exception handler (the TLB miss + # may still be resolved by a panic handler) + lds.l @r15+, macl + lds.l @r15+, mach + ldc.l @r15+, gbr + lds.l @r15+, pr + + stc vbr, r0 + mov #1, r1 + shll8 r1 + add r1, r0 + jmp @r0 + nop + +.align 4 + +.gint: + .long _gint +.tea_sh4: + .long 0xff00000c +.tea_sh3: + .long 0xfffffffc +.max_mapped_rom: + .long 0x00300000 + _srom +.TLB_LoadPTEH: + .long ___TLB_LoadPTEH diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index 695f0ff..4117463 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -57,7 +57,7 @@ GDATA static uint time = 0; /* buffer_push(): Add an event in the keyboard buffer Returns non-zero if the event cannot be pushed. */ -static int buffer_push(driver_event_t ev) +GMAPPED static int buffer_push(driver_event_t ev) { int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE; if(next == buffer_start) return 1; @@ -79,7 +79,7 @@ static int buffer_poll(driver_event_t *ev) } /* keysc_frame(): Generate driver events from KEYSC state */ -static void keysc_frame(void) +GMAPPED static void keysc_frame(void) { GALIGNED(2) uint8_t scan[12] = { 0 }; @@ -94,9 +94,9 @@ static void keysc_frame(void) } /* Compare new data with the internal state. Push releases before - presses so that a key change occuring within a single analysis frame - can be performed. This happens all the time when going back to the - main MENU via gint_osmenu() on a keybind. */ + presses so that a key change occurring within a single analysis + frame can be performed. This happens all the time when going back to + the main MENU via gint_osmenu() on a keybind. */ for(int row = 0; row < 12; row++) { int diff = ~scan[row] & state[row]; @@ -248,7 +248,7 @@ int keydown_any(int key, ...) // Driver initialization and status //--- -static int callback(GUNUSED volatile void *arg) +GMAPPED static int callback(GUNUSED volatile void *arg) { keysc_frame(); time++; diff --git a/src/render-cg/topti-asm.s b/src/render-cg/topti-asm.s index ac3247e..94d3091 100644 --- a/src/render-cg/topti-asm.s +++ b/src/render-cg/topti-asm.s @@ -1,7 +1,7 @@ .global _topti_glyph_fg_bg .global _topti_glyph_fg .global _topti_glyph_bg -.section .pretext +.text # Glyph rendering functions. # These are pretty naive, using only word accesses to index the VRAM and diff --git a/src/render-cg/topti.c b/src/render-cg/topti.c index 39a9a3d..95d8c8f 100644 --- a/src/render-cg/topti.c +++ b/src/render-cg/topti.c @@ -25,7 +25,6 @@ font_t const * topti_font = &gint_font8x9; @height Subglyph height @dataw Glyph width @fg @bg Foreground and background colors */ -GSECTION(".pretext") static void topti_glyph(uint16_t *vram, uint32_t const * data, int left, int top, int width, int height, int dataw, int fg, int bg) { @@ -42,7 +41,6 @@ static void topti_glyph(uint16_t *vram, uint32_t const * data, int left, dataw - width, index); } -GSECTION(".pretext") void topti_render(int x, int y, char const *str, size_t size, font_t const *f, int fg, int bg) { @@ -119,7 +117,6 @@ void topti_render(int x, int y, char const *str, size_t size, font_t const *f, } /* dtext() - display a string of text */ -GSECTION(".pretext") void dtext(int x, int y, char const *str, int fg, int bg) { topti_render(x, y, str, strlen(str), topti_font, fg, bg); diff --git a/src/render-fx/dtext.c b/src/render-fx/dtext.c index e85b64b..e388b52 100644 --- a/src/render-fx/dtext.c +++ b/src/render-fx/dtext.c @@ -3,7 +3,6 @@ #include "topti-asm.h" /* dtext(): Display a string of text */ -GSECTION(".pretext") void dtext(int x, int y, char const *str, int fg, int bg) { if((uint)fg >= 8 || (uint)bg >= 8) return; diff --git a/src/render-fx/topti-asm.s b/src/render-fx/topti-asm.s index ffa0efa..fa89079 100644 --- a/src/render-fx/topti-asm.s +++ b/src/render-fx/topti-asm.s @@ -1,6 +1,5 @@ .global _topti_asm_text -.section .pretext # REGISTER ALLOCATION: # r0: x or ~x @@ -12,7 +11,7 @@ # r6: operators # r7: number of rows (r7>0, otherwise the font is clearly ill-formed) -# Mind that there are pipeline optimisation efforts in this file: +# Mind that there are pipeline optimization efforts in this file: # * Doing memory accesses on 4-aligned instructions to avoid contention between # IF and MA (1 cycle); # * Avoid using an operand just after it is fetched from memory because of the diff --git a/src/render-fx/topti.c b/src/render-fx/topti.c index f30874d..7ffdf3e 100644 --- a/src/render-fx/topti.c +++ b/src/render-fx/topti.c @@ -24,7 +24,6 @@ font_t const * topti_font = &gint_font5x7; 0, call topti_draw() and reset the operators. If it's negative, call topti_draw() then do another pass of topti_split() to recover the missing information. */ -GSECTION(".pretext") static int topti_split(uint32_t const * glyph, int width, int height, int free, uint32_t *operators) { @@ -81,7 +80,6 @@ static int topti_split(uint32_t const * glyph, int width, int height, int free, } /* topti_render(): Render a string on the VRAM */ -GSECTION(".pretext") void topti_render(int x, int y, char const *str, font_t const *f, asm_text_t *asm_fg, asm_text_t *asm_bg, uint32_t *v1, uint32_t *v2) { diff --git a/src/render/topti.c b/src/render/topti.c index a087eb0..6d7b869 100644 --- a/src/render/topti.c +++ b/src/render/topti.c @@ -7,7 +7,6 @@ #define PROP_SPACING 5 /* dfont(): Set the default font for text rendering */ -GSECTION(".pretext") font_t const *dfont(font_t const * font) { font_t const *old_font = topti_font; @@ -17,7 +16,6 @@ font_t const *dfont(font_t const * font) } /* charset_size(): Number of elements in each character set */ -GSECTION(".pretext") int charset_size(enum topti_charset set) { int size[] = { 10, 26, 52, 62, 95, 128 }; @@ -25,7 +23,6 @@ int charset_size(enum topti_charset set) } /* charset_decode(): Translate ASCII into reduced character sets */ -GSECTION(".pretext") int charset_decode(enum topti_charset set, uint c) { int x, y; @@ -59,7 +56,6 @@ int charset_decode(enum topti_charset set, uint c) } /* topti_offset(): Use a font index to find the location of a glyph */ -GSECTION(".pretext") int topti_offset(font_t const *f, uint glyph) { /* Non-proportional fonts don't need an index */ diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index d94bb7e..3dd56b4 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -134,7 +134,7 @@ void timer_pause(int id) } /* timer_stop() - stop and free a timer */ -void timer_stop(int id) +GMAPPED void timer_stop(int id) { /* Stop the timer and disable UNIE to indicate that it's free */ timer_pause(id); @@ -167,7 +167,7 @@ void timer_stop(int id) //--- /* timer_timeout() - callback that sets a flag and halts the timer */ -int timer_timeout(volatile void *arg) +GMAPPED int timer_timeout(volatile void *arg) { volatile int *x = arg; (*x)++; @@ -181,7 +181,7 @@ int timer_timeout(volatile void *arg) /* timer_clear() - clear an ETMU flag and possibly stop the timer @timer Timer ID, must be an ETMU @stop Non-zero to stop the timer */ -void timer_clear(int id, int stop) +GMAPPED void timer_clear(int id, int stop) { do ETMU[id-3].TCR.UNF = 0; while(ETMU[id-3].TCR.UNF);