Hi,

This should be submitted to upstream (but not to kvm-devel list), but
this is only the test code that I want to quickly send out for
comments. In case it looks OK, I will send it to upstream later.

Inspired by extboot and conversations with Anthony and HPA, this
linuxboot option ROM is a simple option ROM that intercepts int19 in
order to execute linux setup code. This approach eliminates the need
to manipulate the boot sector for this purpose.

To test it, just load linux kernel with your KVM/QEMU image using
-kernel option in normal way.

I succesfully compiled and tested it with kvm-66 on Ubuntu 7.10, guest
Ubuntu 8.04.

Thanks,
Quynh


# diffstat linuxboot1.diff
 Makefile             |   13 ++++-
 linuxboot/Makefile   |   40 +++++++++++++++
 linuxboot/boot.S     |   54 +++++++++++++++++++++
 linuxboot/farvar.h   |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++
 linuxboot/rom.c      |  104 ++++++++++++++++++++++++++++++++++++++++
 linuxboot/signrom    |binary
 linuxboot/signrom.c  |  128 ++++++++++++++++++++++++++++++++++++++++++++++++++
 linuxboot/util.h     |   69 +++++++++++++++++++++++++++
 qemu/Makefile        |    3 -
 qemu/Makefile.target |    2
 qemu/hw/linuxboot.c  |   39 +++++++++++++++
 qemu/hw/pc.c         |   22 +++++++-
 qemu/hw/pc.h         |    5 +
 13 files changed, 600 insertions(+), 9 deletions(-)
commit f4f1178898c8a4bbbc0a432354dbcc56353099c3
Author: Nguyen Anh Quynh <[EMAIL PROTECTED]>
Date:   Mon Apr 21 12:27:47 2008 +0900

    Linuxboot Option ROM support.
    
    Signed-off-by: Nguyen Anh Quynh <[EMAIL PROTECTED]>

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 <[EMAIL PROTECTED]>
+
+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 <[EMAIL PROTECTED]>, 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 <[EMAIL PROTECTED]>
+ *
+ * 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 <stdint.h>
+
+// 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 <[EMAIL PROTECTED]>, 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 b/linuxboot/signrom
new file mode 100755
index 0000000..cdc39a9
Binary files /dev/null and b/linuxboot/signrom differ
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 <[EMAIL PROTECTED]>
+ *
+ * Copyright by Nguyen Anh Quynh <[EMAIL PROTECTED]>, 2008.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#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 <ROM> <OUTPUT>\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 <[EMAIL PROTECTED]>
+ *
+ * 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 <stdint.h>
+
+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 <[EMAIL PROTECTED]>, 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
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to