@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/iso9660.ts ADDED
@@ -0,0 +1,317 @@
1
+ // Source: https://wiki.osdev.org/ISO_9660
2
+
3
+ // Limitations:
4
+ // - can only generate iso files
5
+ // - only supports a single directory, no file system hierarchy
6
+ // - root directory entry is limited to 2 KiB (~42 files)
7
+ // - filenames are normalised to 8.3 length and [A-Z0-9_.]
8
+
9
+ import { dbg_assert } from './log.js'
10
+
11
+ const BLOCK_SIZE = 2 * 1024 // 0x800
12
+
13
+ const FILE_FLAGS_DIRECTORY = 1 << 1
14
+
15
+ interface ISOBuffer {
16
+ buffer: Uint8Array
17
+ offset: number
18
+ }
19
+
20
+ interface ISOFile {
21
+ name: string
22
+ contents: Uint8Array
23
+ }
24
+
25
+ interface ISOFileWithLBA {
26
+ name: string
27
+ contents: Uint8Array
28
+ lba: number
29
+ }
30
+
31
+ export function generate(files: ISOFile[]): Uint8Array {
32
+ const te = new TextEncoder()
33
+ const date = new Date()
34
+
35
+ const write8 = (b: ISOBuffer, v: number): void => {
36
+ b.buffer[b.offset++] = v
37
+ }
38
+ const write_le16 = (b: ISOBuffer, v: number): void => {
39
+ b.buffer[b.offset++] = v
40
+ b.buffer[b.offset++] = v >> 8
41
+ }
42
+ const write_le32 = (b: ISOBuffer, v: number): void => {
43
+ b.buffer[b.offset++] = v
44
+ b.buffer[b.offset++] = v >> 8
45
+ b.buffer[b.offset++] = v >> 16
46
+ b.buffer[b.offset++] = v >> 24
47
+ }
48
+ const write_be16 = (b: ISOBuffer, v: number): void => {
49
+ b.buffer[b.offset++] = v >> 8
50
+ b.buffer[b.offset++] = v
51
+ }
52
+ const write_be32 = (b: ISOBuffer, v: number): void => {
53
+ b.buffer[b.offset++] = v >> 24
54
+ b.buffer[b.offset++] = v >> 16
55
+ b.buffer[b.offset++] = v >> 8
56
+ b.buffer[b.offset++] = v
57
+ }
58
+ const write_lebe16 = (b: ISOBuffer, v: number): void => {
59
+ write_le16(b, v)
60
+ write_be16(b, v)
61
+ }
62
+ const write_lebe32 = (b: ISOBuffer, v: number): void => {
63
+ write_le32(b, v)
64
+ write_be32(b, v)
65
+ }
66
+ const fill = (b: ISOBuffer, len: number, v: number): void => {
67
+ b.buffer.fill(v, b.offset, (b.offset += len))
68
+ }
69
+ const write_ascii = (b: ISOBuffer, v: string): void => {
70
+ b.offset += te.encodeInto(v, b.buffer.subarray(b.offset)).written
71
+ }
72
+ const write_padded_ascii = (b: ISOBuffer, len: number, v: string): void => {
73
+ b.offset += te.encodeInto(
74
+ v.padEnd(len),
75
+ b.buffer.subarray(b.offset),
76
+ ).written
77
+ }
78
+ const write_dummy_date_ascii = (b: ISOBuffer): void => {
79
+ fill(b, 16, 0x20)
80
+ write8(b, 0)
81
+ }
82
+ const write_date_compact = (b: ISOBuffer): void => {
83
+ write8(b, date.getUTCFullYear() - 1900)
84
+ write8(b, 1 + date.getUTCMonth())
85
+ write8(b, date.getUTCDate())
86
+ write8(b, date.getUTCHours())
87
+ write8(b, date.getUTCMinutes())
88
+ write8(b, date.getUTCSeconds())
89
+ write8(b, 0)
90
+ }
91
+ const skip = (b: ISOBuffer, len: number): void => {
92
+ b.offset += len
93
+ }
94
+
95
+ const write_record = (
96
+ b: ISOBuffer,
97
+ name: string,
98
+ flags: number,
99
+ is_special: boolean,
100
+ lba: number,
101
+ len: number,
102
+ ): void => {
103
+ if (!is_special) name = sanitise_filename(name) + ';1'
104
+ // write name first and get its length
105
+ const START = buffer.offset
106
+ const NAME_OFFSET = 33
107
+ const name_len = te.encodeInto(
108
+ name,
109
+ b.buffer.subarray(b.offset + NAME_OFFSET),
110
+ ).written
111
+ const pad = name_len & 1 ? 0 : 1
112
+ const len_field = 33 + name_len + pad
113
+ dbg_assert(len_field < 256)
114
+ write8(buffer, len_field) // Length of directory record
115
+ write8(buffer, 0) // Extended Attribute Record length
116
+ write_lebe32(buffer, lba) // Location of extent (LBA)
117
+ write_lebe32(buffer, len) // Data length (size of extent)
118
+ write_date_compact(buffer)
119
+ write8(buffer, flags)
120
+ write8(buffer, 0) // File unit size for files recorded in interleaved mode, zero otherwise
121
+ write8(buffer, 0) // Interleave gap size for files recorded in interleaved mode, zero otherwise
122
+ write_lebe16(buffer, 1) // Volume sequence number - the volume that this extent is recorded on
123
+ write8(buffer, name_len) // length of file name
124
+ dbg_assert(buffer.offset === START + NAME_OFFSET)
125
+ skip(buffer, name_len + pad) // File name: was already written
126
+ dbg_assert(buffer.offset === START + len_field)
127
+ }
128
+ const write_special_directory_record = (
129
+ b: ISOBuffer,
130
+ name: string,
131
+ lba: number,
132
+ len: number,
133
+ ): void => write_record(b, name, FILE_FLAGS_DIRECTORY, true, lba, len)
134
+ const write_file_record = (
135
+ b: ISOBuffer,
136
+ name: string,
137
+ lba: number,
138
+ len: number,
139
+ ): void => write_record(b, name, 0, false, lba, len)
140
+
141
+ function round_byte_size_to_block_size(n: number): number {
142
+ return 1 + Math.floor((n - 1) / BLOCK_SIZE)
143
+ }
144
+ dbg_assert(round_byte_size_to_block_size(0) === 0)
145
+ dbg_assert(round_byte_size_to_block_size(1) === 1)
146
+ dbg_assert(round_byte_size_to_block_size(BLOCK_SIZE - 1) === 1)
147
+ dbg_assert(round_byte_size_to_block_size(BLOCK_SIZE) === 1)
148
+ dbg_assert(round_byte_size_to_block_size(BLOCK_SIZE + 1) === 2)
149
+ dbg_assert(round_byte_size_to_block_size(2 * BLOCK_SIZE) === 2)
150
+ dbg_assert(round_byte_size_to_block_size(2 * BLOCK_SIZE + 1) === 3)
151
+ dbg_assert(round_byte_size_to_block_size(10 * BLOCK_SIZE + 1) === 11)
152
+
153
+ function to_msdos_filename(name: string): string {
154
+ const dot = name.lastIndexOf('.')
155
+ if (dot === -1) return name.substr(0, 8)
156
+ return name.substr(0, Math.min(8, dot)) + '.' + name.substr(dot + 1, 3)
157
+ }
158
+
159
+ dbg_assert(to_msdos_filename('abcdefghijkl.qwerty') === 'abcdefgh.qwe')
160
+ dbg_assert(to_msdos_filename('abcdefghijkl') === 'abcdefgh')
161
+
162
+ function sanitise_filename(name: string): string {
163
+ return to_msdos_filename(name.toUpperCase().replace(/[^A-Z0-9_.]/g, ''))
164
+ }
165
+
166
+ // layout:
167
+ // (lba = one block of BLOCK_SIZE bytes)
168
+ // LBA | contents
169
+ // ------+--------
170
+ // 0..15 | System Area (could be used for mbr, but not used by us)
171
+ // 16 | Primary Volume Descriptor
172
+ // 17 | Volume Descriptor Set Terminator
173
+ // 18 | empty
174
+ // 19 | Little Endian Path Table
175
+ // 20 | empty
176
+ // 21 | Big Endian Path Table
177
+ // 22 | empty
178
+ // 23 | Root directory
179
+ // 24..n | File contents
180
+ const SYSTEM_AREA_SIZE = 16 * BLOCK_SIZE
181
+ const PRIMARY_VOLUME_LBA = 16
182
+ const VOLUME_SET_TERMINATOR_LBA = 17
183
+ const LE_PATH_TABLE_LBA = 19
184
+ const BE_PATH_TABLE_LBA = 21
185
+ const ROOT_DIRECTORY_LBA = 23
186
+ const LE_PATH_TABLE_SIZE = BLOCK_SIZE
187
+ const BE_PATH_TABLE_SIZE = BLOCK_SIZE
188
+ const ROOT_DIRECTORY_SIZE = BLOCK_SIZE
189
+
190
+ let next_file_lba = 24
191
+ const mapped_files: ISOFileWithLBA[] = files.map(({ name, contents }) => {
192
+ const lba = next_file_lba
193
+ next_file_lba += round_byte_size_to_block_size(contents.length)
194
+ name = to_msdos_filename(name)
195
+ return { name, contents, lba }
196
+ })
197
+
198
+ const N_LBAS = next_file_lba
199
+ const total_size = N_LBAS * BLOCK_SIZE
200
+
201
+ const buffer: ISOBuffer = {
202
+ buffer: new Uint8Array(total_size),
203
+ offset: SYSTEM_AREA_SIZE,
204
+ }
205
+
206
+ // LBA 16: Primary Volume Descriptor
207
+ dbg_assert(buffer.offset === PRIMARY_VOLUME_LBA * BLOCK_SIZE)
208
+ write8(buffer, 0x01) // Volume Descriptor type: Primary Volume Descriptor
209
+ write_ascii(buffer, 'CD001') // Always CD001
210
+ write8(buffer, 0x01) // Version
211
+ write8(buffer, 0x00) // unused
212
+ write_padded_ascii(buffer, 32, 'V86') // System Identifier
213
+ write_padded_ascii(buffer, 32, 'CDROM') // Identification of this volume
214
+ skip(buffer, 8) // unused
215
+ write_lebe32(buffer, N_LBAS)
216
+ skip(buffer, 32) // unused
217
+ dbg_assert(buffer.offset === 0x8000 + 120)
218
+
219
+ write_lebe16(buffer, 1) // Volume Set Size
220
+ write_lebe16(buffer, 1) // Volume Sequence Number
221
+ dbg_assert(buffer.offset === 0x8080)
222
+
223
+ write_lebe16(buffer, BLOCK_SIZE)
224
+
225
+ write_lebe32(buffer, 10) // Path Table Size
226
+ write_le32(buffer, LE_PATH_TABLE_LBA) // Location of Type-L Path Table
227
+ write_le32(buffer, 0) // Location of the Optional Type-L Path Table
228
+ write_be32(buffer, BE_PATH_TABLE_LBA) // Location of Type-M Path Table
229
+ write_be32(buffer, 0) // Location of the Optional Type-M Path Table
230
+ dbg_assert(buffer.offset === 0x8000 + 156)
231
+
232
+ // Directory entry for the root directory
233
+ write_special_directory_record(buffer, '\x00', ROOT_DIRECTORY_LBA, 0x800)
234
+ dbg_assert(buffer.offset === 0x8000 + 190)
235
+
236
+ fill(buffer, 128, 0x20) // Volume Set Identifier
237
+ fill(buffer, 128, 0x20) // Publisher Identifier
238
+ fill(buffer, 128, 0x20) // Data Preparer Identifier
239
+ fill(buffer, 128, 0x20) // Application Identifier
240
+ fill(buffer, 37, 0x20) // Copyright File Identifier
241
+ fill(buffer, 37, 0x20) // Abstract File Identifier
242
+ fill(buffer, 37, 0x20) // Bibliographic File Identifier
243
+
244
+ dbg_assert(buffer.offset === 0x8000 + 813)
245
+
246
+ write_dummy_date_ascii(buffer) // Volume Creation Date and Time
247
+ write_dummy_date_ascii(buffer) // Volume Modification Date and Time
248
+ write_dummy_date_ascii(buffer) // Volume Expiration Date and Time
249
+ write_dummy_date_ascii(buffer) // Volume Effective Date and Time
250
+
251
+ write8(buffer, 0x01) // File Structure Version
252
+ dbg_assert(buffer.offset === 0x8000 + 882)
253
+
254
+ write8(buffer, 0x00) // Unused
255
+ skip(buffer, 512) // Application Used
256
+ skip(buffer, 653) // Reserved
257
+
258
+ // LBA 17: Volume Descriptor Set Terminator
259
+ dbg_assert(buffer.offset === VOLUME_SET_TERMINATOR_LBA * BLOCK_SIZE)
260
+ write8(buffer, 0xff) // 0xFF: Volume Descriptor Set Terminator
261
+ write_ascii(buffer, 'CD001') // Always CD001
262
+ write8(buffer, 0x01) // Version
263
+
264
+ // LBA 19: Little Endian Path Table
265
+ buffer.offset = LE_PATH_TABLE_LBA * BLOCK_SIZE
266
+ write8(buffer, 0x01) // Length of Directory Identifier
267
+ write8(buffer, 0x00) // Extended Attribute Record Length
268
+ write_le32(buffer, ROOT_DIRECTORY_LBA) // Location of Extent (LBA)
269
+ write_le16(buffer, 1) // Directory number of parent directory
270
+ write_ascii(buffer, '\x00') // file name
271
+ dbg_assert(
272
+ buffer.offset < LE_PATH_TABLE_LBA * BLOCK_SIZE + LE_PATH_TABLE_SIZE,
273
+ )
274
+
275
+ // LBA 21: Big Endian Path Table
276
+ buffer.offset = BE_PATH_TABLE_LBA * BLOCK_SIZE
277
+ write8(buffer, 0x01) // Length of Directory Identifier
278
+ write8(buffer, 0x00) // Extended Attribute Record Length
279
+ write_be32(buffer, ROOT_DIRECTORY_LBA) // Location of Extent (LBA)
280
+ write_be16(buffer, 1) // Directory number of parent directory
281
+ write_ascii(buffer, '\x00') // file name
282
+ dbg_assert(
283
+ buffer.offset < BE_PATH_TABLE_LBA * BLOCK_SIZE + BE_PATH_TABLE_SIZE,
284
+ )
285
+
286
+ // LBA 23: root directory
287
+ buffer.offset = ROOT_DIRECTORY_LBA * BLOCK_SIZE
288
+ write_special_directory_record(buffer, '\x00', ROOT_DIRECTORY_LBA, 0x800) // "."
289
+ write_special_directory_record(buffer, '\x01', ROOT_DIRECTORY_LBA, 0x800) // ".."
290
+ for (const { name, contents, lba } of mapped_files) {
291
+ write_file_record(buffer, name, lba, contents.length)
292
+ }
293
+ // TODO: this assertion can fail if too many files are used as input
294
+ // ROOT_DIRECTORY_SIZE should be choosen dynamically
295
+ dbg_assert(
296
+ buffer.offset < ROOT_DIRECTORY_LBA * BLOCK_SIZE + ROOT_DIRECTORY_SIZE,
297
+ )
298
+
299
+ // file contents
300
+ for (const { contents, lba } of mapped_files) {
301
+ buffer.buffer.set(contents, lba * BLOCK_SIZE)
302
+ }
303
+
304
+ return buffer.buffer
305
+ }
306
+
307
+ export function is_probably_iso9660_file(buffer: Uint8Array): boolean {
308
+ return (
309
+ buffer.length >= 17 * BLOCK_SIZE &&
310
+ buffer[BLOCK_SIZE + 0] === 1 && // Primary Volume Descriptor
311
+ buffer[BLOCK_SIZE + 1] === 67 && // "C"
312
+ buffer[BLOCK_SIZE + 2] === 68 && // "D"
313
+ buffer[BLOCK_SIZE + 3] === 48 && // "0"
314
+ buffer[BLOCK_SIZE + 4] === 48 && // "0"
315
+ buffer[BLOCK_SIZE + 5] === 49 // "1"
316
+ )
317
+ }
package/src/kernel.ts ADDED
@@ -0,0 +1,250 @@
1
+ import { h } from './lib.js'
2
+ import { dbg_assert, dbg_log } from './log.js'
3
+
4
+ // https://www.kernel.org/doc/Documentation/x86/boot.txt
5
+
6
+ const LINUX_BOOT_HDR_SETUP_SECTS = 0x1f1
7
+ const LINUX_BOOT_HDR_SYSSIZE = 0x1f4
8
+ const LINUX_BOOT_HDR_VIDMODE = 0x1fa
9
+ const LINUX_BOOT_HDR_BOOT_FLAG = 0x1fe
10
+ const LINUX_BOOT_HDR_HEADER = 0x202
11
+ const LINUX_BOOT_HDR_VERSION = 0x206
12
+ const LINUX_BOOT_HDR_TYPE_OF_LOADER = 0x210
13
+ const LINUX_BOOT_HDR_LOADFLAGS = 0x211
14
+ const LINUX_BOOT_HDR_CODE32_START = 0x214
15
+ const LINUX_BOOT_HDR_RAMDISK_IMAGE = 0x218
16
+ const LINUX_BOOT_HDR_RAMDISK_SIZE = 0x21c
17
+ const LINUX_BOOT_HDR_HEAP_END_PTR = 0x224
18
+ const LINUX_BOOT_HDR_CMD_LINE_PTR = 0x228
19
+ const LINUX_BOOT_HDR_INITRD_ADDR_MAX = 0x22c
20
+ const LINUX_BOOT_HDR_KERNEL_ALIGNMENT = 0x230
21
+ const LINUX_BOOT_HDR_RELOCATABLE_KERNEL = 0x234
22
+ const LINUX_BOOT_HDR_MIN_ALIGNMENT = 0x235
23
+ const LINUX_BOOT_HDR_XLOADFLAGS = 0x236
24
+ const LINUX_BOOT_HDR_CMDLINE_SIZE = 0x238
25
+ const LINUX_BOOT_HDR_PAYLOAD_OFFSET = 0x248
26
+ const LINUX_BOOT_HDR_PAYLOAD_LENGTH = 0x24c
27
+ const LINUX_BOOT_HDR_PREF_ADDRESS = 0x258
28
+ const LINUX_BOOT_HDR_INIT_SIZE = 0x260
29
+
30
+ const LINUX_BOOT_HDR_CHECKSUM1 = 0xaa55
31
+ const LINUX_BOOT_HDR_CHECKSUM2 = 0x53726448
32
+
33
+ const LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED = 0xff
34
+
35
+ const LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH = 1 << 0
36
+ const LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG = 1 << 5
37
+ const LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS = 1 << 6
38
+ const LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS = 1 << 7
39
+
40
+ interface KernelBootRom {
41
+ name: string
42
+ data: Uint8Array
43
+ }
44
+
45
+ export function load_kernel(
46
+ mem8: Uint8Array,
47
+ bzimage: ArrayBuffer,
48
+ initrd: ArrayBuffer | undefined,
49
+ cmdline: string,
50
+ ): KernelBootRom | undefined {
51
+ dbg_log('Trying to load kernel of size ' + bzimage.byteLength)
52
+
53
+ const KERNEL_HIGH_ADDRESS = 0x100000
54
+
55
+ // Put the initrd at the 64 MB boundary. This means the minimum memory size
56
+ // is 64 MB plus the size of the initrd.
57
+ // Note: If set too low, kernel may fail to load the initrd with "invalid magic at start of compressed archive"
58
+ const INITRD_ADDRESS = 64 << 20
59
+
60
+ const quiet = false
61
+
62
+ const bzimage8 = new Uint8Array(bzimage)
63
+ const bzimage16 = new Uint16Array(bzimage)
64
+ const bzimage32 = new Uint32Array(bzimage)
65
+
66
+ const setup_sects = bzimage8[LINUX_BOOT_HDR_SETUP_SECTS] || 4
67
+ const _syssize = bzimage32[LINUX_BOOT_HDR_SYSSIZE >> 2] << 4
68
+
69
+ const _vidmode = bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1]
70
+
71
+ const checksum1 = bzimage16[LINUX_BOOT_HDR_BOOT_FLAG >> 1]
72
+ if (checksum1 !== LINUX_BOOT_HDR_CHECKSUM1) {
73
+ dbg_log('Bad checksum1: ' + h(checksum1))
74
+ return
75
+ }
76
+
77
+ // Not aligned, so split into two 16-bit reads
78
+ const checksum2 =
79
+ bzimage16[LINUX_BOOT_HDR_HEADER >> 1] |
80
+ (bzimage16[(LINUX_BOOT_HDR_HEADER + 2) >> 1] << 16)
81
+ if (checksum2 !== LINUX_BOOT_HDR_CHECKSUM2) {
82
+ dbg_log('Bad checksum2: ' + h(checksum2))
83
+ return
84
+ }
85
+
86
+ const protocol = bzimage16[LINUX_BOOT_HDR_VERSION >> 1]
87
+ dbg_assert(protocol >= 0x202) // older not supported by us
88
+
89
+ const flags = bzimage8[LINUX_BOOT_HDR_LOADFLAGS]
90
+ dbg_assert(!!(flags & LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH)) // low kernels not supported by us
91
+
92
+ // we don't relocate the kernel, so we don't care much about most of these
93
+
94
+ const flags2 = bzimage16[LINUX_BOOT_HDR_XLOADFLAGS >> 1]
95
+ const initrd_addr_max = bzimage32[LINUX_BOOT_HDR_INITRD_ADDR_MAX >> 2]
96
+ const kernel_alignment = bzimage32[LINUX_BOOT_HDR_KERNEL_ALIGNMENT >> 2]
97
+ const relocatable_kernel = bzimage8[LINUX_BOOT_HDR_RELOCATABLE_KERNEL]
98
+ const min_alignment = bzimage8[LINUX_BOOT_HDR_MIN_ALIGNMENT]
99
+ const cmdline_size =
100
+ protocol >= 0x206 ? bzimage32[LINUX_BOOT_HDR_CMDLINE_SIZE >> 2] : 255
101
+ const payload_offset = bzimage32[LINUX_BOOT_HDR_PAYLOAD_OFFSET >> 2]
102
+ const payload_length = bzimage32[LINUX_BOOT_HDR_PAYLOAD_LENGTH >> 2]
103
+ const pref_address = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS >> 2]
104
+ const pref_address_high = bzimage32[(LINUX_BOOT_HDR_PREF_ADDRESS + 4) >> 2]
105
+ const init_size = bzimage32[LINUX_BOOT_HDR_INIT_SIZE >> 2]
106
+
107
+ dbg_log('kernel boot protocol version: ' + h(protocol))
108
+ dbg_log('flags=' + h(flags) + ' xflags=' + h(flags2))
109
+ dbg_log('code32_start=' + h(bzimage32[LINUX_BOOT_HDR_CODE32_START >> 2]))
110
+ dbg_log('initrd_addr_max=' + h(initrd_addr_max))
111
+ dbg_log('kernel_alignment=' + h(kernel_alignment))
112
+ dbg_log('relocatable=' + relocatable_kernel)
113
+ dbg_log('min_alignment=' + h(min_alignment))
114
+ dbg_log('cmdline max=' + h(cmdline_size))
115
+ dbg_log(
116
+ 'payload offset=' + h(payload_offset) + ' size=' + h(payload_length),
117
+ )
118
+ dbg_log('pref_address=' + h(pref_address_high) + ':' + h(pref_address))
119
+ dbg_log('init_size=' + h(init_size))
120
+
121
+ const real_mode_segment = 0x8000
122
+ const base_ptr = real_mode_segment << 4
123
+
124
+ const heap_end = 0xe000
125
+ const heap_end_ptr = heap_end - 0x200
126
+
127
+ // fill in the kernel boot header with infos the kernel needs to know
128
+
129
+ bzimage8[LINUX_BOOT_HDR_TYPE_OF_LOADER] =
130
+ LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED
131
+
132
+ const new_flags =
133
+ ((quiet
134
+ ? flags | LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG
135
+ : flags & ~LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG) &
136
+ ~LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS) |
137
+ LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS
138
+ bzimage8[LINUX_BOOT_HDR_LOADFLAGS] = new_flags
139
+
140
+ bzimage16[LINUX_BOOT_HDR_HEAP_END_PTR >> 1] = heap_end_ptr
141
+
142
+ // should parse the vga=... paramter from cmdline here, but we don't really care
143
+ bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1] = 0xffff // normal
144
+
145
+ dbg_log('heap_end_ptr=' + h(heap_end_ptr))
146
+
147
+ cmdline += '\x00'
148
+ dbg_assert(cmdline.length < cmdline_size)
149
+
150
+ const cmd_line_ptr = base_ptr + heap_end
151
+ dbg_log('cmd_line_ptr=' + h(cmd_line_ptr))
152
+
153
+ bzimage32[LINUX_BOOT_HDR_CMD_LINE_PTR >> 2] = cmd_line_ptr
154
+ for (let i = 0; i < cmdline.length; i++) {
155
+ mem8[cmd_line_ptr + i] = cmdline.charCodeAt(i)
156
+ }
157
+
158
+ const prot_mode_kernel_start = (setup_sects + 1) * 512
159
+ dbg_log('prot_mode_kernel_start=' + h(prot_mode_kernel_start))
160
+
161
+ const real_mode_kernel = new Uint8Array(bzimage, 0, prot_mode_kernel_start)
162
+ const protected_mode_kernel = new Uint8Array(
163
+ bzimage,
164
+ prot_mode_kernel_start,
165
+ )
166
+
167
+ let ramdisk_address = 0
168
+ let ramdisk_size = 0
169
+
170
+ if (initrd) {
171
+ ramdisk_address = INITRD_ADDRESS
172
+ ramdisk_size = initrd.byteLength
173
+
174
+ dbg_assert(
175
+ KERNEL_HIGH_ADDRESS + protected_mode_kernel.length <
176
+ ramdisk_address,
177
+ )
178
+
179
+ mem8.set(new Uint8Array(initrd), ramdisk_address)
180
+ }
181
+
182
+ bzimage32[LINUX_BOOT_HDR_RAMDISK_IMAGE >> 2] = ramdisk_address
183
+ bzimage32[LINUX_BOOT_HDR_RAMDISK_SIZE >> 2] = ramdisk_size
184
+
185
+ dbg_assert(base_ptr + real_mode_kernel.length < 0xa0000)
186
+
187
+ mem8.set(real_mode_kernel, base_ptr)
188
+ mem8.set(protected_mode_kernel, KERNEL_HIGH_ADDRESS)
189
+
190
+ return {
191
+ name: 'genroms/kernel.bin',
192
+ data: make_linux_boot_rom(real_mode_segment, heap_end),
193
+ }
194
+ }
195
+
196
+ function make_linux_boot_rom(
197
+ real_mode_segment: number,
198
+ heap_end: number,
199
+ ): Uint8Array {
200
+ // This rom will be executed by seabios after its initialisation
201
+ // It sets up segment registers, the stack and calls the kernel real mode entry point
202
+
203
+ const SIZE = 0x200
204
+
205
+ const data8 = new Uint8Array(SIZE)
206
+ const data16 = new Uint16Array(data8.buffer)
207
+
208
+ data16[0] = 0xaa55
209
+ data8[2] = SIZE / 0x200
210
+
211
+ let i = 3
212
+
213
+ data8[i++] = 0xfa // cli
214
+ data8[i++] = 0xb8 // mov ax, real_mode_segment
215
+ data8[i++] = real_mode_segment >> 0
216
+ data8[i++] = real_mode_segment >> 8
217
+ data8[i++] = 0x8e // mov es, ax
218
+ data8[i++] = 0xc0
219
+ data8[i++] = 0x8e // mov ds, ax
220
+ data8[i++] = 0xd8
221
+ data8[i++] = 0x8e // mov fs, ax
222
+ data8[i++] = 0xe0
223
+ data8[i++] = 0x8e // mov gs, ax
224
+ data8[i++] = 0xe8
225
+ data8[i++] = 0x8e // mov ss, ax
226
+ data8[i++] = 0xd0
227
+ data8[i++] = 0xbc // mov sp, heap_end
228
+ data8[i++] = heap_end >> 0
229
+ data8[i++] = heap_end >> 8
230
+ data8[i++] = 0xea // jmp (real_mode_segment+0x20):0x0
231
+ data8[i++] = 0x00
232
+ data8[i++] = 0x00
233
+ data8[i++] = (real_mode_segment + 0x20) >> 0
234
+ data8[i++] = (real_mode_segment + 0x20) >> 8
235
+
236
+ dbg_assert(i < SIZE)
237
+
238
+ const checksum_index = i
239
+ data8[checksum_index] = 0
240
+
241
+ let checksum = 0
242
+
243
+ for (let i = 0; i < data8.length; i++) {
244
+ checksum += data8[i]
245
+ }
246
+
247
+ data8[checksum_index] = -checksum
248
+
249
+ return data8
250
+ }