diff --git a/index.html b/index.html index 1fb8afaa..cc815caa 100644 --- a/index.html +++ b/index.html @@ -46,7 +46,7 @@

Setup

- + @@ -62,6 +62,14 @@ + + + diff --git a/src/browser/main.js b/src/browser/main.js index b6f9a10f..72390313 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -188,6 +188,16 @@ settings.hda = { buffer: hd_file }; } + if($("multiboot_image")) + { + var multiboot_file = $("multiboot_image").files[0]; + if(multiboot_file) + { + last_file = multiboot_file; + settings.multiboot = { buffer: multiboot_file }; + } + } + if(last_file) { set_title(last_file.name); @@ -474,6 +484,7 @@ settings.fda = infos.fda; settings.cdrom = infos.cdrom; settings.hda = infos.hda + settings.multiboot = infos.multiboot settings.memory_size = infos.memory_size; settings.vga_memory_size = infos.vga_memory_size; @@ -689,6 +700,8 @@ "hda": settings.hda, "cdrom": settings.cdrom, + "multiboot": settings.multiboot, + "initial_state": settings.initial_state, "filesystem": settings.filesystem || {}, diff --git a/src/browser/starter.js b/src/browser/starter.js index f46c1df7..7c6e6d1d 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -167,6 +167,10 @@ function V86Starter(options) settings.fdb = this.disk_images["fdb"] = buffer; break; + case "multiboot": + settings.multiboot = this.disk_images["multiboot"] = buffer; + break; + case "bios": settings.bios = buffer.buffer; break; @@ -211,7 +215,8 @@ function V86Starter(options) size: file["size"], }; - if(name === "bios" || name === "vga_bios" || name === "initial_state") + if(name === "bios" || name === "vga_bios" || + name === "initial_state" || name === "multiboot") { // Ignore async for these because they must be availabe before boot. // This should make result.buffer available after the object is loaded @@ -285,7 +290,7 @@ function V86Starter(options) var image_names = [ "bios", "vga_bios", "cdrom", "hda", "hdb", "fda", "fdb", - "initial_state", + "initial_state", "multiboot", ]; for(var i = 0; i < image_names.length; i++) diff --git a/src/cpu.js b/src/cpu.js index 4cebf69a..6f6bdb09 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -720,12 +720,191 @@ CPU.prototype.init = function(settings, device_bus) } } + if(settings.multiboot) + { + dbg_assert(settings.multiboot.buffer); + this.load_multiboot(settings.multiboot.buffer); + } + if(DEBUG) { this.debug.init(); } }; +CPU.prototype.load_multiboot = function(buffer) +{ + // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html + + dbg_log("Trying multiboot from buffer of size " + buffer.byteLength, LOG_CPU); + + const MAGIC = 0x1BADB002; + const ELF_MAGIC = 0x464C457F; + const MULTIBOOT_HEADER_ADDRESS = 0x10000; + const MULTIBOOT_SEARCH_BYTES = 8192; + + var buf32 = new Int32Array(buffer); + + for(var offset = 0; offset < MULTIBOOT_SEARCH_BYTES; offset += 4) + { + if(buf32[offset >> 2] === MAGIC) + { + var flags = buf32[offset + 4 >> 2]; + var checksum = buf32[offset + 8 >> 2]; + var total = MAGIC + flags + checksum | 0; + + if(total) + { + dbg_log("Multiboot checksum check failed", LOG_CPU); + continue + } + } + else + { + continue; + } + + dbg_log("Multiboot magic found, flags: " + h(flags >>> 0, 8), LOG_CPU); + dbg_assert((flags & ~MULTIBOOT_HEADER_ADDRESS) === 0, "TODO"); + + this.reg32s[reg_eax] = 0x2BADB002; + + let multiboot_info_addr = 0x7C00 + this.reg32s[reg_ebx] = multiboot_info_addr; + this.write32(multiboot_info_addr, 0); + + this.cr[0] = 1; + this.protected_mode = true; + this.flags = flags_default + this.update_cs_size(true); + this.stack_size_32 = true; + + for(var i = 0; i < 6; i++) + { + this.segment_is_null[i] = 0; + this.segment_offsets[i] = 0; + this.segment_limits[i] = 0xFFFFFFFF; + + // Value doesn't matter, OS isn't allowed to reload without setting + // up a proper GDT + this.sreg[i] = 0xB002; + } + + if(flags & MULTIBOOT_HEADER_ADDRESS) + { + dbg_log("Multiboot specifies its own address table", LOG_CPU); + + var header_addr = buf32[offset + 12 >> 2]; + var load_addr = buf32[offset + 16 >> 2]; + var load_end_addr = buf32[offset + 20 >> 2]; + var bss_end_addr = buf32[offset + 24 >> 2]; + var entry_addr = buf32[offset + 28 >> 2]; + + dbg_log("header=" + h(header_addr, 8) + + " load=" + h(load_addr, 8) + + " load_end=" + h(load_end_addr, 8) + + " bss_end=" + h(bss_end_addr, 8) + + " entry=" + h(entry_addr, 8)) + + dbg_assert(load_addr <= header_addr); + + var file_start = offset - (header_addr - load_addr); + + if(load_end_addr === 0) + { + var length = undefined; + } + else + { + dbg_assert(load_end_addr >= load_addr); + var length = load_end_addr - load_addr; + } + + let blob = new Uint8Array(buffer, file_start, length); + this.write_blob(blob, load_addr); + + this.instruction_pointer = this.get_seg(reg_cs) + entry_addr | 0; + } + else if(buf32[0] === ELF_MAGIC) + { + dbg_log("Multiboot image is in elf format", LOG_CPU); + + let elf = read_elf(buffer); + + this.instruction_pointer = this.get_seg(reg_cs) + elf.header.entry | 0; + + for(let program of elf.program_headers) + { + if(program.type === 0) + { + // null + } + else if(program.type === 1) + { + // load + + // Since multiboot specifies that paging is disabled, + // virtual and physical address must be equal + dbg_assert(program.paddr === program.vaddr); + dbg_assert(program.filesz <= program.memsz); + + let blob = new Uint8Array(buffer, program.offset, program.filesz); + this.write_blob(blob, program.paddr); + } + else if(program.type === 4 || + program.type === 0x6474e550 || + program.type === 0x6474e551) + { + // ignore for now + } + else + { + dbg_assert(false, "unimplemented elf section type"); + } + } + } + else + { + dbg_assert(false, "Not a bootable multiboot format"); + } + + // only for kvm-unit-test + this.io.register_write_consecutive(0xF4, this, + function(value) + { + console.log("Test exited with code " + h(value, 2)); + throw "HALT"; + }, + function() {}, + function() {}, + function() {}); + + // only for kvm-unit-test + for(let i = 0xE; i <= 0xF; i++) + { + this.io.register_write(0x2000 + i, this, + function(value) + { + dbg_log("kvm-unit-test: Set irq " + h(i) + " to " + h(value, 2)); + if(value) + { + this.device_raise_irq(i); + } + else + { + this.device_lower_irq(i); + } + }); + } + + dbg_log("Starting multiboot kernel at:", LOG_CPU); + this.debug.dump_state(); + this.debug.dump_regs(); + + break; + } +}; + CPU.prototype.fill_cmos = function(rtc, settings) { var boot_order = settings.boot_order || 0x213;
CD imageCD image
Disk images are not uploaded to the server