Hi,

The attached patch is an option ROM that allows x86 guests to boot from any block device supported by QEMU (and the guest kernel). It works by hijacking the BIOS int13 function and then uses a very simply protocol to communicate all block requests to QEMU.

This patch also adds a "boot=on|off" parameter to the -drive option. When this option is specified, the extboot option ROM is automatically loaded and this device will be exposes by int13 as '0x80'. What '-boot c' really means is boot from the first disk driver or BIOS drive '0x80' so the -boot option is complimentary to this new boot flag.

I have successfully booted Linux, Win2k, and WinXP from SCSI with this patch. Windows takes a bit of prep work to be able to boot from SCSI. For win2k, you just need to make sure you have the right driver installed. WinXP, however, seems to require that it be installed on SCSI to boot from SCSI. If anyone knows how to modify WinXP after the fact, please let me know.

I can also use this new flag to boot a Linux guest from my previously posted virtio blk driver.

This patch is against my previous virtio patches but does not strictly depend on them. When applying this patch, you'll have to build extboot in extboot/ and then copy extboot/extboot.bin to pc-bios/extboot.bin.

Regards,

Anthony Liguori
Index: qemu/extboot/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/extboot/Makefile	2007-12-05 09:43:11.000000000 -0600
@@ -0,0 +1,21 @@
+OBJCOPY=objcopy
+
+CFLAGS=-Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin \
+       -fno-stack-protector
+
+all: extboot.bin
+
+%.o: %.S
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+extboot.img: extboot.o
+	$(LD) --oformat binary -Ttext 0 -o $@ $<
+
+extboot.bin: extboot.img signrom
+	./signrom extboot.img extboot.bin
+
+signrom: signrom.c
+	$(CC) -o $@ -g -Wall $^
+
+clean:
+	$(RM) *.o *.img *.bin signrom *~
Index: qemu/extboot/extboot.S
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/extboot/extboot.S	2007-12-05 09:43:11.000000000 -0600
@@ -0,0 +1,742 @@
+/*
+ * 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]>
+ */
+
+.code16
+.text
+	.global _start
+_start:
+	.short 0xaa55
+	.byte (_end - _start) / 512
+	push %ax
+	push %bx
+	push %cx
+	push %dx
+	push %ds
+
+	/* setup ds so we can access the IVT */
+	xor %ax, %ax
+	mov %ax, %ds
+
+	/* save old int 19 at int 2b */
+	mov $(0x19 * 4), %bx
+	mov 0(%bx), %ax
+	mov 2(%bx), %cx
+
+	mov $(0x2b * 4), %bx
+	mov %ax, 0(%bx)
+	mov %cx, 2(%bx)
+
+	/* install out int 19 handler */
+	mov $(0x19 * 4), %bx
+	mov $int19_handler, %ax
+	mov %ax, 0(%bx)
+	mov %cs, 2(%bx)
+
+	pop %ds	
+	pop %dx
+	pop %cx
+	pop %bx
+	pop %ax
+	lret
+
+int19_handler:
+	push %ax
+	push %bx
+	push %cx
+	push %dx
+	push %ds
+
+	movw $0x404, %dx
+	inb %dx, %al
+	cmp $1, %al
+	je 1f
+	cmp $2, %al
+	je 2f
+	jmp 3f
+
+1: /* hook int13: intb(0x404) == 1 */
+	/* setup ds to access IVT */
+	xor %ax, %ax
+	mov %ax, %ds
+
+	/* save old int 13 to int 2c */
+	mov $(0x13 * 4), %bx
+	mov 0(%bx), %ax
+	mov 2(%bx), %cx
+
+	mov $(0x2c * 4), %bx
+	mov %ax, 0(%bx)
+	mov %cx, 2(%bx)
+
+	/* install our int 13 handler */
+	mov $(0x13 * 4), %bx
+	mov $int13_handler, %ax
+
+	mov %ax, 0(%bx)
+	mov %cs, 2(%bx)
+	jmp 3f
+	
+2: /* linux boot: intb(0x404) == 2 */
+	cli
+	cld
+	mov $0x9000, %ax
+	mov %ax, %ds
+	mov %ax, %es
+	mov %ax, %fs
+	mov %ax, %gs
+	mov %ax, %ss
+	mov $0x8ffe, %sp
+	ljmp $0x9000 + 0x20, $0
+
+3: /* fall through: inb(0x404) == 0 */
+	pop %ds
+	pop %dx
+	pop %cx
+	pop %bx
+	pop %ax
+	int $0x2b
+
+#define FLAGS_CF	0x01
+	
+.macro clc
+	push %ax
+	pushf
+	pop %ax
+	and $(~FLAGS_CF), %ax
+	push %ax
+	popf
+	pop %ax
+.endm
+
+.macro stc
+	push %ax
+	pushf
+	pop %ax
+	or $(FLAGS_CF), %ax
+	push %ax
+	popf
+	pop %ax
+.endm
+
+/* we clobber %bx */
+.macro alloca size
+	push %ds
+	push %bp
+	mov %sp, %bp  /* remember the current stack position */
+	
+	mov %ss, %bx
+	mov %bx, %ds
+	
+	sub \size, %sp
+	and $(~0x0F), %sp
+	mov %sp, %bx
+	
+	push %bp
+	mov 0(%bp), %bp
+.endm
+
+/* we clobber %bp */
+.macro allocbpa size
+	mov %sp, %bp  /* remember the current stack position */
+	sub \size, %sp
+	and $(~0x0F), %sp
+	push %bp
+	mov %sp, %bp
+	add $2, %bp
+.endm
+
+.macro freea
+	pop %sp
+	add $2, %sp
+	pop %ds
+.endm
+
+.macro freebpa
+	pop %sp
+.endm
+
+.macro dump reg
+	push %ax
+	push %dx
+
+	mov \reg, %ax
+	mov $0x406, %dx
+	outw %ax, %dx
+
+	pop %dx
+	pop %ax
+.endm
+
+.macro callout value
+	push %bp
+	push %bx
+	mov %sp, %bp
+	alloca $16
+	push %ax
+	push %dx
+
+	mov %ax, 0(%bx)     /* ax */
+	mov 0(%bp), %ax     /* bx */
+	mov %ax, 2(%bx)
+	mov %cx, 4(%bx)     /* cx */
+	mov %dx, 6(%bx)     /* dx */
+	mov %si, 8(%bx)     /* si */
+	mov %ds, 10(%bx)    /* ds */
+	mov %es, 12(%bx)    /* ds */
+	movw \value, 14(%bx) /* value */
+
+	mov %bx, %ax
+	shr $4, %ax
+	mov %ds, %dx
+	add %dx, %ax
+
+	mov $0x407, %dx
+	outw %ax, %dx
+
+	pop %dx
+	pop %ax
+	freea
+	pop %bx
+	pop %bp
+.endm
+	
+send_command:
+	push %bp
+	mov %sp, %bp
+	push %ax
+	push %bx
+	push %dx
+
+	mov 4(%bp), %ax
+	shr $4, %ax
+	and $0x0FFF, %ax
+	mov %ss, %bx
+	add %bx, %ax
+	
+	mov $0x405, %dx
+	outw %ax, %dx
+
+	pop %dx
+	pop %bx
+	pop %ax
+	pop %bp
+
+	push %ax
+	mov 2(%bx), %ax
+	pop %ax
+	
+	ret
+	
+add32:  /* lo, hi, lo, hi */
+	push %bp
+	mov %sp, %bp
+
+	movw 4(%bp), %cx  /* hi */
+	movw 6(%bp), %dx  /* lo */
+
+	add  10(%bp), %dx
+	jnc 1f
+	add $1, %cx
+1:	add 8(%bp), %cx
+	
+	pop %bp
+	ret
+
+mul32:  /* lo,      hi,     lo,     hi */
+	/* 10(%bp), 8(%bp), 6(%bp), 4(%bp) */
+	push %bp
+	mov %sp, %bp
+	push %ax
+	push %bx
+
+	xor %cx, %cx
+	xor %dx, %dx
+
+	/* for (i = 0; i < 16;) */
+	xor %bx, %bx
+0:
+	cmp $16, %bx
+	jge 2f
+	
+	mov 6(%bp), %ax
+	and $1, %ax
+	cmp $1, %ax
+	jne 1f
+	push 10(%bp)
+	push 8(%bp)
+	push %dx
+	push %cx
+	call add32
+	add $8, %sp
+1:
+	shlw $1, 8(%bp)
+	movw 10(%bp), %ax
+	and $0x8000, %ax
+	cmp $0x8000, %ax
+	jne 1f
+	orw $1, 8(%bp)
+1:
+	shlw $1, 10(%bp)
+	shrw $1, 6(%bp)
+
+	/* i++) { */
+	add $1, %bx
+	jmp 0b
+	
+2:
+	pop %bx
+	pop %ax
+	pop %bp
+	ret
+
+disk_reset:
+	movb $0, %ah
+	clc
+	ret
+
+/* this really should be a function, not a macro but i'm lazy */
+.macro read_write_disk_sectors cmd
+	push %ax
+	push %bx
+	push %cx
+	push %dx
+	push %si
+
+	push %bp
+	sub $10, %sp
+	mov %sp, %bp
+
+	/* save nb_sectors */
+	mov %al, 6(%bp)
+	movb $0, 7(%bp)
+
+	/* save buffer */
+	mov %bx, 8(%bp)
+	
+	/* cylinders */
+	xor %ax, %ax
+	mov %cl, %al
+	shl $2, %ax
+	and $0x300, %ax
+	mov %ch, %al
+	mov %ax, 0(%bp)
+
+	/* heads */
+	xor %ax, %ax
+	mov %dh, %al
+	mov %ax, 2(%bp)
+
+	/* sectors - 1 */
+	xor %ax, %ax
+	mov %cl, %al
+	and $0x3F, %al
+	sub $1, %ax
+	mov %ax, 4(%bp)
+
+	alloca $8
+
+	movw $0, 0(%bx) /* read c,h,s */
+	push %bx
+	call send_command
+	add $2, %sp
+
+	mov 6(%bx), %ax /* total_sectors */
+	mov 2(%bp), %si /* *= heads */
+	mul %si
+	add 4(%bp), %ax /* += sectors - 1 */
+
+	push 4(%bx) /* total_heads */
+	push $0
+	push 6(%bx) /* total_sectors */
+	push $0
+	call mul32
+	add $8, %sp
+
+	push 0(%bp) /* cylinders */
+	push $0
+	push %dx
+	push %cx
+	call mul32
+	add $8, %sp
+
+	add %ax, %dx
+	jnc 1f
+	add $1, %cx
+1:	
+	freea
+
+	alloca $16
+
+	movw \cmd, 0(%bx) /* read */
+	movw 6(%bp), %ax /* nb_sectors */
+	movw %ax, 2(%bx)
+	movw %es, 4(%bx) /* segment */
+	movw 8(%bp), %ax /* offset */
+	mov %ax, 6(%bx)
+	movw %dx, 8(%bx) /* sector */
+	movw %cx, 10(%bx)
+	movw $0, 12(%bx)
+	movw $0, 14(%bx)
+
+	push %bx
+	call send_command
+	add $2, %sp
+	
+	freea
+	
+	add $10, %sp
+	pop %bp
+
+	pop %si
+	pop %dx
+	pop %cx
+	pop %bx
+	pop %ax
+
+	mov $0, %ah
+	clc
+	ret
+.endm
+	
+read_disk_sectors:
+	read_write_disk_sectors $0x01
+
+write_disk_sectors:
+	read_write_disk_sectors $0x02
+	
+read_disk_drive_parameters:
+	push %bx
+
+	/* allocate memory for packet, pointer gets returned in bx */
+	alloca $8
+
+	/* issue command */
+	movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+	push %bx
+	call send_command
+	add $2, %sp
+
+	/* normalize sector value */
+	movb 6(%bx), %cl
+	andb $0x3F, %cl
+	movb %cl, 6(%bx)
+
+	/* normalize cylinders */
+	subw $2, 2(%bx)
+
+	/* normalize heads */
+	subw $1, 4(%bx)
+
+	/* return code */
+	mov $0, %ah
+
+	/* cylinders */
+	movb 2(%bx), %ch
+	movb 3(%bx), %cl
+	shlb $6, %cl
+	andb $0xC0, %cl
+
+	/* sectors */
+	orb 6(%bx), %cl
+
+	/* heads */
+	movb 4(%bx), %dh
+
+	/* drives */
+	movb $1, %dl
+
+	/* status */
+	mov $0, %ah
+	
+	freea
+
+	pop %bx
+
+	/* do this last since it's the most sensitive */
+	clc
+	ret
+
+alternate_disk_reset:
+	movb $0, %ah
+	clc
+	ret
+
+read_disk_drive_size:
+	push %bx
+	alloca $8
+
+	movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+	push %bx
+	call send_command
+	add $2, %sp
+
+	/* cylinders - 1 to cx:dx */
+	mov 2(%bx), %dx
+	xor %cx, %cx
+	sub $1, %dx
+
+	/* heads */
+	push 4(%bx)
+	push $0
+	push %dx
+	push %cx
+	call mul32
+	add $8, %sp
+
+	/* sectors */
+	push 6(%bx)
+	push $0
+	push %dx
+	push %cx
+	call mul32
+	add $8, %sp
+
+	/* status */
+	mov $3, %ah
+	
+	freea
+	pop %bx
+
+	clc
+	ret
+
+check_if_extensions_present:
+	mov $0x30, %ah
+	mov $0xAA55, %bx
+	mov $0x07, %cx
+	clc
+	ret
+
+.macro extended_read_write_sectors cmd
+	cmpb $10, 0(%si)
+	jg 1f
+	mov $1, %ah
+	stc
+	ret
+1:
+	push %ax
+	push %bp
+	allocbpa $16
+
+	movw \cmd, 0(%bp) /* read */
+	movw 2(%si), %ax   /* nb_sectors */
+	movw %ax, 2(%bp)
+	movw 4(%si), %ax   /* offset */
+	movw %ax, 6(%bp)
+	movw 6(%si), %ax   /* segment */
+	movw %ax, 4(%bp)
+	movw 8(%si), %ax   /* block */
+	movw %ax, 8(%bp)
+	movw 10(%si), %ax
+	movw %ax, 10(%bp)
+	movw 12(%si), %ax
+	movw %ax, 12(%bp)
+	movw 14(%si), %ax
+	movw %ax, 14(%bp)
+
+	push %bp
+	call send_command
+	add $2, %sp
+
+	freebpa
+	pop %bp
+	pop %ax
+
+	mov $0, %ah
+	clc
+	ret
+.endm
+
+extended_read_sectors:
+	extended_read_write_sectors $0x01
+
+extended_write_sectors:
+	extended_read_write_sectors $0x02
+
+get_extended_drive_parameters:
+	mov $1, %ah
+	stc
+	ret
+#if 0
+	/* this function is seriously borked */
+1:
+	push %ax
+	push %bp
+	push %cx
+	push %dx
+	
+	allocbpa $8
+
+	movw $0, 0(%bp) /* read c,h,s */
+	push %bp
+	call send_command
+	add $2, %sp
+
+	/* check the size of the passed in data */
+	cmpw $26, 0(%si)
+	mov 0(%si), %ax
+	dump %ax
+	jle 0b
+
+	/* set flags to 2 */
+	movw $2, 2(%si)
+
+	/* cylinders */
+	mov 2(%bp), %ax
+	mov %ax, 4(%si)
+	xor %ax, %ax
+	mov %ax, 6(%si)
+
+	/* heads */
+	mov 4(%bp), %ax
+	mov %ax, 8(%si)
+	xor %ax, %ax
+	mov %ax, 10(%si)
+
+	/* sectors */
+	mov 6(%bp), %ax
+	mov %ax, 12(%si)
+	xor %ax, %ax
+	mov %ax, 14(%si)
+
+	/* calculate total sectors */
+
+	/* cx:dx = cylinders */
+	mov 2(%bp), %dx
+	xor %cx, %cx
+
+	/* *= heads */
+	push 4(%bp)
+	push $0
+	push %dx
+	push %cx
+	call mul32
+	add $8, %sp
+
+	/* *= sectors */
+	push 6(%bp)
+	push $0
+	push %dx
+	push %cx
+	call mul32
+	add $8, %sp
+
+	/* total number of sectors */
+	mov %dx, 16(%si)
+	mov %cx, 18(%si)
+	xor %ax, %ax
+	mov %ax, 20(%si)
+	mov %ax, 22(%si)
+
+	/* number of bytes per sector */
+	movw $512, 24(%si)
+
+	/* optional segmention:offset to EDD config */
+	cmpw $30, 0(%si)
+	jl 1f
+
+	movw $0xFFFF, 26(%si)
+	movw $0xFFFF, 28(%si)
+	
+1:
+	freebpa
+	
+	pop %dx
+	pop %cx
+	pop %bp
+	pop %ax
+
+	mov $0, %ah
+	clc
+	ret
+#endif
+
+terminate_disk_emulation:
+	mov $1, %ah
+	stc
+	ret
+
+int13_handler:
+	cmp $0x80, %dl
+	je 1f
+	int $0x2c
+	iret
+1:	
+	cmp $0x0, %ah
+	jne 1f
+	call disk_reset
+	iret
+1:	
+	cmp $0x2, %ah
+	jne 1f
+	call read_disk_sectors
+	iret
+1:
+	cmp $0x8, %ah
+	jne 1f
+	call read_disk_drive_parameters
+	iret
+1:
+	cmp $0x15, %ah
+	jne 1f
+	call read_disk_drive_size
+	iret
+1:
+	cmp $0x41, %ah
+	jne 1f
+	call check_if_extensions_present
+	iret
+1:
+	cmp $0x42, %ah
+	jne 1f
+	call extended_read_sectors
+	iret
+1:
+	cmp $0x48, %ah
+	jne 1f
+	call get_extended_drive_parameters
+	iret
+1:
+	cmp $0x4b, %ah
+	jne 1f
+	call terminate_disk_emulation
+	iret
+1:
+	cmp $0x0d, %ah
+	jne 1f
+	call alternate_disk_reset
+	iret
+1:
+	cmp $0x03, %ah
+	jne 1f
+	call write_disk_sectors
+	iret
+1:
+	cmp $0x43, %ah
+	jne 1f
+	call extended_write_sectors
+	iret
+1:
+	int $0x18  /* boot failed */
+	iret
+
+.align 512, 0
+_end:
Index: qemu/extboot/signrom.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/extboot/signrom.c	2007-12-05 09:43:11.000000000 -0600
@@ -0,0 +1,79 @@
+/*
+ * 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]>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+	FILE *fin, *fout;
+	char buffer[512], oldbuffer[512];
+	int i, size, lag = 0;
+	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;
+	}
+
+	do {
+		size = fread(buffer, 512, 1, fin);
+		if (size == 1) {
+			for (i = 0; i < 512; i++)
+				sum += buffer[i];
+
+			if (lag) {
+				if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+					fprintf(stderr, "Write failed\n");
+					return 1;
+				}
+			}
+			lag = 1;
+			memcpy(oldbuffer, buffer, 512);
+		}
+	} while (size == 1);
+
+	if (size != 0) {
+		fprintf(stderr, "Failed to read from input file\n");
+		return 1;
+	}
+
+	oldbuffer[511] = -sum;
+
+	if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+		fprintf(stderr, "Failed to write to output file\n");
+		return 1;
+	}
+
+	fclose(fin);
+	fclose(fout);
+
+	return 0;
+}
Index: qemu/Makefile.target
===================================================================
--- qemu.orig/Makefile.target	2007-12-05 09:43:27.000000000 -0600
+++ qemu/Makefile.target	2007-12-05 09:43:34.000000000 -0600
@@ -443,7 +443,7 @@
 VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
 VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
-VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
+VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o extboot.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
Index: qemu/hw/extboot.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/hw/extboot.c	2007-12-05 09:45:02.000000000 -0600
@@ -0,0 +1,128 @@
+/*
+ * Virtio Network Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ *  Anthony Liguori   <[EMAIL PROTECTED]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "block.h"
+
+/* Extended Boot ROM suport */
+
+union extboot_cmd
+{
+    uint16_t type;
+    struct {
+	uint16_t type;
+	uint16_t cylinders;
+	uint16_t heads;
+	uint16_t sectors;
+    } query_geometry;
+    struct {
+	uint16_t type;
+	uint16_t nb_sectors;
+	uint16_t segment;
+	uint16_t offset;
+	uint64_t sector;
+    } xfer;
+};
+
+static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int *s)
+{
+    bdrv_get_geometry_hint(bs, c, h, s);
+
+    if (*c <= 1024) {
+	*c >>= 0;
+	*h <<= 0;
+    } else if (*c <= 2048) {
+	*c >>= 1;
+	*h <<= 1;
+    } else if (*c <= 4096) {
+	*c >>= 2;
+	*h <<= 2;
+    } else if (*c <= 8192) {
+	*c >>= 3;
+	*h <<= 3;
+    } else {
+	*c >>= 4;
+	*h <<= 4;
+    }
+
+    /* what is the correct algorithm for this?? */
+    if (*h == 256) {
+	*h = 255;
+	*c = *c + 1;
+    }
+}
+
+static uint32_t extboot_read(void *opaque, uint32_t addr)
+{
+    int *pcmd = opaque;
+    return *pcmd;
+}
+
+static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value)
+{
+    union extboot_cmd *cmd = (void *)(phys_ram_base + ((value & 0xFFFF) << 4));
+    BlockDriverState *bs = opaque;
+    int cylinders, heads, sectors, err;
+
+    get_translated_chs(bs, &cylinders, &heads, &sectors);
+
+    if (cmd->type == 0x01 || cmd->type == 0x02) {
+	target_ulong pa = cmd->xfer.segment * 16 + cmd->xfer.segment;
+
+	/* possible buffer overflow */
+	if ((pa + cmd->xfer.nb_sectors * 512) > phys_ram_size)
+	    return;
+    }
+
+    switch (cmd->type) {
+    case 0x00:
+	cmd->query_geometry.cylinders = cylinders;
+	cmd->query_geometry.heads = heads;
+	cmd->query_geometry.sectors = sectors;
+	cpu_physical_memory_set_dirty((value & 0xFFFF) << 4);
+	break;
+    case 0x01:
+	err = bdrv_read(bs, cmd->xfer.sector, phys_ram_base +
+			cmd->xfer.segment * 16 + cmd->xfer.offset,
+			cmd->xfer.nb_sectors);
+	if (err)
+	    printf("Read failed\n");
+	break;
+    case 0x02:
+	err = bdrv_write(bs, cmd->xfer.sector, phys_ram_base +
+			 cmd->xfer.segment * 16 + cmd->xfer.offset,
+			 cmd->xfer.nb_sectors);
+	if (err)
+	    printf("Write failed\n");
+
+	cpu_physical_memory_set_dirty(cmd->xfer.segment * 16 + cmd->xfer.offset);
+	break;
+    }
+}
+
+void extboot_init(BlockDriverState *bs, int cmd)
+{
+    int *pcmd;
+
+    pcmd = qemu_mallocz(sizeof(int));
+    if (!pcmd) {
+	fprintf(stderr, "Error allocating memory\n");
+	exit(1);
+    }
+
+    *pcmd = cmd;
+    register_ioport_read(0x404, 1, 1, extboot_read, pcmd);
+    register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
+}
Index: qemu/hw/pc.h
===================================================================
--- qemu.orig/hw/pc.h	2007-12-05 09:43:27.000000000 -0600
+++ qemu/hw/pc.h	2007-12-05 09:43:34.000000000 -0600
@@ -151,4 +151,8 @@
 void *virtio_blk_init(PCIBus *bus, uint16_t vendor, uint16_t device,
 		      BlockDriverState *bs);
 
+/* extboot.c */
+
+void extboot_init(BlockDriverState *bs, int cmd);
+
 #endif
Index: qemu/hw/pc.c
===================================================================
--- qemu.orig/hw/pc.c	2007-12-05 09:43:51.000000000 -0600
+++ qemu/hw/pc.c	2007-12-05 10:11:43.000000000 -0600
@@ -38,6 +38,7 @@
 #define BIOS_FILENAME "bios.bin"
 #define VGABIOS_FILENAME "vgabios.bin"
 #define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+#define EXTBOOT_FILENAME "extboot.bin"
 
 /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables.  */
 #define ACPI_DATA_SIZE       0x10000
@@ -696,6 +697,31 @@
     nb_ne2k++;
 }
 
+static int load_option_rom(const char *filename, int offset)
+{
+    ram_addr_t option_rom_offset;
+    int size, ret;
+
+    size = get_image_size(filename);
+    if (size < 0) {
+	fprintf(stderr, "Could not load option rom '%s'\n", filename);
+	exit(1);
+    }
+    if (size > (0x10000 - offset))
+	goto option_rom_error;
+    option_rom_offset = qemu_ram_alloc(size);
+    ret = load_image(filename, phys_ram_base + option_rom_offset);
+    if (ret != size) {
+    option_rom_error:
+	fprintf(stderr, "Too many option ROMS\n");
+	exit(1);
+    }
+    size = (size + 4095) & ~4095;
+    cpu_register_physical_memory(0xd0000 + offset,
+				 size, option_rom_offset | IO_MEM_ROM);
+    return size;
+}
+
 /* PC hardware initialisation */
 static void pc_init1(int ram_size, int vga_ram_size,
                      const char *boot_device, DisplayState *ds,
@@ -706,7 +732,7 @@
     char buf[1024];
     int ret, linux_boot, i;
     ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
-    int bios_size, isa_bios_size, vga_bios_size;
+    int bios_size, isa_bios_size, vga_bios_size, opt_rom_offset;
     PCIBus *pci_bus;
     int piix3_devfn = -1;
     CPUState *env;
@@ -804,32 +830,13 @@
                                  isa_bios_size,
                                  (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
 
-    {
-        ram_addr_t option_rom_offset;
-        int size, offset;
-
-        offset = 0;
-        for (i = 0; i < nb_option_roms; i++) {
-            size = get_image_size(option_rom[i]);
-            if (size < 0) {
-                fprintf(stderr, "Could not load option rom '%s'\n",
-                        option_rom[i]);
-                exit(1);
-            }
-            if (size > (0x10000 - offset))
-                goto option_rom_error;
-            option_rom_offset = qemu_ram_alloc(size);
-            ret = load_image(option_rom[i], phys_ram_base + option_rom_offset);
-            if (ret != size) {
-            option_rom_error:
-                fprintf(stderr, "Too many option ROMS\n");
-                exit(1);
-            }
-            size = (size + 4095) & ~4095;
-            cpu_register_physical_memory(0xd0000 + offset,
-                                         size, option_rom_offset | IO_MEM_ROM);
-            offset += size;
-        }
+    opt_rom_offset = 0;
+    for (i = 0; i < nb_option_roms; i++)
+	opt_rom_offset += load_option_rom(option_rom[i], 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);
     }
 
     /* map all the bios at the top of memory */
@@ -1020,6 +1027,18 @@
 	    unit_id++;
 	}
     }
+
+    if (extboot_drive != -1) {
+	DriveInfo *info = &drives_table[extboot_drive];
+	int cyls, heads, secs;
+
+	if (info->interface != IF_IDE) {
+	    bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs);
+	    bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs);
+	}
+
+	extboot_init(info->bdrv, 1);
+    }
 }
 
 static void pc_init_pci(int ram_size, int vga_ram_size,
Index: qemu/sysemu.h
===================================================================
--- qemu.orig/sysemu.h	2007-12-05 09:43:51.000000000 -0600
+++ qemu/sysemu.h	2007-12-05 09:44:01.000000000 -0600
@@ -133,6 +133,7 @@
 
 int nb_drives;
 DriveInfo drives_table[MAX_DRIVES+1];
+int extboot_drive;
 
 extern int drive_get_index(BlockInterfaceType interface, int bus, int unit);
 extern int drive_get_max_bus(BlockInterfaceType interface);
Index: qemu/vl.c
===================================================================
--- qemu.orig/vl.c	2007-12-05 09:43:51.000000000 -0600
+++ qemu/vl.c	2007-12-05 09:44:01.000000000 -0600
@@ -167,6 +167,7 @@
    to store the VM snapshots */
 DriveInfo drives_table[MAX_DRIVES+1];
 int nb_drives;
+int extboot_drive = -1;
 /* point to the block driver where the snapshots are managed */
 BlockDriverState *bs_snapshots;
 int vga_ram_size;
@@ -4852,7 +4853,8 @@
     int max_devs;
     int index;
     char *params[] = { "bus", "unit", "if", "index", "cyls", "heads",
-                       "secs", "trans", "media", "snapshot", "file", NULL };
+                       "secs", "trans", "media", "snapshot", "file", "boot",
+		       NULL };
 
     if (check_params(buf, sizeof(buf), params, str) < 0) {
          fprintf(stderr, "qemu: unknowm parameter '%s' in '%s'\n",
@@ -5008,6 +5010,19 @@
 	}
     }
 
+    if (get_param_value(buf, sizeof(buf), "boot", str)) {
+	if (!strcmp(buf, "on")) {
+	    if (extboot_drive != -1) {
+		fprintf(stderr, "qemu: two bootable drives specified\n");
+		return -1;
+	    }
+	    extboot_drive = nb_drives;
+	} else if (strcmp(buf, "off")) {
+	    fprintf(stderr, "qemu: '%s' invalid boot option\n", str);
+	    return -1;
+	}
+    }
+
     get_param_value(file, sizeof(file), "file", str);
 
     /* compute bus and unit according index */
@@ -7443,7 +7458,8 @@
            "-hdc/-hdd file  use 'file' as IDE hard disk 2/3 image\n"
            "-cdrom file     use 'file' as IDE cdrom image (cdrom is ide1 master)\n"
 	   "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][index=i]\n"
-           "       [,cyls=c,heads=h,secs=s[,trans=t]][snapshot=on|off]\n"
+           "       [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
+	   "       [,boot=on|off]\n"
 	   "                use 'file' as a drive image\n"
            "-mtdblock file  use 'file' as on-board Flash memory image\n"
            "-sd file        use 'file' as SecureDigital card image\n"
Index: qemu/Makefile
===================================================================
--- qemu.orig/Makefile	2007-12-05 10:12:45.000000000 -0600
+++ qemu/Makefile	2007-12-05 10:13:04.000000000 -0600
@@ -171,7 +171,7 @@
 	mkdir -p "$(DESTDIR)$(datadir)"
 	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; do \
+		pxe-rtl8139.bin pxe-pcnet.bin extboot.bin; do \
 		$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
 	done
 ifndef CONFIG_WIN32
@@ -271,6 +271,7 @@
         $(datadir)/pxe-ne2k_pci.bin \
 	$(datadir)/pxe-rtl8139.bin \
         $(datadir)/pxe-pcnet.bin \
+	$(datadir)/extboot.bin \
 	$(docdir)/qemu-doc.html \
 	$(docdir)/qemu-tech.html \
 	$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )

Reply via email to