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}