code wiki / _hdl_build / rv64im_min_elf_loader.nx
rv64im_min_elf_loader.nx
buildroot/runtime/_hdl_build/rv64im_min_elf_loader.nx
about
rv64im_min_elf_loader.nx -- ELF64 loader for the RV64IM-min sim.
Parses an ELF64 (little-endian, EM_RISCV) byte buffer and copies
every PT_LOAD segment into the simulator's mem_buf at the
segment's physical address. Sets sim.pc to e_entry.
This is what lets the sim boot the actual nishi-os kernel ELF
(nishi-os/kernel/nishi-kernel.elf) without hand-coding instructions.
Follow-on commits add an end-to-end harness that:
1. Reads kernel ELF bytes via sys_openat + sys_read
2. Calls nx_elf_load to populate sim.mem_buf
3. Runs the sim until halted (or 100k cycles, whichever first)
4. Asserts uart.tx_buf contains the 10 expected [m0-*] markers
Status: SEED. 2026-05-26. Loader-only. ELF parsing per the
System V ABI (ELF Specification 1.2) + RISC-V ELF psABI.
dependencies 9 imports · 5 importers
imports: nx_syscalls.nxnishi_hdl_primitives.nxrv64im_min_decoder.nxrv64im_min_alu.nxrv64im_min_regfile.nxrv64im_min_csr.nxrv64im_min_clint.nxrv64im_min_uart.nxrv64im_min_sim.nx
imported by: nx_rv64im_cpu_crosscheck.nxrv64im_min_cpu_crosscheck.nxrv64im_min_hot_report.nxrv64im_min_kernel_boot_smoke.nxrv64im_min_symtab.nx
structs
| 134 | struct NxElfLoad |
consts
| 29 | const NX_ELF_MAG0: i64 = 0x7f |
| 30 | const NX_ELF_MAG1: i64 = 0x45 // 'E' |
| 31 | const NX_ELF_MAG2: i64 = 0x4c // 'L' |
| 32 | const NX_ELF_MAG3: i64 = 0x46 // 'F' |
| 34 | const NX_ELF_CLASS_64: i64 = 2 // EI_CLASS = ELFCLASS64 |
| 35 | const NX_ELF_DATA_LSB: i64 = 1 // EI_DATA = ELFDATA2LSB (little-endian) |
| 36 | const NX_ELF_VERSION_CURRENT: i64 = 1 |
| 37 | const NX_ELF_MACHINE_RISCV: i64 = 243 // EM_RISCV = 0xF3 |
| 40 | const NX_ELF_ET_EXEC: i64 = 2 |
| 41 | const NX_ELF_ET_DYN: i64 = 3 |
| 44 | const NX_ELF_PT_NULL: i64 = 0 |
| 45 | const NX_ELF_PT_LOAD: i64 = 1 |
| 46 | const NX_ELF_PT_DYNAMIC: i64 = 2 |
| 47 | const NX_ELF_PT_INTERP: i64 = 3 |
| 48 | const NX_ELF_PT_NOTE: i64 = 4 |
| 49 | const NX_ELF_PT_PHDR: i64 = 6 |
| 50 | const NX_ELF_PT_GNU_STACK: i64 = 0x6474e551 |
| 53 | const NX_ELF_OFF_E_IDENT: i64 = 0 |
| 54 | const NX_ELF_OFF_E_TYPE: i64 = 16 |
| 55 | const NX_ELF_OFF_E_MACHINE: i64 = 18 |
| 56 | const NX_ELF_OFF_E_VERSION: i64 = 20 |
| 57 | const NX_ELF_OFF_E_ENTRY: i64 = 24 |
| 58 | const NX_ELF_OFF_E_PHOFF: i64 = 32 |
| 59 | const NX_ELF_OFF_E_SHOFF: i64 = 40 |
| 60 | const NX_ELF_OFF_E_FLAGS: i64 = 48 |
| 61 | const NX_ELF_OFF_E_EHSIZE: i64 = 52 |
| 62 | const NX_ELF_OFF_E_PHENTSIZE: i64 = 54 |
| 63 | const NX_ELF_OFF_E_PHNUM: i64 = 56 |
| 64 | const NX_ELF_OFF_E_SHENTSIZE: i64 = 58 |
| 65 | const NX_ELF_OFF_E_SHNUM: i64 = 60 |
| 66 | const NX_ELF_OFF_E_SHSTRNDX: i64 = 62 |
| 67 | const NX_ELF_EHDR_SIZE: i64 = 64 |
| 70 | const NX_ELF_PH_OFF_P_TYPE: i64 = 0 |
| 71 | const NX_ELF_PH_OFF_P_FLAGS: i64 = 4 |
| 72 | const NX_ELF_PH_OFF_P_OFFSET: i64 = 8 |
| 73 | const NX_ELF_PH_OFF_P_VADDR: i64 = 16 |
| 74 | const NX_ELF_PH_OFF_P_PADDR: i64 = 24 |
| 75 | const NX_ELF_PH_OFF_P_FILESZ: i64 = 32 |
| 76 | const NX_ELF_PH_OFF_P_MEMSZ: i64 = 40 |
| 77 | const NX_ELF_PH_OFF_P_ALIGN: i64 = 48 |
| 78 | const NX_ELF_PHDR_SIZE: i64 = 56 |
| 81 | const NX_ELF_OK: i64 = 0 |
| 82 | const NX_ELF_BAD_MAGIC: i64 = 1 |
| 83 | const NX_ELF_NOT_64BIT: i64 = 2 |
| 84 | const NX_ELF_NOT_LITTLE_ENDIAN: i64 = 3 |
| 85 | const NX_ELF_BAD_VERSION: i64 = 4 |
| 86 | const NX_ELF_NOT_RISCV: i64 = 5 |
| 87 | const NX_ELF_BAD_TYPE: i64 = 6 |
| 88 | const NX_ELF_TRUNCATED: i64 = 7 |
| 89 | const NX_ELF_BAD_PHENTSIZE: i64 = 8 |
| 90 | const NX_ELF_SEGMENT_OUT_OF_RANGE: i64 = 9 // segment doesn't fit in sim.mem |
| 91 | const NX_ELF_FILESZ_GT_MEMSZ: i64 = 10 // malformed: filesz > memsz |
functions
| 99 | func nx_elf_read_u8(buf: *u8, off: i64) -> i64 called by 1: nx_elf_load_symtab |
| 103 | func nx_elf_read_u16(buf: *u8, off: i64) -> i64 |
| 109 | func nx_elf_read_u32(buf: *u8, off: i64) -> i64 |
| 117 | func nx_elf_read_u64(buf: *u8, off: i64) -> i64 |
| 146 | func nx_elf_validate_ident(buf: *u8, size: i64) -> i64 |
| 171 | func nx_elf_copy_segment(buf: *u8, buf_size: i64, sim: *NxRv64imSim, called by 1: nx_elf_load |
| 200 | func nx_elf_load(buf: *u8, buf_size: i64, sim: *NxRv64imSim, out: *NxElfLoad) -> i64 called by 1: main calls 5: nx_elf_validate_identnx_elf_read_u64nx_elf_read_u16nx_elf_read_u32nx_elf_copy_segment |
| 259 | func nx_elf_entry(buf: *u8) -> i64 calls 1: nx_elf_read_u64 |
| 263 | func nx_elf_phnum(buf: *u8) -> i64 calls 1: nx_elf_read_u16 |
| 267 | func nx_elf_e_type(buf: *u8) -> i64 calls 1: nx_elf_read_u16 |