@aptre/v86 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. package/src/virtio_net.ts +326 -0
package/src/cpu.ts ADDED
@@ -0,0 +1,2891 @@
1
+ declare const DEBUG: boolean
2
+
3
+ import {
4
+ LOG_CPU,
5
+ LOG_BIOS,
6
+ FW_CFG_SIGNATURE,
7
+ FW_CFG_SIGNATURE_QEMU,
8
+ WASM_TABLE_SIZE,
9
+ WASM_TABLE_OFFSET,
10
+ FW_CFG_ID,
11
+ FW_CFG_RAM_SIZE,
12
+ FW_CFG_NB_CPUS,
13
+ FW_CFG_MAX_CPUS,
14
+ FW_CFG_NUMA,
15
+ FW_CFG_FILE_DIR,
16
+ FW_CFG_FILE_START,
17
+ FW_CFG_CUSTOM_START,
18
+ FLAGS_DEFAULT,
19
+ MMAP_BLOCK_BITS,
20
+ MMAP_BLOCK_SIZE,
21
+ MMAP_MAX,
22
+ REG_ESP,
23
+ REG_EBP,
24
+ REG_ESI,
25
+ REG_EAX,
26
+ REG_EBX,
27
+ REG_ECX,
28
+ REG_EDX,
29
+ REG_EDI,
30
+ REG_CS,
31
+ REG_DS,
32
+ REG_ES,
33
+ REG_FS,
34
+ REG_GS,
35
+ REG_SS,
36
+ CR0_PG,
37
+ CR4_PAE,
38
+ REG_LDTR,
39
+ FLAG_VM,
40
+ FLAG_INTERRUPT,
41
+ FLAG_CARRY,
42
+ FLAG_ADJUST,
43
+ FLAG_ZERO,
44
+ FLAG_SIGN,
45
+ FLAG_TRAP,
46
+ FLAG_DIRECTION,
47
+ FLAG_OVERFLOW,
48
+ FLAG_PARITY,
49
+ } from './const.js'
50
+ import { h, view, pads, Bitmap, dump_file } from './lib.js'
51
+ import { dbg_assert, dbg_log } from './log.js'
52
+
53
+ import { SB16 } from './sb16.js'
54
+ import { ACPI } from './acpi.js'
55
+ import { PIT } from './pit.js'
56
+ import { DMA } from './dma.js'
57
+ import { UART } from './uart.js'
58
+ import { Ne2k } from './ne2k.js'
59
+ import { IO } from './io.js'
60
+ import { VirtioConsole } from './virtio_console.js'
61
+ import { PCI } from './pci.js'
62
+ import { PS2 } from './ps2.js'
63
+ import { read_elf } from './elf.js'
64
+
65
+ import { FloppyController } from './floppy.js'
66
+ import { IDEController } from './ide.js'
67
+ import { VirtioNet } from './virtio_net.js'
68
+ import { VGAScreen } from './vga.js'
69
+ import { VirtioBalloon } from './virtio_balloon.js'
70
+ import { Virtio9p, Virtio9pHandler, Virtio9pProxy } from '../lib/9p.js'
71
+
72
+ import { load_kernel } from './kernel.js'
73
+
74
+ import {
75
+ RTC,
76
+ CMOS_EQUIPMENT_INFO,
77
+ CMOS_BIOS_SMP_COUNT,
78
+ CMOS_MEM_HIGHMEM_HIGH,
79
+ CMOS_MEM_HIGHMEM_MID,
80
+ CMOS_MEM_HIGHMEM_LOW,
81
+ BOOT_ORDER_CD_FIRST,
82
+ CMOS_BIOS_BOOTFLAG1,
83
+ CMOS_BIOS_BOOTFLAG2,
84
+ CMOS_MEM_BASE_LOW,
85
+ CMOS_MEM_BASE_HIGH,
86
+ CMOS_MEM_OLD_EXT_LOW,
87
+ CMOS_MEM_OLD_EXT_HIGH,
88
+ CMOS_MEM_EXTMEM_LOW,
89
+ CMOS_MEM_EXTMEM_HIGH,
90
+ CMOS_MEM_EXTMEM2_LOW,
91
+ CMOS_MEM_EXTMEM2_HIGH,
92
+ } from './rtc.js'
93
+
94
+ // For Types Only
95
+
96
+ import { BusConnector } from './bus.js'
97
+
98
+ // Resources:
99
+ // https://pdos.csail.mit.edu/6.828/2006/readings/i386/toc.htm
100
+ // https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
101
+ // http://ref.x86asm.net/geek32.html
102
+
103
+ const DUMP_GENERATED_WASM = false
104
+ const DUMP_UNCOMPILED_ASSEMBLY = false
105
+
106
+ type MmapReadFn = (addr: number) => number
107
+ type MmapWriteFn = (addr: number, value: number) => void
108
+
109
+ interface CPUDevices {
110
+ pci: PCI
111
+ acpi: ACPI
112
+ rtc: RTC
113
+ dma: DMA
114
+ vga: VGAScreen
115
+ ps2: PS2
116
+ uart0: UART
117
+ uart1: UART
118
+ uart2: UART
119
+ uart3: UART
120
+ fdc: FloppyController
121
+ ide: IDEController
122
+
123
+ cdrom: any
124
+ pit: PIT
125
+ net: Ne2k
126
+ sb16: SB16
127
+
128
+ virtio_9p: any
129
+ virtio_console: VirtioConsole
130
+ virtio_net: VirtioNet
131
+ virtio_balloon: VirtioBalloon
132
+ }
133
+
134
+ interface OptionRom {
135
+ name: string
136
+ data: Uint8Array
137
+ }
138
+
139
+ interface WasmModule {
140
+ exports: Record<string, any>
141
+ wasm_table: WebAssembly.Table
142
+ }
143
+
144
+ export class CPU {
145
+ stop_idling: () => void
146
+ wm: WasmModule
147
+ wasm_memory!: WebAssembly.Memory
148
+
149
+ memory_size!: Int32Array
150
+
151
+ mem8: Uint8Array
152
+ mem32s: Int32Array
153
+
154
+ segment_is_null!: Uint8Array
155
+ segment_offsets!: Int32Array
156
+ segment_limits!: Uint32Array
157
+ segment_access_bytes!: Uint8Array
158
+
159
+ protected_mode!: Int32Array
160
+
161
+ idtr_size!: Int32Array
162
+ idtr_offset!: Int32Array
163
+
164
+ gdtr_size!: Int32Array
165
+ gdtr_offset!: Int32Array
166
+
167
+ tss_size_32!: Int32Array
168
+
169
+ page_fault!: Uint32Array
170
+
171
+ cr!: Int32Array
172
+
173
+ cpl!: Uint8Array
174
+
175
+ is_32!: Int32Array
176
+
177
+ stack_size_32!: Int32Array
178
+
179
+ in_hlt!: Uint8Array
180
+
181
+ last_virt_eip!: Int32Array
182
+ eip_phys!: Int32Array
183
+
184
+ sysenter_cs!: Int32Array
185
+ sysenter_esp!: Int32Array
186
+ sysenter_eip!: Int32Array
187
+
188
+ prefixes!: Int32Array
189
+
190
+ flags!: Int32Array
191
+
192
+ flags_changed!: Int32Array
193
+
194
+ last_op_size!: Int32Array
195
+ last_op1!: Int32Array
196
+ last_result!: Int32Array
197
+
198
+ current_tsc!: Uint32Array
199
+
200
+ devices: CPUDevices
201
+
202
+ instruction_pointer!: Int32Array
203
+ previous_ip!: Int32Array
204
+
205
+ apic_enabled!: Uint8Array
206
+ acpi_enabled!: Uint8Array
207
+
208
+ memory_map_read8: (MmapReadFn | undefined)[]
209
+ memory_map_write8: (MmapWriteFn | undefined)[]
210
+ memory_map_read32: (MmapReadFn | undefined)[]
211
+ memory_map_write32: (MmapWriteFn | undefined)[]
212
+
213
+ bios: { main: ArrayBuffer | null; vga: ArrayBuffer | null }
214
+
215
+ instruction_counter!: Uint32Array
216
+
217
+ reg32!: Int32Array
218
+
219
+ fpu_st!: Int32Array
220
+ fpu_stack_empty!: Uint8Array
221
+ fpu_stack_ptr!: Uint8Array
222
+ fpu_control_word!: Uint16Array
223
+ fpu_status_word!: Uint16Array
224
+ fpu_ip!: Int32Array
225
+ fpu_ip_selector!: Int32Array
226
+ fpu_opcode!: Int32Array
227
+ fpu_dp!: Int32Array
228
+ fpu_dp_selector!: Int32Array
229
+
230
+ reg_xmm32s!: Int32Array
231
+
232
+ mxcsr!: Int32Array
233
+
234
+ sreg!: Uint16Array
235
+
236
+ dreg!: Int32Array
237
+
238
+ reg_pdpte!: Int32Array
239
+
240
+ svga_dirty_bitmap_min_offset!: Int32Array
241
+ svga_dirty_bitmap_max_offset!: Int32Array
242
+
243
+ fw_value: any
244
+ fw_pointer: number
245
+ option_roms: OptionRom[]
246
+
247
+ io!: IO
248
+
249
+ bus: BusConnector
250
+
251
+ jit_imports!: Record<string, any>
252
+
253
+ // Debug-only properties
254
+ seen_code!: Record<number, number>
255
+ seen_code_uncompiled!: Record<number, number>
256
+
257
+ capstone_decoder?: any
258
+
259
+ wabt?: any
260
+
261
+ // Test hooks (optional)
262
+ test_hook_did_generate_wasm?: (code: Uint8Array) => void
263
+ test_hook_did_finalize_wasm?: (code: Uint8Array) => void
264
+
265
+ // WASM exports (assigned in wasm_patch)
266
+ reset_cpu!: () => void
267
+ getiopl!: () => number
268
+ get_eflags!: () => number
269
+ handle_irqs!: () => void
270
+ main_loop!: () => number
271
+ reboot_internal!: () => void
272
+ set_jit_config!: (a: number, b: number) => void
273
+
274
+ read8!: (addr: number) => number
275
+ read16!: (addr: number) => number
276
+ read32s!: (addr: number) => number
277
+ write8!: (addr: number, value: number) => void
278
+ write16!: (addr: number, value: number) => void
279
+ write32!: (addr: number, value: number) => void
280
+ in_mapped_range!: (addr: number) => boolean
281
+
282
+ fpu_load_tag_word!: (word: number) => void
283
+ fpu_load_status_word!: (word: number) => void
284
+ fpu_get_sti_f64!: (i: number) => number
285
+
286
+ translate_address_system_read!: (addr: number) => number
287
+
288
+ get_seg_cs!: () => number
289
+ get_real_eip!: () => number
290
+
291
+ clear_tlb!: () => void
292
+ full_clear_tlb!: () => void
293
+ update_state_flags!: () => void
294
+
295
+ set_tsc!: (low: number, high: number) => void
296
+ store_current_tsc!: () => void
297
+
298
+ set_cpuid_level!: (level: number) => void
299
+
300
+ device_raise_irq!: (irq: number) => void
301
+ device_lower_irq!: (irq: number) => void
302
+
303
+ apic_timer!: (now: number) => number
304
+
305
+ jit_force_generate_unsafe?: (addr: number) => void
306
+
307
+ jit_clear_cache!: () => void
308
+ jit_dirty_cache!: (start: number, end: number) => void
309
+ codegen_finalize_finished!: (
310
+ index: number,
311
+ start: number,
312
+ flags: number,
313
+ ) => void
314
+
315
+ allocate_memory!: (size: number) => number
316
+ zero_memory!: (start: number, length: number) => void
317
+ is_memory_zeroed!: (start: number, length: number) => boolean
318
+
319
+ svga_allocate_memory!: (size: number) => number
320
+ svga_allocate_dest_buffer!: (size: number) => number
321
+ svga_fill_pixel_buffer!: (bpp: number, offset: number) => void
322
+ svga_mark_dirty!: () => void
323
+
324
+ get_pic_addr_master!: () => number
325
+ get_pic_addr_slave!: () => number
326
+ get_apic_addr!: () => number
327
+ get_ioapic_addr!: () => number
328
+
329
+ zstd_create_ctx!: (len: number) => number
330
+ zstd_get_src_ptr!: (ctx: number) => number
331
+ zstd_free_ctx!: (ctx: number) => void
332
+ zstd_read!: (ctx: number, len: number) => number
333
+ zstd_read_free!: (ptr: number, len: number) => void
334
+
335
+ name: string
336
+
337
+ constructor(bus: BusConnector, wm: WasmModule, stop_idling: () => void) {
338
+ this.name = 'cpu'
339
+ this.stop_idling = stop_idling
340
+ this.wm = wm
341
+ this.wasm_patch()
342
+ this.create_jit_imports()
343
+
344
+ const memory = this.wm.exports['memory']
345
+
346
+ this.wasm_memory = memory
347
+
348
+ this.memory_size = view(Uint32Array, memory, 812, 1)
349
+
350
+ this.mem8 = new Uint8Array(0)
351
+ this.mem32s = new Int32Array(this.mem8.buffer)
352
+
353
+ this.segment_is_null = view(Uint8Array, memory, 724, 8)
354
+ this.segment_offsets = view(Int32Array, memory, 736, 8)
355
+ this.segment_limits = view(Uint32Array, memory, 768, 8)
356
+ this.segment_access_bytes = view(Uint8Array, memory, 512, 8)
357
+
358
+ // Whether or not in protected mode
359
+ this.protected_mode = view(Int32Array, memory, 800, 1)
360
+
361
+ this.idtr_size = view(Int32Array, memory, 564, 1)
362
+ this.idtr_offset = view(Int32Array, memory, 568, 1)
363
+
364
+ // global descriptor table register
365
+ this.gdtr_size = view(Int32Array, memory, 572, 1)
366
+ this.gdtr_offset = view(Int32Array, memory, 576, 1)
367
+
368
+ this.tss_size_32 = view(Int32Array, memory, 1128, 1)
369
+
370
+ // whether or not a page fault occured
371
+ this.page_fault = view(Uint32Array, memory, 540, 8)
372
+
373
+ this.cr = view(Int32Array, memory, 580, 8)
374
+
375
+ // current privilege level
376
+ this.cpl = view(Uint8Array, memory, 612, 1)
377
+
378
+ // current operand/address size
379
+ this.is_32 = view(Int32Array, memory, 804, 1)
380
+
381
+ this.stack_size_32 = view(Int32Array, memory, 808, 1)
382
+
383
+ // Was the last instruction a hlt?
384
+ this.in_hlt = view(Uint8Array, memory, 616, 1)
385
+
386
+ this.last_virt_eip = view(Int32Array, memory, 620, 1)
387
+ this.eip_phys = view(Int32Array, memory, 624, 1)
388
+
389
+ this.sysenter_cs = view(Int32Array, memory, 636, 1)
390
+
391
+ this.sysenter_esp = view(Int32Array, memory, 640, 1)
392
+
393
+ this.sysenter_eip = view(Int32Array, memory, 644, 1)
394
+
395
+ this.prefixes = view(Int32Array, memory, 648, 1)
396
+
397
+ this.flags = view(Int32Array, memory, 120, 1)
398
+
399
+ // bitmap of flags which are not updated in the flags variable
400
+ // changed by arithmetic instructions, so only relevant to arithmetic flags
401
+ this.flags_changed = view(Int32Array, memory, 100, 1)
402
+
403
+ // enough infos about the last arithmetic operation to compute eflags
404
+ this.last_op_size = view(Int32Array, memory, 96, 1)
405
+ this.last_op1 = view(Int32Array, memory, 104, 1)
406
+ this.last_result = view(Int32Array, memory, 112, 1)
407
+
408
+ this.current_tsc = view(Uint32Array, memory, 960, 2) // 64 bit
409
+
410
+ // @ts-expect-error Devices are populated during init()
411
+ this.devices = {}
412
+
413
+ this.instruction_pointer = view(Int32Array, memory, 556, 1)
414
+ this.previous_ip = view(Int32Array, memory, 560, 1)
415
+
416
+ // configured by guest
417
+ this.apic_enabled = view(Uint8Array, memory, 548, 1)
418
+ // configured when the emulator starts (changes bios initialisation)
419
+ this.acpi_enabled = view(Uint8Array, memory, 552, 1)
420
+
421
+ // managed in io.js
422
+ this.memory_map_read8 = []
423
+ this.memory_map_write8 = []
424
+ this.memory_map_read32 = []
425
+ this.memory_map_write32 = []
426
+
427
+ this.bios = {
428
+ main: null,
429
+ vga: null,
430
+ }
431
+
432
+ this.instruction_counter = view(Uint32Array, memory, 664, 1)
433
+
434
+ // registers
435
+ this.reg32 = view(Int32Array, memory, 64, 8)
436
+
437
+ this.fpu_st = view(Int32Array, memory, 1152, 4 * 8)
438
+
439
+ this.fpu_stack_empty = view(Uint8Array, memory, 816, 1)
440
+ this.fpu_stack_empty[0] = 0xff
441
+ this.fpu_stack_ptr = view(Uint8Array, memory, 1032, 1)
442
+ this.fpu_stack_ptr[0] = 0
443
+
444
+ this.fpu_control_word = view(Uint16Array, memory, 1036, 1)
445
+ this.fpu_control_word[0] = 0x37f
446
+ this.fpu_status_word = view(Uint16Array, memory, 1040, 1)
447
+ this.fpu_status_word[0] = 0
448
+ this.fpu_ip = view(Int32Array, memory, 1048, 1)
449
+ this.fpu_ip[0] = 0
450
+ this.fpu_ip_selector = view(Int32Array, memory, 1052, 1)
451
+ this.fpu_ip_selector[0] = 0
452
+ this.fpu_opcode = view(Int32Array, memory, 1044, 1)
453
+ this.fpu_opcode[0] = 0
454
+ this.fpu_dp = view(Int32Array, memory, 1056, 1)
455
+ this.fpu_dp[0] = 0
456
+ this.fpu_dp_selector = view(Int32Array, memory, 1060, 1)
457
+ this.fpu_dp_selector[0] = 0
458
+
459
+ this.reg_xmm32s = view(Int32Array, memory, 832, 8 * 4)
460
+
461
+ this.mxcsr = view(Int32Array, memory, 824, 1)
462
+
463
+ // segment registers, tr and ldtr
464
+ this.sreg = view(Uint16Array, memory, 668, 8)
465
+
466
+ // debug registers
467
+ this.dreg = view(Int32Array, memory, 684, 8)
468
+
469
+ this.reg_pdpte = view(Int32Array, memory, 968, 8)
470
+
471
+ this.svga_dirty_bitmap_min_offset = view(Uint32Array, memory, 716, 1)
472
+ this.svga_dirty_bitmap_max_offset = view(Uint32Array, memory, 720, 1)
473
+
474
+ this.fw_value = []
475
+ this.fw_pointer = 0
476
+ this.option_roms = []
477
+
478
+ this.io = undefined!
479
+
480
+ this.bus = bus
481
+
482
+ this.set_tsc(0, 0)
483
+
484
+ if (DEBUG) {
485
+ this.seen_code = {}
486
+ this.seen_code_uncompiled = {}
487
+ }
488
+ }
489
+
490
+ mmap_read8(addr: number): number {
491
+ const value = this.memory_map_read8[addr >>> MMAP_BLOCK_BITS]!(addr)
492
+ dbg_assert(value >= 0 && value <= 0xff)
493
+ return value
494
+ }
495
+
496
+ mmap_write8(addr: number, value: number): void {
497
+ dbg_assert(value >= 0 && value <= 0xff)
498
+ this.memory_map_write8[addr >>> MMAP_BLOCK_BITS]!(addr, value)
499
+ }
500
+
501
+ mmap_write16(addr: number, value: number): void {
502
+ const fn = this.memory_map_write8[addr >>> MMAP_BLOCK_BITS]!
503
+
504
+ dbg_assert(value >= 0 && value <= 0xffff)
505
+ fn(addr, value & 0xff)
506
+ fn((addr + 1) | 0, value >> 8)
507
+ }
508
+
509
+ mmap_read32(addr: number): number {
510
+ const aligned_addr = addr >>> MMAP_BLOCK_BITS
511
+
512
+ return this.memory_map_read32[aligned_addr]!(addr)
513
+ }
514
+
515
+ mmap_write32(addr: number, value: number): void {
516
+ const aligned_addr = addr >>> MMAP_BLOCK_BITS
517
+
518
+ this.memory_map_write32[aligned_addr]!(addr, value)
519
+ }
520
+
521
+ mmap_write64(addr: number, value0: number, value1: number): void {
522
+ const aligned_addr = addr >>> MMAP_BLOCK_BITS
523
+ // This should hold since writes across pages are split up
524
+ dbg_assert(aligned_addr === (addr + 7) >>> MMAP_BLOCK_BITS)
525
+
526
+ const write_func32 = this.memory_map_write32[aligned_addr]!
527
+ write_func32(addr, value0)
528
+ write_func32(addr + 4, value1)
529
+ }
530
+
531
+ mmap_write128(
532
+ addr: number,
533
+ value0: number,
534
+ value1: number,
535
+ value2: number,
536
+ value3: number,
537
+ ): void {
538
+ const aligned_addr = addr >>> MMAP_BLOCK_BITS
539
+ // This should hold since writes across pages are split up
540
+ dbg_assert(aligned_addr === (addr + 12) >>> MMAP_BLOCK_BITS)
541
+
542
+ const write_func32 = this.memory_map_write32[aligned_addr]!
543
+ write_func32(addr, value0)
544
+ write_func32(addr + 4, value1)
545
+ write_func32(addr + 8, value2)
546
+ write_func32(addr + 12, value3)
547
+ }
548
+
549
+ write_blob(blob: Uint8Array | number[], offset: number): void {
550
+ dbg_assert(blob && blob.length >= 0)
551
+
552
+ if (blob.length) {
553
+ dbg_assert(!this.in_mapped_range(offset))
554
+ dbg_assert(!this.in_mapped_range(offset + blob.length - 1))
555
+
556
+ this.jit_dirty_cache(offset, offset + blob.length)
557
+ this.mem8.set(blob, offset)
558
+ }
559
+ }
560
+
561
+ read_blob(offset: number, length: number): Uint8Array {
562
+ if (length) {
563
+ dbg_assert(!this.in_mapped_range(offset))
564
+ dbg_assert(!this.in_mapped_range(offset + length - 1))
565
+ }
566
+ return this.mem8.subarray(offset, offset + length)
567
+ }
568
+
569
+ clear_opstats(): void {
570
+ new Uint8Array(this.wasm_memory.buffer, 0x8000, 0x20000).fill(0)
571
+ this.wm.exports['profiler_init']()
572
+ }
573
+
574
+ create_jit_imports(): void {
575
+ // Set this.jit_imports as generated WASM modules will expect
576
+
577
+ const jit_imports = Object.create(null)
578
+
579
+ jit_imports['m'] = this.wm.exports['memory']
580
+
581
+ for (const name of Object.keys(this.wm.exports)) {
582
+ if (
583
+ name.startsWith('_') ||
584
+ name.startsWith('zstd') ||
585
+ name.endsWith('_js')
586
+ ) {
587
+ continue
588
+ }
589
+
590
+ jit_imports[name] = this.wm.exports[name]
591
+ }
592
+
593
+ this.jit_imports = jit_imports
594
+ }
595
+
596
+ wasm_patch(): void {
597
+ const get_optional_import = (name: string) => this.wm.exports[name]
598
+
599
+ const get_import = (name: string) => {
600
+ const f = get_optional_import(name)
601
+ console.assert(f, 'Missing import: ' + name)
602
+ return f
603
+ }
604
+
605
+ this.reset_cpu = get_import('reset_cpu')
606
+
607
+ this.getiopl = get_import('getiopl')
608
+ this.get_eflags = get_import('get_eflags')
609
+
610
+ this.handle_irqs = get_import('handle_irqs')
611
+
612
+ this.main_loop = get_import('main_loop')
613
+
614
+ this.reboot_internal = get_import('reboot_internal')
615
+
616
+ this.set_jit_config = get_import('set_jit_config')
617
+
618
+ this.read8 = get_import('read8')
619
+ this.read16 = get_import('read16')
620
+ this.read32s = get_import('read32s')
621
+ this.write8 = get_import('write8')
622
+ this.write16 = get_import('write16')
623
+ this.write32 = get_import('write32')
624
+ this.in_mapped_range = get_import('in_mapped_range')
625
+
626
+ // used by nasmtests
627
+ this.fpu_load_tag_word = get_import('fpu_load_tag_word')
628
+ this.fpu_load_status_word = get_import('fpu_load_status_word')
629
+ this.fpu_get_sti_f64 = get_import('fpu_get_sti_f64')
630
+
631
+ this.translate_address_system_read = get_import(
632
+ 'translate_address_system_read_js',
633
+ )
634
+
635
+ this.get_seg_cs = get_import('get_seg_cs')
636
+ this.get_real_eip = get_import('get_real_eip')
637
+
638
+ this.clear_tlb = get_import('clear_tlb')
639
+ this.full_clear_tlb = get_import('full_clear_tlb')
640
+ this.update_state_flags = get_import('update_state_flags')
641
+
642
+ this.set_tsc = get_import('set_tsc')
643
+ this.store_current_tsc = get_import('store_current_tsc')
644
+
645
+ this.set_cpuid_level = get_import('set_cpuid_level')
646
+
647
+ this.device_raise_irq = get_import('device_raise_irq')
648
+ this.device_lower_irq = get_import('device_lower_irq')
649
+
650
+ this.apic_timer = get_import('apic_timer')
651
+
652
+ if (DEBUG) {
653
+ this.jit_force_generate_unsafe = get_optional_import(
654
+ 'jit_force_generate_unsafe',
655
+ )
656
+ }
657
+
658
+ this.jit_clear_cache = get_import('jit_clear_cache_js')
659
+ this.jit_dirty_cache = get_import('jit_dirty_cache')
660
+ this.codegen_finalize_finished = get_import('codegen_finalize_finished')
661
+
662
+ this.allocate_memory = get_import('allocate_memory')
663
+ this.zero_memory = get_import('zero_memory')
664
+ this.is_memory_zeroed = get_import('is_memory_zeroed')
665
+
666
+ this.svga_allocate_memory = get_import('svga_allocate_memory')
667
+ this.svga_allocate_dest_buffer = get_import('svga_allocate_dest_buffer')
668
+ this.svga_fill_pixel_buffer = get_import('svga_fill_pixel_buffer')
669
+ this.svga_mark_dirty = get_import('svga_mark_dirty')
670
+
671
+ this.get_pic_addr_master = get_import('get_pic_addr_master')
672
+ this.get_pic_addr_slave = get_import('get_pic_addr_slave')
673
+ this.get_apic_addr = get_import('get_apic_addr')
674
+ this.get_ioapic_addr = get_import('get_ioapic_addr')
675
+
676
+ this.zstd_create_ctx = get_import('zstd_create_ctx')
677
+ this.zstd_get_src_ptr = get_import('zstd_get_src_ptr')
678
+ this.zstd_free_ctx = get_import('zstd_free_ctx')
679
+ this.zstd_read = get_import('zstd_read')
680
+ this.zstd_read_free = get_import('zstd_read_free')
681
+ }
682
+
683
+ jit_force_generate(addr: number): void {
684
+ if (!this.jit_force_generate_unsafe) {
685
+ dbg_assert(
686
+ false,
687
+ 'Not supported in this wasm build: jit_force_generate_unsafe',
688
+ )
689
+ return
690
+ }
691
+
692
+ this.jit_force_generate_unsafe(addr)
693
+ }
694
+
695
+ jit_clear_func(index: number): void {
696
+ dbg_assert(index >= 0 && index < WASM_TABLE_SIZE)
697
+ this.wm.wasm_table.set(index + WASM_TABLE_OFFSET, null)
698
+ }
699
+
700
+ jit_clear_all_funcs(): void {
701
+ const table = this.wm.wasm_table
702
+
703
+ for (let i = 0; i < WASM_TABLE_SIZE; i++) {
704
+ table.set(WASM_TABLE_OFFSET + i, null)
705
+ }
706
+ }
707
+
708
+ get_state(): any[] {
709
+ const state: any[] = []
710
+
711
+ state[0] = this.memory_size[0]
712
+ state[1] = new Uint8Array([
713
+ ...this.segment_is_null,
714
+ ...this.segment_access_bytes,
715
+ ])
716
+ state[2] = this.segment_offsets
717
+ state[3] = this.segment_limits
718
+ state[4] = this.protected_mode[0]
719
+ state[5] = this.idtr_offset[0]
720
+ state[6] = this.idtr_size[0]
721
+ state[7] = this.gdtr_offset[0]
722
+ state[8] = this.gdtr_size[0]
723
+ state[9] = this.page_fault[0]
724
+ state[10] = this.cr
725
+ state[11] = this.cpl[0]
726
+
727
+ state[13] = this.is_32[0]
728
+
729
+ state[16] = this.stack_size_32[0]
730
+ state[17] = this.in_hlt[0]
731
+ state[18] = this.last_virt_eip[0]
732
+ state[19] = this.eip_phys[0]
733
+
734
+ state[22] = this.sysenter_cs[0]
735
+ state[23] = this.sysenter_eip[0]
736
+ state[24] = this.sysenter_esp[0]
737
+ state[25] = this.prefixes[0]
738
+ state[26] = this.flags[0]
739
+ state[27] = this.flags_changed[0]
740
+ state[28] = this.last_op1[0]
741
+
742
+ state[30] = this.last_op_size[0]
743
+
744
+ state[37] = this.instruction_pointer[0]
745
+ state[38] = this.previous_ip[0]
746
+ state[39] = this.reg32
747
+ state[40] = this.sreg
748
+ state[41] = this.dreg
749
+ state[42] = this.reg_pdpte
750
+
751
+ this.store_current_tsc()
752
+ state[43] = this.current_tsc
753
+
754
+ state[45] = this.devices.virtio_9p
755
+ state[46] = this.get_state_apic()
756
+ state[47] = this.devices.rtc
757
+ state[48] = this.devices.pci
758
+ state[49] = this.devices.dma
759
+ state[50] = this.devices.acpi
760
+ // 51 (formerly hpet)
761
+ state[52] = this.devices.vga
762
+ state[53] = this.devices.ps2
763
+ state[54] = this.devices.uart0
764
+ state[55] = this.devices.fdc
765
+
766
+ if (!this.devices.ide.secondary) {
767
+ if (this.devices.ide.primary?.master.is_atapi) {
768
+ state[56] = this.devices.ide.primary
769
+ } else {
770
+ state[57] = this.devices.ide.primary
771
+ }
772
+ } else {
773
+ state[85] = this.devices.ide
774
+ }
775
+
776
+ state[58] = this.devices.pit
777
+ state[59] = this.devices.net
778
+ state[60] = this.get_state_pic()
779
+ state[61] = this.devices.sb16
780
+
781
+ state[62] = this.fw_value
782
+
783
+ state[63] = this.get_state_ioapic()
784
+
785
+ state[64] = this.tss_size_32[0]
786
+
787
+ state[66] = this.reg_xmm32s
788
+
789
+ state[67] = this.fpu_st
790
+ state[68] = this.fpu_stack_empty[0]
791
+ state[69] = this.fpu_stack_ptr[0]
792
+ state[70] = this.fpu_control_word[0]
793
+ state[71] = this.fpu_ip[0]
794
+ state[72] = this.fpu_ip_selector[0]
795
+ state[73] = this.fpu_dp[0]
796
+ state[74] = this.fpu_dp_selector[0]
797
+ state[75] = this.fpu_opcode[0]
798
+
799
+ const { packed_memory, bitmap } = this.pack_memory()
800
+ state[77] = packed_memory
801
+ state[78] = new Uint8Array(bitmap.get_buffer())
802
+
803
+ state[79] = this.devices.uart1
804
+ state[80] = this.devices.uart2
805
+ state[81] = this.devices.uart3
806
+ state[82] = this.devices.virtio_console
807
+ state[83] = this.devices.virtio_net
808
+ state[84] = this.devices.virtio_balloon
809
+
810
+ // state[85] new ide set above
811
+
812
+ state[86] = this.last_result
813
+ state[87] = this.fpu_status_word
814
+ state[88] = this.mxcsr
815
+
816
+ return state
817
+ }
818
+
819
+ get_state_pic(): any[] {
820
+ const pic_size = 13
821
+ const pic = new Uint8Array(
822
+ this.wasm_memory.buffer,
823
+ this.get_pic_addr_master(),
824
+ pic_size,
825
+ )
826
+ const pic_slave = new Uint8Array(
827
+ this.wasm_memory.buffer,
828
+ this.get_pic_addr_slave(),
829
+ pic_size,
830
+ )
831
+
832
+ const state: any[] = []
833
+
834
+ const state_slave: any[] = []
835
+
836
+ state[0] = pic[0] // irq_mask
837
+ state[1] = pic[1] // irq_map
838
+ state[2] = pic[2] // isr
839
+ state[3] = pic[3] // irr
840
+ state[4] = pic[4] // is_master
841
+ state[5] = state_slave
842
+ state[6] = pic[6] // expect_icw4
843
+ state[7] = pic[7] // state
844
+ state[8] = pic[8] // read_isr
845
+ state[9] = pic[9] // auto_eoi
846
+ state[10] = pic[10] // elcr
847
+ state[11] = pic[11] // irq_value
848
+ state[12] = pic[12] // special_mask_mode
849
+
850
+ state_slave[0] = pic_slave[0] // irq_mask
851
+ state_slave[1] = pic_slave[1] // irq_map
852
+ state_slave[2] = pic_slave[2] // isr
853
+ state_slave[3] = pic_slave[3] // irr
854
+ state_slave[4] = pic_slave[4] // is_master
855
+ state_slave[5] = null
856
+ state_slave[6] = pic_slave[6] // expect_icw4
857
+ state_slave[7] = pic_slave[7] // state
858
+ state_slave[8] = pic_slave[8] // read_isr
859
+ state_slave[9] = pic_slave[9] // auto_eoi
860
+ state_slave[10] = pic_slave[10] // elcr
861
+ state_slave[11] = pic_slave[11] // irq_value
862
+ state_slave[12] = pic_slave[12] // special_mask_mode
863
+
864
+ return state
865
+ }
866
+
867
+ get_state_apic(): Uint8Array {
868
+ const APIC_STRUCT_SIZE = 4 * 46 // keep in sync with apic.rs
869
+ return new Uint8Array(
870
+ this.wasm_memory.buffer,
871
+ this.get_apic_addr(),
872
+ APIC_STRUCT_SIZE,
873
+ )
874
+ }
875
+
876
+ get_state_ioapic(): Uint8Array {
877
+ const IOAPIC_STRUCT_SIZE = 4 * 52 // keep in sync with ioapic.rs
878
+ return new Uint8Array(
879
+ this.wasm_memory.buffer,
880
+ this.get_ioapic_addr(),
881
+ IOAPIC_STRUCT_SIZE,
882
+ )
883
+ }
884
+
885
+ set_state(state: any[]): void {
886
+ this.memory_size[0] = state[0]
887
+
888
+ if (this.mem8.length !== this.memory_size[0]) {
889
+ console.warn(
890
+ 'Note: Memory size mismatch. we=' +
891
+ this.mem8.length +
892
+ ' state=' +
893
+ this.memory_size[0],
894
+ )
895
+ }
896
+
897
+ if (state[1].length === 8) {
898
+ // NOTE: support for old state images; delete this when bumping STATE_VERSION
899
+ this.segment_is_null.set(state[1])
900
+ this.segment_access_bytes.fill(0x80 | (3 << 5) | 0x10 | 0x02)
901
+ this.segment_access_bytes[REG_CS] =
902
+ 0x80 | (3 << 5) | 0x10 | 0x08 | 0x02
903
+ } else if (state[1].length === 16) {
904
+ this.segment_is_null.set(state[1].subarray(0, 8))
905
+ this.segment_access_bytes.set(state[1].subarray(8, 16))
906
+ } else {
907
+ dbg_assert(
908
+ false,
909
+ 'Unexpected cpu segment state length:' + state[1].length,
910
+ )
911
+ }
912
+ this.segment_offsets.set(state[2])
913
+ this.segment_limits.set(state[3])
914
+
915
+ this.protected_mode[0] = state[4]
916
+ this.idtr_offset[0] = state[5]
917
+ this.idtr_size[0] = state[6]
918
+ this.gdtr_offset[0] = state[7]
919
+ this.gdtr_size[0] = state[8]
920
+ this.page_fault[0] = state[9]
921
+ this.cr.set(state[10])
922
+ this.cpl[0] = state[11]
923
+
924
+ this.is_32[0] = state[13]
925
+
926
+ this.stack_size_32[0] = state[16]
927
+
928
+ this.in_hlt[0] = state[17]
929
+ this.last_virt_eip[0] = state[18]
930
+ this.eip_phys[0] = state[19]
931
+
932
+ this.sysenter_cs[0] = state[22]
933
+ this.sysenter_eip[0] = state[23]
934
+ this.sysenter_esp[0] = state[24]
935
+ this.prefixes[0] = state[25]
936
+
937
+ this.flags[0] = state[26]
938
+ this.flags_changed[0] = state[27]
939
+ this.last_op1[0] = state[28]
940
+
941
+ this.last_op_size[0] = state[30]
942
+
943
+ this.instruction_pointer[0] = state[37]
944
+ this.previous_ip[0] = state[38]
945
+ this.reg32.set(state[39])
946
+ this.sreg.set(state[40])
947
+ this.dreg.set(state[41])
948
+ if (state[42]) this.reg_pdpte.set(state[42])
949
+
950
+ this.set_tsc(state[43][0], state[43][1])
951
+
952
+ if (this.devices.virtio_9p) this.devices.virtio_9p.set_state(state[45])
953
+ if (state[46]) this.set_state_apic(state[46])
954
+ if (this.devices.rtc) this.devices.rtc.set_state(state[47])
955
+ if (this.devices.dma) this.devices.dma.set_state(state[49])
956
+ if (this.devices.acpi) this.devices.acpi.set_state(state[50])
957
+ // 51 (formerly hpet)
958
+ if (this.devices.vga) this.devices.vga.set_state(state[52])
959
+ if (this.devices.ps2) this.devices.ps2.set_state(state[53])
960
+ if (this.devices.uart0) this.devices.uart0.set_state(state[54])
961
+ if (this.devices.fdc) this.devices.fdc.set_state(state[55])
962
+
963
+ if (state[56] || state[57]) {
964
+ // ide device from older version of v86, only primary: state[56] contains cdrom, state[57] contains hard drive
965
+
966
+ const ide_config: any = [
967
+ [undefined, undefined],
968
+ [undefined, undefined],
969
+ ]
970
+ if (state[56]) {
971
+ ide_config[0][0] = {
972
+ is_cdrom: true,
973
+ buffer: this.devices.cdrom.buffer,
974
+ }
975
+ } else {
976
+ ide_config[0][0] = {
977
+ is_cdrom: false,
978
+ buffer: this.devices.ide.primary!.master.buffer,
979
+ }
980
+ }
981
+ this.devices.ide = new IDEController(
982
+ this,
983
+ this.devices.ide.bus,
984
+ ide_config,
985
+ )
986
+ this.devices.cdrom = state[56]
987
+ ? this.devices.ide.primary!.master
988
+ : undefined
989
+ this.devices.ide.primary!.set_state(state[56] || state[57])
990
+ } else if (state[85]) {
991
+ this.devices.ide.set_state(state[85])
992
+ }
993
+
994
+ if (this.devices.pci) this.devices.pci.set_state(state[48])
995
+
996
+ if (this.devices.pit) this.devices.pit.set_state(state[58])
997
+ if (this.devices.net) this.devices.net.set_state(state[59])
998
+ this.set_state_pic(state[60])
999
+ if (this.devices.sb16) this.devices.sb16.set_state(state[61])
1000
+
1001
+ if (this.devices.uart1) this.devices.uart1.set_state(state[79])
1002
+ if (this.devices.uart2) this.devices.uart2.set_state(state[80])
1003
+ if (this.devices.uart3) this.devices.uart3.set_state(state[81])
1004
+ if (this.devices.virtio_console)
1005
+ this.devices.virtio_console.set_state(state[82])
1006
+ if (this.devices.virtio_net)
1007
+ this.devices.virtio_net.set_state(state[83])
1008
+ if (this.devices.virtio_balloon)
1009
+ this.devices.virtio_balloon.set_state(state[84])
1010
+
1011
+ this.fw_value = state[62]
1012
+
1013
+ if (state[63]) this.set_state_ioapic(state[63])
1014
+
1015
+ this.tss_size_32[0] = state[64]
1016
+
1017
+ this.reg_xmm32s.set(state[66])
1018
+
1019
+ this.fpu_st.set(state[67])
1020
+ this.fpu_stack_empty[0] = state[68]
1021
+ this.fpu_stack_ptr[0] = state[69]
1022
+ this.fpu_control_word[0] = state[70]
1023
+ this.fpu_ip[0] = state[71]
1024
+ this.fpu_ip_selector[0] = state[72]
1025
+ this.fpu_dp[0] = state[73]
1026
+ this.fpu_dp_selector[0] = state[74]
1027
+ this.fpu_opcode[0] = state[75]
1028
+
1029
+ if (state[86] !== undefined) this.last_result = state[86]
1030
+ if (state[87] !== undefined) this.fpu_status_word = state[87]
1031
+ if (state[88] !== undefined) this.mxcsr = state[88]
1032
+
1033
+ const bitmap = new Bitmap(state[78].buffer)
1034
+ const packed_memory = state[77]
1035
+ this.unpack_memory(bitmap, packed_memory)
1036
+
1037
+ this.update_state_flags()
1038
+
1039
+ this.full_clear_tlb()
1040
+
1041
+ this.jit_clear_cache()
1042
+ }
1043
+
1044
+ set_state_pic(state: any[]): void {
1045
+ // Note: This could exists for compatibility with old state images
1046
+ // It should be deleted when the state version changes
1047
+
1048
+ const pic_size = 13
1049
+ const pic = new Uint8Array(
1050
+ this.wasm_memory.buffer,
1051
+ this.get_pic_addr_master(),
1052
+ pic_size,
1053
+ )
1054
+ const pic_slave = new Uint8Array(
1055
+ this.wasm_memory.buffer,
1056
+ this.get_pic_addr_slave(),
1057
+ pic_size,
1058
+ )
1059
+
1060
+ pic[0] = state[0] // irq_mask
1061
+ pic[1] = state[1] // irq_map
1062
+ pic[2] = state[2] // isr
1063
+ pic[3] = state[3] // irr
1064
+ pic[4] = state[4] // is_master
1065
+ const state_slave = state[5]
1066
+ pic[6] = state[6] // expect_icw4
1067
+ pic[7] = state[7] // state
1068
+ pic[8] = state[8] // read_isr
1069
+ pic[9] = state[9] // auto_eoi
1070
+ pic[10] = state[10] // elcr
1071
+ pic[11] = state[11] // irq_value (undefined in old state images)
1072
+ pic[12] = state[12] // special_mask_mode (undefined in old state images)
1073
+
1074
+ pic_slave[0] = state_slave[0] // irq_mask
1075
+ pic_slave[1] = state_slave[1] // irq_map
1076
+ pic_slave[2] = state_slave[2] // isr
1077
+ pic_slave[3] = state_slave[3] // irr
1078
+ pic_slave[4] = state_slave[4] // is_master
1079
+ // dummy
1080
+ pic_slave[6] = state_slave[6] // expect_icw4
1081
+ pic_slave[7] = state_slave[7] // state
1082
+ pic_slave[8] = state_slave[8] // read_isr
1083
+ pic_slave[9] = state_slave[9] // auto_eoi
1084
+ pic_slave[10] = state_slave[10] // elcr
1085
+ pic_slave[11] = state_slave[11] // irq_value (undefined in old state images)
1086
+ pic_slave[12] = state_slave[12] // special_mask_mode (undefined in old state images)
1087
+ }
1088
+
1089
+ set_state_apic(state: any): void {
1090
+ const APIC_STRUCT_SIZE = 4 * 46 // keep in sync with apic.rs
1091
+ const IOAPIC_CONFIG_MASKED = 1 << 16
1092
+
1093
+ if (state instanceof Array) {
1094
+ // old js state image; delete this code path when the state version changes
1095
+ const apic = new Int32Array(
1096
+ this.wasm_memory.buffer,
1097
+ this.get_apic_addr(),
1098
+ APIC_STRUCT_SIZE >> 2,
1099
+ )
1100
+ apic[0] = state[0] // apic_id
1101
+ apic[1] = state[1] // timer_divier
1102
+ apic[2] = state[2] // timer_divider_shift
1103
+ apic[3] = state[3] // timer_initial_count
1104
+ apic[4] = state[4] // timer_current_count
1105
+ // skip next_tick (in js: state[4]; in rust: apic[6] and apic[7])
1106
+ apic[8] = state[6] // lvt_timer
1107
+ apic[9] = state[7] // lvt_perf_counter
1108
+ apic[10] = state[8] // lvt_int0
1109
+ apic[11] = state[9] // lvt_int1
1110
+ apic[12] = state[10] // lvt_error
1111
+ apic[13] = state[11] // tpr
1112
+ apic[14] = state[12] // icr0
1113
+ apic[15] = state[13] // icr1
1114
+ apic.set(state[15], 16) // irr
1115
+ apic.set(state[15], 24) // isr
1116
+ apic.set(state[16], 32) // tmr
1117
+ apic[40] = state[17] // spurious_vector
1118
+ apic[41] = state[18] // destination_format
1119
+ apic[42] = state[19] // local_destination
1120
+ apic[43] = state[20] // error
1121
+ apic[44] = state[21] // read_error
1122
+ apic[45] = state[22] || IOAPIC_CONFIG_MASKED // lvt_thermal_sensor
1123
+ } else {
1124
+ const apic = new Uint8Array(
1125
+ this.wasm_memory.buffer,
1126
+ this.get_apic_addr(),
1127
+ APIC_STRUCT_SIZE,
1128
+ )
1129
+ dbg_assert(state instanceof Uint8Array)
1130
+ dbg_assert(state.length === apic.length) // later versions might need to handle state upgrades here
1131
+ apic.set(state)
1132
+ }
1133
+ }
1134
+
1135
+ set_state_ioapic(state: any): void {
1136
+ const IOAPIC_STRUCT_SIZE = 4 * 52 // keep in sync with ioapic.rs
1137
+
1138
+ if (state instanceof Array) {
1139
+ // old js state image; delete this code path when the state version changes
1140
+ dbg_assert(state[0].length === 24)
1141
+ dbg_assert(state[1].length === 24)
1142
+ dbg_assert(state.length === 6)
1143
+ const ioapic = new Int32Array(
1144
+ this.wasm_memory.buffer,
1145
+ this.get_ioapic_addr(),
1146
+ IOAPIC_STRUCT_SIZE >> 2,
1147
+ )
1148
+ ioapic.set(state[0], 0) // ioredtbl_config
1149
+ ioapic.set(state[1], 24) // ioredtbl_destination
1150
+ ioapic[48] = state[2] // ioregsel
1151
+ ioapic[49] = state[3] // ioapic_id
1152
+ ioapic[50] = state[4] // irr
1153
+ ioapic[51] = state[5] // irq_value
1154
+ } else {
1155
+ const ioapic = new Uint8Array(
1156
+ this.wasm_memory.buffer,
1157
+ this.get_ioapic_addr(),
1158
+ IOAPIC_STRUCT_SIZE,
1159
+ )
1160
+ dbg_assert(state instanceof Uint8Array)
1161
+ dbg_assert(state.length === ioapic.length) // later versions might need to handle state upgrades here
1162
+ ioapic.set(state)
1163
+ }
1164
+ }
1165
+
1166
+ pack_memory(): { bitmap: Bitmap; packed_memory: Uint8Array } {
1167
+ dbg_assert((this.mem8.length & 0xfff) === 0)
1168
+
1169
+ const page_count = this.mem8.length >> 12
1170
+ const nonzero_pages: number[] = []
1171
+ for (let page = 0; page < page_count; page++) {
1172
+ if (!this.is_memory_zeroed(page << 12, 0x1000)) {
1173
+ nonzero_pages.push(page)
1174
+ }
1175
+ }
1176
+
1177
+ const bitmap = new Bitmap(page_count)
1178
+ const packed_memory = new Uint8Array(nonzero_pages.length << 12)
1179
+
1180
+ for (const [i, page] of nonzero_pages.entries()) {
1181
+ bitmap.set(page, 1)
1182
+
1183
+ const offset = page << 12
1184
+ const page_contents = this.mem8.subarray(offset, offset + 0x1000)
1185
+ packed_memory.set(page_contents, i << 12)
1186
+ }
1187
+
1188
+ return { bitmap, packed_memory }
1189
+ }
1190
+
1191
+ unpack_memory(bitmap: Bitmap, packed_memory: Uint8Array): void {
1192
+ this.zero_memory(0, this.memory_size[0])
1193
+
1194
+ const page_count = this.memory_size[0] >> 12
1195
+ let packed_page = 0
1196
+
1197
+ for (let page = 0; page < page_count; page++) {
1198
+ if (bitmap.get(page)) {
1199
+ const offset = packed_page << 12
1200
+ const v = packed_memory.subarray(offset, offset + 0x1000)
1201
+ this.mem8.set(v, page << 12)
1202
+ packed_page++
1203
+ }
1204
+ }
1205
+ }
1206
+
1207
+ reboot(): void {
1208
+ this.reset_cpu()
1209
+
1210
+ this.fw_value = []
1211
+
1212
+ if (this.devices.virtio_9p) {
1213
+ this.devices.virtio_9p.reset()
1214
+ }
1215
+ if (this.devices.virtio_console) {
1216
+ this.devices.virtio_console.reset()
1217
+ }
1218
+ if (this.devices.virtio_net) {
1219
+ this.devices.virtio_net.reset()
1220
+ }
1221
+ if (this.devices.ps2) {
1222
+ this.devices.ps2.reset()
1223
+ }
1224
+
1225
+ this.load_bios()
1226
+ }
1227
+
1228
+ reset_memory(): void {
1229
+ this.mem8.fill(0)
1230
+ }
1231
+
1232
+ create_memory(size: number, minimum_size: number): void {
1233
+ if (size < minimum_size) {
1234
+ size = minimum_size
1235
+ dbg_log('Rounding memory size up to ' + size, LOG_CPU)
1236
+ } else if ((size | 0) < 0) {
1237
+ size = Math.pow(2, 31) - MMAP_BLOCK_SIZE
1238
+ dbg_log('Rounding memory size down to ' + size, LOG_CPU)
1239
+ }
1240
+
1241
+ size = (((size - 1) | (MMAP_BLOCK_SIZE - 1)) + 1) | 0
1242
+ dbg_assert((size | 0) > 0)
1243
+ dbg_assert((size & (MMAP_BLOCK_SIZE - 1)) === 0)
1244
+
1245
+ console.assert(
1246
+ this.memory_size[0] === 0,
1247
+ 'Expected uninitialised memory',
1248
+ )
1249
+
1250
+ this.memory_size[0] = size
1251
+
1252
+ const memory_offset = this.allocate_memory(size)
1253
+
1254
+ this.mem8 = view(Uint8Array, this.wasm_memory, memory_offset, size)
1255
+ this.mem32s = view(
1256
+ Uint32Array,
1257
+ this.wasm_memory,
1258
+ memory_offset,
1259
+ size >> 2,
1260
+ )
1261
+ }
1262
+
1263
+ init(settings: any, device_bus: BusConnector): void {
1264
+ this.create_memory(
1265
+ settings.memory_size || 64 * 1024 * 1024,
1266
+ settings.initrd ? 64 * 1024 * 1024 : 1024 * 1024,
1267
+ )
1268
+
1269
+ if (settings.disable_jit) {
1270
+ this.set_jit_config(0, 1)
1271
+ }
1272
+
1273
+ if (settings.cpuid_level) this.set_cpuid_level(settings.cpuid_level)
1274
+
1275
+ this.acpi_enabled[0] = +settings.acpi
1276
+
1277
+ this.reset_cpu()
1278
+
1279
+ const io = new IO(this)
1280
+ this.io = io
1281
+
1282
+ this.bios.main = settings.bios
1283
+ this.bios.vga = settings.vga_bios
1284
+
1285
+ this.load_bios()
1286
+
1287
+ if (settings.bzimage) {
1288
+ const option_rom = load_kernel(
1289
+ this.mem8,
1290
+ settings.bzimage,
1291
+ settings.initrd,
1292
+ settings.cmdline || '',
1293
+ )
1294
+
1295
+ if (option_rom) {
1296
+ this.option_roms.push(option_rom)
1297
+ }
1298
+ }
1299
+
1300
+ io.register_read(0xb3, this, function (this: CPU) {
1301
+ // seabios smm_relocate_and_restore
1302
+ dbg_log('port 0xB3 read')
1303
+ return 0
1304
+ })
1305
+
1306
+ let a20_byte = 0
1307
+
1308
+ io.register_read(0x92, this, function () {
1309
+ return a20_byte
1310
+ })
1311
+
1312
+ io.register_write(0x92, this, function (_out_byte: number) {
1313
+ a20_byte = _out_byte
1314
+ })
1315
+
1316
+ io.register_read(0x511, this, function (this: CPU) {
1317
+ // bios config port (used by seabios and kvm-unit-test)
1318
+ if (this.fw_pointer < this.fw_value.length) {
1319
+ return this.fw_value[this.fw_pointer++]
1320
+ } else {
1321
+ dbg_assert(false, 'config port: Read past value')
1322
+ return 0
1323
+ }
1324
+ })
1325
+ io.register_write(
1326
+ 0x510,
1327
+ this,
1328
+ undefined,
1329
+ function (this: CPU, value: number) {
1330
+ // https://wiki.osdev.org/QEMU_fw_cfg
1331
+ // https://github.com/qemu/qemu/blob/master/docs/specs/fw_cfg.txt
1332
+
1333
+ dbg_log('bios config port, index=' + h(value))
1334
+
1335
+ function i32(x: number): Uint8Array {
1336
+ return new Uint8Array(Int32Array.of(x).buffer)
1337
+ }
1338
+
1339
+ function to_be16(x: number): number {
1340
+ return (x >> 8) | ((x << 8) & 0xff00)
1341
+ }
1342
+
1343
+ function to_be32(x: number): number {
1344
+ return (
1345
+ (x << 24) |
1346
+ ((x << 8) & 0xff0000) |
1347
+ ((x >> 8) & 0xff00) |
1348
+ (x >>> 24)
1349
+ )
1350
+ }
1351
+
1352
+ this.fw_pointer = 0
1353
+
1354
+ if (value === FW_CFG_SIGNATURE) {
1355
+ // Pretend to be qemu (for seabios)
1356
+ this.fw_value = i32(FW_CFG_SIGNATURE_QEMU)
1357
+ } else if (value === FW_CFG_ID) {
1358
+ this.fw_value = i32(0)
1359
+ } else if (value === FW_CFG_RAM_SIZE) {
1360
+ this.fw_value = i32(this.memory_size[0])
1361
+ } else if (value === FW_CFG_NB_CPUS) {
1362
+ this.fw_value = i32(1)
1363
+ } else if (value === FW_CFG_MAX_CPUS) {
1364
+ this.fw_value = i32(1)
1365
+ } else if (value === FW_CFG_NUMA) {
1366
+ this.fw_value = new Uint8Array(16)
1367
+ } else if (value === FW_CFG_FILE_DIR) {
1368
+ const buffer_size = 4 + 64 * this.option_roms.length
1369
+ const buffer32 = new Int32Array(buffer_size)
1370
+ const buffer8 = new Uint8Array(buffer32.buffer)
1371
+
1372
+ buffer32[0] = to_be32(this.option_roms.length)
1373
+
1374
+ for (let i = 0; i < this.option_roms.length; i++) {
1375
+ const { name, data } = this.option_roms[i]
1376
+ const file_struct_ptr = 4 + 64 * i
1377
+
1378
+ dbg_assert(FW_CFG_FILE_START + i < 0x10000)
1379
+ buffer32[(file_struct_ptr + 0) >> 2] = to_be32(
1380
+ data.length,
1381
+ )
1382
+ buffer32[(file_struct_ptr + 4) >> 2] = to_be16(
1383
+ FW_CFG_FILE_START + i,
1384
+ )
1385
+
1386
+ dbg_assert(name.length < 64 - 8)
1387
+
1388
+ for (let j = 0; j < name.length; j++) {
1389
+ buffer8[file_struct_ptr + 8 + j] =
1390
+ name.charCodeAt(j)
1391
+ }
1392
+ }
1393
+
1394
+ this.fw_value = buffer8
1395
+ } else if (
1396
+ value >= FW_CFG_CUSTOM_START &&
1397
+ value < FW_CFG_FILE_START
1398
+ ) {
1399
+ this.fw_value = i32(0)
1400
+ } else if (
1401
+ value >= FW_CFG_FILE_START &&
1402
+ value - FW_CFG_FILE_START < this.option_roms.length
1403
+ ) {
1404
+ const i = value - FW_CFG_FILE_START
1405
+ this.fw_value = this.option_roms[i].data
1406
+ } else {
1407
+ dbg_log('Warning: Unimplemented fw index: ' + h(value))
1408
+ this.fw_value = i32(0)
1409
+ }
1410
+ },
1411
+ )
1412
+
1413
+ if (DEBUG) {
1414
+ // Avoid logging noisey ports
1415
+ io.register_write(0x80, this, function (_out_byte: number) {})
1416
+ io.register_read(0x80, this, function () {
1417
+ return 0xff
1418
+ })
1419
+ io.register_write(0xe9, this, function (_out_byte: number) {})
1420
+ }
1421
+
1422
+ // @ts-expect-error Devices are populated below
1423
+ this.devices = {}
1424
+
1425
+ // TODO: Make this more configurable
1426
+ if (settings.load_devices) {
1427
+ this.devices.pci = new PCI(this)
1428
+
1429
+ if (this.acpi_enabled[0]) {
1430
+ this.devices.acpi = new ACPI(this)
1431
+ }
1432
+
1433
+ this.devices.rtc = new RTC(this)
1434
+ this.fill_cmos(this.devices.rtc, settings)
1435
+
1436
+ this.devices.dma = new DMA(this)
1437
+
1438
+ this.devices.vga = new VGAScreen(
1439
+ this,
1440
+ device_bus,
1441
+ settings.screen,
1442
+ settings.vga_memory_size || 8 * 1024 * 1024,
1443
+ )
1444
+
1445
+ this.devices.ps2 = new PS2(this, device_bus)
1446
+
1447
+ this.devices.uart0 = new UART(this, 0x3f8, device_bus)
1448
+
1449
+ if (settings.uart1) {
1450
+ this.devices.uart1 = new UART(this, 0x2f8, device_bus)
1451
+ }
1452
+ if (settings.uart2) {
1453
+ this.devices.uart2 = new UART(this, 0x3e8, device_bus)
1454
+ }
1455
+ if (settings.uart3) {
1456
+ this.devices.uart3 = new UART(this, 0x2e8, device_bus)
1457
+ }
1458
+
1459
+ this.devices.fdc = new FloppyController(
1460
+ this,
1461
+ settings.fda,
1462
+ settings.fdb,
1463
+ )
1464
+
1465
+ const ide_config: any = [
1466
+ [undefined, undefined],
1467
+ [undefined, undefined],
1468
+ ]
1469
+ if (settings.hda) {
1470
+ ide_config[0][0] = { buffer: settings.hda }
1471
+ ide_config[0][1] = { buffer: settings.hdb }
1472
+ }
1473
+ ide_config[1][0] = { is_cdrom: true, buffer: settings.cdrom }
1474
+ this.devices.ide = new IDEController(this, device_bus, ide_config)
1475
+ this.devices.cdrom = this.devices.ide.secondary!.master
1476
+
1477
+ this.devices.pit = new PIT(this, device_bus)
1478
+
1479
+ if (settings.net_device.type === 'ne2k') {
1480
+ this.devices.net = new Ne2k(
1481
+ this,
1482
+ device_bus,
1483
+ settings.preserve_mac_from_state_image,
1484
+ settings.mac_address_translation,
1485
+ )
1486
+ } else if (settings.net_device.type === 'virtio') {
1487
+ this.devices.virtio_net = new VirtioNet(
1488
+ this,
1489
+ device_bus,
1490
+ settings.preserve_mac_from_state_image,
1491
+ settings.net_device.mtu,
1492
+ )
1493
+ }
1494
+
1495
+ if (settings.fs9p) {
1496
+ this.devices.virtio_9p = new Virtio9p(
1497
+ settings.fs9p,
1498
+ this,
1499
+ device_bus,
1500
+ )
1501
+ } else if (settings.handle9p) {
1502
+ this.devices.virtio_9p = new Virtio9pHandler(
1503
+ settings.handle9p,
1504
+ this,
1505
+ )
1506
+ } else if (settings.proxy9p) {
1507
+ this.devices.virtio_9p = new Virtio9pProxy(
1508
+ settings.proxy9p,
1509
+ this,
1510
+ )
1511
+ }
1512
+ if (settings.virtio_console) {
1513
+ this.devices.virtio_console = new VirtioConsole(
1514
+ this,
1515
+ device_bus,
1516
+ )
1517
+ }
1518
+ if (settings.virtio_balloon) {
1519
+ this.devices.virtio_balloon = new VirtioBalloon(
1520
+ this,
1521
+ device_bus,
1522
+ )
1523
+ }
1524
+
1525
+ this.devices.sb16 = new SB16(this, device_bus)
1526
+ }
1527
+
1528
+ if (settings.multiboot) {
1529
+ dbg_log('loading multiboot', LOG_CPU)
1530
+ const option_rom = this.load_multiboot_option_rom(
1531
+ settings.multiboot,
1532
+ settings.initrd,
1533
+ settings.cmdline,
1534
+ )
1535
+
1536
+ if (option_rom) {
1537
+ if (this.bios.main) {
1538
+ dbg_log('adding option rom for multiboot', LOG_CPU)
1539
+ this.option_roms.push(option_rom)
1540
+ } else {
1541
+ dbg_log('loaded multiboot without bios', LOG_CPU)
1542
+ this.reg32[REG_EAX] = this.io.port_read32(0xf4)
1543
+ }
1544
+ }
1545
+ }
1546
+
1547
+ this.debug_init()
1548
+ }
1549
+
1550
+ load_multiboot(buffer: ArrayBuffer): void {
1551
+ if (this.bios.main) {
1552
+ dbg_assert(false, 'load_multiboot not supported with BIOS')
1553
+ }
1554
+
1555
+ const option_rom = this.load_multiboot_option_rom(buffer, undefined, '')
1556
+ if (option_rom) {
1557
+ dbg_log('loaded multiboot', LOG_CPU)
1558
+ this.reg32[REG_EAX] = this.io.port_read32(0xf4)
1559
+ }
1560
+ }
1561
+
1562
+ load_multiboot_option_rom(
1563
+ buffer: ArrayBuffer,
1564
+ initrd: ArrayBuffer | undefined,
1565
+ cmdline: string | undefined,
1566
+ ): OptionRom | undefined {
1567
+ // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
1568
+
1569
+ dbg_log(
1570
+ 'Trying multiboot from buffer of size ' + buffer.byteLength,
1571
+ LOG_CPU,
1572
+ )
1573
+
1574
+ const ELF_MAGIC = 0x464c457f
1575
+ const MULTIBOOT_HEADER_MAGIC = 0x1badb002
1576
+ const MULTIBOOT_HEADER_MEMORY_INFO = 0x2
1577
+ const MULTIBOOT_HEADER_ADDRESS = 0x10000
1578
+ const MULTIBOOT_BOOTLOADER_MAGIC = 0x2badb002
1579
+ const MULTIBOOT_SEARCH_BYTES = 8192
1580
+ const MULTIBOOT_INFO_STRUCT_LEN = 116
1581
+ const MULTIBOOT_INFO_CMDLINE = 0x4
1582
+ const MULTIBOOT_INFO_MODS = 0x8
1583
+ const MULTIBOOT_INFO_MEM_MAP = 0x40
1584
+
1585
+ let buf32: Int32Array
1586
+ if (buffer.byteLength < MULTIBOOT_SEARCH_BYTES) {
1587
+ buf32 = new Int32Array(MULTIBOOT_SEARCH_BYTES / 4)
1588
+ new Uint8Array(buf32.buffer).set(new Uint8Array(buffer))
1589
+ } else {
1590
+ buf32 = new Int32Array(buffer, 0, MULTIBOOT_SEARCH_BYTES / 4)
1591
+ }
1592
+
1593
+ for (let offset = 0; offset < MULTIBOOT_SEARCH_BYTES; offset += 4) {
1594
+ let flags: number
1595
+ if (buf32[offset >> 2] === MULTIBOOT_HEADER_MAGIC) {
1596
+ flags = buf32[(offset + 4) >> 2]
1597
+ const checksum = buf32[(offset + 8) >> 2]
1598
+ const total = (MULTIBOOT_HEADER_MAGIC + flags + checksum) | 0
1599
+
1600
+ if (total) {
1601
+ dbg_log('Multiboot checksum check failed', LOG_CPU)
1602
+ continue
1603
+ }
1604
+ } else {
1605
+ continue
1606
+ }
1607
+
1608
+ dbg_log(
1609
+ 'Multiboot magic found, flags: ' + h(flags >>> 0, 8),
1610
+ LOG_CPU,
1611
+ )
1612
+ // bit 0 : load modules on page boundaries (may as well, if we load modules)
1613
+ // bit 1 : provide a memory map (which we always will)
1614
+ dbg_assert((flags & ~MULTIBOOT_HEADER_ADDRESS & ~3) === 0, 'TODO')
1615
+
1616
+ // do this in a io register hook, so it can happen after BIOS does its work
1617
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1618
+ const cpu = this
1619
+
1620
+ this.io.register_read(
1621
+ 0xf4,
1622
+ this,
1623
+ function () {
1624
+ return 0
1625
+ },
1626
+ function () {
1627
+ return 0
1628
+ },
1629
+ function () {
1630
+ // actually do the load and return the multiboot magic
1631
+ const multiboot_info_addr = 0x7c00
1632
+ let multiboot_data =
1633
+ multiboot_info_addr + MULTIBOOT_INFO_STRUCT_LEN
1634
+ let info = 0
1635
+
1636
+ // command line
1637
+ if (cmdline) {
1638
+ info |= MULTIBOOT_INFO_CMDLINE
1639
+
1640
+ cpu.write32(multiboot_info_addr + 16, multiboot_data)
1641
+
1642
+ cmdline += '\x00'
1643
+ const encoder = new TextEncoder()
1644
+ const cmdline_utf8 = encoder.encode(cmdline)
1645
+ cpu.write_blob(cmdline_utf8, multiboot_data)
1646
+ multiboot_data += cmdline_utf8.length
1647
+ }
1648
+
1649
+ // memory map
1650
+ if (flags & MULTIBOOT_HEADER_MEMORY_INFO) {
1651
+ info |= MULTIBOOT_INFO_MEM_MAP
1652
+ let multiboot_mmap_count = 0
1653
+ cpu.write32(multiboot_info_addr + 44, 0)
1654
+ cpu.write32(multiboot_info_addr + 48, multiboot_data)
1655
+
1656
+ // Create a memory map for the multiboot kernel
1657
+ // does not exclude traditional bios exclusions
1658
+ let start = 0
1659
+ let was_memory = false
1660
+ for (
1661
+ let addr = 0;
1662
+ addr < MMAP_MAX;
1663
+ addr += MMAP_BLOCK_SIZE
1664
+ ) {
1665
+ if (
1666
+ was_memory &&
1667
+ cpu.memory_map_read8[
1668
+ addr >>> MMAP_BLOCK_BITS
1669
+ ] !== undefined
1670
+ ) {
1671
+ cpu.write32(multiboot_data, 20) // size
1672
+ cpu.write32(multiboot_data + 4, start) //addr (64-bit)
1673
+ cpu.write32(multiboot_data + 8, 0)
1674
+ cpu.write32(multiboot_data + 12, addr - start) // len (64-bit)
1675
+ cpu.write32(multiboot_data + 16, 0)
1676
+ cpu.write32(multiboot_data + 20, 1) // type (MULTIBOOT_MEMORY_AVAILABLE)
1677
+ multiboot_data += 24
1678
+ multiboot_mmap_count += 24
1679
+ was_memory = false
1680
+ } else if (
1681
+ !was_memory &&
1682
+ cpu.memory_map_read8[
1683
+ addr >>> MMAP_BLOCK_BITS
1684
+ ] === undefined
1685
+ ) {
1686
+ start = addr
1687
+ was_memory = true
1688
+ }
1689
+ }
1690
+ dbg_assert(
1691
+ !was_memory,
1692
+ "top of 4GB shouldn't have memory",
1693
+ )
1694
+ cpu.write32(
1695
+ multiboot_info_addr + 44,
1696
+ multiboot_mmap_count,
1697
+ )
1698
+ }
1699
+
1700
+ let entrypoint = 0
1701
+ let top_of_load = 0
1702
+
1703
+ if (flags & MULTIBOOT_HEADER_ADDRESS) {
1704
+ dbg_log(
1705
+ 'Multiboot specifies its own address table',
1706
+ LOG_CPU,
1707
+ )
1708
+
1709
+ const header_addr = buf32[(offset + 12) >> 2]
1710
+ const load_addr = buf32[(offset + 16) >> 2]
1711
+ const load_end_addr = buf32[(offset + 20) >> 2]
1712
+ const bss_end_addr = buf32[(offset + 24) >> 2]
1713
+ const entry_addr = buf32[(offset + 28) >> 2]
1714
+
1715
+ dbg_log(
1716
+ 'header=' +
1717
+ h(header_addr, 8) +
1718
+ ' load=' +
1719
+ h(load_addr, 8) +
1720
+ ' load_end=' +
1721
+ h(load_end_addr, 8) +
1722
+ ' bss_end=' +
1723
+ h(bss_end_addr, 8) +
1724
+ ' entry=' +
1725
+ h(entry_addr, 8),
1726
+ )
1727
+
1728
+ dbg_assert(load_addr <= header_addr)
1729
+
1730
+ const file_start = offset - (header_addr - load_addr)
1731
+
1732
+ let length: number | undefined
1733
+ if (load_end_addr === 0) {
1734
+ length = undefined
1735
+ } else {
1736
+ dbg_assert(load_end_addr >= load_addr)
1737
+ length = load_end_addr - load_addr
1738
+ }
1739
+
1740
+ const blob = new Uint8Array(buffer, file_start, length)
1741
+ cpu.write_blob(blob, load_addr)
1742
+
1743
+ entrypoint = entry_addr | 0
1744
+ top_of_load = Math.max(load_end_addr, bss_end_addr)
1745
+ } else if (buf32[0] === ELF_MAGIC) {
1746
+ dbg_log('Multiboot image is in elf format', LOG_CPU)
1747
+
1748
+ const elf = read_elf(buffer)
1749
+
1750
+ entrypoint = elf.header.entry
1751
+
1752
+ for (const program of elf.program_headers) {
1753
+ if (program.type === 0) {
1754
+ // null
1755
+ } else if (program.type === 1) {
1756
+ // load
1757
+
1758
+ dbg_assert(program.filesz <= program.memsz)
1759
+
1760
+ if (
1761
+ program.paddr + program.memsz <
1762
+ cpu.memory_size[0]
1763
+ ) {
1764
+ if (
1765
+ program.filesz
1766
+ ) // offset might be outside of buffer if filesz is 0
1767
+ {
1768
+ const blob = new Uint8Array(
1769
+ buffer,
1770
+ program.offset,
1771
+ program.filesz,
1772
+ )
1773
+ cpu.write_blob(blob, program.paddr)
1774
+ }
1775
+ top_of_load = Math.max(
1776
+ top_of_load,
1777
+ program.paddr + program.memsz,
1778
+ )
1779
+ dbg_log(
1780
+ 'prg load ' +
1781
+ program.paddr +
1782
+ ' to ' +
1783
+ (program.paddr + program.memsz),
1784
+ LOG_CPU,
1785
+ )
1786
+
1787
+ // Since multiboot specifies that paging is disabled, we load to the physical address;
1788
+ // but the entry point is specified in virtual addresses so adjust the entrypoint if needed
1789
+
1790
+ if (
1791
+ entrypoint === elf.header.entry &&
1792
+ program.vaddr <= entrypoint &&
1793
+ program.vaddr + program.memsz >
1794
+ entrypoint
1795
+ ) {
1796
+ entrypoint =
1797
+ entrypoint -
1798
+ program.vaddr +
1799
+ program.paddr
1800
+ }
1801
+ } else {
1802
+ dbg_log(
1803
+ 'Warning: Skipped loading section, paddr=' +
1804
+ h(program.paddr) +
1805
+ ' memsz=' +
1806
+ program.memsz,
1807
+ LOG_CPU,
1808
+ )
1809
+ }
1810
+ } else if (
1811
+ program.type === 2 || // dynamic
1812
+ program.type === 3 || // interp
1813
+ program.type === 4 || // note
1814
+ program.type === 6 || // phdr
1815
+ program.type === 7 || // tls
1816
+ program.type === 0x6474e550 || // gnu_eh_frame
1817
+ program.type === 0x6474e551 || // gnu_stack
1818
+ program.type === 0x6474e552 || // gnu_relro
1819
+ program.type === 0x6474e553
1820
+ ) // gnu_property
1821
+ {
1822
+ dbg_log(
1823
+ 'skip load type ' +
1824
+ program.type +
1825
+ ' ' +
1826
+ program.paddr +
1827
+ ' to ' +
1828
+ (program.paddr + program.memsz),
1829
+ LOG_CPU,
1830
+ )
1831
+ // ignore for now
1832
+ } else {
1833
+ dbg_assert(
1834
+ false,
1835
+ 'unimplemented elf section type: ' +
1836
+ h(program.type),
1837
+ )
1838
+ }
1839
+ }
1840
+ } else {
1841
+ dbg_assert(false, 'Not a bootable multiboot format')
1842
+ }
1843
+
1844
+ if (initrd) {
1845
+ info |= MULTIBOOT_INFO_MODS
1846
+
1847
+ cpu.write32(multiboot_info_addr + 20, 1) // mods_count
1848
+ cpu.write32(multiboot_info_addr + 24, multiboot_data) // mods_addr;
1849
+
1850
+ let ramdisk_address = top_of_load
1851
+ if ((ramdisk_address & 4095) !== 0) {
1852
+ ramdisk_address = (ramdisk_address & ~4095) + 4096
1853
+ }
1854
+ dbg_log('ramdisk address ' + ramdisk_address)
1855
+ const ramdisk_top = ramdisk_address + initrd.byteLength
1856
+
1857
+ cpu.write32(multiboot_data, ramdisk_address) // mod_start
1858
+ cpu.write32(multiboot_data + 4, ramdisk_top) // mod_end
1859
+ cpu.write32(multiboot_data + 8, 0) // string
1860
+ cpu.write32(multiboot_data + 12, 0) // reserved
1861
+ multiboot_data += 16
1862
+
1863
+ dbg_assert(ramdisk_top < cpu.memory_size[0])
1864
+
1865
+ cpu.write_blob(new Uint8Array(initrd), ramdisk_address)
1866
+ }
1867
+
1868
+ cpu.write32(multiboot_info_addr, info)
1869
+
1870
+ // set state for multiboot
1871
+
1872
+ cpu.reg32[REG_EBX] = multiboot_info_addr
1873
+ cpu.cr[0] = 1
1874
+ cpu.protected_mode[0] = +true
1875
+ cpu.flags[0] = FLAGS_DEFAULT
1876
+ cpu.is_32[0] = +true
1877
+ cpu.stack_size_32[0] = +true
1878
+
1879
+ for (let i = 0; i < 6; i++) {
1880
+ cpu.segment_is_null[i] = 0
1881
+ cpu.segment_offsets[i] = 0
1882
+ cpu.segment_limits[i] = 0xffffffff
1883
+ // cpu.segment_access_bytes[i]
1884
+ // Value doesn't matter, OS isn't allowed to reload without setting
1885
+ // up a proper GDT
1886
+ cpu.sreg[i] = 0xb002
1887
+ }
1888
+ cpu.instruction_pointer[0] =
1889
+ (cpu.get_seg_cs() + entrypoint) | 0
1890
+ cpu.update_state_flags()
1891
+ dbg_log('Starting multiboot kernel at:', LOG_CPU)
1892
+ cpu.dump_state()
1893
+ cpu.dump_regs_short()
1894
+
1895
+ return MULTIBOOT_BOOTLOADER_MAGIC
1896
+ },
1897
+ )
1898
+
1899
+ // only for kvm-unit-test
1900
+ this.io.register_write_consecutive(
1901
+ 0xf4,
1902
+ this,
1903
+ function (value: number) {
1904
+ console.log('Test exited with code ' + h(value, 2))
1905
+ throw 'HALT'
1906
+ },
1907
+ function () {},
1908
+ function () {},
1909
+ function () {},
1910
+ )
1911
+
1912
+ // only for kvm-unit-test
1913
+ for (let i = 0; i <= 0xf; i++) {
1914
+ function handle_write(this: CPU, value: number) {
1915
+ dbg_log(
1916
+ 'kvm-unit-test: Set irq ' + h(i) + ' to ' + h(value, 2),
1917
+ )
1918
+ if (value) {
1919
+ this.device_raise_irq(i)
1920
+ } else {
1921
+ this.device_lower_irq(i)
1922
+ }
1923
+ }
1924
+
1925
+ this.io.register_write(
1926
+ 0x2000 + i,
1927
+ this,
1928
+ handle_write,
1929
+ handle_write,
1930
+ handle_write,
1931
+ )
1932
+ }
1933
+
1934
+ // This rom will be executed by seabios after its initialisation
1935
+ // It sets up the multiboot environment.
1936
+ const SIZE = 0x200
1937
+
1938
+ const data8 = new Uint8Array(SIZE)
1939
+ const data16 = new Uint16Array(data8.buffer)
1940
+
1941
+ data16[0] = 0xaa55
1942
+ data8[2] = SIZE / 0x200
1943
+ let i = 3
1944
+ // trigger load
1945
+ data8[i++] = 0x66 // in 0xF4
1946
+ data8[i++] = 0xe5
1947
+ data8[i++] = 0xf4
1948
+
1949
+ dbg_assert(i < SIZE)
1950
+
1951
+ const checksum_index = i
1952
+ data8[checksum_index] = 0
1953
+
1954
+ let rom_checksum = 0
1955
+
1956
+ for (let i = 0; i < data8.length; i++) {
1957
+ rom_checksum += data8[i]
1958
+ }
1959
+
1960
+ data8[checksum_index] = -rom_checksum
1961
+
1962
+ return {
1963
+ name: 'genroms/multiboot.bin',
1964
+ data: data8,
1965
+ }
1966
+ }
1967
+ dbg_log('Multiboot header not found', LOG_CPU)
1968
+ return undefined
1969
+ }
1970
+
1971
+ fill_cmos(rtc: RTC, settings: any): void {
1972
+ const boot_order = settings.boot_order || BOOT_ORDER_CD_FIRST
1973
+
1974
+ // Used by seabios to determine the boot order
1975
+ // Nibble
1976
+ // 1: FloppyPrio
1977
+ // 2: HDPrio
1978
+ // 3: CDPrio
1979
+ // 4: BEVPrio
1980
+ // bootflag 1, high nibble, lowest priority
1981
+ // Low nibble: Disable floppy signature check (1)
1982
+ rtc.cmos_write(CMOS_BIOS_BOOTFLAG1, 1 | ((boot_order >> 4) & 0xf0))
1983
+
1984
+ // bootflag 2, both nibbles, high and middle priority
1985
+ rtc.cmos_write(CMOS_BIOS_BOOTFLAG2, boot_order & 0xff)
1986
+
1987
+ // 640k or less if less memory is used
1988
+ rtc.cmos_write(CMOS_MEM_BASE_LOW, 640 & 0xff)
1989
+ rtc.cmos_write(CMOS_MEM_BASE_HIGH, 640 >> 8)
1990
+
1991
+ let memory_above_1m = 0 // in k
1992
+ if (this.memory_size[0] >= 1024 * 1024) {
1993
+ memory_above_1m = (this.memory_size[0] - 1024 * 1024) >> 10
1994
+ memory_above_1m = Math.min(memory_above_1m, 0xffff)
1995
+ }
1996
+
1997
+ rtc.cmos_write(CMOS_MEM_OLD_EXT_LOW, memory_above_1m & 0xff)
1998
+ rtc.cmos_write(CMOS_MEM_OLD_EXT_HIGH, (memory_above_1m >> 8) & 0xff)
1999
+ rtc.cmos_write(CMOS_MEM_EXTMEM_LOW, memory_above_1m & 0xff)
2000
+ rtc.cmos_write(CMOS_MEM_EXTMEM_HIGH, (memory_above_1m >> 8) & 0xff)
2001
+
2002
+ let memory_above_16m = 0 // in 64k blocks
2003
+ if (this.memory_size[0] >= 16 * 1024 * 1024) {
2004
+ memory_above_16m = (this.memory_size[0] - 16 * 1024 * 1024) >> 16
2005
+ memory_above_16m = Math.min(memory_above_16m, 0xffff)
2006
+ }
2007
+ rtc.cmos_write(CMOS_MEM_EXTMEM2_LOW, memory_above_16m & 0xff)
2008
+ rtc.cmos_write(CMOS_MEM_EXTMEM2_HIGH, (memory_above_16m >> 8) & 0xff)
2009
+
2010
+ // memory above 4G (not supported by this emulator)
2011
+ rtc.cmos_write(CMOS_MEM_HIGHMEM_LOW, 0)
2012
+ rtc.cmos_write(CMOS_MEM_HIGHMEM_MID, 0)
2013
+ rtc.cmos_write(CMOS_MEM_HIGHMEM_HIGH, 0)
2014
+
2015
+ rtc.cmos_write(CMOS_EQUIPMENT_INFO, 0x2f)
2016
+
2017
+ rtc.cmos_write(CMOS_BIOS_SMP_COUNT, 0)
2018
+
2019
+ // Used by bochs BIOS to skip the boot menu delay.
2020
+ if (settings.fastboot) rtc.cmos_write(0x3f, 0x01)
2021
+ }
2022
+
2023
+ load_bios(): void {
2024
+ const bios = this.bios.main
2025
+ const vga_bios = this.bios.vga
2026
+
2027
+ if (!bios) {
2028
+ dbg_log('Warning: No BIOS')
2029
+ return
2030
+ }
2031
+
2032
+ dbg_assert(bios instanceof ArrayBuffer)
2033
+
2034
+ // load bios
2035
+ const data = new Uint8Array(bios),
2036
+ start = 0x100000 - bios.byteLength
2037
+
2038
+ this.write_blob(data, start)
2039
+
2040
+ if (vga_bios) {
2041
+ dbg_assert(vga_bios instanceof ArrayBuffer)
2042
+
2043
+ // load vga bios
2044
+ const vga_bios8 = new Uint8Array(vga_bios)
2045
+
2046
+ // older versions of seabios
2047
+ this.write_blob(vga_bios8, 0xc0000)
2048
+
2049
+ // newer versions of seabios (needs to match pci rom address, see vga.js)
2050
+ this.io.mmap_register(
2051
+ 0xfeb00000,
2052
+ 0x100000,
2053
+ function (addr: number) {
2054
+ addr = (addr - 0xfeb00000) | 0
2055
+ if (addr < vga_bios8.length) {
2056
+ return vga_bios8[addr]
2057
+ } else {
2058
+ return 0
2059
+ }
2060
+ },
2061
+ function (_addr: number, _value: number) {
2062
+ dbg_assert(false, 'Unexpected write to VGA rom')
2063
+ },
2064
+ )
2065
+ } else {
2066
+ dbg_log('Warning: No VGA BIOS')
2067
+ }
2068
+
2069
+ // seabios expects the bios to be mapped to 0xFFF00000 also
2070
+ this.io.mmap_register(
2071
+ 0xfff00000,
2072
+ 0x100000,
2073
+ (addr: number) => {
2074
+ addr &= 0xfffff
2075
+ return this.mem8[addr]
2076
+ },
2077
+ (addr: number, value: number) => {
2078
+ addr &= 0xfffff
2079
+ this.mem8[addr] = value
2080
+ },
2081
+ )
2082
+ }
2083
+
2084
+ codegen_finalize(
2085
+ wasm_table_index: number,
2086
+ start: number,
2087
+ state_flags: number,
2088
+ ptr: number,
2089
+ len: number,
2090
+ ): void {
2091
+ ptr >>>= 0
2092
+ len >>>= 0
2093
+
2094
+ dbg_assert(wasm_table_index >= 0 && wasm_table_index < WASM_TABLE_SIZE)
2095
+
2096
+ const code = new Uint8Array(this.wasm_memory.buffer, ptr, len)
2097
+
2098
+ if (DEBUG) {
2099
+ if (DUMP_GENERATED_WASM && !this.seen_code[start]) {
2100
+ this.dump_wasm(code)
2101
+
2102
+ const DUMP_ASSEMBLY = false
2103
+
2104
+ if (DUMP_ASSEMBLY) {
2105
+ let end = 0
2106
+
2107
+ if ((start ^ end) & ~0xfff) {
2108
+ dbg_log(
2109
+ 'truncated disassembly start=' +
2110
+ h(start >>> 0) +
2111
+ ' end=' +
2112
+ h(end >>> 0),
2113
+ )
2114
+ end = (start | 0xfff) + 1 // until the end of the page
2115
+ }
2116
+
2117
+ dbg_assert(end >= start)
2118
+
2119
+ const buffer = new Uint8Array(end - start)
2120
+
2121
+ for (let i = start; i < end; i++) {
2122
+ buffer[i - start] = this.read8(i)
2123
+ }
2124
+
2125
+ this.debug_dump_code(this.is_32[0] ? 1 : 0, buffer, start)
2126
+ }
2127
+ }
2128
+
2129
+ this.seen_code[start] = (this.seen_code[start] || 0) + 1
2130
+
2131
+ if (this.test_hook_did_generate_wasm) {
2132
+ this.test_hook_did_generate_wasm(code)
2133
+ }
2134
+ }
2135
+
2136
+ const SYNC_COMPILATION = false
2137
+
2138
+ if (SYNC_COMPILATION) {
2139
+ const module = new WebAssembly.Module(code)
2140
+ const result = new WebAssembly.Instance(module, {
2141
+ e: this.jit_imports,
2142
+ })
2143
+ const f = result.exports['f']
2144
+
2145
+ this.wm.wasm_table.set(wasm_table_index + WASM_TABLE_OFFSET, f)
2146
+ this.codegen_finalize_finished(wasm_table_index, start, state_flags)
2147
+
2148
+ if (this.test_hook_did_finalize_wasm) {
2149
+ this.test_hook_did_finalize_wasm(code)
2150
+ }
2151
+
2152
+ return
2153
+ }
2154
+
2155
+ const result = WebAssembly.instantiate(code, {
2156
+ e: this.jit_imports,
2157
+ }).then((result) => {
2158
+ const f = result.instance.exports['f']
2159
+
2160
+ this.wm.wasm_table.set(wasm_table_index + WASM_TABLE_OFFSET, f)
2161
+ this.codegen_finalize_finished(wasm_table_index, start, state_flags)
2162
+
2163
+ if (this.test_hook_did_finalize_wasm) {
2164
+ this.test_hook_did_finalize_wasm(code)
2165
+ }
2166
+ })
2167
+
2168
+ if (DEBUG) {
2169
+ result.catch((e) => {
2170
+ console.log(e)
2171
+ throw e
2172
+ })
2173
+ }
2174
+ }
2175
+
2176
+ log_uncompiled_code(start: number, end: number): void {
2177
+ if (!DEBUG || !DUMP_UNCOMPILED_ASSEMBLY) {
2178
+ return
2179
+ }
2180
+
2181
+ if ((this.seen_code_uncompiled[start] || 0) < 100) {
2182
+ this.seen_code_uncompiled[start] =
2183
+ (this.seen_code_uncompiled[start] || 0) + 1
2184
+
2185
+ end += 8 // final jump is not included
2186
+
2187
+ if ((start ^ end) & ~0xfff) {
2188
+ dbg_log(
2189
+ 'truncated disassembly start=' +
2190
+ h(start >>> 0) +
2191
+ ' end=' +
2192
+ h(end >>> 0),
2193
+ )
2194
+ end = (start | 0xfff) + 1 // until the end of the page
2195
+ }
2196
+
2197
+ if (end < start) end = start
2198
+
2199
+ dbg_assert(end >= start)
2200
+
2201
+ const buffer = new Uint8Array(end - start)
2202
+
2203
+ for (let i = start; i < end; i++) {
2204
+ buffer[i - start] = this.read8(i)
2205
+ }
2206
+
2207
+ dbg_log('Uncompiled code:')
2208
+ this.debug_dump_code(this.is_32[0] ? 1 : 0, buffer, start)
2209
+ }
2210
+ }
2211
+
2212
+ dump_function_code(block_ptr: number, count: number): void {
2213
+ if (!DEBUG || !DUMP_GENERATED_WASM) {
2214
+ return
2215
+ }
2216
+
2217
+ const SIZEOF_BASIC_BLOCK_IN_DWORDS = 7
2218
+
2219
+ const mem32 = new Int32Array(this.wasm_memory.buffer)
2220
+
2221
+ dbg_assert((block_ptr & 3) === 0)
2222
+
2223
+ const is_32 = this.is_32[0]
2224
+
2225
+ for (let i = 0; i < count; i++) {
2226
+ const struct_start =
2227
+ (block_ptr >> 2) + i * SIZEOF_BASIC_BLOCK_IN_DWORDS
2228
+ const start = mem32[struct_start + 0]
2229
+ const end = mem32[struct_start + 1]
2230
+ const is_entry_block = mem32[struct_start + 6] & 0xff00
2231
+
2232
+ const buffer = new Uint8Array(end - start)
2233
+
2234
+ for (let j = start; j < end; j++) {
2235
+ buffer[j - start] = this.read8(
2236
+ this.translate_address_system_read(j),
2237
+ )
2238
+ }
2239
+
2240
+ dbg_log('---' + (is_entry_block ? ' entry' : ''))
2241
+ this.debug_dump_code(is_32 ? 1 : 0, buffer, start)
2242
+ }
2243
+ }
2244
+
2245
+ run_hardware_timers(acpi_enabled: number, now: number): number {
2246
+ const pit_time = this.devices.pit.timer(now, false)
2247
+ const rtc_time = this.devices.rtc.timer(now, false)
2248
+
2249
+ let acpi_time = 100
2250
+ let apic_time = 100
2251
+ if (acpi_enabled) {
2252
+ acpi_time = this.devices.acpi.timer(now)
2253
+ apic_time = this.apic_timer(now)
2254
+ }
2255
+
2256
+ return Math.min(pit_time, rtc_time, acpi_time, apic_time)
2257
+ }
2258
+
2259
+ debug_init(): void {
2260
+ if (!DEBUG) return
2261
+
2262
+ if (this.io) {
2263
+ // write seabios debug output to console
2264
+ let seabios_debug = ''
2265
+
2266
+ function handle(_out_byte: number) {
2267
+ if (_out_byte === 10) {
2268
+ dbg_log(seabios_debug, LOG_BIOS)
2269
+ seabios_debug = ''
2270
+ } else {
2271
+ seabios_debug += String.fromCharCode(_out_byte)
2272
+ }
2273
+ }
2274
+
2275
+ this.io.register_write(0x402, this, handle) // seabios
2276
+ this.io.register_write(0x500, this, handle) // vgabios
2277
+ }
2278
+ }
2279
+
2280
+ dump_stack(start?: number, end?: number): void {
2281
+ if (!DEBUG) return
2282
+
2283
+ const esp = this.reg32[REG_ESP]
2284
+ dbg_log('========= STACK ==========')
2285
+
2286
+ if (end === undefined || end >= start!) {
2287
+ start = 5
2288
+ end = -5
2289
+ }
2290
+
2291
+ for (let i = start!; i > end; i--) {
2292
+ let line = ' '
2293
+
2294
+ if (!i) line = '=> '
2295
+
2296
+ line += h(i, 2) + ' | '
2297
+
2298
+ dbg_log(
2299
+ line +
2300
+ h(esp + 4 * i, 8) +
2301
+ ' | ' +
2302
+ h(this.read32s(esp + 4 * i) >>> 0),
2303
+ )
2304
+ }
2305
+ }
2306
+
2307
+ debug_get_state(where?: string): string | undefined {
2308
+ if (!DEBUG) return undefined
2309
+
2310
+ const mode = this.protected_mode[0] ? 'prot' : 'real'
2311
+ const _vm = this.flags[0] & FLAG_VM ? 1 : 0
2312
+ const flags = this.get_eflags()
2313
+ const iopl = this.getiopl()
2314
+ const cpl = this.cpl[0]
2315
+ const cs_eip =
2316
+ h(this.sreg[REG_CS], 4) + ':' + h(this.get_real_eip() >>> 0, 8)
2317
+ const ss_esp =
2318
+ h(this.sreg[REG_SS], 4) + ':' + h(this.reg32[REG_ES] >>> 0, 8)
2319
+ const op_size = this.is_32[0] ? '32' : '16'
2320
+ const if_ = this.flags[0] & FLAG_INTERRUPT ? 1 : 0
2321
+
2322
+ const flag_names: Record<number, string> = {
2323
+ [FLAG_CARRY]: 'c',
2324
+ [FLAG_PARITY]: 'p',
2325
+ [FLAG_ADJUST]: 'a',
2326
+ [FLAG_ZERO]: 'z',
2327
+ [FLAG_SIGN]: 's',
2328
+ [FLAG_TRAP]: 't',
2329
+ [FLAG_INTERRUPT]: 'i',
2330
+ [FLAG_DIRECTION]: 'd',
2331
+ [FLAG_OVERFLOW]: 'o',
2332
+ }
2333
+ let flag_string = ''
2334
+
2335
+ for (let i = 0; i < 16; i++) {
2336
+ if (flag_names[1 << i]) {
2337
+ if (flags & (1 << i)) {
2338
+ flag_string += flag_names[1 << i]
2339
+ } else {
2340
+ flag_string += ' '
2341
+ }
2342
+ }
2343
+ }
2344
+
2345
+ return (
2346
+ 'mode=' +
2347
+ mode +
2348
+ '/' +
2349
+ op_size +
2350
+ ' paging=' +
2351
+ +((this.cr[0] & CR0_PG) !== 0) +
2352
+ ' pae=' +
2353
+ +((this.cr[4] & CR4_PAE) !== 0) +
2354
+ ' iopl=' +
2355
+ iopl +
2356
+ ' cpl=' +
2357
+ cpl +
2358
+ ' if=' +
2359
+ if_ +
2360
+ ' cs:eip=' +
2361
+ cs_eip +
2362
+ ' cs_off=' +
2363
+ h(this.get_seg_cs() >>> 0, 8) +
2364
+ ' flgs=' +
2365
+ h(this.get_eflags() >>> 0, 6) +
2366
+ ' (' +
2367
+ flag_string +
2368
+ ')' +
2369
+ ' ss:esp=' +
2370
+ ss_esp +
2371
+ ' ssize=' +
2372
+ +this.stack_size_32[0] +
2373
+ (where ? ' in ' + where : '')
2374
+ )
2375
+ }
2376
+
2377
+ dump_state(where?: string): void {
2378
+ if (!DEBUG) return
2379
+
2380
+ dbg_log(this.debug_get_state(where)!, LOG_CPU)
2381
+ }
2382
+
2383
+ get_regs_short(): [string, string] | undefined {
2384
+ if (!DEBUG) return undefined
2385
+
2386
+ const r32: Record<string, number> = {
2387
+ eax: REG_EAX,
2388
+ ecx: REG_ECX,
2389
+ edx: REG_EDX,
2390
+ ebx: REG_EBX,
2391
+ esp: REG_ESP,
2392
+ ebp: REG_EBP,
2393
+ esi: REG_ESI,
2394
+ edi: REG_EDI,
2395
+ }
2396
+ const r32_names = [
2397
+ 'eax',
2398
+ 'ecx',
2399
+ 'edx',
2400
+ 'ebx',
2401
+ 'esp',
2402
+ 'ebp',
2403
+ 'esi',
2404
+ 'edi',
2405
+ ]
2406
+ let line1 = ''
2407
+ let line2 = ''
2408
+
2409
+ for (let i = 0; i < 4; i++) {
2410
+ line1 +=
2411
+ r32_names[i] +
2412
+ '=' +
2413
+ h(this.reg32[r32[r32_names[i]]] >>> 0, 8) +
2414
+ ' '
2415
+ line2 +=
2416
+ r32_names[i + 4] +
2417
+ '=' +
2418
+ h(this.reg32[r32[r32_names[i + 4]]] >>> 0, 8) +
2419
+ ' '
2420
+ }
2421
+
2422
+ line1 +=
2423
+ ' ds=' +
2424
+ h(this.sreg[REG_DS], 4) +
2425
+ ' es=' +
2426
+ h(this.sreg[REG_ES], 4) +
2427
+ ' fs=' +
2428
+ h(this.sreg[REG_FS], 4)
2429
+ line2 +=
2430
+ ' gs=' +
2431
+ h(this.sreg[REG_GS], 4) +
2432
+ ' cs=' +
2433
+ h(this.sreg[REG_CS], 4) +
2434
+ ' ss=' +
2435
+ h(this.sreg[REG_SS], 4)
2436
+
2437
+ return [line1, line2]
2438
+ }
2439
+
2440
+ dump_regs_short(): void {
2441
+ if (!DEBUG) return
2442
+
2443
+ const lines = this.get_regs_short()!
2444
+
2445
+ dbg_log(lines[0], LOG_CPU)
2446
+ dbg_log(lines[1], LOG_CPU)
2447
+ }
2448
+
2449
+ dump_gdt_ldt(): void {
2450
+ if (!DEBUG) return
2451
+
2452
+ dbg_log('gdt: (len = ' + h(this.gdtr_size[0]) + ')')
2453
+ const dump_table = (addr: number, size: number) => {
2454
+ for (let i = 0; i < size; i += 8, addr += 8) {
2455
+ const base =
2456
+ this.read16(addr + 2) |
2457
+ (this.read8(addr + 4) << 16) |
2458
+ (this.read8(addr + 7) << 24)
2459
+ let limit =
2460
+ this.read16(addr) | ((this.read8(addr + 6) & 0xf) << 16)
2461
+ const access = this.read8(addr + 5)
2462
+ const flags = this.read8(addr + 6) >> 4
2463
+ let flags_str = ''
2464
+ const dpl = (access >> 5) & 3
2465
+
2466
+ if (!(access & 128)) {
2467
+ // present bit not set
2468
+ //continue;
2469
+ flags_str += 'NP '
2470
+ } else {
2471
+ flags_str += ' P '
2472
+ }
2473
+
2474
+ if (access & 16) {
2475
+ if (flags & 4) {
2476
+ flags_str += '32b '
2477
+ } else {
2478
+ flags_str += '16b '
2479
+ }
2480
+
2481
+ if (access & 8) {
2482
+ // executable
2483
+ flags_str += 'X '
2484
+
2485
+ if (access & 4) {
2486
+ flags_str += 'C '
2487
+ }
2488
+ } else {
2489
+ // data
2490
+ flags_str += 'R '
2491
+ }
2492
+
2493
+ flags_str += 'RW '
2494
+ } else {
2495
+ // system
2496
+ flags_str += 'sys: ' + h(access & 15)
2497
+ }
2498
+
2499
+ if (flags & 8) {
2500
+ limit = (limit << 12) | 0xfff
2501
+ }
2502
+
2503
+ dbg_log(
2504
+ h(i & ~7, 4) +
2505
+ ' ' +
2506
+ h(base >>> 0, 8) +
2507
+ ' (' +
2508
+ h(limit >>> 0, 8) +
2509
+ ' bytes) ' +
2510
+ flags_str +
2511
+ '; dpl = ' +
2512
+ dpl +
2513
+ ', a = ' +
2514
+ access.toString(2) +
2515
+ ', f = ' +
2516
+ flags.toString(2),
2517
+ )
2518
+ }
2519
+ }
2520
+
2521
+ dump_table(
2522
+ this.translate_address_system_read(this.gdtr_offset[0]),
2523
+ this.gdtr_size[0],
2524
+ )
2525
+
2526
+ dbg_log('\nldt: (len = ' + h(this.segment_limits[REG_LDTR]) + ')')
2527
+ dump_table(
2528
+ this.translate_address_system_read(this.segment_offsets[REG_LDTR]),
2529
+ this.segment_limits[REG_LDTR],
2530
+ )
2531
+ }
2532
+
2533
+ dump_idt(): void {
2534
+ if (!DEBUG) return
2535
+
2536
+ for (let i = 0; i < this.idtr_size[0]; i += 8) {
2537
+ const addr = this.translate_address_system_read(
2538
+ this.idtr_offset[0] + i,
2539
+ )
2540
+ const base = this.read16(addr) | (this.read16(addr + 6) << 16)
2541
+ const selector = this.read16(addr + 2)
2542
+ const type = this.read8(addr + 5)
2543
+ let line: string
2544
+ const dpl = (type >> 5) & 3
2545
+
2546
+ if ((type & 31) === 5) {
2547
+ line = 'task gate '
2548
+ } else if ((type & 31) === 14) {
2549
+ line = 'intr gate '
2550
+ } else if ((type & 31) === 15) {
2551
+ line = 'trap gate '
2552
+ } else {
2553
+ line = 'invalid '
2554
+ }
2555
+
2556
+ if (type & 128) {
2557
+ line += ' P'
2558
+ } else {
2559
+ // present bit not set
2560
+ //continue;
2561
+ line += 'NP'
2562
+ }
2563
+
2564
+ dbg_log(
2565
+ h(i >> 3, 4) +
2566
+ ' ' +
2567
+ h(base >>> 0, 8) +
2568
+ ', ' +
2569
+ h(selector, 4) +
2570
+ '; ' +
2571
+ line +
2572
+ '; dpl = ' +
2573
+ dpl +
2574
+ ', t = ' +
2575
+ type.toString(2),
2576
+ )
2577
+ }
2578
+ }
2579
+
2580
+ dump_page_structures(): void {
2581
+ const pae = !!(this.cr[4] & CR4_PAE)
2582
+ if (pae) {
2583
+ dbg_log('PAE enabled')
2584
+
2585
+ for (let i = 0; i < 4; i++) {
2586
+ const addr = this.cr[3] + 8 * i
2587
+ const dword = this.read32s(addr)
2588
+ if (dword & 1) {
2589
+ this.dump_page_directory(dword & 0xfffff000, true, i << 30)
2590
+ }
2591
+ }
2592
+ } else {
2593
+ dbg_log('PAE disabled')
2594
+ this.dump_page_directory(this.cr[3], false, 0)
2595
+ }
2596
+ }
2597
+
2598
+ // NOTE: PAE entries are 64-bits, we ignore the high half here.
2599
+ dump_page_directory(pd_addr: number, pae: boolean, start: number): void {
2600
+ if (!DEBUG) return
2601
+
2602
+ function load_page_entry(
2603
+ dword_entry: number,
2604
+ pae: boolean,
2605
+ is_directory: boolean,
2606
+ ):
2607
+ | false
2608
+ | {
2609
+ size: boolean
2610
+ global: boolean
2611
+ accessed: boolean
2612
+ dirty: boolean
2613
+ cache_disable: boolean
2614
+ user: boolean
2615
+ read_write: boolean
2616
+ address: number
2617
+ } {
2618
+ if (!DEBUG) return false
2619
+
2620
+ if (!(dword_entry & 1)) {
2621
+ // present bit not set
2622
+ return false
2623
+ }
2624
+
2625
+ const size = (dword_entry & 128) === 128
2626
+ let address: number
2627
+
2628
+ if (size && !is_directory) {
2629
+ address = dword_entry & (pae ? 0xffe00000 : 0xffc00000)
2630
+ } else {
2631
+ address = dword_entry & 0xfffff000
2632
+ }
2633
+
2634
+ return {
2635
+ size: size,
2636
+ global: (dword_entry & 256) === 256,
2637
+ accessed: (dword_entry & 0x20) === 0x20,
2638
+ dirty: (dword_entry & 0x40) === 0x40,
2639
+ cache_disable: (dword_entry & 16) === 16,
2640
+ user: (dword_entry & 4) === 4,
2641
+ read_write: (dword_entry & 2) === 2,
2642
+ address: address >>> 0,
2643
+ }
2644
+ }
2645
+
2646
+ const n = pae ? 512 : 1024
2647
+ const entry_size = pae ? 8 : 4
2648
+ const pd_shift = pae ? 21 : 22
2649
+
2650
+ for (let i = 0; i < n; i++) {
2651
+ const addr = pd_addr + i * entry_size
2652
+ let dword = this.read32s(addr)
2653
+ const entry = load_page_entry(dword, pae, true)
2654
+
2655
+ if (!entry) {
2656
+ continue
2657
+ }
2658
+
2659
+ let flags = ''
2660
+
2661
+ flags += entry.size ? 'S ' : ' '
2662
+ flags += entry.accessed ? 'A ' : ' '
2663
+ flags += entry.cache_disable ? 'Cd ' : ' '
2664
+ flags += entry.user ? 'U ' : ' '
2665
+ flags += entry.read_write ? 'Rw ' : ' '
2666
+
2667
+ if (entry.size) {
2668
+ dbg_log(
2669
+ '=== ' +
2670
+ h((start + (i << pd_shift)) >>> 0, 8) +
2671
+ ' -> ' +
2672
+ h(entry.address >>> 0, 8) +
2673
+ ' | ' +
2674
+ flags,
2675
+ )
2676
+ continue
2677
+ } else {
2678
+ dbg_log(
2679
+ '=== ' +
2680
+ h((start + (i << pd_shift)) >>> 0, 8) +
2681
+ ' | ' +
2682
+ flags,
2683
+ )
2684
+ }
2685
+
2686
+ for (let j = 0; j < n; j++) {
2687
+ const sub_addr = entry.address + j * entry_size
2688
+ dword = this.read32s(sub_addr)
2689
+
2690
+ const subentry = load_page_entry(dword, pae, false)
2691
+
2692
+ if (subentry) {
2693
+ flags = ''
2694
+
2695
+ flags += subentry.cache_disable ? 'Cd ' : ' '
2696
+ flags += subentry.user ? 'U ' : ' '
2697
+ flags += subentry.read_write ? 'Rw ' : ' '
2698
+ flags += subentry.global ? 'G ' : ' '
2699
+ flags += subentry.accessed ? 'A ' : ' '
2700
+ flags += subentry.dirty ? 'Di ' : ' '
2701
+
2702
+ dbg_log(
2703
+ '# ' +
2704
+ h(
2705
+ (start + ((i << pd_shift) | (j << 12))) >>> 0,
2706
+ 8,
2707
+ ) +
2708
+ ' -> ' +
2709
+ h(subentry.address, 8) +
2710
+ ' | ' +
2711
+ flags +
2712
+ ' (at ' +
2713
+ h(sub_addr, 8) +
2714
+ ')',
2715
+ )
2716
+ }
2717
+ }
2718
+ }
2719
+ }
2720
+
2721
+ get_memory_dump(start?: number, count?: number): ArrayBuffer | undefined {
2722
+ if (!DEBUG) return undefined
2723
+
2724
+ if (start === undefined) {
2725
+ start = 0
2726
+ count = this.memory_size[0]
2727
+ } else if (count === undefined) {
2728
+ count = start
2729
+ start = 0
2730
+ }
2731
+
2732
+ return this.mem8.slice(start, start + count!).buffer
2733
+ }
2734
+
2735
+ memory_hex_dump(addr: number, length?: number): void {
2736
+ if (!DEBUG) return
2737
+
2738
+ length = length || 4 * 0x10
2739
+ let line: string, byt: number
2740
+
2741
+ for (let i = 0; i < length >> 4; i++) {
2742
+ line = h(addr + (i << 4), 5) + ' '
2743
+
2744
+ for (let j = 0; j < 0x10; j++) {
2745
+ byt = this.read8(addr + (i << 4) + j)
2746
+ line += h(byt, 2) + ' '
2747
+ }
2748
+
2749
+ line += ' '
2750
+
2751
+ for (let j = 0; j < 0x10; j++) {
2752
+ byt = this.read8(addr + (i << 4) + j)
2753
+ line += byt < 33 || byt > 126 ? '.' : String.fromCharCode(byt)
2754
+ }
2755
+
2756
+ dbg_log(line)
2757
+ }
2758
+ }
2759
+
2760
+ used_memory_dump(): void {
2761
+ if (!DEBUG) return
2762
+
2763
+ const width = 0x80
2764
+ const height = 0x10
2765
+ const block_size = (this.memory_size[0] / width / height) | 0
2766
+ let row: string
2767
+
2768
+ for (let i = 0; i < height; i++) {
2769
+ row = h(i * width * block_size, 8) + ' | '
2770
+
2771
+ for (let j = 0; j < width; j++) {
2772
+ const used = this.mem32s[(i * width + j) * block_size] > 0
2773
+
2774
+ row += used ? 'X' : ' '
2775
+ }
2776
+
2777
+ dbg_log(row)
2778
+ }
2779
+ }
2780
+
2781
+ debug_interrupt(_interrupt_nr: number): void {
2782
+ // Debug interrupt handler (commented out implementations in original)
2783
+ }
2784
+
2785
+ debug_dump_code(
2786
+ is_32: number,
2787
+ buffer: Uint8Array | number[],
2788
+ start: number,
2789
+ ): void {
2790
+ if (!DEBUG) return
2791
+
2792
+ if (!this.capstone_decoder) {
2793
+ let cs = (globalThis as any).cs
2794
+
2795
+ if (typeof require === 'function') {
2796
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
2797
+ cs = require('./capstone-x86.min.js')
2798
+ }
2799
+
2800
+ if (cs === undefined) {
2801
+ dbg_log(
2802
+ 'Warning: Missing capstone library, disassembly not available',
2803
+ )
2804
+ return
2805
+ }
2806
+
2807
+ this.capstone_decoder = [
2808
+ new cs.Capstone(cs.ARCH_X86, cs.MODE_16),
2809
+ new cs.Capstone(cs.ARCH_X86, cs.MODE_32),
2810
+ ]
2811
+ }
2812
+
2813
+ if (buffer instanceof Array) {
2814
+ buffer = new Uint8Array(buffer)
2815
+ }
2816
+
2817
+ try {
2818
+ const instructions = this.capstone_decoder[+is_32].disasm(
2819
+ buffer,
2820
+ start,
2821
+ )
2822
+
2823
+ instructions.forEach(function (instr: any) {
2824
+ dbg_log(
2825
+ h(instr.address >>> 0) +
2826
+ ': ' +
2827
+ pads(
2828
+ instr.bytes
2829
+ .map((x: number) => h(x, 2).slice(-2))
2830
+ .join(' '),
2831
+ 20,
2832
+ ) +
2833
+ ' ' +
2834
+ instr.mnemonic +
2835
+ ' ' +
2836
+ instr.op_str,
2837
+ )
2838
+ })
2839
+ dbg_log('')
2840
+ } catch {
2841
+ dbg_log(
2842
+ 'Could not disassemble: ' +
2843
+ Array.from(buffer)
2844
+ .map((x) => h(x, 2))
2845
+ .join(' '),
2846
+ )
2847
+ }
2848
+ }
2849
+
2850
+ dump_wasm(buffer: Uint8Array): void {
2851
+ if (!DEBUG) return
2852
+
2853
+ if (this.wabt === undefined) {
2854
+ if (typeof require === 'function') {
2855
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
2856
+ this.wabt = require('./libwabt.cjs')
2857
+ } else {
2858
+ this.wabt = new (globalThis as any).WabtModule()
2859
+ }
2860
+
2861
+ if (this.wabt === undefined) {
2862
+ dbg_log('Warning: Missing libwabt, wasm dump not available')
2863
+ return
2864
+ }
2865
+ }
2866
+
2867
+ // Need to make a small copy otherwise libwabt goes nuts trying to copy
2868
+ // the whole underlying buffer
2869
+ buffer = buffer.slice()
2870
+
2871
+ let module: any
2872
+ try {
2873
+ module = this.wabt.readWasm(buffer, { readDebugNames: false })
2874
+ module.generateNames()
2875
+ module.applyNames()
2876
+ const result = module.toText({
2877
+ foldExprs: true,
2878
+ inlineExport: true,
2879
+ })
2880
+ dbg_log(result)
2881
+ } catch (e) {
2882
+ // @ts-expect-error Uint8Array is a valid BlobPart
2883
+ dump_file(buffer, 'failed.wasm')
2884
+ console.log(String(e))
2885
+ } finally {
2886
+ if (module) {
2887
+ module.destroy()
2888
+ }
2889
+ }
2890
+ }
2891
+ }