@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.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- 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
|
+
}
|