123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- "use strict";
- // 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;
- 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;
- }
|