code wiki / (root) / nx_emu_rv64.nx

nx_emu_rv64.nx source

↩ module page · 363 lines · 16666 B

1// nx_emu_rv64.nx -- sovereign RV64 interpreter (NX-EMU). Decodes + 2// executes the RV64I + RV64M base needed to run nxc2-compiled RV64 3// programs over a flat guest address space, with a Linux RV64 syscall 4// surface (exit/write/read). NO qemu -- the foreign emulator is demoted 5// to a build-time differential ORACLE only. Replaces qemu for the 6// non-native lanes (RV64/aarch64/arm32 on an x86_64 host). 7// 8// Guest memory model (per the grounded design): ONE flat mmap region; 9// vaddr == offset (GUEST_BASE 0); pc is a guest vaddr fetched via g_ld; 10// MAP_ANON zero-fill gives free .bss; loads/stores hit the same region; 11// sp (x2) initialised by the loader. All guest pointers are translated 12// (mem + va) before any host syscall. 13// 14// Decoded: lui(sign-ext) auipc | addi slti sltiu xori ori andi slli 15// srli srai | add sub sll slt sltu xor srl sra or and | mul div divu 16// rem remu (RV64M; div/rem edge cases per spec 13.2) | lb lh lw ld lbu 17// lhu lwu | sb sh sw sd | beq bne blt bge bltu bgeu | jal jalr | ecall 18// (exit 93/94, write 64, read 63). 19// 20// genealogy_id: riscv_unprivileged_isa_v20240411 + linux_riscv_abi 21// lineage_id: nishi_nx_emu_rv64_m7 22// license_tier: ORIGINAL 23 24import "nx_syscalls_x86_64.nx" 25const RV_MAGIC_4096: i64 = 4096 26const RV_MAGIC_8192: i64 = 8192 27const RV_MAGIC_2097152: i64 = 2097152 28const RV_MAGIC_4294967296: i64 = 4294967296 29const RV_MAGIC_200000000: i64 = 200000000 30 31const RV_OP_LOAD: i64 = 0x03 32const RV_OP_IMM: i64 = 0x13 33const RV_OP_AUIPC: i64 = 0x17 34const RV_OP_STORE: i64 = 0x23 35const RV_OP_REG: i64 = 0x33 36const RV_OP_LUI: i64 = 0x37 37const RV_OP_IMM_W: i64 = 0x1b // addiw/slliw/srliw/sraiw (32-bit) 38const RV_OP_W: i64 = 0x3b // addw/subw/sllw/srlw/sraw + mulw/divw... 39const RV_OP_BRANCH: i64 = 0x63 40const RV_OP_JALR: i64 = 0x67 41const RV_OP_JAL: i64 = 0x6f 42const RV_SYSTEM: i64 = 0x73 43 44const RV_SYS_READ: i64 = 63 45const RV_SYS_WRITE: i64 = 64 46const RV_SYS_EXIT: i64 = 93 47const RV_SYS_EXIT_GROUP: i64 = 94 48 49const EMU_RV_UNSUPPORTED: i64 = -1 50const EMU_RV_RANOFF: i64 = -2 51const EMU_RV_FAULT: i64 = -3 52 53const EMU_GUEST_SIZE: i64 = 16777216 // 16 MiB flat guest RAM 54 55func emu_rv_sext12(v: i64) -> i64 { 56 if (v & 0x800) != 0 { return v - RV_MAGIC_4096 } 57 return v 58} 59func emu_rv_bimm(w: i64) -> i64 { 60 let b12: i64 = (w >> 31) & 1 61 let b10_5: i64 = (w >> 25) & 0x3f 62 let b4_1: i64 = (w >> 8) & 0xf 63 let b11: i64 = (w >> 7) & 1 64 var v: i64 = (b12 << 12) | (b11 << 11) | (b10_5 << 5) | (b4_1 << 1) 65 if (v & 0x1000) != 0 { v = v - RV_MAGIC_8192 } 66 return v 67} 68func emu_rv_jimm(w: i64) -> i64 { 69 let j20: i64 = (w >> 31) & 1 70 let j10_1: i64 = (w >> 21) & 0x3ff 71 let j11: i64 = (w >> 20) & 1 72 let j19_12: i64 = (w >> 12) & 0xff 73 var v: i64 = (j20 << 20) | (j19_12 << 12) | (j11 << 11) | (j10_1 << 1) 74 if (v & 0x100000) != 0 { v = v - RV_MAGIC_2097152 } 75 return v 76} 77// unsigned a < b (flip the sign bit, then signed compare) 78func emu_ltu(a: i64, b: i64) -> i64 { 79 let m: i64 = 1 << 63 80 if (a ^ m) < (b ^ m) { return 1 } 81 return 0 82} 83// logical right shift (zero-fill); NishiLang >> is arithmetic, so mask. 84func emu_srl(v: i64, n: i64) -> i64 { 85 if n == 0 { return v } 86 let mask: i64 = (1 << (64 - n)) - 1 87 return (v >> n) & mask 88} 89// sign-extend the low 32 bits to 64 (the *W result rule) 90func emu_sext32(v: i64) -> i64 { 91 let lo: i64 = v & 0xffffffff 92 if (lo & 0x80000000) != 0 { return lo - RV_MAGIC_4294967296 } 93 return lo 94} 95// little-endian guest load of `width` bytes at vaddr; sign-extend if signd. 96func emu_g_ld(mem: *u8, va: i64, width: i64, signd: i64) -> i64 { 97 var v: i64 = 0 98 var i: i64 = 0 99 while i < width { v = v | ((mem[va + i] & 0xff) << (i * 8)); i = i + 1 } 100 if signd == 1 { 101 if width < 8 { 102 let sign: i64 = 1 << (width * 8 - 1) 103 if (v & sign) != 0 { v = v - (1 << (width * 8)) } 104 } 105 } 106 return v 107} 108func emu_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { 109 var i: i64 = 0 110 while i < width { mem[va + i] = (val >> (i * 8)) & 0xff; i = i + 1 } 111 return 0 112} 113 114// Core engine: run the program already loaded into `mem`, entry pc, sp. 115// Returns exit status (0..255) or a negative EMU_RV_* sentinel. 116func emu_rv64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp: i64) -> i64 { 117 let x: *i64 = sys_mmap(32 * 8) as *i64 118 var i: i64 = 0 119 while i < 32 { x[i] = 0; i = i + 1 } 120 x[2] = sp // sp 121 var pc: i64 = entry 122 var steps: i64 = 0 123 var result: i64 = EMU_RV_RANOFF 124 var halted: i64 = 0 125 let MIN: i64 = 1 << 63 126 while halted == 0 && steps < RV_MAGIC_200000000 { 127 if pc < 0 { result = EMU_RV_FAULT; halted = 1 } 128 if pc + 4 > mem_size { result = EMU_RV_FAULT; halted = 1 } 129 if halted == 0 { 130 let w: i64 = emu_g_ld(mem, pc, 4, 0) 131 let opcode: i64 = w & 0x7f 132 let rd: i64 = (w >> 7) & 0x1f 133 let f3: i64 = (w >> 12) & 0x7 134 let rs1: i64 = (w >> 15) & 0x1f 135 let rs2: i64 = (w >> 20) & 0x1f 136 let f7: i64 = (w >> 25) & 0x7f 137 var next: i64 = pc + 4 138 var handled: i64 = 0 139 140 if opcode == RV_OP_LUI { 141 handled = 1 142 var v: i64 = w & 0xfffff000 143 if (v & 0x80000000) != 0 { v = v - RV_MAGIC_4294967296 } // sign-ext bit31 144 if rd != 0 { x[rd] = v } 145 } 146 if opcode == RV_OP_AUIPC { 147 handled = 1 148 var v2: i64 = w & 0xfffff000 149 if (v2 & 0x80000000) != 0 { v2 = v2 - RV_MAGIC_4294967296 } 150 if rd != 0 { x[rd] = pc + v2 } 151 } 152 if opcode == RV_OP_IMM { 153 handled = 1 154 let imm: i64 = emu_rv_sext12((w >> 20) & 0xfff) 155 let shamt: i64 = (w >> 20) & 0x3f 156 var r: i64 = x[rd] 157 if f3 == 0 { r = x[rs1] + imm } // addi 158 if f3 == 1 { r = x[rs1] << shamt } // slli 159 if f3 == 2 { if x[rs1] < imm { r = 1 } else { r = 0 } } // slti 160 if f3 == 3 { r = emu_ltu(x[rs1], imm) } // sltiu 161 if f3 == 4 { r = x[rs1] ^ imm } // xori 162 if f3 == 5 { 163 if (w >> 30) & 1 == 0 { r = emu_srl(x[rs1], shamt) } // srli 164 else { r = x[rs1] >> shamt } // srai 165 } 166 if f3 == 6 { r = x[rs1] | imm } // ori 167 if f3 == 7 { r = x[rs1] & imm } // andi 168 if rd != 0 { x[rd] = r } 169 } 170 if opcode == RV_OP_REG { 171 handled = 1 172 var r2: i64 = x[rd] 173 if f7 == 1 { 174 // RV64M 175 if f3 == 0 { r2 = x[rs1] * x[rs2] } // mul 176 if f3 == 4 { // div (signed) 177 if x[rs2] == 0 { r2 = 0 - 1 } else { 178 if x[rs1] == MIN && x[rs2] == (0 - 1) { r2 = MIN } else { r2 = x[rs1] / x[rs2] } 179 } 180 } 181 if f3 == 5 { // divu 182 if x[rs2] == 0 { r2 = 0 - 1 } else { r2 = x[rs1] / x[rs2] } 183 } 184 if f3 == 6 { // rem (signed) 185 if x[rs2] == 0 { r2 = x[rs1] } else { 186 if x[rs1] == MIN && x[rs2] == (0 - 1) { r2 = 0 } else { r2 = x[rs1] - (x[rs1] / x[rs2]) * x[rs2] } 187 } 188 } 189 if f3 == 7 { // remu 190 if x[rs2] == 0 { r2 = x[rs1] } else { r2 = x[rs1] - (x[rs1] / x[rs2]) * x[rs2] } 191 } 192 } else { 193 let sh: i64 = x[rs2] & 0x3f 194 if f3 == 0 { 195 if f7 == 0x20 { r2 = x[rs1] - x[rs2] } else { r2 = x[rs1] + x[rs2] } // add/sub 196 } 197 if f3 == 1 { r2 = x[rs1] << sh } // sll 198 if f3 == 2 { if x[rs1] < x[rs2] { r2 = 1 } else { r2 = 0 } } // slt 199 if f3 == 3 { r2 = emu_ltu(x[rs1], x[rs2]) } // sltu 200 if f3 == 4 { r2 = x[rs1] ^ x[rs2] } // xor 201 if f3 == 5 { 202 if f7 == 0x20 { r2 = x[rs1] >> sh } else { r2 = emu_srl(x[rs1], sh) } // sra/srl 203 } 204 if f3 == 6 { r2 = x[rs1] | x[rs2] } // or 205 if f3 == 7 { r2 = x[rs1] & x[rs2] } // and 206 } 207 if rd != 0 { x[rd] = r2 } 208 } 209 // OP-IMM-W: addiw/slliw/srliw/sraiw (operate on low 32, sign-extend) 210 if opcode == RV_OP_IMM_W { 211 handled = 1 212 let immw: i64 = emu_rv_sext12((w >> 20) & 0xfff) 213 let sh5: i64 = (w >> 20) & 0x1f 214 var rw: i64 = x[rd] 215 if f3 == 0 { rw = emu_sext32(x[rs1] + immw) } // addiw 216 if f3 == 1 { rw = emu_sext32((x[rs1] & 0xffffffff) << sh5) } // slliw 217 if f3 == 5 { 218 if ((w >> 30) & 1) == 0 { rw = emu_sext32(emu_srl(x[rs1] & 0xffffffff, sh5)) } // srliw 219 else { rw = emu_sext32(emu_sext32(x[rs1]) >> sh5) } // sraiw 220 } 221 if rd != 0 { x[rd] = rw } 222 } 223 // OP-W: addw/subw/sllw/srlw/sraw + mulw/divw/divuw/remw/remuw 224 if opcode == RV_OP_W { 225 handled = 1 226 let f7w: i64 = (w >> 25) & 0x7f 227 let shw: i64 = x[rs2] & 0x1f 228 let a32: i64 = emu_sext32(x[rs1]) 229 let b32: i64 = emu_sext32(x[rs2]) 230 var rw2: i64 = x[rd] 231 if f7w == 1 { 232 if f3 == 0 { rw2 = emu_sext32(x[rs1] * x[rs2]) } // mulw 233 if f3 == 4 { if b32 == 0 { rw2 = 0 - 1 } else { rw2 = emu_sext32(a32 / b32) } } // divw 234 if f3 == 5 { 235 let ua: i64 = x[rs1] & 0xffffffff 236 let ub: i64 = x[rs2] & 0xffffffff 237 if ub == 0 { rw2 = 0 - 1 } else { rw2 = emu_sext32(ua / ub) } // divuw 238 } 239 if f3 == 6 { if b32 == 0 { rw2 = a32 } else { rw2 = emu_sext32(a32 - (a32 / b32) * b32) } } // remw 240 if f3 == 7 { 241 let ua2: i64 = x[rs1] & 0xffffffff 242 let ub2: i64 = x[rs2] & 0xffffffff 243 if ub2 == 0 { rw2 = emu_sext32(ua2) } else { rw2 = emu_sext32(ua2 - (ua2 / ub2) * ub2) } // remuw 244 } 245 } else { 246 if f3 == 0 { if f7w == 0x20 { rw2 = emu_sext32(x[rs1] - x[rs2]) } else { rw2 = emu_sext32(x[rs1] + x[rs2]) } } // addw/subw 247 if f3 == 1 { rw2 = emu_sext32((x[rs1] & 0xffffffff) << shw) } // sllw 248 if f3 == 5 { 249 if f7w == 0x20 { rw2 = emu_sext32(a32 >> shw) } else { rw2 = emu_sext32(emu_srl(x[rs1] & 0xffffffff, shw)) } // sraw/srlw 250 } 251 } 252 if rd != 0 { x[rd] = rw2 } 253 } 254 if opcode == RV_OP_LOAD { 255 handled = 1 256 let ea: i64 = x[rs1] + emu_rv_sext12((w >> 20) & 0xfff) 257 var lv: i64 = 0 258 if f3 == 0 { lv = emu_g_ld(mem, ea, 1, 1) } // lb 259 if f3 == 1 { lv = emu_g_ld(mem, ea, 2, 1) } // lh 260 if f3 == 2 { lv = emu_g_ld(mem, ea, 4, 1) } // lw 261 if f3 == 3 { lv = emu_g_ld(mem, ea, 8, 0) } // ld 262 if f3 == 4 { lv = emu_g_ld(mem, ea, 1, 0) } // lbu 263 if f3 == 5 { lv = emu_g_ld(mem, ea, 2, 0) } // lhu 264 if f3 == 6 { lv = emu_g_ld(mem, ea, 4, 0) } // lwu 265 if rd != 0 { x[rd] = lv } 266 } 267 if opcode == RV_OP_STORE { 268 handled = 1 269 let simm: i64 = emu_rv_sext12(((w >> 25) << 5) | ((w >> 7) & 0x1f)) 270 let ea2: i64 = x[rs1] + simm 271 if f3 == 0 { emu_g_st(mem, ea2, 1, x[rs2]) } // sb 272 if f3 == 1 { emu_g_st(mem, ea2, 2, x[rs2]) } // sh 273 if f3 == 2 { emu_g_st(mem, ea2, 4, x[rs2]) } // sw 274 if f3 == 3 { emu_g_st(mem, ea2, 8, x[rs2]) } // sd 275 } 276 if opcode == RV_OP_BRANCH { 277 handled = 1 278 let bimm: i64 = emu_rv_bimm(w) 279 var take: i64 = 0 280 if f3 == 0 { if x[rs1] == x[rs2] { take = 1 } } // beq 281 if f3 == 1 { if x[rs1] != x[rs2] { take = 1 } } // bne 282 if f3 == 4 { if x[rs1] < x[rs2] { take = 1 } } // blt 283 if f3 == 5 { if x[rs1] >= x[rs2] { take = 1 } } // bge 284 if f3 == 6 { take = emu_ltu(x[rs1], x[rs2]) } // bltu 285 if f3 == 7 { if emu_ltu(x[rs1], x[rs2]) == 0 { take = 1 } } // bgeu 286 if take == 1 { next = pc + bimm } 287 } 288 if opcode == RV_OP_JAL { 289 handled = 1 290 if rd != 0 { x[rd] = pc + 4 } 291 next = pc + emu_rv_jimm(w) 292 } 293 if opcode == RV_OP_JALR { 294 handled = 1 295 let t: i64 = x[rs1] + emu_rv_sext12((w >> 20) & 0xfff) 296 if rd != 0 { x[rd] = pc + 4 } 297 next = t - (t & 1) 298 } 299 if opcode == RV_SYSTEM { 300 handled = 1 301 let nr: i64 = x[17] 302 if nr == RV_SYS_EXIT { result = x[10] & 0xff; halted = 1 } 303 if nr == RV_SYS_EXIT_GROUP { result = x[10] & 0xff; halted = 1 } 304 if nr == RV_SYS_WRITE { 305 let hp: i64 = (mem as i64) + x[11] 306 x[10] = sys_write(x[10], hp as *u8, x[12]) 307 } 308 if nr == RV_SYS_READ { 309 let hp2: i64 = (mem as i64) + x[11] 310 x[10] = sys_read(x[10], hp2 as *u8, x[12]) 311 } 312 } 313 314 if handled == 0 { result = EMU_RV_UNSUPPORTED; halted = 1 } 315 pc = next 316 steps = steps + 1 317 } 318 } 319 return result 320} 321 322// Convenience: run a flat RV64 code blob loaded at guest vaddr 0. 323func emu_rv64_run(code: *u8, code_len: i64) -> i64 { 324 let mem: *u8 = sys_mmap(EMU_GUEST_SIZE) 325 var i: i64 = 0 326 while i < code_len { mem[i] = code[i]; i = i + 1 } 327 let sp: i64 = 0x00800000 // 8 MiB: above code, below the 16 MiB top 328 return emu_rv64_run_mem(mem, EMU_GUEST_SIZE, 0, sp) 329} 330 331// Load a static RV64 ELF (from buf/len) into a flat guest image and run it. 332// Parses ELF64 header + program headers inline (identity vaddr map), maps 333// every PT_LOAD, seeds a minimal stack (argc=0), pc=e_entry. 334func emu_rv64_load_elf(buf: *u8, len: i64) -> i64 { 335 // ELF64 header field offsets 336 if len < 64 { return EMU_RV_FAULT } 337 if (buf[0] & 0xff) != 0x7f { return EMU_RV_FAULT } 338 let e_entry: i64 = emu_g_ld(buf, 24, 8, 0) 339 let e_phoff: i64 = emu_g_ld(buf, 32, 8, 0) 340 let e_phnum: i64 = emu_g_ld(buf, 56, 2, 0) 341 let e_phent: i64 = emu_g_ld(buf, 54, 2, 0) 342 let mem: *u8 = sys_mmap(EMU_GUEST_SIZE) 343 var idx: i64 = 0 344 while idx < e_phnum { 345 let ph: i64 = e_phoff + idx * e_phent 346 let p_type: i64 = emu_g_ld(buf, ph, 4, 0) 347 if p_type == 1 { // PT_LOAD 348 let p_offset: i64 = emu_g_ld(buf, ph + 8, 8, 0) 349 let p_vaddr: i64 = emu_g_ld(buf, ph + 16, 8, 0) 350 let p_filesz: i64 = emu_g_ld(buf, ph + 32, 8, 0) 351 var k: i64 = 0 352 while k < p_filesz { 353 if (p_vaddr + k) < EMU_GUEST_SIZE { mem[p_vaddr + k] = buf[p_offset + k] } 354 k = k + 1 355 } 356 } 357 idx = idx + 1 358 } 359 // minimal SysV stack: sp 16-aligned, argc=0 at sp 360 let sp: i64 = 0x00F00000 // 15 MiB 361 emu_g_st(mem, sp, 8, 0) // argc = 0 362 return emu_rv64_run_mem(mem, EMU_GUEST_SIZE, e_entry, sp) 363}