From 70a91ddac29ebbcddb284a6c862823eb9adfa80e Mon Sep 17 00:00:00 2001 From: Nursultan Kabylkas Date: Tue, 29 Aug 2023 16:04:18 +0600 Subject: [PATCH] Summary: creating single boot ROM for multicore configs The motive for this commit is explained in Issue 79 on the Chips Alliance Dromajo GitHub page: https://github.com/chipsalliance/dromajo/issues/79. This pull request introduces the implementation for the enhancement. When the checkpoint is saved, a single boot ROM is generated for N cores. The boot code is divided into N sections, where N represents the number of cores. Each core will generate its own recovery code and write it to its designated section. In addition to the recovery code, each core initializes with a preamble code that reads the hart_id and calculates the program counter (PC) based on the id. --- include/riscv_cpu.h | 4 +- src/dromajo.cpp | 2 +- src/riscv_cpu.cpp | 79 ++++++++++++------------------------- src/riscv_machine.cpp | 91 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 102 insertions(+), 74 deletions(-) diff --git a/include/riscv_cpu.h b/include/riscv_cpu.h index 9c2eff7b..f3bd6663 100644 --- a/include/riscv_cpu.h +++ b/include/riscv_cpu.h @@ -46,7 +46,7 @@ #define ROM_SIZE 0x00002000 #define ROM_BASE_ADDR 0x00010000 -#define BOOT_BASE_ADDR 0x00010000 +#define BOOT_BASE_ADDR 0x00010040 // The default RAM base, can be relocated with config "memory_base_addr" #define RAM_BASE_ADDR 0x80000000 @@ -363,6 +363,8 @@ void riscv_set_debug_mode(RISCVCPUState *s, bool on); int riscv_benchmark_exit_code(RISCVCPUState *s); #include "riscv_machine.h" +void generate_core_boot_rom(uint32_t *rom, uint32_t rom_size, uint32_t code_pos, uint32_t data_pos, RISCVCPUState *s, const uint64_t clint_base_addr); +void create_boot_rom_image(uint32_t *rom, uint32_t rom_size_bytes, const char *file_name); void riscv_ram_serialize(RISCVCPUState *s, const char *dump_name); void riscv_cpu_serialize(RISCVCPUState *s, const char *dump_name, const uint64_t clint_base_addr); void riscv_ram_deserialize(RISCVCPUState *s, const char *dump_name); diff --git a/src/dromajo.cpp b/src/dromajo.cpp index c13239ab..ee7ee442 100644 --- a/src/dromajo.cpp +++ b/src/dromajo.cpp @@ -223,7 +223,7 @@ int main(int argc, char **argv) { if (!m) return 1; - int n_cycles = 10000; + int n_cycles = 1; execution_start_ts = get_current_time_in_seconds(); execution_progress_meassure = &m->cpu_state[0]->minstret; signal(SIGINT, sigintr_handler); diff --git a/src/riscv_cpu.cpp b/src/riscv_cpu.cpp index 8e9a4239..375558f8 100644 --- a/src/riscv_cpu.cpp +++ b/src/riscv_cpu.cpp @@ -2354,10 +2354,23 @@ static void deserialize_memory(void *base, size_t size, const char *file) { if (f_fd < 0) err(-3, "trying to read %s", file); - size_t sz = read(f_fd, base, size); + size_t read_size = 0; + uint8_t *ptr = (uint8_t *)base; + int64_t pending_size = size; + do { + /* Linux reads in 2GB chunks at most. */ + size_t sz = read(f_fd, &ptr[read_size], pending_size); + if (sz <= 0) { + err(-3, "%s %zd size failed to read memory size %zd", file, sz, size); + break; + } + + read_size += sz; + pending_size -= sz; + } while(pending_size > 0); - if (sz != size) - err(-3, "%s %zd size does not match memory size %zd", file, sz, size); + if (read_size != size) + err(-3, "%s %zd size does not match memory size %zd", file, read_size, size); close(f_fd); } @@ -2523,22 +2536,10 @@ static void create_hang_nonzero_hart(uint32_t *rom, uint32_t *code_pos, uint32_t // 1: } -static void create_boot_rom(RISCVCPUState *s, const char *file, const uint64_t clint_base_addr) { - uint32_t rom[ROM_SIZE / 4]; - memset(rom, 0, sizeof rom); - - // ROM organization - // 0000..003F wasted - // 0040..0AFF boot code (2,752 B) - // 0B00..0FFF boot data ( 512 B) - - uint32_t code_pos = (BOOT_BASE_ADDR - ROM_BASE_ADDR) / sizeof *rom; - uint32_t data_pos = 0xB00 / sizeof *rom; +void generate_core_boot_rom(uint32_t *rom, uint32_t rom_size, uint32_t code_pos, uint32_t data_pos, RISCVCPUState *s, const uint64_t clint_base_addr) { + /* Remember the start position for boundry checks. */ uint32_t data_pos_start = data_pos; - if (s->machine->ncpus == 1) // FIXME: May be interesting to freeze hartid >= ncpus - create_hang_nonzero_hart(rom, &code_pos, &data_pos); - create_csr64_recovery(rom, &code_pos, &data_pos, 0x7b1, s->pc); // Write to DPC (CSR, 0x7b1) // Write current priviliege level to prv in dcsr (0 user, 1 supervisor, 2 user) @@ -2668,7 +2669,7 @@ static void create_boot_rom(RISCVCPUState *s, const char *file, const uint64_t c // dret 0x7b200073 rom[code_pos++] = 0x7b200073; - if (sizeof rom / sizeof *rom <= data_pos || data_pos_start <= code_pos) { + if (rom_size <= data_pos || data_pos_start <= code_pos) { fprintf(dromajo_stderr, "ERROR: ROM is too small. ROM_SIZE should increase. " "Current code_pos=%d data_pos=%d\n", @@ -2677,7 +2678,11 @@ static void create_boot_rom(RISCVCPUState *s, const char *file, const uint64_t c exit(-6); } - serialize_memory(rom, ROM_SIZE, file); +} + +void create_boot_rom_image(uint32_t *rom, uint32_t rom_size_bytes, const char *file_name) { + // Write ROM. + serialize_memory(rom, rom_size_bytes, file_name); } void riscv_ram_serialize(RISCVCPUState *s, const char *dump_name) { @@ -2759,40 +2764,6 @@ void riscv_cpu_serialize(RISCVCPUState *s, const char *dump_name, const uint64_t for (int i = 0; i < 4; i += 2) fprintf(conf_fd, "pmpcfg%d:%llx\n", i, (unsigned long long)s->csr_pmpcfg[i]); for (int i = 0; i < 16; ++i) fprintf(conf_fd, "pmpaddr%d:%llx\n", i, (unsigned long long)s->csr_pmpaddr[i]); - - PhysMemoryRange *boot_ram = 0; - for (int i = s->mem_map->n_phys_mem_range - 1; i >= 0; --i) { - PhysMemoryRange *pr = &s->mem_map->phys_mem_range[i]; - fprintf(conf_fd, "mrange%d:0x%llx 0x%llx %s\n", i, (long long)pr->addr, (long long)pr->size, pr->is_ram ? "ram" : "io"); - - if (pr->is_ram && pr->addr == ROM_BASE_ADDR) { - assert(!boot_ram); - boot_ram = pr; - } - } - - if (!boot_ram) { - fprintf(dromajo_stderr, "ERROR: could not find boot RAM.\n"); - exit(-3); - } - - n = strlen(dump_name) + 64; - char *f_name = (char *)alloca(n); - snprintf(f_name, n, "%s.bootram", dump_name); - - if (s->priv != 3 || ROM_BASE_ADDR + ROM_SIZE < s->pc) { - fprintf(dromajo_stderr, "NOTE: creating a new boot ROM.\n"); - create_boot_rom(s, f_name, clint_base_addr); - } else if (BOOT_BASE_ADDR < s->pc) { - fprintf(dromajo_stderr, "ERROR: could not checkpoint when running inside the ROM.\n"); - exit(-4); - } else if (s->pc == BOOT_BASE_ADDR && boot_ram) { - fprintf(dromajo_stderr, "NOTE: using the default dromajo ROM.\n"); - serialize_memory(boot_ram->phys_mem, boot_ram->size, f_name); - } else { - fprintf(dromajo_stderr, "ERROR: unexpected PC address 0x%llx.\n", (long long)s->pc); - exit(-4); - } } void riscv_ram_deserialize(RISCVCPUState *s, const char *dump_name) { @@ -2818,6 +2789,6 @@ void riscv_cpu_deserialize(RISCVCPUState *s, const char *dump_name) { snprintf(boot_name, n, "%s.bootram", dump_name); deserialize_memory(pr->phys_mem, pr->size, boot_name); - } + } } } diff --git a/src/riscv_machine.cpp b/src/riscv_machine.cpp index 3cf37776..82ba27db 100644 --- a/src/riscv_machine.cpp +++ b/src/riscv_machine.cpp @@ -1199,7 +1199,9 @@ RISCVMachine *virt_machine_init(const VirtMachineParams *p) { /* RAM */ cpu_register_ram(s->mem_map, s->ram_base_addr, s->ram_size, 0); - cpu_register_ram(s->mem_map, ROM_BASE_ADDR, ROM_SIZE, 0); + + /* Boot ROM. */ + cpu_register_ram(s->mem_map, ROM_BASE_ADDR, s->ncpus * ROM_SIZE, 0); for (int i = 0; i < s->ncpus; ++i) { s->cpu_state[i]->physical_addr_len = p->physical_addr_len; @@ -1416,32 +1418,85 @@ void virt_machine_end(RISCVMachine *s) { } void virt_machine_serialize(RISCVMachine *m, const char *dump_name) { - /* Serialize core states. */ - for (int i = 0; i < m->ncpus; ++i) { + /* Check that all cores are out of the ROM. */ + bool is_serializable = true; + for (int i = 0; i < m->ncpus && is_serializable; ++i) { RISCVCPUState *s = m->cpu_state[i]; + is_serializable = s->priv != 3 || (ROM_BASE_ADDR + (m->ncpus * ROM_SIZE) < s->pc); + } - vm_error("plic: %x %x timecmp=%llx\n", m->plic_pending_irq, m->plic_served_irq, (unsigned long long)s->timecmp); + /* Serialize core states. */ + if (is_serializable) { + /* Create serialization file per core. */ + for (int i = 0; i < m->ncpus; ++i) { + RISCVCPUState *s = m->cpu_state[i]; - /* Append core number suffix to the file name. */ - std::stringstream core_dump_name; - core_dump_name << dump_name << i; - riscv_cpu_serialize(s, core_dump_name.str().c_str(), m->clint_base_addr); - } + vm_error("plic: %x %x timecmp=%llx\n", m->plic_pending_irq, m->plic_served_irq, (unsigned long long)s->timecmp); - /* Serialize memory. */ - riscv_ram_serialize(m->cpu_state[0], dump_name); + /* Append core number suffix to the file name. */ + std::stringstream core_dump_name; + core_dump_name << dump_name << i; + + riscv_cpu_serialize(s, core_dump_name.str().c_str(), m->clint_base_addr); + } + + /* Generate single boot ROM for all cores. */ + const uint32_t kTotalRomSize = (m->ncpus * ROM_SIZE) / 4; + uint32_t rom[kTotalRomSize]; + memset(rom, 0, sizeof(rom)); + + // ROM organization + // Core 0: + // 0000..003F wasted + // 0040..0AFF boot code (2,752 B) + // 0B00..0FFF boot data ( 512 B) + // Core 1: + // 1000..003F wasted + // 1040..0AFF boot code (2,752 B) + // 1B00..0FFF boot data ( 512 B) + // repeats for each core ... + for (int i = 0; i < m->ncpus; ++i) { + RISCVCPUState *s = m->cpu_state[i]; + uint32_t code_pos = ((i << 12) | (BOOT_BASE_ADDR - ROM_BASE_ADDR)) / sizeof(*rom); + uint32_t data_pos = ((i << 12) | 0xB00) / sizeof(*rom); + + /* All cores start by determining the PC they should jump to. */ + rom[code_pos++] = 0xf1402573; // csrr a0, mhartid + rom[code_pos++] = 0x00c5151b; // slliw a0, a0, 12 + + /* These four instructions must be the last in preamble. */ + /* If other instructions should be added, add them before these four. */ + rom[code_pos++] = 0x00000597; // auipc a1, 0x0 + rom[code_pos++] = 0x0105859b; // addiw a1, a1, 0xc + rom[code_pos++] = 0x00b5053b; // addw a0, a0, a1 + rom[code_pos++] = 0x50067; // jr a0 + + /* Generates and appends recovery code for each core to rom. */ + generate_core_boot_rom(rom, kTotalRomSize, code_pos, data_pos, s, m->clint_base_addr); + } + + /* Write generated boot ROM to file. */ + uint32_t name_len = strlen(dump_name) + 64; + char *f_name = (char *)alloca(name_len); + snprintf(f_name, name_len, "%s.bootram", dump_name); + create_boot_rom_image(rom, 4 * kTotalRomSize, f_name); + + /* Write memory state. */ + riscv_ram_serialize(m->cpu_state[0], dump_name); + } + else + { + fprintf(dromajo_stderr, "ERROR: could not checkpoint. One or more cores running inside the ROM.\n"); + exit(-4); + } } void virt_machine_deserialize(RISCVMachine *m, const char *dump_name) { /* Deserialize core states. */ - for (int i = 0; i < m->ncpus; ++i) { - RISCVCPUState *s = m->cpu_state[i]; + RISCVCPUState *s = m->cpu_state[0]; - /* Append core number suffix to the file name. */ - std::stringstream core_dump_name; - core_dump_name << dump_name << i; - riscv_cpu_deserialize(s, core_dump_name.str().c_str()); - } + /* Append core number suffix to the file name. */ + riscv_cpu_deserialize(s, dump_name); /* Deserialize memory. */ riscv_ram_deserialize(m->cpu_state[0], dump_name);