Skip to content

Commit

Permalink
Load / store emulator state
Browse files Browse the repository at this point in the history
This allows to load and store the emulator state at 0xfff000 in the REU. This way,
you can get Linux *running* relatively(!) quickly on a C-64 by avoiding the lengthy
boot process.
  • Loading branch information
onnokort committed Aug 31, 2023
1 parent 5ebfa78 commit fc3aae2
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 15 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ OBJS := \
plic.o \
uart.o \
main.o \
persistence.o \
$(OBJS_EXTRA)

ifeq ($(C64), 1)
Expand Down
75 changes: 67 additions & 8 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
#include "persistence.h"

/* Define fetch separately since it is simpler (fixed width, already checked
* alignment, only main RAM is executable).
Expand Down Expand Up @@ -363,6 +364,23 @@ vm_t vm = {
.mem_store = mem_store
};

static void print_some_emu_state() {
printf("PC: %lx\n", vm.pc);
printf("TIMER LO, HI: %lx, %lx\n", emu.timer_lo, emu.timer_hi);
printf("stopped: %d\n", emu.stopped);
printf("UART: %d %d\n", emu.uart.in_ready, emu.uart.in_char);
printf("PLIC: %lx %lx %lx %lx\n",
emu.plic.masked,
emu.plic.ip,
emu.plic.ie,
emu.plic.active);
}

#if C64
// FIXME: Load on-the fly from REU to avoid using all this stack/RAM at once
uint8_t reu_saved_state[250];
#endif

__attribute__((nonreentrant))
static int semu_start(int argc, char **argv)
{
Expand Down Expand Up @@ -427,17 +445,35 @@ static int semu_start(int argc, char **argv)
atexit(unmap_files);
#endif

/* Set up RISC-V hart */
emu.timer_hi = emu.timer_lo = 0xFFFFFFFF;
vm.page_table_addr = 0;
vm.s_mode = true;
vm.x_regs[RV_R_A0] = 0; /* hart ID. i.e., cpuid */
vm.x_regs[RV_R_A1] = dtb_addr;
bool checkpoint_loaded = false;
uint8_t *pbase=NULL;
#if C64
load_from_reu(&reu_saved_state, PERSISTENCE_BASEADR, sizeof(reu_saved_state));

pbase = reu_saved_state;
checkpoint_loaded = load_all(&vm, &pbase);
printf("checkpoint loaded: %d\n", checkpoint_loaded);
printf("number of bytes deserialized: %d\n", pbase - reu_saved_state);
#else
pbase = (uint8_t*)emu.ram + PERSISTENCE_BASEADR;
checkpoint_loaded = load_all(&vm, &pbase);
printf("Checkpoint loaded: %d\n", checkpoint_loaded);
printf("Number of bytes deserialized: %ld\n", pbase - (uint8_t*)emu.ram - PERSISTENCE_BASEADR);
#endif

if (!checkpoint_loaded) {
/* Set up RISC-V hart */
emu.timer_hi = emu.timer_lo = 0xFFFFFFFF;
vm.page_table_addr = 0;
vm.s_mode = true;
vm.x_regs[RV_R_A0] = 0; /* hart ID. i.e., cpuid */
vm.x_regs[RV_R_A1] = dtb_addr;
}
/* Set up peripherals */
emu.uart.in_fd = 0, emu.uart.out_fd = 1;

#if !C64
print_some_emu_state();
capture_keyboard_input(); /* set up uart */
#if SEMU_HAS(VIRTIONET)
if (!virtio_net_init(&(emu.vnet)))
Expand Down Expand Up @@ -497,8 +533,31 @@ static int semu_start(int argc, char **argv)
vm_error_report(&vm);
return 2;
}

/* unreachable */
printf("\n\nVM RISCV insn count: %lu\n", (long unsigned)(vm.insn_count));
#if C64
pbase = reu_saved_state;
save_all(&vm, &pbase);
// might seem meaningless to print here, but it shows up on kernalemu
printf("number of bytes serialized: %d\n", pbase - reu_saved_state);
save_to_reu(PERSISTENCE_BASEADR, &reu_saved_state, pbase - reu_saved_state);
void (*reset_vect)() = (void*)0xfce2;
reset_vect();
#else
printf("Emulator stopped.\n");
print_some_emu_state();
printf("PC: %lx\n", vm.pc);
pbase = (uint8_t*)emu.ram + PERSISTENCE_BASEADR;
save_all(&vm, &pbase);
printf("Number of bytes serialized: %ld\n", pbase - (uint8_t*)emu.ram - PERSISTENCE_BASEADR);
FILE *reufile = fopen("reufile.semu.written", "wb");
assert(reufile != NULL);
int wreu = fwrite(emu.ram, 1, 16777216, reufile);
printf("WROTE REU: %d\n", wreu);

// Note: This might fail if running repeatedly on the output file and it triggers another save.
assert(wreu == 16777216);
fclose(reufile);
#endif
return 0;
}

Expand Down
129 changes: 129 additions & 0 deletions persistence.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#if !C64
#include <assert.h>
#endif
#include "device.h"
#include "persistence.h"

void save_cpu(const vm_t *vm,
uint8_t **obufp) {
for (int i=0; i<32; i++) SER32(vm->x_regs[i]);
SER32(vm->lr_reservation);
SER32(vm->pc);
SER32(vm->current_pc);
SER32(vm->insn_count);
SER32(vm->insn_count_hi);
#if !C64
assert(vm->error <=255);
#endif
SER8(vm->error);
SER32(vm->exc_cause);
SER32(vm->exc_val);
SER8(vm->s_mode);
SER8(vm->sstatus_spp);
SER8(vm->sstatus_spie);

SER32(vm->sepc);
SER32(vm->scause);
SER32(vm->stval);

SER8(vm->sstatus_mxr);
SER8(vm->sstatus_sum);
SER8(vm->sstatus_sie);

SER32(vm->sie);
SER32(vm->sip);
SER32(vm->stvec_addr);

SER8(vm->stvec_vectored);

SER32(vm->sscratch);
SER32(vm->scounteren);
SER32(vm->satp);
SER32(vm->page_table_addr);
}

static void save_timers(const vm_t *vm, uint8_t **obufp) {
const emu_state_t *data = (const emu_state_t *) vm->priv;
SER32(data->timer_lo);
SER32(data->timer_hi);
}

void save_all(const vm_t *vm, uint8_t **obufp) {
uint32_t version=1, marker;
SER32(version);
marker='M';
SER8(marker); // machine
save_cpu(vm, obufp);

marker='I';
SER8(marker); // interrupt controller
save_plic(vm, obufp);
marker='U';
SER8(marker); // UART
save_uart(vm, obufp);
marker='T';
SER8(marker);
save_timers(vm, obufp);
// FIXME: save network/blockdev devices if they are enabled
}

void load_cpu(vm_t *vm,
uint8_t **ibufp) {
for (int i=0; i<32; i++) DESER32(vm->x_regs[i]);
DESER32(vm->lr_reservation);
DESER32(vm->pc);
DESER32(vm->current_pc);
DESER32(vm->insn_count);
DESER32(vm->insn_count_hi);
DESER8(vm->error);
DESER32(vm->exc_cause);
DESER32(vm->exc_val);
DESER8(vm->s_mode);
DESER8(vm->sstatus_spp);
DESER8(vm->sstatus_spie);

DESER32(vm->sepc);
DESER32(vm->scause);
DESER32(vm->stval);

DESER8(vm->sstatus_mxr);
DESER8(vm->sstatus_sum);
DESER8(vm->sstatus_sie);

DESER32(vm->sie);
DESER32(vm->sip);
DESER32(vm->stvec_addr);

DESER8(vm->stvec_vectored);
DESER32(vm->sscratch);
DESER32(vm->scounteren);
DESER32(vm->satp);
DESER32(vm->page_table_addr);
}


static void load_timers(vm_t *vm, uint8_t **ibufp) {
emu_state_t *data = (emu_state_t *) vm->priv;
DESER32(data->timer_lo);
DESER32(data->timer_hi);
}

bool load_all(vm_t *vm, uint8_t **ibufp) {
uint32_t version, marker;
DESER32(version); // version field
if (version != 1) return false;
DESER8(marker);
if (marker != 'M') return false;
load_cpu(vm, ibufp);
DESER8(marker);
if (marker != 'I') return false;
load_plic(vm, ibufp);
DESER8(marker);
if (marker != 'U') return false;
load_uart(vm, ibufp);
DESER8(marker);
if (marker != 'T') return false;
load_timers(vm, ibufp);
// FIXME: load network/blockdev devices if they are enabled
return true;
}
53 changes: 53 additions & 0 deletions persistence.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once
#include <stdint.h>
#include "riscv.h"

// set value to 0 in deser first to deal with variable sized fields

#define SER(value, N) \
do { \
uint8_t *obuf = *obufp; \
uint8_t *v = (uint8_t*)&(value); \
for (int i=0; i < (N); i++) \
*obuf++ = v[i]; \
*obufp+=(N); \
} while(0); \

#define DESER(value, N) \
do { \
(value)=0; \
uint8_t *ibuf = *ibufp; \
uint8_t *v = (uint8_t*)(&(value)); \
for (int i=0; i < (N); i++) \
v[i] = *ibuf++; \
*ibufp+=(N); \
} while(0); \

#define SER8(value) SER(value, 1)
#define SER32(value) SER(value, 4)
#define DESER8(value) DESER(value, 1)
#define DESER32(value) DESER(value, 4)

extern void save_cpu(const vm_t *vm,
uint8_t **obufp);

extern void save_plic(const vm_t *vm,
uint8_t **obufp);

extern void save_uart(const vm_t *vm,
uint8_t **obufp);

extern void save_all(const vm_t *vm, uint8_t **obufp);

extern void load_cpu(vm_t *vm,
uint8_t **ibufp);

extern void load_plic(vm_t *vm,
uint8_t **ibufp);

extern void load_uart(vm_t *vm,
uint8_t **ibufp);

extern bool load_all(vm_t *vm, uint8_t **ibufp);

#define PERSISTENCE_BASEADR 0xfff000
21 changes: 21 additions & 0 deletions plic.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
#include "persistence.h"

/* Make PLIC as simple as possible: 32 interrupts, no priority */

void save_plic(const vm_t *vm,
uint8_t **obufp) {
const emu_state_t *data = (const emu_state_t *) vm->priv;
const plic_state_t plic = data->plic;
SER32(plic.masked);
SER32(plic.ip);
SER32(plic.ie);
SER32(plic.active);
}

void load_plic(vm_t *vm,
uint8_t **ibufp) {
emu_state_t *data = (const emu_state_t *) vm->priv;
plic_state_t* plic = &data->plic;
DESER32(plic->masked);
DESER32(plic->ip);
DESER32(plic->ie);
DESER32(plic->active);
}

void plic_update_interrupts(vm_t *vm, plic_state_t *plic)
{
/* Update pending interrupts */
Expand Down
43 changes: 36 additions & 7 deletions uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,44 @@
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
#include "persistence.h"

/* Emulate 8250 (plain, without loopback mode support) */

#define U8250_INT_THRE 1

void save_uart(const vm_t *vm,
uint8_t **obufp) {
const emu_state_t *data = (const emu_state_t *) vm->priv;
const u8250_state_t uart = data->uart;
SER8(uart.dll);
SER8(uart.dlh);
SER8(uart.lcr);
SER8(uart.ier);
SER8(uart.current_int);
SER8(uart.pending_ints);
SER8(uart.mcr);
// in_fd, out_fd cannot be saved
SER8(uart.in_ready);
SER8(uart.in_char);
}

void load_uart(vm_t *vm,
uint8_t **ibufp) {
emu_state_t *data = (const emu_state_t *) vm->priv;
u8250_state_t* uart = &data->uart;
DESER8(uart->dll);
DESER8(uart->dlh);
DESER8(uart->lcr);
DESER8(uart->ier);
DESER8(uart->current_int);
DESER8(uart->pending_ints);
DESER8(uart->mcr);
DESER8(uart->in_ready);
DESER8(uart->in_char);
}


#if !C64
static void reset_keyboard_input()
{
Expand Down Expand Up @@ -85,6 +118,7 @@ extern vm_t vm;

char *login_stop_test="buildroot login";
//char *login_stop_test="syslogd";
//char *login_stop_test="buildroot login:";

static void login_stop(uint8_t value) {
static char *ptr = NULL;
Expand All @@ -93,13 +127,8 @@ static void login_stop(uint8_t value) {
ptr++;
} else ptr = login_stop_test;
if (!*ptr) {
printf("\n\nVM RISCV insn count: %lu\n", (long unsigned)(vm.insn_count));
#if C64
void (*reset_vect)() = (void*)0xfce2;
reset_vect();
#else
exit(0);
#endif
emu_state_t* emu = (emu_state_t*)vm.priv;
emu->stopped = true;
}
}

Expand Down

0 comments on commit fc3aae2

Please sign in to comment.