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.
This commit is contained in:
Lephe 2024-01-16 11:06:37 +01:00
parent 235fa8a361
commit 5655699cd8
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
4 changed files with 109 additions and 7 deletions

View File

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

View File

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

View File

@ -11,6 +11,7 @@
#include <gint/exc.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
#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

View File

@ -3,8 +3,11 @@
#include <gint/gint.h>
#include <gint/exc.h>
#include <gint/defs/call.h>
#include <gint/hardware.h>
#include "kernel.h"
#include <stdlib.h>
#include <string.h>
//---
// 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;
}