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!
This commit is contained in:
Lephe 2020-06-14 18:22:20 +02:00
parent 4ad2110efc
commit d8886c7dbf
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
18 changed files with 138 additions and 93 deletions

View file

@ -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

View file

@ -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);

View file

@ -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))

View file

@ -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 */

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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();

View file

@ -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:

65
src/core/tlbh.S Normal file
View file

@ -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

View file

@ -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++;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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)
{

View file

@ -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 */

View file

@ -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);