From 5655699cd87317de80ab373c97faf1dc9de1bb3d Mon Sep 17 00:00:00 2001 From: Lephe Date: Tue, 16 Jan 2024 11:06:37 +0100 Subject: [PATCH] gint: reload on-chip sections after world, with option to backup (#26) Solves the power off crash, at least for programs that don't store long-term data in on-chip memory. --- include/gint/gint.h | 42 ++++++++++++++++++++++++++++++++++++++++ src/kernel/kernel.h | 3 +++ src/kernel/start.c | 24 ++++++++++++++++------- src/kernel/world.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/include/gint/gint.h b/include/gint/gint.h index 13b7e1d..3f65ce5 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -80,6 +80,48 @@ void gint_osmenu_native(void); @restart 0 to exit, 1 to restart by using gint_osmenu() */ void gint_setrestart(int restart); +/* gint_set_onchip_save_mode(): Specify memory save policy for world switches + + World switches can cause corruption in on-chip memory in two ways. First, if + the calculator powers off while in the OS world, on-chip memory will be + wiped because it's not powered when the calculator is off. Second, the OS + may run code that overwrites on-chip memory (we don't have any examples of + that but it's not part of the add-in interface). + + As a result, the best option is to backup on-chip memory and restore it when + loading back into the add-in. The issue is that there's 20 kB of on-chip + memory (4 kB ILRAM + 16 kB XYRAM) and on some machines (fx-9860G-like) or in + some applications we don't have that much memory lying around. + + This function selects between three modes for handling this save: + + * [Reinitialization mode] + Don't save on-chip memory at all, instead just reinitialize the globals + and leave the rest corrupted. This is useful if on-chip memory is used + only for temporaries (e.g. frames in Azur), for which we don't care if + they're corrupted, or code (e.g gint interrupt handling code), which will + be reloaded and is otherwise a constant. + + * [Backup mode] + Save on-chip memory to a user-provided buffer of size GINT_ONCHIP_BUFSIZE. + + * [Nothing mode] + Don't do anything, and let the world switch function choose its preferred + saving method. This allows application-specific compromises. + + This is not a problem on SH3 because on-chip memory is only used on SH4. */ +enum { + GINT_ONCHIP_REINITIALIZE = 0, + GINT_ONCHIP_BACKUP = 1, + GINT_ONCHIP_NOTHING = 2, +}; +#define GINT_ONCHIP_BUFSIZE (20 << 10) + +void gint_set_onchip_save_mode(int mode, void *ptr); + +/* gint_get_onchip_save_mode(): Get the current on-chip memory save policy */ +int gint_get_onchip_save_mode(void **ptr); + /* This function has been moved to the INTC driver */ __attribute__((deprecated("Use intc_handler() instead"))) static GINLINE void *gint_inthandler(int code, void const *h, size_t size) { diff --git a/src/kernel/kernel.h b/src/kernel/kernel.h index 5d41079..a136c9a 100644 --- a/src/kernel/kernel.h +++ b/src/kernel/kernel.h @@ -5,6 +5,9 @@ #ifndef GINT_CORE_KERNEL #define GINT_CORE_KERNEL +/* gint_load_onchip_sections(): Initialize on-chip memory sections */ +void gint_load_onchip_sections(void); + /* kinit(): Install and start gint */ void kinit(void); diff --git a/src/kernel/start.c b/src/kernel/start.c index 0902967..bebc8da 100644 --- a/src/kernel/start.c +++ b/src/kernel/start.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "kernel.h" @@ -94,6 +95,21 @@ static void callarray(void (**f)(void), void (**l)(void)) while(f < l) (*(*f++))(); } +void gint_load_onchip_sections(void) +{ + /* Do not load data to ILRAM, XRAM or YRAM on SH3 - the areas don't + exist. If you use them, you're responsible! */ + if(!isSH3()) + { + /* Clear the areas so that we have less to save in case of a + return to menu leading to a poweroff. */ + memset((void *)0xe5200000, 0, 4096); + regcpy(&lilram, &silram, &rilram); + memset((void *)0xe500e000, 0, 16384); + regcpy(&lxyram, &sxyram, &rxyram); + } +} + static int start2(int isappli, int optnum) { /* We are currently in a dynamic userspace mapping of an add-in run @@ -141,13 +157,7 @@ static int start2(int isappli, int optnum) regcpy(&ldata, &sdata, &rdata); regclr(&rbss, &sbss); - /* Do not load data to ILRAM, XRAM or YRAM on SH3 - the areas don't - exist. If you use them, you're responsible! */ - if(!isSH3()) - { - regcpy(&lilram, &silram, &rilram); - regcpy(&lxyram, &sxyram, &rxyram); - } + gint_load_onchip_sections(); #ifdef FX9860G /* Copy permanently-mapped code to start of user RAM (on fx-CG 50 it diff --git a/src/kernel/world.c b/src/kernel/world.c index 7334dd7..4b006ff 100644 --- a/src/kernel/world.c +++ b/src/kernel/world.c @@ -3,8 +3,11 @@ #include #include #include +#include +#include "kernel.h" #include +#include //--- // World buffer @@ -56,6 +59,9 @@ void gint_world_sync(void) // World switch with driver state saves //--- +static int onchip_save_mode = GINT_ONCHIP_REINITIALIZE; +static void *onchip_save_buffer = NULL; + void gint_world_switch_in(gint_world_t world_os, gint_world_t world_addin) { /* Unbind from the OS driver and complete foreign asynchronous tasks */ @@ -144,13 +150,41 @@ int gint_world_switch(gint_call_t call) extern void *gint_stack_top; gint_world_switch_out(gint_world_addin, gint_world_os); + void *ILRAM = (void *)0xe5200000; + void *XRAM = (void *)0xe500e000; + void *YRAM = (void *)0xe5010000; + /* Watch out for stack overflows */ uint32_t *canary = gint_stack_top; if(canary) *canary = 0xb7c0ffee; + /* Save on-chip memory if requested */ + if(!isSH3() && onchip_save_mode == GINT_ONCHIP_BACKUP) { + void *ptr = onchip_save_buffer; + memcpy(ptr, ILRAM, 4096); + ptr += 4096; + memcpy(ptr, XRAM, 8192); + ptr += 8192; + memcpy(ptr, YRAM, 8192); + ptr += 8192; + } + int rc = gint_call(call); + /* Restore or reinitialize on-chip memory */ + if(!isSH3() && onchip_save_mode == GINT_ONCHIP_BACKUP) { + void *ptr = onchip_save_buffer; + memcpy(ILRAM, ptr, 4096); + ptr += 4096; + memcpy(XRAM, ptr, 8192); + ptr += 8192; + memcpy(YRAM, ptr, 8192); + ptr += 8192; + } + else if(!isSH3() && onchip_save_mode == GINT_ONCHIP_REINITIALIZE) + gint_load_onchip_sections(); + /* The canary check needs to occur before switching in the gint world; otherwise we just crash due to the overflow. gint_panic() isn't really designed to work from the OS world, but it does fine on the @@ -167,3 +201,16 @@ void gint_switch(void (*function)(void)) { gint_world_switch(GINT_CALL(function)); } + +void gint_set_onchip_save_mode(int mode, void *ptr) +{ + onchip_save_mode = mode; + onchip_save_buffer = ptr; +} + +int gint_get_onchip_save_mode(void **ptr) +{ + if(ptr) + *ptr = onchip_save_buffer; + return onchip_save_mode; +}