summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhpa <hpa>2001-12-10 06:46:18 +0000
committerhpa <hpa>2001-12-10 06:46:18 +0000
commita47c6d65c1fbab9d821b869fc0bd30ae441f24fc (patch)
tree5c60a193c3575213d90b1cf6494b9865ee802e01
parentb4809196e7d8ea83991b0cd51f989e7210b5b5b0 (diff)
downloadsyslinux-a47c6d65c1fbab9d821b869fc0bd30ae441f24fc.tar.gz
Yet another snapshot. Now we can actually compile the sucker...
-rw-r--r--memdisk/Makefile26
-rw-r--r--memdisk/e820.h3
-rw-r--r--memdisk/init.S16150
-rw-r--r--memdisk/memdisk.asm20
-rw-r--r--memdisk/msetup.c157
-rw-r--r--memdisk/setup.c271
6 files changed, 462 insertions, 165 deletions
diff --git a/memdisk/Makefile b/memdisk/Makefile
index 52b33f71..9e81685c 100644
--- a/memdisk/Makefile
+++ b/memdisk/Makefile
@@ -12,16 +12,21 @@
## -----------------------------------------------------------------------
CC = gcc
-CFLAGS = -g -O2 -fomit-frame-pointer -march=i386 -malign-functions=0 -malign-jumps=0 -malign-loops=0
+CFLAGS = -g -O2 -fomit-frame-pointer -march=i386 \
+ -malign-functions=0 -malign-jumps=0 -malign-loops=0
LDFLAGS =
AS = as
LD = ld
NASM = nasm
+OBJCOPY = objcopy
-all: e820func.o16 msetup.o16 e820test memdisk.bin
+# Important: init.o16 must be first!!
+OBJS = init.o16 setup.o16 msetup.o16 e820func.o16 memdisk.o
+
+all: memdisk e820test
clean:
- rm -f *.o *.s *.o16 *.s16 *.bin *.lst e820test
+ rm -f *.o *.s *.o16 *.s16 *.bin *.lst *.elf e820test memdisk
%.o16: %.s16
$(AS) -o $@ $<
@@ -29,15 +34,30 @@ clean:
%.s16: %.s
echo '.code16' | cat - $< > $@
+%.s: %.S
+ $(CC) -x c $(CFLAGS) -Wp,-traditional -E -o $@ $<
+
+%.s16: %.S16
+ $(CC) -x c $(CFLAGS) -Wp,-traditional -E -o $@ $<
+
%.s: %.c
$(CC) $(CFLAGS) -S -o $@ $<
+%.i: %.c
+ $(CC) $(CFLAGS) -E -o $@ $<
+
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.bin: %.asm
$(NASM) -f bin -o $@ -l $*.lst $<
+memdisk.elf: $(OBJS)
+ $(LD) -Ttext 0 -o $@ $^
+
+memdisk: memdisk.elf
+ $(OBJCOPY) -O binary $< $@
+
e820test: e820func.o msetup.o e820test.o memdisk.o
$(CC) $(LDFLAGS) -o $@ $^
diff --git a/memdisk/e820.h b/memdisk/e820.h
index ec261d7f..7f064c43 100644
--- a/memdisk/e820.h
+++ b/memdisk/e820.h
@@ -26,5 +26,8 @@ struct e820range {
extern struct e820range ranges[];
extern int nranges;
+extern uint32_t dos_mem, low_mem, high_mem;
extern void insertrange(uint64_t, uint64_t, uint32_t);
+extern void get_mem(void);
+extern void parse_mem(void);
diff --git a/memdisk/init.S16 b/memdisk/init.S16
new file mode 100644
index 00000000..0231d8f7
--- /dev/null
+++ b/memdisk/init.S16
@@ -0,0 +1,150 @@
+/* $Id$ */
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * 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, Inc., 53 Temple Place Ste 330,
+ * Bostom MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * init.S
+ *
+ * memdisk initialization code
+ *
+ * This module *MUST* get linked first into the image
+ */
+
+ .section ".text", "ax"
+ .code16
+
+/*
+ * The header derives directly from the Linux kernel and must be first
+ * in the binary, hence section .init
+ */
+ .org 497
+setup_sects: .byte 0 # Filled in later
+root_flags: .word 0
+syssize: .word 0
+swap_dev: .word 0
+ram_size: .word 0
+vid_mode: .word 0
+root_dev: .word 0
+boot_flag: .word 0xAA55
+
+ .globl _start
+_start:
+ jmp start
+
+# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+
+ .ascii "HdrS" # header signature
+ .word 0x0203 # header version number (>= 0x0105)
+ # or else old loadlin-1.5 will fail)
+realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
+start_sys_seg: .word 0
+ .word kernel_version # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5).
+
+type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
+ # Bootlin, SYSLX, bootsect...)
+ # See Documentation/i386/boot.txt for
+ # assigned ids
+
+# flags, unused bits must be zero (RFU) bit within loadflags
+loadflags:
+LOADED_HIGH = 1 # If set, the kernel is loaded high
+CAN_USE_HEAP = 0x80 # If set, the loader also has set
+ # heap_end_ptr to tell how much
+ # space behind setup.S can be used for
+ # heap purposes.
+ # Only the loader knows what is free
+
+ .byte LOADED_HIGH # Dont force the loader to move
+ # us to 0x90000
+
+setup_move_size: .word 0 # Unused
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+#ifndef __BIG_KERNEL__
+ .long 0x1000 # 0x1000 = default for zImage
+#else
+ .long 0x100000 # 0x100000 = default for big kernel
+#endif
+
+ramdisk_image: .long 0 # address of loaded ramdisk image
+ # Here the loader puts the 32-bit
+ # address where it loaded the image.
+ # This only will be read by the kernel.
+
+ramdisk_size: .long 0 # its size in bytes
+
+bootsect_kludge:
+ .word 0, 0
+
+heap_end_ptr: .word 0 # (Header version 0x0201 or later)
+ # space from here (exclusive) down to
+ # end of setup code can be used by setup
+ # for local heap purposes.
+
+pad1: .word 0
+cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
+ # If nonzero, a 32-bit pointer
+ # to the kernel command line.
+ # The command line should be
+ # located between the start of
+ # setup and the end of low
+ # memory (0xa0000), or it may
+ # get overwritten before it
+ # gets read. If this field is
+ # used, there is no longer
+ # anything magical about the
+ # 0x90000 segment; the setup
+ # can be located anywhere in
+ # low memory 0x10000 or higher.
+
+ramdisk_max: .long 0xffffffff # Load ramdisk as high as
+ # absolutely possible
+
+/* ------------------- End of setup header --------------------------- */
+
+/*
+ * Canonicalize CS to match the other segments; also, the C
+ * code uses 32-bit registers to make sure the high part of
+ * %esp is zero.
+ */
+start:
+ .byte 0x66,0x0f,0xb7,0xe4 # movzwl %sp,%esp
+ pushw %ds
+ pushw $main
+ lret
+
+main:
+ sti
+ call setup # Call the C code
+ # The setup function returns the drive number,
+ # which should be returned in %dl
+ movw %ax,%dx
+
+ # If the C code returns, we are good to go, and the
+ # new boot sector is already loaded
+ cli
+ movw $0x7c00,%sp
+ xorw %si,%si # Not a partition BS - SI <- 0
+ movw %si,%ds
+ movw %si,%es
+ movw %si,%fs
+ movw %si,%gs
+ movw %si,%ss
+ ljmp $0,$0x7c00 # Entrypoint at 0000:7C00
+
+ .section ".rodata", "a"
+kernel_version: .ascii "MEMDISK ..."
+ .byte 0
+
diff --git a/memdisk/memdisk.asm b/memdisk/memdisk.asm
index 46e19236..3c9513dd 100644
--- a/memdisk/memdisk.asm
+++ b/memdisk/memdisk.asm
@@ -127,8 +127,16 @@ GetDriveType:
pop ax ; Drop return address
mov ah,[DriveNo]
shr ah,7
+ pushf
or ah,02h ; CF = 0
mov P_AH,ah
+ popf
+ jz .floppy
+ mov ax,[DiskSize] ; For hard disk return the size
+ mov P_DX,ax
+ mov ax,[DiskSize+2]
+ mov P_CX,ax
+.floppy:
mov [LastStatus],byte 0 ; Success, but AH returns a value
jmp short DoneWeird
@@ -402,15 +410,12 @@ Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
db 00h ; Extended access rights
Mover_dst2: db 0 ; High 8 bits of source addy
+LastStatus db 0 ; Last return status
+
section .bss
alignb 4
PatchArea equ $ ; This gets filled in by the installer
-DriveNo resb 1 ; Our drive number
-DriveType resb 1 ; Our drive type (floppies)
-LastStatus resb 1 ; Last return status
- resb 1 ; pad
-
Cylinders resw 1 ; Cylinder count
Heads resw 1 ; Head count
Sectors resd 1 ; Sector count (zero-extended)
@@ -425,6 +430,11 @@ MemInt1588 resd 1 ; 1MB-65MB memory amount (1K)
OldInt13 resd 1 ; INT 13h in chain
OldInt15 resd 1 ; INT 15h in chain
+OldDosMem resw 1 ; Old position of DOS mem end
+
+DriveNo resb 1 ; Our drive number
+DriveType resb 1 ; Our drive type (floppies)
+
; End patch area
Stack resd 2 ; Saved SS:ESP on invocation
diff --git a/memdisk/msetup.c b/memdisk/msetup.c
index 713f368f..c37169be 100644
--- a/memdisk/msetup.c
+++ b/memdisk/msetup.c
@@ -181,160 +181,3 @@ void parse_mem(void)
}
}
}
-
-extern const char _binary_memdisk_bin_start[], _binary_memdisk_bin_end[];
-extern const char _binary_memdisk_bin_size[]; /* Weird, I know */
-struct memdisk_header {
- uint16_t int13_offs;
- uint16_t int15_offs;
- uint16_t patch_offs;
- uint16_t total_size;
-};
-struct patch_area {
- uint8_t driveno;
- uint8_t drivetype;
- uint8_t laststatus;
- uint8_t _pad1;
-
- uint16_t cylinders;
- uint16_t heads;
- uint32_t sectors;
- uint32_t disksize;
- uint32_t diskbuf;
-
- uint32_t e820table;
- uint32_t mem1mb;
- uint32_t mem16mb;
- uint32_t memint1588;
-
- uint32_t oldint13;
- uint32_t oldint15;
- uint16_t olddosmem;
-};
-
-/* Access to objects in the zero page */
-static inline void
-wrz_8(uint32_t addr, uint8_t data)
-{
- asm volatile("movb %0,%%fs:%1" :: "ri" (data), "m" (*(uint8_t *)addr));
-}
-static inline void
-wrz_16(uint32_t addr, uint16_t data)
-{
- asm volatile("movw %0,%%fs:%1" :: "ri" (data), "m" (*(uint16_t *)addr));
-}
-static inline void
-wrz_32(uint32_t addr, uint16_t data)
-{
- asm volatile("movl %0,%%fs:%1" :: "ri" (data), "m" (*(uint32_t *)addr));
-}
-static inline uint8_t
-rdz_8(uint32_t addr)
-{
- uint8_t data;
- asm volatile("movb %%fs:%1,%0" : "=r" (data) : "m" (*(uint8_t *)addr));
- return data;
-}
-static inline uint16_t
-rdz_16(uint32_t addr)
-{
- uint16_t data;
- asm volatile("movw %%fs:%1,%0" : "=r" (data) : "m" (*(uint16_t *)addr));
- return data;
-}
-static inline uint8_t
-rdz_32(uint32_t addr)
-{
- uint32_t data;
- asm volatile("movl %%fs:%1,%0" : "=r" (data) : "m" (*(uint32_t *)addr));
- return data;
-}
-
-/* Addresses in the zero page */
-#define BIOS_INT13 (0x13*4) /* INT 13h vector */
-#define BIOS_INT15 (0x15*4) /* INT 13h vector */
-#define BIOS_BASEMEM 0x413 /* Amount of DOS memory */
-
-void setup(void)
-{
- unsigned int size = (int) &_binary_memdisk_bin_size;
- struct memdisk_header *hptr;
- struct patch_area *pptr;
- uint16_t driverseg;
- uint32_t driverptr, driveraddr;
- uint8_t driveno = 0;
- uint8_t status;
- uint16_t exitcode;
-
- /* Point %fs to the zero page */
- asm volatile("movw %0,%%fs" :: "r" (0));
-
- get_mem();
- parse_mem();
-
- /* Figure out where it needs to go */
- hptr = (struct memdisk_header *) &_binary_memdisk_bin_start;
- pptr = (struct patch_area *)(_binary_memdisk_bin_start + hptr->patch_offs);
-
- if ( hptr->total_size > dos_mem ) {
- /* Badness... */
- }
-
- pptr->olddosmem = rdz_16(BIOS_BASEMEM);
-
- driveraddr = dos_mem - hptr->total_size;
- driveraddr &= ~0x3FF;
-
- /* Reserve this range of memory */
- insertrange(driveraddr, dos_mem-driveraddr, 2);
- parse_mem();
-
- pptr->mem1mb = low_mem >> 10;
- pptr->mem16mb = high_mem >> 16;
- pptr->memint1588 = (low_mem == 0xf00000)
- ? ((high_mem > 0x30ffc00) ? 0xffff : (high_mem >> 10)+0x3c00)
- : (low_mem >> 10);
-
- driverseg = driveraddr >> 4;
- driverptr = driverseg << 16;
-
- pptr->oldint13 = rdz_32(BIOS_INT13);
- pptr->oldint15 = rdz_32(BIOS_INT15);
-
- /* Claim the memory and copy the driver into place */
- wrz_16(BIOS_BASEMEM, dos_mem >> 10);
-
- asm volatile("pushw %%es ; "
- "movw %0,%%es ; "
- "rep ; movsl %%ds:(%%si), %%es:(%%di) ; "
- "popw %%es"
- :: "r" (driverseg),
- "c" (size >> 2),
- "S" (&_binary_memdisk_bin_start),
- "D" (0));
-
- /* Install the interrupt handlers */
- wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
- wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
-
- /* Reboot into the new "disk" */
- asm volatile("pushw %%es ; "
- "xorw %%cx,%%cx ; "
- "movw %%cx,%%es ; "
- "incw %%cx ; "
- "movw $0x0201,%%ax ; "
- "movw $0x7c00,%%bx ; "
- "int $0x13 ; "
- "setc %0 ; "
- "popw %%es"
- : "=r" (status), "=a" (exitcode)
- : "d" ((uint16_t)driveno)
- : "ebx", "ecx", "edx", "esi", "edi", "ebp");
-
- if ( status ) {
- /* Badness... */
- }
-
- /* On return the assembly code will jump to the boot vector */
-}
-
diff --git a/memdisk/setup.c b/memdisk/setup.c
new file mode 100644
index 00000000..d40f21bb
--- /dev/null
+++ b/memdisk/setup.c
@@ -0,0 +1,271 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * 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, Inc., 53 Temple Place Ste 330,
+ * Bostom MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdint.h>
+#include "e820.h"
+
+extern const char _binary_memdisk_bin_start[], _binary_memdisk_bin_end[];
+extern const char _binary_memdisk_bin_size[]; /* Weird, I know */
+
+struct memdisk_header {
+ uint16_t int13_offs;
+ uint16_t int15_offs;
+ uint16_t patch_offs;
+ uint16_t total_size;
+};
+
+struct patch_area {
+ uint16_t cylinders;
+ uint16_t heads;
+ uint32_t sectors;
+ uint32_t disksize;
+ uint32_t diskbuf;
+
+ uint32_t e820table;
+ uint32_t mem1mb;
+ uint32_t mem16mb;
+ uint32_t memint1588;
+
+ uint32_t oldint13;
+ uint32_t oldint15;
+
+ uint16_t olddosmem;
+
+ uint8_t driveno;
+ uint8_t drivetype;
+};
+
+/* This is the header in the boot sector/setup area */
+struct setup_header {
+ uint8_t dummy[0x1f1]; /* Boot sector */
+ uint8_t setup_secs;
+ uint16_t syssize;
+ uint16_t swap_dev;
+ uint16_t ram_size;
+ uint16_t vid_mode;
+ uint16_t root_dev;
+ uint16_t boot_flag;
+ uint16_t jump;
+ char header[4];
+ uint16_t version;
+ uint32_t realmode_swtch;
+ uint32_t start_sys;
+ uint8_t type_of_loader;
+ uint8_t loadflags;
+ uint16_t setup_move_size;
+ uint32_t code32_start;
+ uint32_t ramdisk_image;
+ uint32_t ramdisk_size;
+ uint32_t bootsect_kludge;
+ uint16_t head_end_ptr;
+ uint16_t pad1;
+ uint32_t cmd_line_ptr;
+ uint32_t initrd_addr_max;
+};
+
+const struct setup_header * const shdr = (struct setup_header *)0;
+
+/* Access to objects in the zero page */
+static inline void
+wrz_8(uint32_t addr, uint8_t data)
+{
+ asm volatile("movb %0,%%fs:%1" :: "ri" (data), "m" (*(uint8_t *)addr));
+}
+static inline void
+wrz_16(uint32_t addr, uint16_t data)
+{
+ asm volatile("movw %0,%%fs:%1" :: "ri" (data), "m" (*(uint16_t *)addr));
+}
+static inline void
+wrz_32(uint32_t addr, uint16_t data)
+{
+ asm volatile("movl %0,%%fs:%1" :: "ri" (data), "m" (*(uint32_t *)addr));
+}
+static inline uint8_t
+rdz_8(uint32_t addr)
+{
+ uint8_t data;
+ asm volatile("movb %%fs:%1,%0" : "=r" (data) : "m" (*(uint8_t *)addr));
+ return data;
+}
+static inline uint16_t
+rdz_16(uint32_t addr)
+{
+ uint16_t data;
+ asm volatile("movw %%fs:%1,%0" : "=r" (data) : "m" (*(uint16_t *)addr));
+ return data;
+}
+static inline uint8_t
+rdz_32(uint32_t addr)
+{
+ uint32_t data;
+ asm volatile("movl %%fs:%1,%0" : "=r" (data) : "m" (*(uint32_t *)addr));
+ return data;
+}
+
+/* Addresses in the zero page */
+#define BIOS_INT13 (0x13*4) /* INT 13h vector */
+#define BIOS_INT15 (0x15*4) /* INT 13h vector */
+#define BIOS_BASEMEM 0x413 /* Amount of DOS memory */
+
+/*
+ * Figure out the "geometry" of the disk in question
+ */
+struct geometry {
+ uint32_t size; /* Size in bytes */
+ uint32_t sectors; /* 512-byte sector limit */
+ uint32_t c, h, s; /* C/H/S geometry */
+ uint8_t type; /* Type byte for INT 13h AH=08h */
+ uint8_t driveno; /* Drive no */
+};
+
+static const uint32_t image_sizes[] =
+{ 360*1024,
+ 720*1024,
+ 1200*1024,
+ 1440*1024,
+ 2880*1024,
+ 0 };
+static const struct geometry geometries[] =
+{ { 360*1024, 720, 40, 2, 9, 0x01, 0 },
+ { 720*1024, 1440, 80, 2, 9, 0x03, 0 },
+ { 1200*1024, 2400, 80, 2, 15, 0x02, 0 },
+ { 1440*1024, 2880, 80, 2, 18, 0x04, 0 },
+ { 2880*1024, 5760, 80, 2, 36, 0x06, 0 } };
+#define known_geometries (sizeof(geometries)/sizeof(struct geometry))
+
+const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size)
+{
+ static struct geometry hd_geometry;
+ int i;
+
+ for ( i = 0 ; i < known_geometries ; i++ ) {
+ if ( size == geometries[i].size ) {
+ return &geometries[i];
+ }
+ }
+
+ /* No match, must be a hard disk image */
+ /* Need to examine the partition table for geometry */
+
+ /* For now, though, just use a simple standard geometry CHS = Nx16x63 */
+
+ hd_geometry.size = size & ~0x1FF;
+ hd_geometry.sectors = size >> 9;
+ hd_geometry.c = size/(16*63*512);
+ hd_geometry.h = 16;
+ hd_geometry.s = 63;
+ hd_geometry.type = 0;
+ hd_geometry.driveno = 0x80; /* Hard drive */
+
+ return &hd_geometry;
+}
+
+/*
+ * Actual setup routine
+ * Returns the drive number (which is then passed in %dl to the
+ * called routine.)
+ */
+uint32_t setup(void)
+{
+ unsigned int size = (int) &_binary_memdisk_bin_size;
+ struct memdisk_header *hptr;
+ struct patch_area *pptr;
+ uint16_t driverseg;
+ uint32_t driverptr, driveraddr;
+ uint8_t driveno = 0;
+ uint8_t status;
+ uint16_t exitcode;
+ const struct geometry *geometry;
+
+ /* Point %fs to the zero page */
+ asm volatile("movw %0,%%fs" :: "r" (0));
+
+ geometry = get_disk_image_geometry(shdr->ramdisk_image, shdr->ramdisk_size);
+
+ get_mem();
+ parse_mem();
+
+ /* Figure out where it needs to go */
+ hptr = (struct memdisk_header *) &_binary_memdisk_bin_start;
+ pptr = (struct patch_area *)(_binary_memdisk_bin_start + hptr->patch_offs);
+
+ if ( hptr->total_size > dos_mem ) {
+ /* Badness... */
+ }
+
+ pptr->olddosmem = rdz_16(BIOS_BASEMEM);
+ pptr->driveno = geometry->driveno;
+ pptr->drivetype = geometry->type;
+ pptr->cylinders = geometry->c;
+ pptr->heads = geometry->h;
+ pptr->sectors = geometry->s;
+ pptr->disksize = geometry->sectors;
+ pptr->diskbuf = shdr->ramdisk_image;
+
+ driveraddr = dos_mem - hptr->total_size;
+ driveraddr &= ~0x3FF;
+
+ /* Reserve this range of memory */
+ insertrange(driveraddr, dos_mem-driveraddr, 2);
+ parse_mem();
+
+ pptr->mem1mb = low_mem >> 10;
+ pptr->mem16mb = high_mem >> 16;
+ pptr->memint1588 = (low_mem == 0xf00000)
+ ? ((high_mem > 0x30ffc00) ? 0xffff : (high_mem >> 10)+0x3c00)
+ : (low_mem >> 10);
+
+ driverseg = driveraddr >> 4;
+ driverptr = driverseg << 16;
+
+ pptr->oldint13 = rdz_32(BIOS_INT13);
+ pptr->oldint15 = rdz_32(BIOS_INT15);
+
+ /* Claim the memory and copy the driver into place */
+ wrz_16(BIOS_BASEMEM, dos_mem >> 10);
+
+ asm volatile("pushw %%es ; "
+ "movw %0,%%es ; "
+ "rep ; movsl %%ds:(%%si), %%es:(%%di) ; "
+ "popw %%es"
+ :: "r" (driverseg),
+ "c" (size >> 2),
+ "S" (&_binary_memdisk_bin_start),
+ "D" (0));
+
+ /* Install the interrupt handlers */
+ wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
+ wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
+
+ /* Reboot into the new "disk" */
+ asm volatile("pushw %%es ; "
+ "xorw %%cx,%%cx ; "
+ "movw %%cx,%%es ; "
+ "incw %%cx ; "
+ "movw $0x0201,%%ax ; "
+ "movw $0x7c00,%%bx ; "
+ "int $0x13 ; "
+ "setc %0 ; "
+ "popw %%es"
+ : "=r" (status), "=a" (exitcode)
+ : "d" ((uint16_t)driveno)
+ : "ebx", "ecx", "edx", "esi", "edi", "ebp");
+
+ if ( status ) {
+ /* Badness... */
+ }
+
+ /* On return the assembly code will jump to the boot vector */
+ return driveno;
+}