Skip to content

Commit 8f9fd34

Browse files
committed
Fix system emulation reboot
At 7d13e82, the SBI RST extension did not distinguish between reboot and shutdown type. When a userspace reboot command was issued, the hart was incorrectly halted as if it were a shutdown. These changes fix the issue for both modes (interpreter and JIT) by properly detecting and handling two reboot types: cold and warm. Each type has its own handler: rv_cold_reboot() and rv_warm_reboot(). Since the initial system power-on is treated as a cold reboot, the initialization code has been refactored to share logic with the reboot path, adhering to the DRY principle. Key changes: 1. Rename rv_reset() to rv_cold_reboot() - Full system reset including processor, memory, and all peripherals. The initial power-on is treated as a cold reboot. 2. Introduce rv_warm_reboot() - Faster reboot that only resets processor and memory, skipping peripheral reinitialization. Can be triggered via echo "warm" > /sys/kernel/reboot/mode in guestOS. 3. Refactor boot image loading - Extract load_boot_images() helper to load kernel, DTB, and initrd, reducing code duplication between cold and warm reboot paths. 4. Introduce rv_reset_hart() - Static helper function to reset only hart state (GPRs, CSRs, PC), shared by both reboot modes. 5. Add reboot-safe resource management - Add "check for reboot" comments throughout initialization to reuse already-allocated resources (memory, fd_map, PLIC, UART, vblk, block_map, etc) instead of re-allocating. For JIT state resources, might free and re-allocate (e.g., calling jit_state_exit()) to reuse the existing APIs. 6. Use calloc for vblk/disk arrays - Changed from malloc to calloc so pointers are initialized to NULL, simplifying reboot checks. 7. Use setjmp/longjmp for clean reboot - Reboot rewrites all registers, causing call stack values to become stale. setjmp in rv_step() establishes a return point, longjmp after reboot provides a clean call stack. 8. Add plic_reset() and u8250_reset() - New device reset functions to reinitialize state without free/realloc. 9. Add sbi_rst_type_str() and sbi_rst_reason_str() - Helper functions for human-readable SBI reset type/reason in dmesg.
1 parent dccb5a8 commit 8f9fd34

File tree

10 files changed

+641
-370
lines changed

10 files changed

+641
-370
lines changed

src/devices/plic.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,8 @@ void plic_delete(plic_t *plic)
8989
{
9090
free(plic);
9191
}
92+
93+
void plic_reset(plic_t *plic)
94+
{
95+
memset(plic, 0, sizeof(plic_t));
96+
}

src/devices/plic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ plic_t *plic_new();
3939

4040
/* delete a PLIC instance */
4141
void plic_delete(plic_t *plic);
42+
43+
/* reset a PLIC instance */
44+
void plic_reset(plic_t *plic);

src/devices/uart.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,8 @@ void u8250_delete(u8250_state_t *uart)
197197
{
198198
free(uart);
199199
}
200+
201+
void u8250_reset(u8250_state_t *uart)
202+
{
203+
memset(uart, 0, sizeof(u8250_state_t));
204+
}

src/devices/uart.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@ u8250_state_t *u8250_new();
4949

5050
/* delete a UART instance */
5151
void u8250_delete(u8250_state_t *uart);
52+
53+
/* reset a UART instance */
54+
void u8250_reset(u8250_state_t *uart);

src/devices/virtio-blk.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,12 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
403403
vblk->disk_fd = -1;
404404

405405
/* Allocate memory for the private member */
406-
vblk->priv = calloc(1, sizeof(struct virtio_blk_config));
407-
assert(vblk->priv);
406+
if (vblk->priv) { /* check for reboot */
407+
memset(vblk->priv, 0, sizeof(struct virtio_blk_config));
408+
} else {
409+
vblk->priv = calloc(1, sizeof(struct virtio_blk_config));
410+
assert(vblk->priv);
411+
}
408412

409413
/* No disk image is provided */
410414
if (!disk_file) {

src/emulate.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,6 @@ static void rv_check_interrupt(riscv_t *rv)
10931093
vm_attr_t *attr = PRIV(rv);
10941094
if (peripheral_update_ctr-- == 0) {
10951095
peripheral_update_ctr = 64;
1096-
10971096
#if defined(__EMSCRIPTEN__)
10981097
escape_seq:
10991098
#endif
@@ -1143,7 +1142,24 @@ void rv_step(void *arg)
11431142
/* find or translate a block for starting PC */
11441143
const uint64_t cycles_target = rv->csr_cycle + cycles;
11451144

1146-
/* loop until hitting the cycle target */
1145+
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
1146+
/* Set up the jump point for handling reboots */
1147+
if (setjmp(rv->reboot_jmp) != 0) {
1148+
/* longjmp to here after a reboot happens */
1149+
#if !RV32_HAS(JIT)
1150+
need_clear_block_map = false;
1151+
#endif
1152+
is_branch_taken = false;
1153+
reloc_enable_mmu_jalr_addr = 0;
1154+
reloc_enable_mmu = false;
1155+
need_retranslate = false;
1156+
need_handle_signal = false;
1157+
prev = NULL;
1158+
last_pc = 0;
1159+
}
1160+
#endif
1161+
1162+
/* loop until hitting the cycle target or hart is halted */
11471163
while (rv->csr_cycle < cycles_target && !rv->halt) {
11481164
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
11491165
/* check for any interrupt after every block emulation */

0 commit comments

Comments
 (0)