code wiki / (root) / nx_emu_arm64.nx

nx_emu_arm64.nx source

↩ module page · 425 lines · 18408 B

1// nx_emu_arm64.nx -- sovereign aarch64 (A64) interpreter (NX-EMU). 2// Mirror of nx_emu_rv64.nx for the third architecture. Flat guest RAM 3// (vaddr==offset), NZCV flag engine, Linux aarch64 syscall surface 4// (exit/write/read). NO qemu -- decode/execute is pure NishiLang per 5// the Arm A64 ISA (the spec is the oracle). Register model: X0..X30 in 6// x[], SP + PC as scalars, reg field 31 = XZR (read 0 / write discard). 7// 8// genealogy_id: arm_a64_isa_ddi0487 + linux_arm64_abi 9// lineage_id: nishi_nx_emu_arm64_m6 10// license_tier: ORIGINAL 11 12import "nx_syscalls_x86_64.nx" 13const A64_MAGIC_200000000: i64 = 200000000 14 15const A64_GUEST_SIZE: i64 = 16777216 16const A64_SYS_READ: i64 = 63 17const A64_SYS_WRITE: i64 = 64 18const A64_SYS_EXIT: i64 = 93 19const A64_SYS_EXITG: i64 = 94 20const A64E_UNSUPPORTED: i64 = -1 21const A64E_FAULT: i64 = -3 22 23// ---- helpers ---- 24func a64e_srl(v: i64, n: i64) -> i64 { 25 if n == 0 { return v } 26 let mask: i64 = (1 << (64 - n)) - 1 27 return (v >> n) & mask 28} 29func a64e_ltu(a: i64, b: i64) -> i64 { 30 let m: i64 = 1 << 63 31 if (a ^ m) < (b ^ m) { return 1 } 32 return 0 33} 34// true unsigned 64/64 divide (bit-serial restoring; b != 0). Needed because the 35// host `/` is SIGNED -- interpreting a top-bit-set dividend as negative is wrong for UDIV. 36func a64e_udiv(a: i64, b: i64) -> i64 { 37 var q: i64 = 0 38 var r: i64 = 0 39 var i: i64 = 63 40 while i >= 0 { 41 r = (r << 1) | (a64e_srl(a, i) & 1) 42 if a64e_ltu(r, b) == 0 { r = r - b; q = q | (1 << i) } 43 i = i - 1 44 } 45 return q 46} 47func a64e_g_ld(mem: *u8, va: i64, width: i64, signd: i64) -> i64 { 48 var v: i64 = 0 49 var i: i64 = 0 50 while i < width { v = v | ((mem[va + i] & 0xff) << (i * 8)); i = i + 1 } 51 if signd == 1 { 52 if width < 8 { 53 let s: i64 = 1 << (width * 8 - 1) 54 if (v & s) != 0 { v = v - (1 << (width * 8)) } 55 } 56 } 57 return v 58} 59func a64e_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { 60 var i: i64 = 0 61 while i < width { mem[va + i] = (val >> (i * 8)) & 0xff; i = i + 1 } 62 return 0 63} 64func a64e_rd(x: *i64, r: i64) -> i64 { 65 if r == 31 { return 0 } // XZR 66 return x[r] 67} 68func a64e_wr(x: *i64, r: i64, v: i64) -> i64 { 69 if r != 31 { x[r] = v } // XZR write discarded 70 return 0 71} 72// flag engine: subs/cmp = a - b ; writes f[0..3] = N,Z,C,V (64-bit) 73func a64e_subs(a: i64, b: i64, f: *i64) -> i64 { 74 let r: i64 = a - b 75 f[0] = (r >> 63) & 1 76 var z: i64 = 0 77 if r == 0 { z = 1 } 78 f[1] = z 79 var c: i64 = 1 80 if a64e_ltu(a, b) == 1 { c = 0 } // C = NOT borrow = a >=u b 81 f[2] = c 82 var v: i64 = 0 83 if (((a ^ b) & (a ^ r)) >> 63) & 1 == 1 { v = 1 } 84 f[3] = v 85 return r 86} 87func a64e_adds(a: i64, b: i64, f: *i64) -> i64 { 88 let r: i64 = a + b 89 f[0] = (r >> 63) & 1 90 var z: i64 = 0 91 if r == 0 { z = 1 } 92 f[1] = z 93 var c: i64 = 0 94 if a64e_ltu(r, a) == 1 { c = 1 } // unsigned carry-out 95 f[2] = c 96 var v: i64 = 0 97 let nb: i64 = (a ^ b) ^ (0 - 1) // ~(a^b) 98 if ((nb & (a ^ r)) >> 63) & 1 == 1 { v = 1 } 99 f[3] = v 100 return r 101} 102// condition-code test (Arm C1.2.4) 103func a64e_cond(cc: i64, nf: i64, zf: i64, cf: i64, vf: i64) -> i64 { 104 if cc == 0 { return zf } // EQ 105 if cc == 1 { if zf == 0 { return 1 } return 0 } // NE 106 if cc == 2 { return cf } // CS/HS 107 if cc == 3 { if cf == 0 { return 1 } return 0 } // CC/LO 108 if cc == 4 { return nf } // MI 109 if cc == 5 { if nf == 0 { return 1 } return 0 } // PL 110 if cc == 6 { return vf } // VS 111 if cc == 7 { if vf == 0 { return 1 } return 0 } // VC 112 if cc == 8 { if cf == 1 { if zf == 0 { return 1 } } return 0 } // HI 113 if cc == 9 { if cf == 0 { return 1 } if zf == 1 { return 1 } return 0 } // LS 114 if cc == 10 { if nf == vf { return 1 } return 0 } // GE 115 if cc == 11 { if nf != vf { return 1 } return 0 } // LT 116 if cc == 12 { if zf == 0 { if nf == vf { return 1 } } return 0 } // GT 117 if cc == 13 { if zf == 1 { return 1 } if nf != vf { return 1 } return 0 } // LE 118 return 1 // AL/NV 119} 120func a64e_shiftreg(val: i64, typ: i64, amt: i64) -> i64 { 121 if typ == 0 { return val << amt } 122 if typ == 1 { return a64e_srl(val, amt) } 123 if typ == 2 { return val >> amt } 124 return a64e_srl(val, amt) | (val << (64 - amt)) // ROR 125} 126 127// ---- core engine ---- 128func emu_arm64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 { 129 let x: *i64 = sys_mmap(32 * 8) as *i64 130 let f: *i64 = sys_mmap(32) as *i64 131 var i: i64 = 0 132 while i < 32 { x[i] = 0; i = i + 1 } 133 var sp: i64 = sp0 134 var nf: i64 = 0 135 var zf: i64 = 0 136 var cf: i64 = 0 137 var vf: i64 = 0 138 var pc: i64 = entry 139 var steps: i64 = 0 140 var result: i64 = A64E_FAULT 141 var halted: i64 = 0 142 while halted == 0 && steps < A64_MAGIC_200000000 { 143 if pc < 0 { halted = 1; result = A64E_FAULT } 144 if pc + 4 > mem_size { halted = 1; result = A64E_FAULT } 145 if halted == 0 { 146 let w: i64 = a64e_g_ld(mem, pc, 4, 0) 147 let Rd: i64 = w & 0x1F 148 let Rn: i64 = (w >> 5) & 0x1F 149 let Rm: i64 = (w >> 16) & 0x1F 150 let sf: i64 = (w >> 31) & 1 151 var next: i64 = pc + 4 152 var handled: i64 = 0 153 154 // MOVZ / MOVN / MOVK (bits 28:23 = 100101) 155 if (w & 0x7F800000) == 0x52800000 { // MOVZ 156 handled = 1 157 let hw: i64 = (w >> 21) & 3 158 a64e_wr(x, Rd, ((w >> 5) & 0xFFFF) << (hw * 16)) 159 } 160 if (w & 0x7F800000) == 0x12800000 { // MOVN 161 handled = 1 162 let hw2: i64 = (w >> 21) & 3 163 a64e_wr(x, Rd, (((w >> 5) & 0xFFFF) << (hw2 * 16)) ^ (0 - 1)) 164 } 165 if (w & 0x7F800000) == 0x72800000 { // MOVK (keep other lanes) 166 handled = 1 167 let hw3: i64 = (w >> 21) & 3 168 let pos: i64 = hw3 * 16 169 let keep: i64 = a64e_rd(x, Rd) & ((0xFFFF << pos) ^ (0 - 1)) 170 a64e_wr(x, Rd, keep | (((w >> 5) & 0xFFFF) << pos)) 171 } 172 // ADD/SUB immediate (bits 28:24 = 10001) 173 if (w & 0x1F000000) == 0x11000000 { 174 handled = 1 175 let op: i64 = (w >> 30) & 1 176 let S: i64 = (w >> 29) & 1 177 var imm: i64 = (w >> 10) & 0xFFF 178 if ((w >> 22) & 1) == 1 { imm = imm << 12 } 179 var a: i64 = sp 180 if Rn != 31 { a = x[Rn] } 181 if S == 0 { 182 var r: i64 = a + imm 183 if op == 1 { r = a - imm } 184 if Rd == 31 { sp = r } else { x[Rd] = r } 185 } else { 186 var r2: i64 = 0 187 if op == 1 { r2 = a64e_subs(a, imm, f) } else { r2 = a64e_adds(a, imm, f) } 188 nf = f[0]; zf = f[1]; cf = f[2]; vf = f[3] 189 a64e_wr(x, Rd, r2) 190 } 191 } 192 // ADD/SUB shifted register (bits 28:24=01011, bit21=0) 193 if (w & 0x1F200000) == 0x0B000000 { 194 handled = 1 195 let op: i64 = (w >> 30) & 1 196 let S: i64 = (w >> 29) & 1 197 let shift: i64 = (w >> 22) & 3 198 let imm6: i64 = (w >> 10) & 0x3F 199 let op2: i64 = a64e_shiftreg(a64e_rd(x, Rm), shift, imm6) 200 if S == 0 { 201 var r: i64 = a64e_rd(x, Rn) + op2 202 if op == 1 { r = a64e_rd(x, Rn) - op2 } 203 a64e_wr(x, Rd, r) 204 } else { 205 var r2: i64 = 0 206 if op == 1 { r2 = a64e_subs(a64e_rd(x, Rn), op2, f) } else { r2 = a64e_adds(a64e_rd(x, Rn), op2, f) } 207 nf = f[0]; zf = f[1]; cf = f[2]; vf = f[3] 208 a64e_wr(x, Rd, r2) 209 } 210 } 211 // Logical shifted register AND/ORR/EOR/ANDS (bits 28:24=01010) 212 if (w & 0x1F000000) == 0x0A000000 { 213 handled = 1 214 let opc: i64 = (w >> 29) & 3 215 let shift: i64 = (w >> 22) & 3 216 let nbit: i64 = (w >> 21) & 1 217 let imm6: i64 = (w >> 10) & 0x3F 218 var op2: i64 = a64e_shiftreg(a64e_rd(x, Rm), shift, imm6) 219 if nbit == 1 { op2 = op2 ^ (0 - 1) } 220 var r: i64 = a64e_rd(x, Rn) & op2 221 if opc == 1 { r = a64e_rd(x, Rn) | op2 } 222 if opc == 2 { r = a64e_rd(x, Rn) ^ op2 } 223 if opc == 3 { 224 r = a64e_rd(x, Rn) & op2 225 nf = (r >> 63) & 1 226 var z: i64 = 0 227 if r == 0 { z = 1 } 228 zf = z; cf = 0; vf = 0 229 } 230 a64e_wr(x, Rd, r) 231 } 232 // MADD/MSUB (bits 31? family 0x1B000000 with bit24..) 233 if (w & 0x7FE08000) == 0x1B000000 { 234 handled = 1 235 let Ra: i64 = (w >> 10) & 0x1F 236 let o0: i64 = (w >> 15) & 1 237 var r: i64 = a64e_rd(x, Ra) + a64e_rd(x, Rn) * a64e_rd(x, Rm) 238 if o0 == 1 { r = a64e_rd(x, Ra) - a64e_rd(x, Rn) * a64e_rd(x, Rm) } 239 a64e_wr(x, Rd, r) 240 } 241 // SDIV/UDIV -- DDI0602: sf 0 0 11010110 Rm 00001 o1 Rn Rd. Bit 10 = o1 (1=SDIV, 242 // 0=UDIV) so it must be FREE in the mask: the old mask 0x7FE0FC00 pinned it and 243 // matched UDIV only (real sdiv fell through to UNSUPPORTED), and both arms computed 244 // the same signed division. Fixed: mask 0x7FE0F800, SDIV = host signed / (trunc 245 // toward zero, matches A64; INT_MIN/-1 defined as INT_MIN), UDIV = a64e_udiv. 246 if (w & 0x7FE0F800) == 0x1AC00800 { 247 handled = 1 248 let o1: i64 = (w >> 10) & 1 249 let dvs: i64 = a64e_rd(x, Rm) 250 let dvd: i64 = a64e_rd(x, Rn) 251 var r: i64 = 0 252 if dvs != 0 { 253 if o1 == 1 { 254 let imin: i64 = 1 << 63 255 if dvd == imin { if dvs == (0 - 1) { r = imin } else { r = dvd / dvs } } else { r = dvd / dvs } 256 } else { r = a64e_udiv(dvd, dvs) } 257 } 258 a64e_wr(x, Rd, r) 259 } 260 // CSINC -- DDI0602: sf 0 S=0 11010100 Rm cond 0 o2=1 Rn Rd (base 0x1A800400). 261 // cond TRUE -> Rn, else Rm+1. CSET Rd,cc = CSINC Rd,XZR,XZR,invert(cc). 262 if (w & 0x7FE00C00) == 0x1A800400 { 263 handled = 1 264 let ccc: i64 = (w >> 12) & 0xF 265 if a64e_cond(ccc, nf, zf, cf, vf) == 1 { a64e_wr(x, Rd, a64e_rd(x, Rn)) } else { a64e_wr(x, Rd, a64e_rd(x, Rm) + 1) } 266 } 267 // LSLV/LSRV/ASRV/RORV -- DDI0602: sf 0 S=0 11010110 Rm 0010 op2 Rn Rd (base 268 // 0x1AC02000); op2 at bits 11:10 maps 1:1 onto a64e_shiftreg's type; amount = Rm mod 64. 269 if (w & 0x7FE0F000) == 0x1AC02000 { 270 handled = 1 271 let sop: i64 = (w >> 10) & 3 272 let amt: i64 = a64e_rd(x, Rm) & 63 273 a64e_wr(x, Rd, a64e_shiftreg(a64e_rd(x, Rn), sop, amt)) 274 } 275 // LDR/STR (register offset) -- DDI0602: size 111 V=0 00 opc 1 Rm option S 10 Rn Rt. 276 // nxc2 emits option=011 (LSL) S=0 -> ea = Xn + Xm (the large-frame slot form). 277 // Other options are not emitted by the backend; treat offset as raw Xm. 278 if (w & 0x3B200C00) == 0x38200800 { 279 handled = 1 280 let size2: i64 = (w >> 30) & 3 281 let opc2: i64 = (w >> 22) & 3 282 var base2: i64 = sp 283 if Rn != 31 { base2 = x[Rn] } 284 let ea2: i64 = base2 + a64e_rd(x, Rm) 285 if opc2 == 0 { a64e_g_st(mem, ea2, 1 << size2, a64e_rd(x, Rd)) } 286 if opc2 == 1 { a64e_wr(x, Rd, a64e_g_ld(mem, ea2, 1 << size2, 0)) } 287 } 288 // LDR/STR unsigned offset (bits: 0x39000000 family) 289 if (w & 0x3B000000) == 0x39000000 { 290 handled = 1 291 let size: i64 = (w >> 30) & 3 292 let opc: i64 = (w >> 22) & 3 293 let imm12: i64 = (w >> 10) & 0xFFF 294 var base: i64 = sp 295 if Rn != 31 { base = x[Rn] } 296 let ea: i64 = base + (imm12 << size) 297 if opc == 0 { a64e_g_st(mem, ea, 1 << size, a64e_rd(x, Rd)) } 298 if opc == 1 { a64e_wr(x, Rd, a64e_g_ld(mem, ea, 1 << size, 0)) } 299 } 300 // LDUR/STUR (unscaled immediate, simm9): size 111 V=0 00 opc 0 imm9 00 Rn Rt 301 if (w & 0x3B200C00) == 0x38000000 { 302 handled = 1 303 let size: i64 = (w >> 30) & 3 304 let opc: i64 = (w >> 22) & 3 305 var imm9: i64 = (w >> 12) & 0x1FF 306 if (imm9 & 0x100) != 0 { imm9 = imm9 - 0x200 } 307 var base: i64 = sp 308 if Rn != 31 { base = x[Rn] } 309 let ea: i64 = base + imm9 310 if opc == 0 { a64e_g_st(mem, ea, 1 << size, a64e_rd(x, Rd)) } 311 if opc == 1 { a64e_wr(x, Rd, a64e_g_ld(mem, ea, 1 << size, 0)) } 312 } 313 // STP/LDP (load/store PAIR, 64-bit GPR): opc=10 101 V=0 idx L imm7 Rt2 Rn Rt 314 if (w & 0xFC000000) == 0xA8000000 { 315 handled = 1 316 let idx: i64 = (w >> 23) & 7 // 001 post / 010 offset / 011 pre 317 let L: i64 = (w >> 22) & 1 // 0 store, 1 load 318 let Rt2: i64 = (w >> 10) & 0x1F 319 var imm7: i64 = (w >> 15) & 0x7F 320 if (imm7 & 0x40) != 0 { imm7 = imm7 - 0x80 } 321 let off: i64 = imm7 * 8 322 var base: i64 = sp 323 if Rn != 31 { base = x[Rn] } 324 var ea: i64 = base 325 if idx == 2 { ea = base + off } // signed offset 326 if idx == 3 { ea = base + off } // pre-index: writeback before access 327 if L == 0 { 328 a64e_g_st(mem, ea, 8, a64e_rd(x, Rd)) 329 a64e_g_st(mem, ea + 8, 8, a64e_rd(x, Rt2)) 330 } else { 331 a64e_wr(x, Rd, a64e_g_ld(mem, ea, 8, 0)) 332 a64e_wr(x, Rt2, a64e_g_ld(mem, ea + 8, 8, 0)) 333 } 334 if idx == 1 { let nb: i64 = base + off; if Rn == 31 { sp = nb } else { x[Rn] = nb } } // post 335 if idx == 3 { let nb: i64 = base + off; if Rn == 31 { sp = nb } else { x[Rn] = nb } } // pre 336 } 337 // B / BL (bits 31:26 = 000101 / 100101) 338 if (w & 0xFC000000) == 0x14000000 { // B 339 handled = 1 340 var off: i64 = (w & 0x03FFFFFF) << 2 341 if (off & 0x08000000) != 0 { off = off - 0x10000000 } 342 next = pc + off 343 } 344 if (w & 0xFC000000) == 0x94000000 { // BL 345 handled = 1 346 x[30] = pc + 4 347 var off: i64 = (w & 0x03FFFFFF) << 2 348 if (off & 0x08000000) != 0 { off = off - 0x10000000 } 349 next = pc + off 350 } 351 // B.cond (bits 31:24 = 01010100, bit4=0) 352 if (w & 0xFF000010) == 0x54000000 { 353 handled = 1 354 let cc: i64 = w & 0xF 355 var off: i64 = ((w >> 5) & 0x7FFFF) << 2 356 if (off & 0x00100000) != 0 { off = off - 0x00200000 } 357 if a64e_cond(cc, nf, zf, cf, vf) == 1 { next = pc + off } 358 } 359 // CBZ / CBNZ (bits 30:25 = 011010) 360 if (w & 0x7E000000) == 0x34000000 { 361 handled = 1 362 let opb: i64 = (w >> 24) & 1 363 let Rt: i64 = w & 0x1F 364 var off: i64 = ((w >> 5) & 0x7FFFF) << 2 365 if (off & 0x00100000) != 0 { off = off - 0x00200000 } 366 let opnd: i64 = a64e_rd(x, Rt) 367 var take: i64 = 0 368 if opb == 0 { if opnd == 0 { take = 1 } } else { if opnd != 0 { take = 1 } } 369 if take == 1 { next = pc + off } 370 } 371 // BR/BLR/RET (bits 31:24 = 11010110) 372 if (w & 0xFF000000) == 0xD6000000 { 373 handled = 1 374 let opc: i64 = (w >> 21) & 0xF 375 if opc == 1 { x[30] = pc + 4 } 376 next = a64e_rd(x, Rn) 377 } 378 // SVC #0 379 if (w & 0xFFE0001F) == 0xD4000001 { 380 handled = 1 381 let nr: i64 = x[8] 382 if nr == A64_SYS_EXIT { result = x[0] & 0xff; halted = 1 } 383 if nr == A64_SYS_EXITG { result = x[0] & 0xff; halted = 1 } 384 if nr == A64_SYS_WRITE { x[0] = sys_write(x[0], ((mem as i64) + x[1]) as *u8, x[2]) } 385 if nr == A64_SYS_READ { x[0] = sys_read(x[0], ((mem as i64) + x[1]) as *u8, x[2]) } 386 } 387 388 if handled == 0 { result = A64E_UNSUPPORTED; halted = 1 } 389 pc = next 390 steps = steps + 1 391 } 392 } 393 return result 394} 395 396func emu_arm64_run(code: *u8, code_len: i64) -> i64 { 397 let mem: *u8 = sys_mmap(A64_GUEST_SIZE) 398 var i: i64 = 0 399 while i < code_len { mem[i] = code[i]; i = i + 1 } 400 return emu_arm64_run_mem(mem, A64_GUEST_SIZE, 0, 0x00800000) 401} 402 403func emu_arm64_load_elf(buf: *u8, len: i64) -> i64 { 404 if len < 64 { return A64E_FAULT } 405 let e_entry: i64 = a64e_g_ld(buf, 24, 8, 0) 406 let e_phoff: i64 = a64e_g_ld(buf, 32, 8, 0) 407 let e_phnum: i64 = a64e_g_ld(buf, 56, 2, 0) 408 let e_phent: i64 = a64e_g_ld(buf, 54, 2, 0) 409 let mem: *u8 = sys_mmap(A64_GUEST_SIZE) 410 var idx: i64 = 0 411 while idx < e_phnum { 412 let ph: i64 = e_phoff + idx * e_phent 413 if a64e_g_ld(buf, ph, 4, 0) == 1 { 414 let p_off: i64 = a64e_g_ld(buf, ph + 8, 8, 0) 415 let p_va: i64 = a64e_g_ld(buf, ph + 16, 8, 0) 416 let p_fs: i64 = a64e_g_ld(buf, ph + 32, 8, 0) 417 var k: i64 = 0 418 while k < p_fs { if (p_va + k) < A64_GUEST_SIZE { mem[p_va + k] = buf[p_off + k] }; k = k + 1 } 419 } 420 idx = idx + 1 421 } 422 let sp: i64 = 0x00F00000 423 a64e_g_st(mem, sp, 8, 0) 424 return emu_arm64_run_mem(mem, A64_GUEST_SIZE, e_entry, sp) 425}