Skip to content

Commit ba3011f

Browse files
committed
Fix system emulation reboot
Previously, 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 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. Additionally, device reset helper functions (plic_reset(), u8250_reset()) are introduced to support peripheral reinitialization during reboot. 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) instead of re-allocating. 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 40fcbfe commit ba3011f

File tree

10 files changed

+577
-368
lines changed

10 files changed

+577
-368
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
@@ -1098,7 +1098,6 @@ static void rv_check_interrupt(riscv_t *rv)
10981098
vm_attr_t *attr = PRIV(rv);
10991099
if (peripheral_update_ctr-- == 0) {
11001100
peripheral_update_ctr = 64;
1101-
11021101
#if defined(__EMSCRIPTEN__)
11031102
escape_seq:
11041103
#endif
@@ -1148,7 +1147,24 @@ void rv_step(void *arg)
11481147
/* find or translate a block for starting PC */
11491148
const uint64_t cycles_target = rv->csr_cycle + cycles;
11501149

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

0 commit comments

Comments
 (0)