commit f4f1178898c8a4bbbc0a432354dbcc56353099c3 Author: Nguyen Anh Quynh Date: Mon Apr 21 12:27:47 2008 +0900 Linuxboot Option ROM support. Signed-off-by: Nguyen Anh Quynh diff --git a/Makefile b/Makefile index 76c149a..fdd9388 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ DESTDIR= rpmrelease = devel -.PHONY: kernel user libkvm qemu bios vgabios extboot clean libfdt +.PHONY: kernel user libkvm qemu bios vgabios extboot linuxboot clean libfdt all: libkvm qemu ifneq '$(filter $(ARCH), x86_64 i386 ia64)' '' @@ -19,7 +19,7 @@ qemu kernel user libkvm: qemu: libkvm ifneq '$(filter $(ARCH), i386 x86_64)' '' - qemu: extboot + qemu: extboot linuxboot endif ifneq '$(filter $(ARCH), powerpc)' '' qemu: libfdt @@ -41,6 +41,14 @@ extboot: || ! cmp -s qemu/pc-bios/extboot.bin extboot/extboot.bin; then \ cp extboot/extboot.bin qemu/pc-bios/extboot.bin; \ fi + +linuxboot: + $(MAKE) -C $@ + if ! [ -f qemu/pc-bios/linuxboot.bin ] \ + || ! cmp -s qemu/pc-bios/linuxboot.bin linuxboot/linuxboot.bin; then \ + cp linuxboot/linuxboot.bin qemu/pc-bios/linuxboot.bin; \ + fi + libfdt: $(MAKE) -C $@ @@ -88,6 +96,7 @@ srpm: tar czf $(RPMTOPDIR)/SOURCES/kernel.tar.gz kernel tar czf $(RPMTOPDIR)/SOURCES/scripts.tar.gz scripts tar czf $(RPMTOPDIR)/SOURCES/extboot.tar.gz extboot + tar czf $(RPMTOPDIR)/SOURCES/linuxboot.tar.gz linuxboot cp Makefile configure kvm_stat $(RPMTOPDIR)/SOURCES rpmbuild --define="_topdir $(RPMTOPDIR)" -bs $(tmpspec) $(RM) $(tmpspec) diff --git a/linuxboot/Makefile b/linuxboot/Makefile new file mode 100644 index 0000000..3bc88a6 --- /dev/null +++ b/linuxboot/Makefile @@ -0,0 +1,40 @@ +# Makefile for linuxboot Option ROM +# Nguyen Anh Quynh + +CC = gcc +CCFLAGS = -g -Wall -Werror -nostdlib -fno-builtin -fomit-frame-pointer -Os + +cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ + /dev/null 2>&1`"; then echo "$(2)"; else echo "$(3)"; fi ;) +CCFLAGS += $(call cc-option,$(CC),-nopie,) +CCFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +CCFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) + +INSTALLDIR = /usr/share/qemu + +.PHONY: all +all: clean linuxboot.bin + +.PHONY: install +install: linuxboot.bin + cp linuxboot.bin $(INSTALLDIR) + +.PHONY: clean +clean: + $(RM) *.o *.img *.bin signrom *~ + +linuxboot.img: boot.o rom.o + $(LD) --oformat binary -Ttext 0 $^ -o $@ + +linuxboot.bin: linuxboot.img signrom + ./signrom linuxboot.img linuxboot.bin + +signrom: signrom.c + $(CC) -o $@ -g -Wall $^ + +%.o: %.c + $(CC) $(CCFLAGS) -c $< + +%.o: %.S + $(CC) $(CCFLAGS) -c $< + diff --git a/linuxboot/boot.S b/linuxboot/boot.S new file mode 100644 index 0000000..a9461d6 --- /dev/null +++ b/linuxboot/boot.S @@ -0,0 +1,54 @@ +/* + * boot.S + * Linux Boot Option ROM for QEMU. + + * Copyright (C) by Nguyen Anh Quynh , 2008. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + .text + .code16gcc + .globl _start + .extern setup +_start: + .short 0xAA55 /* ROM signature */ + .byte 0 /* ROM size - to be patched at built-time */ + /* ROM entry: initializing */ + pushal + /* %es is clobbered, so save it */ + pushw %es + /* just in case */ + pushw %ds + cld + call setup /* call C code */ + popw %ds + popw %es + popal + lretw + + /* interrupt 19 handler */ + .globl int19_handler + .extern int19_handler_C + .extern linux_boot + int19_handler: + /* we never execute the original int19, so no need to care + * about clobbered registers :-) */ + /* Set %ds = %ss */ + movw %ss, %ax + movw %ax, %ds + cld + call int19_handler_C /* call C code */ + /* we actually jump to linux kernel setup, so never reach here */ diff --git a/linuxboot/farvar.h b/linuxboot/farvar.h new file mode 100644 index 0000000..7876186 --- /dev/null +++ b/linuxboot/farvar.h @@ -0,0 +1,130 @@ +/* + * farvar.h + * Code to access multiple segments within gcc. + * + * Copyright (C) 2008 Kevin O'Connor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __FARVAR_H +#define __FARVAR_H + +#include + +// Dummy definitions used to make sure gcc understands dependencies +// between SET_SEG and GET/READ/WRITE_SEG macros. +extern uint16_t __segment_CS, __segment_DS, __segment_ES, __segment_SS; +extern uint16_t __segment_GS, __segment_FS; + +// Low level macros for reading/writing memory via a segment selector. +#define __READ8_SEG(SEG, var) ({ \ + typeof(var) __value; \ + __asm__("movb %%" #SEG ":%1, %b0" : "=Qi"(__value) \ + : "m"(var), "m"(__segment_ ## SEG)); \ + __value; }) +#define __READ16_SEG(SEG, var) ({ \ + typeof(var) __value; \ + __asm__("movw %%" #SEG ":%1, %w0" : "=ri"(__value) \ + : "m"(var), "m"(__segment_ ## SEG)); \ + __value; }) +#define __READ32_SEG(SEG, var) ({ \ + typeof(var) __value; \ + __asm__("movl %%" #SEG ":%1, %0" : "=ri"(__value) \ + : "m"(var), "m"(__segment_ ## SEG)); \ + __value; }) + +#define __WRITE8_SEG(SEG, var, value) \ + __asm__("movb %b1, %%" #SEG ":%0" : "=m"(var) \ + : "Q"(value), "m"(__segment_ ## SEG)) +#define __WRITE16_SEG(SEG, var, value) \ + __asm__("movw %w1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) +#define __WRITE32_SEG(SEG, var, value) \ + __asm__("movl %1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) + +// Low level macros for getting/setting a segment register. +#define __SET_SEG(SEG, value) \ + __asm__("movw %w1, %%" #SEG : "=m"(__segment_ ## SEG) \ + : "r"(value)) +#define __GET_SEG(SEG) ({ \ + uint16_t __seg; \ + __asm__("movw %%" #SEG ", %w0" : "=r"(__seg) \ + : "m"(__segment_ ## SEG)); \ + __seg;}) + +// Macros for automatically choosing the appropriate memory size +// access method. +extern void __force_link_error__unknown_type(); + +#define __GET_VAR(seg, var) ({ \ + typeof(var) __val; \ + if (__builtin_types_compatible_p(typeof(__val), uint8_t) \ + || __builtin_types_compatible_p(typeof(__val), int8_t)) \ + __val = __READ8_SEG(seg, var); \ + else if (__builtin_types_compatible_p(typeof(__val), uint16_t) \ + || __builtin_types_compatible_p(typeof(__val), int16_t)) \ + __val = __READ16_SEG(seg, var); \ + else if (__builtin_types_compatible_p(typeof(__val), uint32_t) \ + || __builtin_types_compatible_p(typeof(__val), int32_t)) \ + __val = __READ32_SEG(seg, var); \ + else \ + __force_link_error__unknown_type(); \ + __val; }) + +#define __SET_VAR(seg, var, val) do { \ + if (__builtin_types_compatible_p(typeof(var), uint8_t) \ + || __builtin_types_compatible_p(typeof(var), int8_t)) \ + __WRITE8_SEG(seg, var, (val)); \ + else if (__builtin_types_compatible_p(typeof(var), uint16_t) \ + || __builtin_types_compatible_p(typeof(var), int16_t)) \ + __WRITE16_SEG(seg, var, (val)); \ + else if (__builtin_types_compatible_p(typeof(var), uint32_t) \ + || __builtin_types_compatible_p(typeof(var), int32_t)) \ + __WRITE32_SEG(seg, var, (val)); \ + else \ + __force_link_error__unknown_type(); \ +} while (0) + +// Macros for converting to/from 32bit style pointers to their +// equivalent 16bit segment/offset values. +#define FARPTR_TO_SEG(p) (((u32)(p)) >> 4) +#define FARPTR_TO_OFFSET(p) (((u32)(p)) & 0xf) +#define MAKE_FARPTR(seg,off) ((void*)(((seg)<<4)+(off))) + +// Macros for accessing a variable in another segment. (They +// automatically update the %es segment and then make the appropriate +// access.) +#define __GET_FARVAR(seg, var) ({ \ + SET_SEG(ES, (seg)); \ + GET_VAR(ES, (var)); }) + +#define __SET_FARVAR(seg, var, val) do { \ + typeof(var) __sfv_val = (val); \ + SET_SEG(ES, (seg)); \ + SET_VAR(ES, (var), __sfv_val); \ +} while (0) + +#define GET_FARVAR(seg, var) __GET_FARVAR((seg), (var)) +#define SET_FARVAR(seg, var, val) __SET_FARVAR((seg), (var), (val)) + +#define GET_VAR(seg, var) __GET_VAR(seg, (var)) +#define SET_VAR(seg, var, val) __SET_VAR(seg, (var), (val)) + +#define SET_SEG(SEG, value) __SET_SEG(SEG, (value)) +#define GET_SEG(SEG) __GET_SEG(SEG) + +#endif diff --git a/linuxboot/rom.c b/linuxboot/rom.c new file mode 100644 index 0000000..4cd600d --- /dev/null +++ b/linuxboot/rom.c @@ -0,0 +1,104 @@ +/* + * rom.c + * Linux Boot Option ROM for QEMU. + * + * Copyright (C) by Nguyen Anh Quynh , 2008. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "util.h" +#include "farvar.h" + +asm (".code16gcc"); + +struct linuxboot_info { + uint16_t cs; /* CS segment */ + uint16_t ds; /* DS segment */ + uint16_t sp; /* SP reg */ +}; + +#define LINUXBOOT_PORT 0x407 + +struct ivector { + uint16_t offset; + uint16_t segment; +}; +/* manipulate interrupt vector entry */ +#define GET_IVT_ENTRY(index,var) \ + GET_FARVAR(0, ((struct ivector *)(index*sizeof(struct ivector)))->var) +#define SET_IVT_ENTRY(index,var,value) \ + SET_FARVAR(0, ((struct ivector *)(index*sizeof(struct ivector)))->var, value) + +/* called from ASM code in boot.S */ +void setup() +{ + /* install our INT 19 handler */ + extern void int19_handler; + SET_IVT_ENTRY(0x19, segment, GET_SEG(CS)); + SET_IVT_ENTRY(0x19, offset, (uint16_t)&int19_handler); +} + +/* send command to QEMU via a dedicated IO port */ +static void get_linuxboot_info(struct linuxboot_info *info) +{ + uint16_t seg; + char tmp[16 + sizeof(*info)], *p; + + /* align to 16 */ + p = (char *)(((uint32_t)tmp + 0xF) & ~0xF); + memcpy(p, info, sizeof(*info)); + + /* send segment contained info to QEMU */ + seg = ((uint32_t)p >> 4) + GET_SEG(SS); + outw(seg, LINUXBOOT_PORT); + + /* now copy back the result to info */ + memcpy(info, p, sizeof(*info)); +} + +/* boot linux by jmp to its kernel setup code */ +static void boot_linux(uint16_t cs, uint16_t ds, uint16_t sp) +{ + asm volatile ( + "cli \n" + "cld \n" + /* setup registers for kernel setup */ + "movw %0, %%ds \n" + "movw %0, %%es \n" + "movw %0, %%fs \n" + "movw %0, %%gs \n" + "movw %0, %%ss \n" + "movw %2, %%sp \n" + /* push CS:IP */ + "pushw %1 \n" + "pushw $0 \n" + /* now (implicitly) jump to CS:IP */ + "lretw \n" + : + : "rm" (ds), "rm" (cs), "rm" (sp) + ); +} + +/* hook INT 13 */ +/* called from ASM code in boot.S */ +void int19_handler_C() +{ + struct linuxboot_info info; + + get_linuxboot_info(&info); + + boot_linux(info.cs, info.ds, info.sp); +} diff --git a/linuxboot/signrom.c b/linuxboot/signrom.c new file mode 100644 index 0000000..2f2a734 --- /dev/null +++ b/linuxboot/signrom.c @@ -0,0 +1,128 @@ +/* + * Extended Boot Option ROM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corporation, 2007 + * Authors: Anthony Liguori + * + * Copyright by Nguyen Anh Quynh , 2008. + */ + +#include +#include +#include +#include +#include +#include + +#define BLOCK 512 + +static long get_file_size(FILE *f) +{ + long where, size; + + where = ftell(f); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, where, SEEK_SET); + + return size; +} + +int main(int argc, char **argv) +{ + FILE *fin, *fout; + char buffer[BLOCK]; + int i, j, size; + long rom_size; + + uint8_t sum = 0; + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + fin = fopen(argv[1], "rb"); + fout = fopen(argv[2], "wb"); + + if (fin == NULL || fout == NULL) { + fprintf(stderr, "Could not open input/output files\n"); + return 1; + } + + rom_size = get_file_size(fin); + if (rom_size == 0) { + fprintf(stderr, "Error: ROM size = 0?\n"); + return 1; + } + + /* set rom_size to blocks of 512 bytes */ + rom_size = (rom_size/512) + 1; + + /* read all data in ROM image, except the last block */ + for (i = 0; i < rom_size - 1; i ++) { + memset(buffer, 0, sizeof(buffer)); + + size = fread(buffer, BLOCK, 1, fin); + if (size == 1) { + if (i == 0) { + /* first block, lets set ROM size */ + buffer[2] = (uint8_t)rom_size; + } + + for (j = 0; j < BLOCK; j++) + sum += buffer[j]; + + if (fwrite(buffer, BLOCK, 1, fout) != 1) { + fprintf(stderr, "Write failed\n"); + return 1; + } + } + else { + fprintf(stderr, "Failed to read from input file\n"); + return 1; + } + } + + /* now read last block of ROM image */ + memset(buffer, 0, sizeof(buffer)); + size = fread(buffer, 1, BLOCK, fin); + if (ferror(fin)) { + fprintf(stderr, "Failed to read from input file\n"); + return 1; + } + + if (rom_size == 1) + /* set ROM size */ + buffer[2] = (uint8_t)rom_size; + + for (i = 0; i < size; i++) + sum += buffer[i]; + + /* set checksum in final byte */ + buffer[BLOCK - 1] = -sum; + + if (fwrite(buffer, BLOCK, 1, fout) != 1) { + fprintf(stderr, "Failed to write to output file\n"); + return 1; + } + + fclose(fin); + fclose(fout); + + return 0; +} diff --git a/linuxboot/util.h b/linuxboot/util.h new file mode 100644 index 0000000..7f859d8 --- /dev/null +++ b/linuxboot/util.h @@ -0,0 +1,69 @@ +/* + * util.h + * + * Copyright (C) 2008, Nguyen Anh Quynh + * + * Some code is lifted from the legacybios project by Kevin O'Connor (http://www.linuxtogo.org/~kevin/) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#include + +static inline void outw(uint16_t value, uint16_t port) +{ + asm volatile("outw %w0, %w1" : : "a"(value), "Nd"(port)); +} + +#define UREG(ER, R, RH, RL) union { uint32_t ER; \ + struct { uint16_t R; uint16_t R ## _hi; }; \ + struct { \ + uint8_t RL; \ + uint8_t RH; \ + uint8_t R ## _hilo; \ + uint8_t R ## _hihi; \ + }; \ +} + +/* Set of registers passed to INT 13 handler. + * These registers can be modified on return. */ +struct iregs { + uint16_t ds; + uint16_t es; + UREG(edi, di, di_hi, di_lo); + UREG(esi, si, si_hi, si_lo); + UREG(ebx, bx, bh, bl); + UREG(edx, dx, dh, dl); + UREG(ecx, cx, ch, cl); + UREG(eax, ax, ah, al); + uint16_t ip; + uint16_t cs; + uint16_t flags; +} __attribute__((packed)); + +static inline void *memcpy(void * dest, void *src, unsigned int count) +{ + char *tmp = (char *)dest, *s = (char *)src; + + while (count--) + *tmp++ = *s++; + + return dest; +} + +#endif diff --git a/qemu/Makefile b/qemu/Makefile index a3054b4..3722b5e 100644 --- a/qemu/Makefile +++ b/qemu/Makefile @@ -195,7 +195,7 @@ endif mkdir -p "$(DESTDIR)$(datadir)" set -e; for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ video.x openbios-sparc32 pxe-ne2k_pci.bin \ - pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin extboot.bin \ + pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin extboot.bin linuxboot.bin \ bamboo.dtb; \ do \ $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ @@ -302,6 +302,7 @@ tarbin: $(datadir)/pxe-rtl8139.bin \ $(datadir)/pxe-pcnet.bin \ $(datadir)/extboot.bin \ + $(datadir)/linuxboot.bin \ $(docdir)/qemu-doc.html \ $(docdir)/qemu-tech.html \ $(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 diff --git a/qemu/Makefile.target b/qemu/Makefile.target index 2fc2988..6a6d21b 100644 --- a/qemu/Makefile.target +++ b/qemu/Makefile.target @@ -591,7 +591,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o -OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o extboot.o +OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o extboot.o linuxboot.o ifeq ($(USE_KVM_PIT), 1) OBJS+= i8254-kvm.o endif diff --git a/qemu/hw/linuxboot.c b/qemu/hw/linuxboot.c new file mode 100644 index 0000000..e6b7e4f --- /dev/null +++ b/qemu/hw/linuxboot.c @@ -0,0 +1,39 @@ +/* + * linuxboot.c + * Linux Boot Option ROM. + * + * Copyright (C) Nguyen Anh Quynh , 2008. + * + * This code is released under the GNU GPL license version 2. + * See the COPYING file in the top-level directory. + */ + +#include "hw.h" +#include "pc.h" + +/* Extended Boot ROM suport */ + +struct linuxboot_info { + /* uint16_t request; */ + uint16_t cs; /* CS segment */ + uint16_t ds; /* DS segment */ + uint16_t sp; /* SP reg */ +}; + +#define LINUXBOOT_PORT 0x407 + +static void linuxboot_write(void *opaque, uint32_t addr, uint32_t value) +{ + struct linuxboot_info *info = (void *)(phys_ram_base + ((value & 0xFFFF) << 4)); + + info->cs = kernel_setup_cs; + info->ds = kernel_setup_ds; + info->sp = kernel_setup_sp; + + cpu_physical_memory_set_dirty((value & 0xFFFF) << 4); +} + +void linuxboot_init() +{ + register_ioport_write(LINUXBOOT_PORT, 1, 2, linuxboot_write, NULL); +} diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c index ae87ab9..fdde4cd 100644 --- a/qemu/hw/pc.c +++ b/qemu/hw/pc.c @@ -41,6 +41,7 @@ #define VGABIOS_FILENAME "vgabios.bin" #define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" #define EXTBOOT_FILENAME "extboot.bin" +#define LINUXBOOT_FILENAME "linuxboot.bin" /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */ #define ACPI_DATA_SIZE 0x10000 @@ -492,6 +493,8 @@ static long get_file_size(FILE *f) return size; } +uint16_t kernel_setup_cs, kernel_setup_ds, kernel_setup_sp; + static void load_linux(const char *kernel_filename, const char *initrd_filename, const char *kernel_cmdline) @@ -638,11 +641,12 @@ static void load_linux(const char *kernel_filename, /* generate bootsector to set up the initial register state */ real_seg = (real_addr-phys_ram_base) >> 4; seg[0] = seg[2] = seg[3] = seg[4] = seg[4] = real_seg; - seg[1] = real_seg+0x20; /* CS */ + kernel_setup_ds = real_seg; + kernel_setup_cs = seg[1] = real_seg+0x20; /* CS */ memset(gpr, 0, sizeof gpr); - gpr[4] = cmdline_addr-real_addr-16; /* SP (-16 is paranoia) */ + kernel_setup_sp = gpr[4] = cmdline_addr-real_addr-16; /* SP (-16 is paranoia) */ - generate_bootsect(gpr, seg, 0); + //generate_bootsect(gpr, seg, 0); } static void main_cpu_reset(void *opaque) @@ -903,6 +907,14 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, for (i = 0; i < nb_option_roms; i++) opt_rom_offset += load_option_rom(option_rom[i], opt_rom_offset); + /* linuxboot option ROM also intercepts int19, but never call original int19, + * so it must be loaded before extboot! */ + if (linux_boot) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, LINUXBOOT_FILENAME); + opt_rom_offset += load_option_rom(buf, opt_rom_offset); + fprintf(stderr, "linuxboot ROM loaded at 0x%x\n", opt_rom_offset); + } + if (extboot_drive != -1) { snprintf(buf, sizeof(buf), "%s/%s", bios_dir, EXTBOOT_FILENAME); opt_rom_offset += load_option_rom(buf, opt_rom_offset); @@ -931,8 +943,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, bochs_bios_init(); - if (linux_boot) + if (linux_boot) { load_linux(kernel_filename, initrd_filename, kernel_cmdline); + linuxboot_init(); + } cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1); i8259 = i8259_init(cpu_irq[0]); diff --git a/qemu/hw/pc.h b/qemu/hw/pc.h index f26fcb6..d86ecfd 100644 --- a/qemu/hw/pc.h +++ b/qemu/hw/pc.h @@ -158,7 +158,10 @@ void *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device, BlockDriverState *bs); /* extboot.c */ - void extboot_init(BlockDriverState *bs, int cmd); +/* linux_boot */ +uint16_t kernel_setup_cs, kernel_setup_ds, kernel_setup_sp; +void linuxboot_init(void); + #endif