Spaces:
Runtime error
Runtime error
File size: 8,305 Bytes
8df6da4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
import { h } from "./lib.js";
import { dbg_assert, dbg_log } from "./log.js";
// https://www.kernel.org/doc/Documentation/x86/boot.txt
const LINUX_BOOT_HDR_SETUP_SECTS = 0x1F1;
const LINUX_BOOT_HDR_SYSSIZE = 0x1F4;
const LINUX_BOOT_HDR_VIDMODE = 0x1FA;
const LINUX_BOOT_HDR_BOOT_FLAG = 0x1FE;
const LINUX_BOOT_HDR_HEADER = 0x202;
const LINUX_BOOT_HDR_VERSION = 0x206;
const LINUX_BOOT_HDR_TYPE_OF_LOADER = 0x210;
const LINUX_BOOT_HDR_LOADFLAGS = 0x211;
const LINUX_BOOT_HDR_CODE32_START = 0x214;
const LINUX_BOOT_HDR_RAMDISK_IMAGE = 0x218;
const LINUX_BOOT_HDR_RAMDISK_SIZE = 0x21C;
const LINUX_BOOT_HDR_HEAP_END_PTR = 0x224;
const LINUX_BOOT_HDR_CMD_LINE_PTR = 0x228;
const LINUX_BOOT_HDR_INITRD_ADDR_MAX = 0x22C;
const LINUX_BOOT_HDR_KERNEL_ALIGNMENT = 0x230;
const LINUX_BOOT_HDR_RELOCATABLE_KERNEL = 0x234;
const LINUX_BOOT_HDR_MIN_ALIGNMENT = 0x235;
const LINUX_BOOT_HDR_XLOADFLAGS = 0x236;
const LINUX_BOOT_HDR_CMDLINE_SIZE = 0x238;
const LINUX_BOOT_HDR_PAYLOAD_OFFSET = 0x248;
const LINUX_BOOT_HDR_PAYLOAD_LENGTH = 0x24C;
const LINUX_BOOT_HDR_PREF_ADDRESS = 0x258;
const LINUX_BOOT_HDR_INIT_SIZE = 0x260;
const LINUX_BOOT_HDR_CHECKSUM1 = 0xAA55;
const LINUX_BOOT_HDR_CHECKSUM2 = 0x53726448;
const LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED = 0xFF;
const LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH = 1 << 0;
const LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG = 1 << 5;
const LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS = 1 << 6;
const LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS = 1 << 7;
export function load_kernel(mem8, bzimage, initrd, cmdline)
{
dbg_log("Trying to load kernel of size " + bzimage.byteLength);
const KERNEL_HIGH_ADDRESS = 0x100000;
// Put the initrd at the 64 MB boundary. This means the minimum memory size
// is 64 MB plus the size of the initrd.
// Note: If set too low, kernel may fail to load the initrd with "invalid magic at start of compressed archive"
const INITRD_ADDRESS = 64 << 20;
const quiet = false;
const bzimage8 = new Uint8Array(bzimage);
const bzimage16 = new Uint16Array(bzimage);
const bzimage32 = new Uint32Array(bzimage);
const setup_sects = bzimage8[LINUX_BOOT_HDR_SETUP_SECTS] || 4;
const syssize = bzimage32[LINUX_BOOT_HDR_SYSSIZE >> 2] << 4;
const vidmode = bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1];
const checksum1 = bzimage16[LINUX_BOOT_HDR_BOOT_FLAG >> 1];
if(checksum1 !== LINUX_BOOT_HDR_CHECKSUM1)
{
dbg_log("Bad checksum1: " + h(checksum1));
return;
}
// Not aligned, so split into two 16-bit reads
const checksum2 =
bzimage16[LINUX_BOOT_HDR_HEADER >> 1] |
bzimage16[LINUX_BOOT_HDR_HEADER + 2 >> 1] << 16;
if(checksum2 !== LINUX_BOOT_HDR_CHECKSUM2)
{
dbg_log("Bad checksum2: " + h(checksum2));
return;
}
const protocol = bzimage16[LINUX_BOOT_HDR_VERSION >> 1];
dbg_assert(protocol >= 0x202); // older not supported by us
const flags = bzimage8[LINUX_BOOT_HDR_LOADFLAGS];
dbg_assert(flags & LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH); // low kernels not supported by us
// we don't relocate the kernel, so we don't care much about most of these
const flags2 = bzimage16[LINUX_BOOT_HDR_XLOADFLAGS >> 1];
const initrd_addr_max = bzimage32[LINUX_BOOT_HDR_INITRD_ADDR_MAX >> 2];
const kernel_alignment = bzimage32[LINUX_BOOT_HDR_KERNEL_ALIGNMENT >> 2];
const relocatable_kernel = bzimage8[LINUX_BOOT_HDR_RELOCATABLE_KERNEL];
const min_alignment = bzimage8[LINUX_BOOT_HDR_MIN_ALIGNMENT];
const cmdline_size = protocol >= 0x206 ? bzimage32[LINUX_BOOT_HDR_CMDLINE_SIZE >> 2] : 255;
const payload_offset = bzimage32[LINUX_BOOT_HDR_PAYLOAD_OFFSET >> 2];
const payload_length = bzimage32[LINUX_BOOT_HDR_PAYLOAD_LENGTH >> 2];
const pref_address = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS >> 2];
const pref_address_high = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS + 4 >> 2];
const init_size = bzimage32[LINUX_BOOT_HDR_INIT_SIZE >> 2];
dbg_log("kernel boot protocol version: " + h(protocol));
dbg_log("flags=" + h(flags) + " xflags=" + h(flags2));
dbg_log("code32_start=" + h(bzimage32[LINUX_BOOT_HDR_CODE32_START >> 2]));
dbg_log("initrd_addr_max=" + h(initrd_addr_max));
dbg_log("kernel_alignment=" + h(kernel_alignment));
dbg_log("relocatable=" + relocatable_kernel);
dbg_log("min_alignment=" + h(min_alignment));
dbg_log("cmdline max=" + h(cmdline_size));
dbg_log("payload offset=" + h(payload_offset) + " size=" + h(payload_length));
dbg_log("pref_address=" + h(pref_address_high) + ":" + h(pref_address));
dbg_log("init_size=" + h(init_size));
const real_mode_segment = 0x8000;
const base_ptr = real_mode_segment << 4;
const heap_end = 0xE000;
const heap_end_ptr = heap_end - 0x200;
// fill in the kernel boot header with infos the kernel needs to know
bzimage8[LINUX_BOOT_HDR_TYPE_OF_LOADER] = LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED;
const new_flags =
(quiet ? flags | LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG : flags & ~LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG)
& ~LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS
| LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS;
bzimage8[LINUX_BOOT_HDR_LOADFLAGS] = new_flags;
bzimage16[LINUX_BOOT_HDR_HEAP_END_PTR >> 1] = heap_end_ptr;
// should parse the vga=... paramter from cmdline here, but we don't really care
bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1] = 0xFFFF; // normal
dbg_log("heap_end_ptr=" + h(heap_end_ptr));
cmdline += "\x00";
dbg_assert(cmdline.length < cmdline_size);
const cmd_line_ptr = base_ptr + heap_end;
dbg_log("cmd_line_ptr=" + h(cmd_line_ptr));
bzimage32[LINUX_BOOT_HDR_CMD_LINE_PTR >> 2] = cmd_line_ptr;
for(let i = 0; i < cmdline.length; i++)
{
mem8[cmd_line_ptr + i] = cmdline.charCodeAt(i);
}
const prot_mode_kernel_start = (setup_sects + 1) * 512;
dbg_log("prot_mode_kernel_start=" + h(prot_mode_kernel_start));
const real_mode_kernel = new Uint8Array(bzimage, 0, prot_mode_kernel_start);
const protected_mode_kernel = new Uint8Array(bzimage, prot_mode_kernel_start);
let ramdisk_address = 0;
let ramdisk_size = 0;
if(initrd)
{
ramdisk_address = INITRD_ADDRESS;
ramdisk_size = initrd.byteLength;
dbg_assert(KERNEL_HIGH_ADDRESS + protected_mode_kernel.length < ramdisk_address);
mem8.set(new Uint8Array(initrd), ramdisk_address);
}
bzimage32[LINUX_BOOT_HDR_RAMDISK_IMAGE >> 2] = ramdisk_address;
bzimage32[LINUX_BOOT_HDR_RAMDISK_SIZE >> 2] = ramdisk_size;
dbg_assert(base_ptr + real_mode_kernel.length < 0xA0000);
mem8.set(real_mode_kernel, base_ptr);
mem8.set(protected_mode_kernel, KERNEL_HIGH_ADDRESS);
return {
name: "genroms/kernel.bin",
data: make_linux_boot_rom(real_mode_segment, heap_end),
};
}
function make_linux_boot_rom(real_mode_segment, heap_end)
{
// This rom will be executed by seabios after its initialisation
// It sets up segment registers, the stack and calls the kernel real mode entry point
const SIZE = 0x200;
const data8 = new Uint8Array(SIZE);
const data16 = new Uint16Array(data8.buffer);
data16[0] = 0xAA55;
data8[2] = SIZE / 0x200;
let i = 3;
data8[i++] = 0xFA; // cli
data8[i++] = 0xB8; // mov ax, real_mode_segment
data8[i++] = real_mode_segment >> 0;
data8[i++] = real_mode_segment >> 8;
data8[i++] = 0x8E; // mov es, ax
data8[i++] = 0xC0;
data8[i++] = 0x8E; // mov ds, ax
data8[i++] = 0xD8;
data8[i++] = 0x8E; // mov fs, ax
data8[i++] = 0xE0;
data8[i++] = 0x8E; // mov gs, ax
data8[i++] = 0xE8;
data8[i++] = 0x8E; // mov ss, ax
data8[i++] = 0xD0;
data8[i++] = 0xBC; // mov sp, heap_end
data8[i++] = heap_end >> 0;
data8[i++] = heap_end >> 8;
data8[i++] = 0xEA; // jmp (real_mode_segment+0x20):0x0
data8[i++] = 0x00;
data8[i++] = 0x00;
data8[i++] = real_mode_segment + 0x20 >> 0;
data8[i++] = real_mode_segment + 0x20 >> 8;
dbg_assert(i < SIZE);
const checksum_index = i;
data8[checksum_index] = 0;
let checksum = 0;
for(let i = 0; i < data8.length; i++)
{
checksum += data8[i];
}
data8[checksum_index] = -checksum;
return data8;
}
|