summaryrefslogtreecommitdiff
path: root/gpxe/src/arch/i386/prefix
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-03-26 16:25:35 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-03-26 16:25:35 -0700
commit9eddd22a7b53b1d02fbae0d987df8af122924248 (patch)
tree882f5152880b0b1aa2d7a0619d30065acc69fb16 /gpxe/src/arch/i386/prefix
parentbbb8f15936b851e6a0ef6f7bb2c95197bff35994 (diff)
downloadsyslinux-9eddd22a7b53b1d02fbae0d987df8af122924248.tar.gz
Add gPXE into the source tree; build unified imagesyslinux-3.70-pre7
Diffstat (limited to 'gpxe/src/arch/i386/prefix')
-rw-r--r--gpxe/src/arch/i386/prefix/bImageprefix.S611
-rw-r--r--gpxe/src/arch/i386/prefix/bootpart.S216
-rw-r--r--gpxe/src/arch/i386/prefix/comprefix.S46
-rw-r--r--gpxe/src/arch/i386/prefix/dskprefix.S375
-rw-r--r--gpxe/src/arch/i386/prefix/elf_dprefix.S94
-rw-r--r--gpxe/src/arch/i386/prefix/elfprefix.S94
-rwxr-xr-xgpxe/src/arch/i386/prefix/exeprefix.S41
-rw-r--r--gpxe/src/arch/i386/prefix/hdprefix.S103
-rw-r--r--gpxe/src/arch/i386/prefix/kpxeprefix.S7
-rw-r--r--gpxe/src/arch/i386/prefix/libprefix.S657
-rw-r--r--gpxe/src/arch/i386/prefix/lkrnprefix.S155
-rw-r--r--gpxe/src/arch/i386/prefix/lmelf_dprefix.S161
-rw-r--r--gpxe/src/arch/i386/prefix/lmelf_prefix.S161
-rw-r--r--gpxe/src/arch/i386/prefix/mbr.S13
-rw-r--r--gpxe/src/arch/i386/prefix/nbiprefix.S76
-rw-r--r--gpxe/src/arch/i386/prefix/nullprefix.S13
-rw-r--r--gpxe/src/arch/i386/prefix/pxeprefix.S666
-rw-r--r--gpxe/src/arch/i386/prefix/romprefix.S383
-rw-r--r--gpxe/src/arch/i386/prefix/unnrv2b.S182
-rw-r--r--gpxe/src/arch/i386/prefix/usbdisk.S23
20 files changed, 4077 insertions, 0 deletions
diff --git a/gpxe/src/arch/i386/prefix/bImageprefix.S b/gpxe/src/arch/i386/prefix/bImageprefix.S
new file mode 100644
index 00000000..30020f73
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/bImageprefix.S
@@ -0,0 +1,611 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+ Eric Biederman (ebiederman@lnxi.com)
+
+ This code also derives a lot from arch/i386/boot/setup.S in
+ the linux kernel.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+ The setup code that is present here does a lot of things
+ exactly the way the linux kernel does them instead of in
+ ways more typical of etherboot. Generally this is so
+ the code can be strongly compatible with the linux kernel.
+ In addition the general etherboot technique of enabling the a20
+ after we switch into protected mode does not work if etherboot
+ is being loaded at 1MB.
+*/
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/* Simple and small GDT entries for booting only */
+#define GDT_ENTRY_BOOT_CS 2
+#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1)
+#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8)
+#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8)
+
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */
+
+/* Signature words to ensure LILO loaded us right */
+#define SIG1 0xAA55
+#define SIG2 0x5A5A
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lkrn file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ jmp trampoline
+# 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 SYSSEG # low load segment (obsolete)
+ .word kernel_version - setup_code
+ # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5). Don't
+ # change it.
+
+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
+
+setup_move_size: .word 0x8000 # size to move, when setup is not
+ # loaded at 0x90000. We will move setup
+ # to 0x90000 then just before jumping
+ # into the kernel. However, only the
+ # loader knows how much data behind
+ # us also needs to be loaded.
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+ .long 0x100000 # 0x100000 = default for big kernel
+
+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:
+ .long 0 # obsolete
+
+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 0 # (Header version 0x0203 or later)
+ # The highest safe address for
+ # the contents of an initrd
+
+trampoline: call start_of_setup
+trampoline_end:
+ .space 1024
+# End of setup header #####################################################
+
+start_of_setup:
+# Set %ds = %cs, we know that SETUPSEG = %cs at this point
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+# Check signature at end of setup
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne bad_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne bad_sig
+
+ jmp good_sig1
+
+# Routine to print asciiz string at ds:si
+prtstr:
+ lodsb
+ andb %al, %al
+ jz fin
+
+ call prtchr
+ jmp prtstr
+
+fin: ret
+
+# Part of above routine, this one just prints ascii al
+prtchr: pushw %ax
+ pushw %cx
+ movw $7,%bx
+ movw $0x01, %cx
+ movb $0x0e, %ah
+ int $0x10
+ popw %cx
+ popw %ax
+ ret
+
+no_sig_mess: .string "No setup signature found ..."
+
+good_sig1:
+ jmp good_sig
+
+# We now have to find the rest of the setup code/data
+bad_sig:
+ movw %cs, %ax # SETUPSEG
+ subw $DELTA_INITSEG, %ax # INITSEG
+ movw %ax, %ds
+ xorb %bh, %bh
+ movb (497), %bl # get setup sect from bootsect
+ subw $4, %bx # LILO loads 4 sectors of setup
+ shlw $8, %bx # convert to words (1sect=2^8 words)
+ movw %bx, %cx
+ shrw $3, %bx # convert to segment
+ addw $SYSSEG, %bx
+ movw %bx, %cs:(start_sys_seg - setup_code)
+# Move rest of setup code/data to here
+ movw $2048, %di # four sectors loaded by LILO
+ subw %si, %si
+ pushw %cs
+ popw %es
+ movw $SYSSEG, %ax
+ movw %ax, %ds
+ rep
+ movsw
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne no_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne no_sig
+
+ jmp good_sig
+
+no_sig:
+ lea (no_sig_mess - setup_code), %si
+ call prtstr
+
+no_sig_loop:
+ hlt
+ jmp no_sig_loop
+
+good_sig:
+ cmpw $0, %cs:(realmode_swtch - setup_code)
+ jz rmodeswtch_normal
+
+ lcall *%cs:(realmode_swtch - setup_code)
+ jmp rmodeswtch_end
+
+rmodeswtch_normal:
+ pushw %cs
+ call default_switch
+
+rmodeswtch_end:
+# we get the code32 start address and modify the below 'jmpi'
+# (loader may have changed it)
+ movl %cs:(code32_start - setup_code), %eax
+ movl %eax, %cs:(code32 - setup_code)
+
+# then we load the segment descriptors
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+
+#
+# Enable A20. This is at the very best an annoying procedure.
+# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+#
+
+A20_TEST_LOOPS = 32 # Iterations per wait
+A20_ENABLE_LOOPS = 255 # Total loops to try
+
+a20_try_loop:
+
+ # First, see if we are on a system with no A20 gate.
+a20_none:
+ call a20_test
+ jnz a20_done
+
+ # Next, try the BIOS (INT 0x15, AX=0x2401)
+a20_bios:
+ movw $0x2401, %ax
+ pushfl # Be paranoid about flags
+ int $0x15
+ popfl
+
+ call a20_test
+ jnz a20_done
+
+ # Try enabling A20 through the keyboard controller
+a20_kbc:
+ call empty_8042
+
+ call a20_test # Just in case the BIOS worked
+ jnz a20_done # but had a delayed reaction.
+
+ movb $0xD1, %al # command write
+ outb %al, $0x64
+ call empty_8042
+
+ movb $0xDF, %al # A20 on
+ outb %al, $0x60
+ call empty_8042
+
+ # Wait until a20 really *is* enabled; it can take a fair amount of
+ # time on certain systems; Toshiba Tecras are known to have this
+ # problem.
+a20_kbc_wait:
+ xorw %cx, %cx
+a20_kbc_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_kbc_wait_loop
+
+ # Final attempt: use "configuration port A"
+a20_fast:
+ inb $0x92, %al # Configuration Port A
+ orb $0x02, %al # "fast A20" version
+ andb $0xFE, %al # don't accidentally reset
+ outb %al, $0x92
+
+ # Wait for configuration port A to take effect
+a20_fast_wait:
+ xorw %cx, %cx
+a20_fast_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_fast_wait_loop
+
+ # A20 is still not responding. Try frobbing it again.
+ #
+ decb (a20_tries - setup_code)
+ jnz a20_try_loop
+
+ movw $(a20_err_msg - setup_code), %si
+ call prtstr
+
+a20_die:
+ hlt
+ jmp a20_die
+
+a20_tries:
+ .byte A20_ENABLE_LOOPS
+
+a20_err_msg:
+ .ascii "linux: fatal error: A20 gate not responding!"
+ .byte 13, 10, 0
+
+ # If we get here, all is good
+a20_done:
+ # Leave the idt alone
+
+ # set up gdt
+ xorl %eax, %eax # Compute gdt_base
+ movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
+ shll $4, %eax
+ addl $(bImage_gdt - setup_code), %eax
+ movl %eax, (bImage_gdt_48+2 - setup_code)
+ DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is
+ # appropriate
+
+ # Switch to protected mode
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ DATA32 ljmp *%ds:(code32 - setup_code)
+code32:
+ .long 0x100000
+ .word __BOOT_CS, 0
+
+# Here's a bunch of information about your current kernel..
+kernel_version: .ascii "Etherboot "
+ .ascii VERSION
+ .byte 0
+
+# This is the default real mode switch routine.
+# to be called just before protected mode transition
+default_switch:
+ cli # no interrupts allowed !
+ movb $0x80, %al # disable NMI for bootup
+ # sequence
+ outb %al, $0x70
+ lret
+
+# This routine tests whether or not A20 is enabled. If so, it
+# exits with zf = 0.
+#
+# The memory address used, 0x200, is the int $0x80 vector, which
+# should be safe.
+
+A20_TEST_ADDR = 4*0x80
+
+a20_test:
+ pushw %cx
+ pushw %ax
+ xorw %cx, %cx
+ movw %cx, %fs # Low memory
+ decw %cx
+ movw %cx, %gs # High memory area
+ movw $A20_TEST_LOOPS, %cx
+ movw %fs:(A20_TEST_ADDR), %ax
+ pushw %ax
+a20_test_wait:
+ incw %ax
+ movw %ax, %fs:(A20_TEST_ADDR)
+ call delay # Serialize and make delay constant
+ cmpw %gs:(A20_TEST_ADDR+0x10), %ax
+ loope a20_test_wait
+
+ popw %fs:(A20_TEST_ADDR)
+ popw %ax
+ popw %cx
+ ret
+
+
+# This routine checks that the keyboard command queue is empty
+# (after emptying the output buffers)
+#
+# Some machines have delusions that the keyboard buffer is always full
+# with no keyboard attached...
+#
+# If there is no keyboard controller, we will usually get 0xff
+# to all the reads. With each IO taking a microsecond and
+# a timeout of 100,000 iterations, this can take about half a
+# second ("delay" == outb to port 0x80). That should be ok,
+# and should also be plenty of time for a real keyboard controller
+# to empty.
+#
+
+empty_8042:
+ pushl %ecx
+ movl $100000, %ecx
+
+empty_8042_loop:
+ decl %ecx
+ jz empty_8042_end_loop
+
+ call delay
+
+ inb $0x64, %al # 8042 status port
+ testb $1, %al # output buffer?
+ jz no_output
+
+ call delay
+ inb $0x60, %al # read it
+ jmp empty_8042_loop
+
+no_output:
+ testb $2, %al # is input buffer full?
+ jnz empty_8042_loop # yes - loop
+empty_8042_end_loop:
+ popl %ecx
+
+
+# Delay is needed after doing I/O
+delay:
+ outb %al,$0x80
+ ret
+
+# Descriptor tables
+#
+# NOTE: The intel manual says gdt should be sixteen bytes aligned for
+# efficiency reasons. However, there are machines which are known not
+# to boot with misaligned GDTs, so alter this at your peril! If you alter
+# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
+# empty GDT entries (one for NULL and one reserved).
+#
+# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
+# true for the Voyager Quad CPU card which will not boot without
+# This directive. 16 byte aligment is recommended by intel.
+#
+ .balign 16
+bImage_gdt:
+ .fill GDT_ENTRY_BOOT_CS,8,0
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9A00 # code read/exec
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9200 # data read/write
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+bImage_gdt_end:
+ .balign 4
+
+ .word 0 # alignment byte
+bImage_idt_48:
+ .word 0 # idt limit = 0
+ .long 0 # idt base = 0L
+
+ .word 0 # alignment byte
+bImage_gdt_48:
+ .word bImage_gdt_end - bImage_gdt - 1 # gdt limit
+ .long bImage_gdt_48 - setup_code # gdt base (filled in later)
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+prefix_exit_end:
+ .previous
+
+
+ .org (PREFIXSIZE - 4)
+# Setup signature -- must be last
+setup_sig1: .word SIG1
+setup_sig2: .word SIG2
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * The linux bImage protocol does not do this, but since we
+ * don't need any information that's left in the prefix, it
+ * doesn't matter: we just have to ensure that we make it to _start
+ *
+ * protected_start will live at 0x100000 and it will be the
+ * the first code called as we enter protected mode.
+ */
+ .code32
+protected_start:
+ /* Load segment registers */
+ movw $__BOOT_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Use the internal etherboot stack */
+ movl $(_prefix_stack_end - protected_start + 0x100000), %esp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushl $0 /* Use prefix exit path mechanism */
+
+ jmp _start
+/*
+ That's about it.
+*/
diff --git a/gpxe/src/arch/i386/prefix/bootpart.S b/gpxe/src/arch/i386/prefix/bootpart.S
new file mode 100644
index 00000000..d60fe9bc
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/bootpart.S
@@ -0,0 +1,216 @@
+#define BOOT_SEG 0x07c0
+#define EXEC_SEG 0x0100
+#define STACK_SEG 0x0200
+#define STACK_SIZE 0x2000
+
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+
+/*
+ * Find active partition
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %bp : Active partition handler routine
+ */
+find_active_partition:
+ /* Set up stack at STACK_SEG:STACK_SIZE */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ /* Relocate self to EXEC_SEG */
+ pushw $BOOT_SEG
+ popw %ds
+ pushw $EXEC_SEG
+ popw %es
+ xorw %si, %si
+ xorw %di, %di
+ movw $0x200, %cx
+ rep movsb
+ ljmp $EXEC_SEG, $1f
+1: pushw %ds
+ popw %es
+ pushw %cs
+ popw %ds
+
+ /* Check for LBA extensions */
+ movb $0x41, %ah
+ movw $0x55aa, %bx
+ stc
+ int $0x13
+ jc 1f
+ cmpw $0xaa55, %bx
+ jne 1f
+ movw $read_lba, read_sectors
+1:
+ /* Read and process root partition table */
+ xorb %dh, %dh
+ movw $0x0001, %cx
+ xorl %esi, %esi
+ xorl %edi, %edi
+ call process_table
+
+ /* Print failure message */
+ movw $10f, %si
+ jmp boot_error
+10: .asciz "Could not locate active partition\r\n"
+
+/*
+ * Print failure message and boot next device
+ *
+ * Parameters:
+ * %si : Failure string
+ */
+boot_error:
+ cld
+ movw $0x0007, %bx
+ movb $0x0e, %ah
+1: lodsb
+ testb %al, %al
+ je 99f
+ int $0x10
+ jmp 1b
+99: /* Boot next device */
+ int $0x18
+
+/*
+ * Process partition table
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %bp : Active partition handler routine
+ *
+ * Returns:
+ * CF set on error
+ */
+process_table:
+ pushal
+ call read_boot_sector
+ jc 99f
+ movw $446, %bx
+1: call process_partition
+ addw $16, %bx
+ cmpw $510, %bx
+ jne 1b
+99: popal
+ ret
+
+/*
+ * Process partition
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %bx : Offset within partition table
+ * %bp : Active partition handler routine
+ */
+process_partition:
+ pushal
+ /* Load C/H/S values from partition entry */
+ movb %es:1(%bx), %dh
+ movw %es:2(%bx), %cx
+ /* Update LBA address from partition entry */
+ addl %es:8(%bx), %edi
+ adcl $0, %esi
+ /* Check active flag */
+ testb $0x80, %es:(%bx)
+ jz 1f
+ call read_boot_sector
+ jc 99f
+ jmp *%bp
+1: /* Check for extended partition */
+ movb %es:4(%bx), %al
+ cmpb $0x05, %al
+ je 2f
+ cmpb $0x0f, %al
+ je 2f
+ cmpb $0x85, %al
+ jne 99f
+2: call process_table
+99: popal
+ /* Reload original partition table */
+ call read_boot_sector
+ ret
+
+/*
+ * Read single sector to %es:0000 and verify 0x55aa signature
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ *
+ * Returns:
+ * CF set on error
+ */
+read_boot_sector:
+ pushw %ax
+ movw $1, %ax
+ call *read_sectors
+ jc 99f
+ cmpw $0xaa55, %es:(510)
+ je 99f
+ stc
+99: popw %ax
+ ret
+
+/*
+ * Read sectors to %es:0000
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %ax : Number of sectors (max 127)
+ *
+ * Returns:
+ * CF set on error
+ */
+read_sectors: .word read_chs
+
+read_chs:
+ /* Read sectors using C/H/S address */
+ pushal
+ xorw %bx, %bx
+ movb $0x02, %ah
+ stc
+ int $0x13
+ sti
+ popal
+ ret
+
+read_lba:
+ /* Read sectors using LBA address */
+ pushal
+ movw %ax, (lba_desc + 2)
+ pushw %es
+ popw (lba_desc + 6)
+ movl %edi, (lba_desc + 8)
+ movl %esi, (lba_desc + 12)
+ movw $lba_desc, %si
+ movb $0x42, %ah
+ int $0x13
+ popal
+ ret
+
+lba_desc:
+ .byte 0x10
+ .byte 0
+ .word 1
+ .word 0x0000
+ .word 0x0000
+ .long 0, 0
diff --git a/gpxe/src/arch/i386/prefix/comprefix.S b/gpxe/src/arch/i386/prefix/comprefix.S
new file mode 100644
index 00000000..2cbbca50
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/comprefix.S
@@ -0,0 +1,46 @@
+/* We need a real mode stack that won't be stomped on by Etherboot
+ which starts at 0x20000. Choose something that's sufficiently high,
+ but not in DOC territory. Note that we couldn't do this in a real
+ .com program since stack variables are in the same segment as the
+ code and data, but this isn't really a .com program, it just looks
+ like one to make DOS load it into memory. It still has the 64kB
+ limitation of .com files though. */
+#define STACK_SEG 0x7000
+#define STACK_SIZE 0x4000
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+/* Cheat a little with the relocations: .COM files are loaded at 0x100 */
+_prefix:
+ /* Set up temporary stack */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Dummy return address - use prefix_exit */
+
+ /* Calculate segment address of image start */
+ pushw %cs
+ popw %ax
+ addw $(0x100/16), %ax
+ pushw %ax
+ pushw $_start
+ /* Calculated lcall to _start with %cs:0000 = image start */
+ lret
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/gpxe/src/arch/i386/prefix/dskprefix.S b/gpxe/src/arch/i386/prefix/dskprefix.S
new file mode 100644
index 00000000..cdc43b37
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/dskprefix.S
@@ -0,0 +1,375 @@
+/* NOTE: this boot sector contains instructions that need at least an 80186.
+ * Yes, as86 has a bug somewhere in the valid instruction set checks.
+ *
+ */
+
+/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
+ * modified by Drew Eckhardt
+ * modified by Bruce Evans (bde)
+ *
+ * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
+ *
+ * It then loads the system at SYSSEG<<4, using BIOS interrupts.
+ *
+ * The loader has been made as simple as possible, and continuous read errors
+ * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
+ * getting whole tracks at a time whenever possible.
+ */
+
+.equ BOOTSEG, 0x07C0 /* original address of boot-sector */
+
+.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */
+
+ .org 0
+ .arch i386
+ .text
+ .section ".prefix", "ax", @progbits
+ .code16
+
+ jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */
+ /* of bootsect + room for stack + 12 for */
+ /* saved disk parm block */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */
+ movw %di,%sp
+
+/* Many BIOS's default disk parameter tables will not recognize multi-sector
+ * reads beyond the maximum sector number specified in the default diskette
+ * parameter tables - this may mean 7 sectors in some cases.
+ *
+ * Since single sector reads are slow and out of the question, we must take care
+ * of this by creating new parameter tables (for the first disk) in RAM. We
+ * will set the maximum sector count to 36 - the most we will encounter on an
+ * ED 2.88. High doesn't hurt. Low does.
+ *
+ * Segments are as follows: ds=es=ss=cs - BOOTSEG
+ */
+
+ xorw %cx,%cx
+ movw %cx,%es /* access segment 0 */
+ movw $0x78, %bx /* 0:bx is parameter table address */
+ pushw %ds /* save ds */
+/* 0:bx is parameter table address */
+ ldsw %es:(%bx),%si /* loads ds and si */
+
+ movw %ax,%es /* ax is BOOTSECT (loaded above) */
+ movb $6, %cl /* copy 12 bytes */
+ cld
+ pushw %di /* keep a copy for later */
+ rep
+ movsw /* ds:si is source, es:di is dest */
+ popw %di
+
+ movb $36,%es:4(%di)
+
+ movw %cx,%ds /* access segment 0 */
+ xchgw %di,(%bx)
+ movw %es,%si
+ xchgw %si,2(%bx)
+ popw %ds /* restore ds */
+ movw %di, dpoff /* save old parameters */
+ movw %si, dpseg /* to restore just before finishing */
+ pushw %ds
+ popw %es /* reload es */
+
+/* Note that es is already set up. Also cx is 0 from rep movsw above. */
+
+ xorb %ah,%ah /* reset FDC */
+ xorb %dl,%dl
+ int $0x13
+
+/* Get disk drive parameters, specifically number of sectors/track.
+ *
+ * It seems that there is no BIOS call to get the number of sectors. Guess
+ * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
+ * 15 if sector 15 can be read. Otherwise guess 9.
+ */
+
+ movw $disksizes, %si /* table of sizes to try */
+
+probe_loop:
+ lodsb
+ cbtw /* extend to word */
+ movw %ax, sectors
+ cmpw $disksizes+4, %si
+ jae got_sectors /* if all else fails, try 9 */
+ xchgw %cx,%ax /* cx = track and sector */
+ xorw %dx,%dx /* drive 0, head 0 */
+ movw $0x0200, %bx /* address after boot sector */
+ /* (512 bytes from origin, es = cs) */
+ movw $0x0201, %ax /* service 2, 1 sector */
+ int $0x13
+ jc probe_loop /* try next value */
+
+got_sectors:
+ movw $msg1end-msg1, %cx
+ movw $msg1, %si
+ call print_str
+
+/* ok, we've written the Loading... message, now we want to load the system */
+
+ movw $SYSSEG, %ax
+ movw %ax,%es /* segment of SYSSEG<<4 */
+ pushw %es
+ call read_it
+
+/* This turns off the floppy drive motor, so that we enter the kernel in a
+ * known state, and don't have to worry about it later.
+ */
+ movw $0x3f2, %dx
+ xorb %al,%al
+ outb %al,%dx
+
+ call print_nl
+ pop %es /* = SYSSEG */
+
+/* Restore original disk parameters */
+ movw $0x78, %bx
+ movw dpoff, %di
+ movw dpseg, %si
+ xorw %ax,%ax
+ movw %ax,%ds
+ movw %di,(%bx)
+ movw %si,2(%bx)
+
+ /* Everything now loaded. %es = SYSSEG, so %es:0000 points to
+ * start of loaded image.
+ */
+
+ /* Jump to loaded copy */
+ ljmp $SYSSEG, $start_runtime
+
+endseg: .word SYSSEG + _load_size_pgh
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long endseg
+ .long 16
+ .long 0
+ .previous
+
+/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
+ * boundaries are crossed. We try to load it as fast as possible, loading whole
+ * tracks whenever we can.
+ *
+ * in: es - starting address segment (normally SYSSEG)
+ */
+read_it:
+ movw $0,sread /* load whole image including prefix */
+ movw %es,%ax
+ testw $0x0fff, %ax
+die: jne die /* es must be at 64kB boundary */
+ xorw %bx,%bx /* bx is starting address within segment */
+rp_read:
+ movw %es,%ax
+ movw %bx,%dx
+ movb $4, %cl
+ shrw %cl,%dx /* bx is always divisible by 16 */
+ addw %dx,%ax
+ cmpw endseg, %ax /* have we loaded all yet? */
+ jb ok1_read
+ ret
+ok1_read:
+ movw sectors, %ax
+ subw sread, %ax
+ movw %ax,%cx
+ shlw $9, %cx
+ addw %bx,%cx
+ jnc ok2_read
+ je ok2_read
+ xorw %ax,%ax
+ subw %bx,%ax
+ shrw $9, %ax
+ok2_read:
+ call read_track
+ movw %ax,%cx
+ addw sread, %ax
+ cmpw sectors, %ax
+ jne ok3_read
+ movw $1, %ax
+ subw head, %ax
+ jne ok4_read
+ incw track
+ok4_read:
+ movw %ax, head
+ xorw %ax,%ax
+ok3_read:
+ movw %ax, sread
+ shlw $9, %cx
+ addw %cx,%bx
+ jnc rp_read
+ movw %es,%ax
+ addb $0x10, %ah
+ movw %ax,%es
+ xorw %bx,%bx
+ jmp rp_read
+
+read_track:
+ pusha
+ pushw %ax
+ pushw %bx
+ pushw %bp /* just in case the BIOS is buggy */
+ movw $0x0e2e, %ax /* 0x2e = . */
+ movw $0x0007, %bx
+ int $0x10
+ popw %bp
+ popw %bx
+ popw %ax
+
+ movw track, %dx
+ movw sread, %cx
+ incw %cx
+ movb %dl,%ch
+ movw head, %dx
+ movb %dl,%dh
+ andw $0x0100, %dx
+ movb $2, %ah
+
+ pushw %dx /* save for error dump */
+ pushw %cx
+ pushw %bx
+ pushw %ax
+
+ int $0x13
+ jc bad_rt
+ addw $8, %sp
+ popa
+ ret
+
+bad_rt: pushw %ax /* save error code */
+ call print_all /* ah = error, al = read */
+
+ xorb %ah,%ah
+ xorb %dl,%dl
+ int $0x13
+
+ addw $10, %sp
+ popa
+ jmp read_track
+
+/* print_all is for debugging purposes. It will print out all of the registers.
+ * The assumption is that this is called from a routine, with a stack frame like
+ * dx
+ * cx
+ * bx
+ * ax
+ * error
+ * ret <- sp
+ */
+
+print_all:
+ call print_nl /* nl for readability */
+ movw $5, %cx /* error code + 4 registers */
+ movw %sp,%bp
+
+print_loop:
+ pushw %cx /* save count left */
+
+ cmpb $5, %cl
+ jae no_reg /* see if register name is needed */
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe05+0x41-1, %ax
+ subb %cl,%al
+ int $0x10
+
+ movb $0x58, %al /* 'X' */
+ int $0x10
+
+ movb $0x3A, %al /* ':' */
+ int $0x10
+
+no_reg:
+ addw $2, %bp /* next register */
+ call print_hex /* print it */
+ movb $0x20, %al /* print a space */
+ int $0x10
+ popw %cx
+ loop print_loop
+ call print_nl /* nl for readability */
+ ret
+
+print_str:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+ ret
+
+print_nl:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe0d, %ax /* CR */
+ int $0x10
+ movb $0xa, %al /* LF */
+ int $0x10
+ ret
+
+/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
+
+print_hex:
+ movw (%bp),%dx /* load word into dx */
+ movb $4, %cl
+ movb $0x0e, %ah /* write char, tty mode */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ call print_digit
+ call print_digit
+ call print_digit
+/* fall through */
+print_digit:
+ rol %cl,%dx /* rotate so that lowest 4 bits are used */
+ movb $0x0f, %al /* mask for nybble */
+ andb %dl,%al
+ addb $0x90, %al /* convert al to ascii hex (four instructions) */
+ daa
+ adcb $0x40, %al
+ daa
+ int $0x10
+ ret
+
+sread: .word 0 /* sectors read of current track */
+head: .word 0 /* current head */
+track: .word 0 /* current track */
+
+sectors:
+ .word 0
+
+dpseg: .word 0
+dpoff: .word 0
+
+disksizes:
+ .byte 36,18,15,9
+
+msg1:
+ .ascii "Loading ROM image"
+msg1end:
+
+ .org 510, 0
+ .word 0xAA55
+
+start_runtime:
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
+
diff --git a/gpxe/src/arch/i386/prefix/elf_dprefix.S b/gpxe/src/arch/i386/prefix/elf_dprefix.S
new file mode 100644
index 00000000..0eac77e0
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/elf_dprefix.S
@@ -0,0 +1,94 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/elfprefix.S b/gpxe/src/arch/i386/prefix/elfprefix.S
new file mode 100644
index 00000000..d712753a
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/elfprefix.S
@@ -0,0 +1,94 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/exeprefix.S b/gpxe/src/arch/i386/prefix/exeprefix.S
new file mode 100755
index 00000000..f1b402b7
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/exeprefix.S
@@ -0,0 +1,41 @@
+/*
+ Prefix for .exe images
+ Doesn't work yet, even though it starts off the same as a .com
+ image as shown by DOS debug.
+*/
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+_prefix:
+ .byte 'M', 'Z'
+ .short _exe_size_tail /* tail */
+ .short _exe_size_pages /* pages */
+ .short 0 /* relocations */
+ .short 2 /* header paras */
+ .short _exe_bss_size /* min */
+ .short 0xFFFF /* max paras */
+ .short _exe_ss_offset /* SS */
+ .short _stack_size /* SP */
+ .short 0 /* checksum */
+ .short 0 /* IP */
+ .short 0 /* CS */
+ .short 0x1C /* reloc offset */
+ .short 0 /* overlay number */
+ .short 0 /* fill */
+ .short 0 /* fill */
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/gpxe/src/arch/i386/prefix/hdprefix.S b/gpxe/src/arch/i386/prefix/hdprefix.S
new file mode 100644
index 00000000..56fcb36d
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/hdprefix.S
@@ -0,0 +1,103 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+ movw $load_image, %bp
+ jmp find_active_partition
+
+#include "bootpart.S"
+
+load_image:
+ /* Get disk geometry */
+ pushal
+ pushw %es
+ movb $0x08, %ah
+ int $0x13
+ jc load_failed
+ movb %cl, max_sector
+ movb %dh, max_head
+ popw %es
+ popal
+
+1: /* Read to end of current track */
+ movb %cl, %al
+ negb %al
+ addb max_sector, %al
+ incb %al
+ andb $0x3f, %al
+ movzbl %al, %eax
+ call *read_sectors
+ jc load_failed
+
+ /* Update %es */
+ movw %es, %bx
+ shll $5, %eax
+ addw %ax, %bx
+ movw %bx, %es
+ shrl $5, %eax
+
+ /* Update LBA address */
+ addl %eax, %edi
+ adcl $0, %esi
+
+ /* Update CHS address */
+ andb $0xc0, %cl
+ orb $0x01, %cl
+ incb %dh
+ cmpb max_head, %dh
+ jbe 2f
+ xorb %dh, %dh
+ incb %ch
+ jnc 2f
+ addb $0xc0, %cl
+2:
+ /* Loop until whole image is read */
+ subl %eax, load_length
+ ja 1b
+ ljmp $BOOT_SEG, $start_image
+
+max_sector:
+ .byte 0
+max_head:
+ .byte 0
+load_length:
+ .long _load_size_sect
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBL"
+ .long load_length
+ .long 512
+ .long 0
+ .previous
+
+
+load_failed:
+ movw $10f, %si
+ jmp boot_error
+10: .asciz "Could not load gPXE\r\n"
+
+ .org 510
+ .byte 0x55, 0xaa
+
+start_image:
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
diff --git a/gpxe/src/arch/i386/prefix/kpxeprefix.S b/gpxe/src/arch/i386/prefix/kpxeprefix.S
new file mode 100644
index 00000000..d708604b
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/kpxeprefix.S
@@ -0,0 +1,7 @@
+/*****************************************************************************
+ * PXE prefix that keep the UNDI portion of the PXE stack present
+ *****************************************************************************
+ */
+
+#define PXELOADER_KEEP_UNDI
+#include "pxeprefix.S"
diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S
new file mode 100644
index 00000000..deea5ab3
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/libprefix.S
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+ .arch i386
+ .section ".prefix.lib", "awx", @progbits
+ .section ".data16", "aw", @progbits
+
+/**
+ * High memory temporary load address
+ *
+ * Temporary buffer into which to copy (or decompress) our runtime
+ * image, prior to calling get_memmap() and relocate(). We don't
+ * actually leave anything here once install() has returned.
+ *
+ * We use the start of an even megabyte so that we don't have to worry
+ * about the current state of the A20 line.
+ *
+ * We use 4MB rather than 2MB because some PXE stack / PMM BIOS
+ * combinations are known to place data required by other UNDI ROMs
+ * loader around the 2MB mark.
+ */
+ .globl HIGHMEM_LOADPOINT
+ .equ HIGHMEM_LOADPOINT, ( 4 << 20 )
+
+/* Image compression enabled */
+#define COMPRESS 1
+
+#define CR0_PE 1
+
+/*****************************************************************************
+ * Utility function: print character (with LF -> LF,CR translation)
+ *
+ * Parameters:
+ * %al : character to print
+ * Returns:
+ * Nothing
+ * Corrupts:
+ * %ax
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_character
+print_character:
+ /* Preserve registers */
+ pushw %bx
+ pushw %bp
+ /* Print character */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ cmpb $0x0a, %al /* '\n'? */
+ jne 1f
+ int $0x10
+ movb $0x0d, %al
+1: int $0x10
+ /* Restore registers and return */
+ popw %bp
+ popw %bx
+ ret
+ .size print_character, . - print_character
+
+/*****************************************************************************
+ * Utility function: print a NUL-terminated string
+ *
+ * Parameters:
+ * %ds:si : string to print
+ * Returns:
+ * %ds:si : character after terminating NUL
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_message
+print_message:
+ /* Preserve registers */
+ pushw %ax
+ /* Print string */
+1: lodsb
+ testb %al, %al
+ je 2f
+ call print_character
+ jmp 1b
+2: /* Restore registers and return */
+ popw %ax
+ ret
+ .size print_message, . - print_message
+
+/*****************************************************************************
+ * Utility functions: print hex digit/byte/word/dword
+ *
+ * Parameters:
+ * %al (low nibble) : digit to print
+ * %al : byte to print
+ * %ax : word to print
+ * %eax : dword to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_hex_dword
+print_hex_dword:
+ rorl $16, %eax
+ call print_hex_word
+ rorl $16, %eax
+ /* Fall through */
+ .size print_hex_dword, . - print_hex_dword
+ .globl print_hex_word
+print_hex_word:
+ xchgb %al, %ah
+ call print_hex_byte
+ xchgb %al, %ah
+ /* Fall through */
+ .size print_hex_word, . - print_hex_word
+ .globl print_hex_byte
+print_hex_byte:
+ rorb $4, %al
+ call print_hex_nibble
+ rorb $4, %al
+ /* Fall through */
+ .size print_hex_byte, . - print_hex_byte
+ .globl print_hex_nibble
+print_hex_nibble:
+ /* Preserve registers */
+ pushw %ax
+ /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
+ andb $0x0f, %al
+ cmpb $10, %al
+ sbbb $0x69, %al
+ das
+ call print_character
+ /* Restore registers and return */
+ popw %ax
+ ret
+ .size print_hex_nibble, . - print_hex_nibble
+
+/****************************************************************************
+ * pm_call (real-mode near call)
+ *
+ * Call routine in 16-bit protected mode for access to extended memory
+ *
+ * Parameters:
+ * %ax : address of routine to call in 16-bit protected mode
+ * Returns:
+ * none
+ * Corrupts:
+ * %ax
+ *
+ * The specified routine is called in 16-bit protected mode, with:
+ *
+ * %cs : 16-bit code segment with base matching real-mode %cs
+ * %ss : 16-bit data segment with base matching real-mode %ss
+ * %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
+ *
+ ****************************************************************************
+ */
+
+#ifndef KEEP_IT_REAL
+
+ /* GDT for protected-mode calls */
+ .section ".prefix.lib"
+ .align 16
+pm_call_vars:
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_base: .long 0
+ .word 0 /* padding */
+pm_cs: /* 16-bit protected-mode code segment */
+ .equ PM_CS, pm_cs - gdt
+ .word 0xffff, 0
+ .byte 0, 0x9b, 0x00, 0
+pm_ss: /* 16-bit protected-mode stack segment */
+ .equ PM_SS, pm_ss - gdt
+ .word 0xffff, 0
+ .byte 0, 0x93, 0x00, 0
+pm_ds: /* 32-bit protected-mode flat data segment */
+ .equ PM_DS, pm_ds - gdt
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+gdt_end:
+ .equ gdt_length, . - gdt
+ .size gdt, . - gdt
+
+ .section ".prefix.lib"
+ .align 16
+pm_saved_gdt:
+ .long 0, 0
+ .size pm_saved_gdt, . - pm_saved_gdt
+
+ .equ pm_call_vars_size, . - pm_call_vars
+#define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
+
+ .section ".prefix.lib"
+ .code16
+pm_call:
+ /* Preserve registers, flags, and RM return point */
+ pushw %bp
+ movw %sp, %bp
+ subw $pm_call_vars_size, %sp
+ andw $0xfff0, %sp
+ pushfl
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ pushw $99f
+
+ /* Set up local variable block, and preserve GDT */
+ pushw %cx
+ pushw %si
+ pushw %di
+ pushw %ss
+ popw %es
+ movw $pm_call_vars, %si
+ leaw PM_CALL_VAR(pm_call_vars)(%bp), %di
+ movw $pm_call_vars_size, %cx
+ cs rep movsb
+ popw %di
+ popw %si
+ popw %cx
+ sgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
+
+ /* Set up GDT bases */
+ pushl %eax
+ pushl %edi
+ xorl %eax, %eax
+ movw %ss, %ax
+ shll $4, %eax
+ movzwl %bp, %edi
+ leal PM_CALL_VAR(gdt)(%eax, %edi), %eax
+ movl %eax, PM_CALL_VAR(gdt_base)(%bp)
+ movw %cs, %ax
+ movw $PM_CALL_VAR(pm_cs), %di
+ call set_seg_base
+ movw %ss, %ax
+ movw $PM_CALL_VAR(pm_ss), %di
+ call set_seg_base
+ popl %edi
+ popl %eax
+
+ /* Switch CPU to protected mode and load up segment registers */
+ pushl %eax
+ cli
+ lgdt PM_CALL_VAR(gdt)(%bp)
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ ljmp $PM_CS, $1f
+1: movw $PM_SS, %ax
+ movw %ax, %ss
+ movw $PM_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ popl %eax
+
+ /* Call PM routine */
+ call *%ax
+
+ /* Set real-mode segment limits on %ds, %es, %fs and %gs */
+ movw %ss, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Return CPU to real mode */
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+
+ /* Restore registers and flags */
+ lret /* will ljmp to 99f */
+99: popw %ss
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ lgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
+ popfl
+ movw %bp, %sp
+ popw %bp
+ ret
+ .size pm_call, . - pm_call
+
+set_seg_base:
+ rolw $4, %ax
+ movw %ax, 2(%bp,%di)
+ andw $0xfff0, 2(%bp,%di)
+ movb %al, 4(%bp,%di)
+ andb $0x0f, 4(%bp,%di)
+ ret
+ .size set_seg_base, . - set_seg_base
+
+#endif /* KEEP_IT_REAL */
+
+/****************************************************************************
+ * copy_bytes (real-mode or 16-bit protected-mode near call)
+ *
+ * Copy bytes
+ *
+ * Parameters:
+ * %ds:esi : source address
+ * %es:edi : destination address
+ * %ecx : length
+ * Returns:
+ * %ds:esi : next source address
+ * %es:edi : next destination address
+ * Corrupts:
+ * None
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+copy_bytes:
+ pushl %ecx
+ rep addr32 movsb
+ popl %ecx
+ ret
+ .size copy_bytes, . - copy_bytes
+
+/****************************************************************************
+ * install_block (real-mode near call)
+ *
+ * Install block to specified address
+ *
+ * Parameters:
+ * %esi : source physical address (must be a multiple of 16)
+ * %edi : destination physical address (must be a multiple of 16)
+ * %ecx : length of (decompressed) data
+ * %edx : total length of block (including any uninitialised data portion)
+ * Returns:
+ * %esi : next source physical address (will be a multiple of 16)
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+install_block:
+
+#ifdef KEEP_IT_REAL
+
+ /* Preserve registers */
+ pushw %ds
+ pushw %es
+ pushl %ecx
+ pushl %edi
+
+ /* Convert %esi and %edi to segment registers */
+ shrl $4, %esi
+ movw %si, %ds
+ xorw %si, %si
+ shrl $4, %edi
+ movw %di, %es
+ xorw %di, %di
+
+#else /* KEEP_IT_REAL */
+
+ /* Call self in protected mode */
+ pushw %ax
+ movw $1f, %ax
+ call pm_call
+ popw %ax
+ ret
+1:
+ /* Preserve registers */
+ pushl %ecx
+ pushl %edi
+
+#endif /* KEEP_IT_REAL */
+
+
+#if COMPRESS
+ /* Decompress source to destination */
+ call decompress16
+#else
+ /* Copy source to destination */
+ call copy_bytes
+#endif
+
+ /* Zero .bss portion */
+ negl %ecx
+ addl %edx, %ecx
+ pushw %ax
+ xorw %ax, %ax
+ rep addr32 stosb
+ popw %ax
+
+ /* Round up %esi to start of next source block */
+ addl $0xf, %esi
+ andl $~0xf, %esi
+
+
+#ifdef KEEP_IT_REAL
+
+ /* Convert %ds:esi back to a physical address */
+ movzwl %ds, %cx
+ shll $4, %ecx
+ addl %ecx, %esi
+
+ /* Restore registers */
+ popl %edi
+ popl %ecx
+ popw %es
+ popw %ds
+
+#else /* KEEP_IT_REAL */
+
+ /* Restore registers */
+ popl %edi
+ popl %ecx
+
+#endif
+
+ ret
+ .size install_block, . - install_block
+
+/****************************************************************************
+ * alloc_basemem (real-mode near call)
+ *
+ * Allocate space for .text16 and .data16 from top of base memory.
+ * Memory is allocated using the BIOS free base memory counter at
+ * 0x40:13.
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl alloc_basemem
+alloc_basemem:
+ /* FBMS => %ax as segment address */
+ movw $0x40, %ax
+ movw %ax, %fs
+ movw %fs:0x13, %ax
+ shlw $6, %ax
+
+ /* .data16 segment address */
+ subw $_data16_size_pgh, %ax
+ pushw %ax
+
+ /* .text16 segment address */
+ subw $_text16_size_pgh, %ax
+ pushw %ax
+
+ /* Update FBMS */
+ shrw $6, %ax
+ movw %ax, %fs:0x13
+
+ /* Return */
+ popw %ax
+ popw %bx
+ ret
+ .size alloc_basemem, . - alloc_basemem
+
+/****************************************************************************
+ * install (real-mode near call)
+ *
+ * Install all text and data segments.
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl install
+install:
+ /* Preserve registers */
+ pushl %esi
+ pushl %edi
+ /* Allocate space for .text16 and .data16 */
+ call alloc_basemem
+ /* Image source = %cs:0000 */
+ xorl %esi, %esi
+ /* Image destination = HIGHMEM_LOADPOINT */
+ movl $HIGHMEM_LOADPOINT, %edi
+ /* Install text and data segments */
+ call install_prealloc
+ /* Restore registers and return */
+ popl %edi
+ popl %esi
+ ret
+ .size install, . - install
+
+/****************************************************************************
+ * install_prealloc (real-mode near call)
+ *
+ * Install all text and data segments.
+ *
+ * Parameters:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * %esi : Image source physical address (or zero for %cs:0000)
+ * %edi : Decompression temporary area physical address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl install_prealloc
+install_prealloc:
+ /* Save registers */
+ pushal
+ pushw %ds
+ pushw %es
+
+ /* Sanity: clear the direction flag asap */
+ cld
+
+ /* Calculate physical address of payload (i.e. first source) */
+ testl %esi, %esi
+ jnz 1f
+ movw %cs, %si
+ shll $4, %esi
+1: addl $_payload_offset, %esi
+
+ /* Install .text16 and .data16 */
+ pushl %edi
+ movzwl %ax, %edi
+ shll $4, %edi
+ movl $_text16_size, %ecx
+ movl %ecx, %edx
+ call install_block /* .text16 */
+ movzwl %bx, %edi
+ shll $4, %edi
+ movl $_data16_progbits_size, %ecx
+ movl $_data16_size, %edx
+ call install_block /* .data16 */
+ popl %edi
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+#ifdef KEEP_IT_REAL
+ /* Initialise libkir */
+ movw %ax, (init_libkir_vector+2)
+ lcall *init_libkir_vector
+#else
+ /* Install .text and .data to temporary area in high memory,
+ * prior to reading the E820 memory map and relocating
+ * properly.
+ */
+ movl $_textdata_progbits_size, %ecx
+ movl $_textdata_size, %edx
+ call install_block
+
+ /* Initialise librm at current location */
+ movw %ax, (init_librm_vector+2)
+ lcall *init_librm_vector
+
+ /* Call relocate() to determine target address for relocation.
+ * relocate() will return with %esi, %edi and %ecx set up
+ * ready for the copy to the new location.
+ */
+ movw %ax, (prot_call_vector+2)
+ pushl $relocate
+ lcall *prot_call_vector
+ popl %edx /* discard */
+
+ /* Copy code to new location */
+ pushl %edi
+ pushw %ax
+ movw $copy_bytes, %ax
+ call pm_call
+ popw %ax
+ popl %edi
+
+ /* Initialise librm at new location */
+ lcall *init_librm_vector
+
+#endif
+ /* Restore registers */
+ popw %es
+ popw %ds
+ popal
+ ret
+ .size install_prealloc, . - install_prealloc
+
+ /* Vectors for far calls to .text16 functions */
+ .section ".data16"
+#ifdef KEEP_IT_REAL
+init_libkir_vector:
+ .word init_libkir
+ .word 0
+ .size init_libkir_vector, . - init_libkir_vector
+#else
+init_librm_vector:
+ .word init_librm
+ .word 0
+ .size init_librm_vector, . - init_librm_vector
+prot_call_vector:
+ .word prot_call
+ .word 0
+ .size prot_call_vector, . - prot_call_vector
+#endif
+
+
+ /* File split information for the compressor */
+#if COMPRESS
+ .section ".zinfo", "a"
+ .ascii "COPY"
+ .long _prefix_load_offset
+ .long _prefix_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _text16_load_offset
+ .long _text16_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _data16_load_offset
+ .long _data16_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _textdata_load_offset
+ .long _textdata_progbits_size
+ .long _max_align
+#else /* COMPRESS */
+ .section ".zinfo", "a"
+ .ascii "COPY"
+ .long _prefix_load_offset
+ .long _load_size
+ .long _max_align
+#endif /* COMPRESS */
diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S
new file mode 100644
index 00000000..a3774d1a
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S
@@ -0,0 +1,155 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+*/
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $1f /* reload cs:ip to match relocation addr */
+1:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _load_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+
+ .org 512
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long syssize
+ .long 16
+ .long 0
+ .previous
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * LILO doesn't do this, but since we don't need any
+ * information that's left in the prefix, it doesn't matter:
+ * we just have to ensure that %cs:0000 is where the start of
+ * the Etherboot image *would* be.
+ */
+ ljmp $(SYSSEG-(PREFIXSIZE/16)), $run_etherboot
+
+
+ .org PREFIXSIZE
+/*
+ We're now at the beginning of the kernel proper.
+ */
+run_etherboot:
+ call install
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
diff --git a/gpxe/src/arch/i386/prefix/lmelf_dprefix.S b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
new file mode 100644
index 00000000..93534df5
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
@@ -0,0 +1,161 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/lmelf_prefix.S b/gpxe/src/arch/i386/prefix/lmelf_prefix.S
new file mode 100644
index 00000000..3c1e7251
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lmelf_prefix.S
@@ -0,0 +1,161 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/mbr.S b/gpxe/src/arch/i386/prefix/mbr.S
new file mode 100644
index 00000000..adfe2041
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/mbr.S
@@ -0,0 +1,13 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+mbr:
+ movw $exec_sector, %bp
+ jmp find_active_partition
+exec_sector:
+ ljmp $0x0000, $0x7c00
+
+#include "bootpart.S"
diff --git a/gpxe/src/arch/i386/prefix/nbiprefix.S b/gpxe/src/arch/i386/prefix/nbiprefix.S
new file mode 100644
index 00000000..d4904b73
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/nbiprefix.S
@@ -0,0 +1,76 @@
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .section ".prefix.data", "aw", @progbits
+ .code16
+ .section ".prefix"
+ .org 0
+
+nbi_header:
+
+/*****************************************************************************
+ * NBI file header
+ *****************************************************************************
+ */
+file_header:
+ .long 0x1b031336 /* Signature */
+ .byte 0x04 /* 16 bytes header, no vendor info */
+ .byte 0
+ .byte 0
+ .byte 0 /* No flags */
+ .word 0x0000, 0x07c0 /* Load header to 0x07c0:0x0000 */
+ .word entry, 0x07c0 /* Start execution at 0x07c0:entry */
+ .size file_header, . - file_header
+
+/*****************************************************************************
+ * NBI segment header
+ *****************************************************************************
+ */
+segment_header:
+ .byte 0x04 /* 16 bytes header, no vendor info */
+ .byte 0
+ .byte 0
+ .byte 0x04 /* Last segment */
+ .long 0x00007e00
+imglen: .long _load_size - 512
+memlen: .long _load_size - 512
+ .size segment_header, . - segment_header
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBL"
+ .long imglen
+ .long 1
+ .long 0
+ .ascii "SUBL"
+ .long memlen
+ .long 1
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * NBI entry point
+ *****************************************************************************
+ */
+entry:
+ /* Install low and high memory regions */
+ call install
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Reboot system */
+ int $0x19
+
+ .previous
+ .size entry, . - entry
+
+nbi_header_end:
+ .org 512
diff --git a/gpxe/src/arch/i386/prefix/nullprefix.S b/gpxe/src/arch/i386/prefix/nullprefix.S
new file mode 100644
index 00000000..032d41e0
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/nullprefix.S
@@ -0,0 +1,13 @@
+ .org 0
+ .text
+ .arch i386
+
+ .section ".prefix", "ax", @progbits
+ .code16
+_prefix:
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S
new file mode 100644
index 00000000..d7125b61
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/pxeprefix.S
@@ -0,0 +1,666 @@
+#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+#define PXENV_STOP_UNDI 0x0015
+#define PXENV_UNLOAD_STACK 0x0070
+
+ .text
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .section ".prefix.data", "aw", @progbits
+ .code16
+
+#include <undi.h>
+
+/*****************************************************************************
+ * Entry point: set operating context, print welcome message
+ *****************************************************************************
+ */
+ .section ".prefix"
+ /* Set up our non-stack segment registers */
+ jmp $0x7c0, $1f
+1: pushfl
+ /* %ax here is the default return type... */
+ movw $5, %ax /* Keep PXE+UNDI */
+ pushal
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ movw %cs, %ax
+ movw %ax, %ds
+
+ movw $0x40, %ax /* BIOS data segment access */
+ movw %ax, %fs
+
+ pushw %fs:0x13
+ /* Record PXENV+ and !PXE nominal addresses */
+ movw %es, pxenv_segment
+ movw %bx, pxenv_offset
+ movw %sp, %bp
+ movw %ss, return_stack_segment
+ movl %esp, return_stack_offset
+ movl 50(%bp), %eax
+ movl %eax, ppxe_segoff /* !PXE address */
+ /* Set up stack just below 0x7c00 */
+ xorw %ax, %ax
+ movw %ax, %ss
+ movw $0x7c00, %sp
+ /* Clear direction flag, for the sake of sanity */
+ cld
+ /* Print welcome message */
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz "PXE->EB:"
+ .previous
+
+/*****************************************************************************
+ * Verify PXENV+ structure and record parameters of interest
+ *****************************************************************************
+ */
+detect_pxenv:
+ /* Signature check */
+ les pxenv_segoff, %di
+ cmpl $0x4e455850, %es:(%di) /* 'PXEN' signature */
+ jne no_pxenv
+ cmpw $0x2b56, %es:4(%di) /* 'V+' signature */
+ jne no_pxenv
+ /* Record entry point and UNDI segments */
+ pushl %es:0x0a(%di) /* Entry point */
+ popl entry_segoff
+ pushw %es:0x24(%di) /* UNDI code segment */
+ pushw %es:0x26(%di) /* UNDI code size */
+ popl undi_code_segoff
+ pushw %es:0x20(%di) /* UNDI data segment */
+ pushw %es:0x22(%di) /* UNDI data size */
+ popl undi_data_segoff
+ /* Print "PXENV+ at <address>" */
+ movw $10f, %si
+ call print_message
+ movw %bx, %di
+ call print_segoff
+ movb $',', %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " PXENV+ at "
+ .previous
+
+no_pxenv:
+ xorl %eax, %eax
+ movl %eax, pxenv_segoff
+
+99:
+
+/*****************************************************************************
+ * Verify !PXE structure and record parameters of interest
+ *****************************************************************************
+ */
+detect_ppxe:
+ /* Signature check */
+ les ppxe_segoff, %di
+ cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
+ jne no_ppxe
+ /* Record structure address, entry point, and UNDI segments */
+ pushw %es
+ popw ppxe_segment
+ movw %di, ppxe_offset
+ pushl %es:0x10(%di) /* Entry point */
+ popl entry_segoff
+ pushw %es:0x30(%di) /* UNDI code segment */
+ pushw %es:0x36(%di) /* UNDI code size */
+ popl undi_code_segoff
+ pushw %es:0x28(%di) /* UNDI data segment */
+ pushw %es:0x2e(%di) /* UNDI data size */
+ popl undi_data_segoff
+ /* Print "!PXE at <address>" */
+ movw $10f, %si
+ call print_message
+ call print_segoff
+ movb $',', %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " !PXE at "
+ .previous
+
+no_ppxe:
+ xorl %eax, %eax
+ movl %eax, ppxe_segoff
+
+99:
+
+/*****************************************************************************
+ * Sanity check: we must have an entry point
+ *****************************************************************************
+ */
+check_have_stack:
+ /* Check for entry point */
+ movl entry_segoff, %eax
+ testl %eax, %eax
+ jnz 99f
+ /* No entry point: print message and skip everything else */
+ movw $10f, %si
+ call print_message
+ jmp finished
+ .section ".prefix.data"
+10: .asciz " No PXE stack found!\n"
+ .previous
+99:
+
+/*****************************************************************************
+ * Calculate base memory usage by UNDI
+ *****************************************************************************
+ */
+find_undi_basemem_usage:
+ movw undi_code_segment, %ax
+ movw undi_code_size, %bx
+ movw undi_data_segment, %cx
+ movw undi_data_size, %dx
+ cmpw %ax, %cx
+ ja 1f
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
+ shrw $6, %ax /* Round down to nearest kB */
+ movw %ax, undi_fbms_start
+ addw $0x0f, %dx /* Round up to next segment */
+ shrw $4, %dx
+ addw %dx, %cx
+ addw $((1024 / 16) - 1), %cx /* Round up to next kB */
+ shrw $6, %cx
+ movw %cx, undi_fbms_end
+
+/*****************************************************************************
+ * Print information about detected PXE stack
+ *****************************************************************************
+ */
+print_structure_information:
+ /* Print entry point */
+ movw $10f, %si
+ call print_message
+ les entry_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz " entry point at "
+ .previous
+ /* Print UNDI code segment */
+ movw $10f, %si
+ call print_message
+ les undi_code_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz "\n UNDI code segment "
+ .previous
+ /* Print UNDI data segment */
+ movw $10f, %si
+ call print_message
+ les undi_data_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz ", data segment "
+ .previous
+ /* Print UNDI memory usage */
+ movw $10f, %si
+ call print_message
+ movw undi_fbms_start, %ax
+ call print_word
+ movb $'-', %al
+ call print_character
+ movw undi_fbms_end, %ax
+ call print_word
+ movw $20f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " ("
+20: .asciz "kB)\n"
+ .previous
+
+/*****************************************************************************
+ * Determine physical device
+ *****************************************************************************
+ */
+get_physical_device:
+ /* Issue PXENV_UNDI_GET_NIC_TYPE */
+ movw $PXENV_UNDI_GET_NIC_TYPE, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp no_physical_device
+1: /* Determine physical device type */
+ movb ( pxe_parameter_structure + 0x02 ), %al
+ cmpb $2, %al
+ je pci_physical_device
+ jmp no_physical_device
+
+pci_physical_device:
+ /* Record PCI bus:dev.fn and vendor/device IDs */
+ movl ( pxe_parameter_structure + 0x03 ), %eax
+ movl %eax, pci_vendor
+ movw ( pxe_parameter_structure + 0x0b ), %ax
+ movw %ax, pci_busdevfn
+ movw $10f, %si
+ call print_message
+ call print_pci_busdevfn
+ movb $0x0a, %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " UNDI device is PCI "
+ .previous
+
+no_physical_device:
+ /* No device found, or device type not understood */
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " Unable to determine UNDI physical device\n"
+ .previous
+
+99:
+
+/*****************************************************************************
+ * Leave NIC in a safe state
+ *****************************************************************************
+ */
+#ifndef PXELOADER_KEEP_UNDI
+shutdown_nic:
+ /* Issue PXENV_UNDI_SHUTDOWN */
+ movw $PXENV_UNDI_SHUTDOWN, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+1:
+
+/*****************************************************************************
+ * Unload PXE base code
+ *****************************************************************************
+ */
+unload_base_code:
+ /* Issue PXENV_UNLOAD_STACK */
+ movw $PXENV_UNLOAD_STACK, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Free base memory used by PXE base code */
+ movw %fs:(0x13), %si
+ movw undi_fbms_start, %di
+ call free_basemem
+99:
+
+/*****************************************************************************
+ * Unload UNDI driver
+ *****************************************************************************
+ */
+
+unload_undi:
+ /* Issue PXENV_STOP_UNDI */
+ movw $PXENV_STOP_UNDI, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Free base memory used by UNDI */
+ movw undi_fbms_start, %si
+ movw undi_fbms_end, %di
+ call free_basemem
+ /* Clear UNDI_FL_STARTED */
+ andw $~UNDI_FL_STARTED, flags
+99:
+
+/*****************************************************************************
+ * Print remaining free base memory
+ *****************************************************************************
+ */
+print_free_basemem:
+ movw $10f, %si
+ call print_message
+ movw %fs:(0x13), %ax
+ call print_word
+ movw $20f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " "
+20: .asciz "kB free base memory after PXE unload\n"
+ .previous
+#endif /* PXELOADER_KEEP_UNDI */
+
+/*****************************************************************************
+ * Exit point
+ *****************************************************************************
+ */
+finished:
+ jmp run_etherboot
+
+/*****************************************************************************
+ * Subroutine: print segment:offset address
+ *
+ * Parameters:
+ * %es:%di : segment:offset address to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_segoff:
+ /* Preserve registers */
+ pushw %ax
+ /* Print "<segment>:offset" */
+ movw %es, %ax
+ call print_hex_word
+ movb $':', %al
+ call print_character
+ movw %di, %ax
+ call print_hex_word
+ /* Restore registers and return */
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: print decimal word
+ *
+ * Parameters:
+ * %ax : word to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_word:
+ /* Preserve registers */
+ pushw %ax
+ pushw %bx
+ pushw %cx
+ pushw %dx
+ /* Build up digit sequence on stack */
+ movw $10, %bx
+ xorw %cx, %cx
+1: xorw %dx, %dx
+ divw %bx, %ax
+ pushw %dx
+ incw %cx
+ testw %ax, %ax
+ jnz 1b
+ /* Print digit sequence */
+1: popw %ax
+ call print_hex_nibble
+ loop 1b
+ /* Restore registers and return */
+ popw %dx
+ popw %cx
+ popw %bx
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: print PCI bus:dev.fn
+ *
+ * Parameters:
+ * %ax : PCI bus:dev.fn to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_pci_busdevfn:
+ /* Preserve registers */
+ pushw %ax
+ /* Print bus */
+ xchgb %al, %ah
+ call print_hex_byte
+ /* Print ":" */
+ movb $':', %al
+ call print_character
+ /* Print device */
+ movb %ah, %al
+ shrb $3, %al
+ call print_hex_byte
+ /* Print "." */
+ movb $'.', %al
+ call print_character
+ /* Print function */
+ movb %ah, %al
+ andb $0x07, %al
+ call print_hex_nibble
+ /* Restore registers and return */
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: zero 1kB block of base memory
+ *
+ * Parameters:
+ * %si : block to zero (in kB)
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+zero_kb:
+ /* Preserve registers */
+ pushw %ax
+ pushw %cx
+ pushw %di
+ pushw %es
+ /* Zero block */
+ movw %si, %ax
+ shlw $6, %ax
+ movw %ax, %es
+ movw $0x400, %cx
+ xorw %di, %di
+ xorw %ax, %ax
+ rep stosb
+ /* Restore registers and return */
+ popw %es
+ popw %di
+ popw %cx
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: free and zero base memory
+ *
+ * Parameters:
+ * %si : Expected current free base memory counter (in kB)
+ * %di : Desired new free base memory counter (in kB)
+ * %fs : BIOS data segment (0x40)
+ * Returns:
+ * %ax : Actual new free base memory counter (in kB)
+ *
+ * The base memory from %si kB to %di kB is unconditionally zeroed.
+ * It will be freed if and only if the expected current free base
+ * memory counter (%si) matches the actual current free base memory
+ * counter in 0x40:0x13; if this does not match then the memory will
+ * be leaked.
+ *****************************************************************************
+ */
+free_basemem:
+ /* Zero base memory */
+ pushw %si
+1: cmpw %si, %di
+ je 2f
+ call zero_kb
+ incw %si
+ jmp 1b
+2: popw %si
+ /* Free base memory */
+ movw %fs:(0x13), %ax /* Current FBMS to %ax */
+ cmpw %ax, %si /* Update FBMS only if "old" value */
+ jne 1f /* is correct */
+ movw %di, %ax
+1: movw %ax, %fs:(0x13)
+ ret
+
+/*****************************************************************************
+ * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
+ *
+ * Parameters:
+ * %bx : PXE API call number
+ * %ds:pxe_parameter_structure : Parameters for PXE API call
+ * Returns:
+ * %ax : PXE status code (not exit code)
+ * CF set if %ax is non-zero
+ *****************************************************************************
+ */
+pxe_call:
+ /* Preserve registers */
+ pushw %di
+ pushw %es
+ /* Set up registers for PXENV+ API. %bx already set up */
+ pushw %ds
+ popw %es
+ movw $pxe_parameter_structure, %di
+ /* Set up stack for !PXE API */
+ pushw %es
+ pushw %di
+ pushw %bx
+ /* Make the API call */
+ lcall *entry_segoff
+ /* Reset the stack */
+ addw $6, %sp
+ movw pxe_parameter_structure, %ax
+ clc
+ testw %ax, %ax
+ jz 1f
+ stc
+1: /* Restore registers and return */
+ popw %es
+ popw %di
+ ret
+
+/*****************************************************************************
+ * Subroutine: print PXE API call error message
+ *
+ * Parameters:
+ * %ax : PXE status code
+ * %bx : PXE API call number
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_pxe_error:
+ pushw %si
+ movw $10f, %si
+ call print_message
+ xchgw %ax, %bx
+ call print_hex_word
+ movw $20f, %si
+ call print_message
+ xchgw %ax, %bx
+ call print_hex_word
+ movw $30f, %si
+ call print_message
+ popw %si
+ ret
+ .section ".prefix.data"
+10: .asciz " UNDI API call "
+20: .asciz " failed: status code "
+30: .asciz "\n"
+ .previous
+
+/*****************************************************************************
+ * PXE data structures
+ *****************************************************************************
+ */
+
+pxe_parameter_structure: .fill 20
+
+undi_code_segoff:
+undi_code_size: .word 0
+undi_code_segment: .word 0
+
+undi_data_segoff:
+undi_data_size: .word 0
+undi_data_segment: .word 0
+
+/* The following fields are part of a struct undi_device */
+
+undi_device:
+
+pxenv_segoff:
+pxenv_offset: .word 0
+pxenv_segment: .word 0
+
+ppxe_segoff:
+ppxe_offset: .word 0
+ppxe_segment: .word 0
+
+entry_segoff:
+entry_offset: .word 0
+entry_segment: .word 0
+
+return_stack_segoff:
+return_stack_offset: .long 0
+return_stack_segment: .word 0
+
+return_type: .word 0 /* Default: unload PXE and boot next */
+
+undi_fbms_start: .word 0
+undi_fbms_end: .word 0
+
+pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
+isapnp_csn: .word UNDI_NO_ISAPNP_CSN
+isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
+
+pci_vendor: .word 0
+pci_device: .word 0
+flags: .word UNDI_FL_STARTED
+
+ .equ undi_device_size, ( . - undi_device )
+
+/*****************************************************************************
+ * Run Etherboot main code
+ *****************************************************************************
+ */
+run_etherboot:
+ /* Install Etherboot */
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+#ifdef PXELOADER_KEEP_UNDI
+ /* Copy our undi_device structure to the preloaded_undi variable */
+ movw %bx, %es
+ movw $preloaded_undi, %di
+ movw $undi_device, %si
+ movw $undi_device_size, %cx
+ rep movsb
+#endif
+
+ /* Jump to .text16 segment with %ds pointing to .data16 */
+ movw %bx, %ds
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "ax", @progbits
+1:
+ /* Run main program */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+#ifdef PXELOADER_KEEP_UNDI
+ /* Boot next device */
+ movw $0x40, %ax
+ movw %ax, %fs
+ movw $preloaded_undi,%bx
+
+ cli
+ movw %ss:return_type-undi_device(%bx),%ax
+ lssl %ss:return_stack_segoff-undi_device(%bx), %esp
+ movw %sp,%bp
+ movw %ax,38(%bp) /* Overwrite return AX value */
+ popw %fs:0x13 /* 0 */
+ popw %gs /* 2 */
+ popw %fs /* 4 */
+ popw %es /* 6 */
+ popw %ds /* 8 */
+ popal /* 10, 14, 18, 22, 26, 30, 34, 38 */
+ popfl /* 42 */
+ lret /* 46 */
+#else
+ int $0x18
+#endif
+
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S
new file mode 100644
index 00000000..d37cce94
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/romprefix.S
@@ -0,0 +1,383 @@
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ *
+ * Also be very careful as the stack is at the rear end of the interrupt
+ * table so using a noticeable amount of stack space is a no-no.
+ */
+
+#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
+#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
+#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define PNP_GET_BBS_VERSION 0x60
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+ .org 0x00
+romheader:
+ .word 0xAA55 /* BIOS extension signature */
+romheader_size: .byte _load_size_sect /* Size in 512-byte blocks */
+ jmp init /* Initialisation vector */
+checksum:
+ .byte 0
+ .org 0x16
+ .word undiheader
+ .org 0x18
+ .word pciheader
+ .org 0x1a
+ .word pnpheader
+ .size romheader, . - romheader
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBB"
+ .long romheader_size
+ .long 512
+ .long 0
+ .previous
+
+pciheader:
+ .ascii "PCIR" /* Signature */
+ .word pci_vendor_id /* Vendor ID */
+ .word pci_device_id /* Device ID */
+ .word 0x0000 /* pointer to vital product data */
+ .word pciheader_len /* PCI data structure length */
+ .byte 0x00 /* PCI data structure revision */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+pciheader_size: .word _load_size_sect /* Image length same as offset 02h */
+ .word 0x0001 /* revision level of code/data */
+ .byte 0x00 /* code type */
+ .byte 0x80 /* Flags (last PCI data structure) */
+ .word 0x0000 /* reserved */
+ .equ pciheader_len, . - pciheader
+ .size pciheader, . - pciheader
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long pciheader_size
+ .long 512
+ .long 0
+ .previous
+
+pnpheader:
+ .ascii "$PnP" /* Signature */
+ .byte 0x01 /* Structure revision */
+ .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
+ .word 0x0000 /* Offset of next header */
+ .byte 0x00 /* Reserved */
+ .byte 0x00 /* Checksum */
+ .long 0x00000000 /* Device identifier */
+ .word mfgstr /* Manufacturer string */
+ .word prodstr /* Product name */
+ .byte 0x02 /* Device base type code */
+ .byte 0x00 /* Device sub-type code */
+ .byte 0x00 /* Device interface type code */
+ .byte 0x54 /* Device indicator */
+ .word 0x0000 /* Boot connection vector */
+ .word 0x0000 /* Disconnect vector */
+ .word bev_entry /* Boot execution vector */
+ .word 0x0000 /* Reserved */
+ .word 0x0000 /* Static resource information vector*/
+ .equ pnpheader_len, . - pnpheader
+ .size pnpheader, . - pnpheader
+
+mfgstr:
+ .asciz "http://etherboot.org"
+ .size mfgstr, . - mfgstr
+prodstr:
+ .asciz "gPXE"
+ .size prodstr, . - prodstr
+
+undiheader:
+ .ascii "UNDI" /* Signature */
+ .byte undiheader_len /* Length of structure */
+ .byte 0 /* Checksum */
+ .byte 0 /* Structure revision */
+ .byte 0,1,2 /* PXE version: 2.1.0 */
+ .word undiloader /* Offset to loader routine */
+ .word _data16_size /* Stack segment size */
+ .word _data16_size /* Data segment size */
+ .word _text16_size /* Code segment size */
+ .equ undiheader_len, . - undiheader
+ .size undiheader, . - undiheader
+
+/* Initialisation (called once during POST)
+ *
+ * Determine whether or not this is a PnP system via a signature
+ * check. If it is PnP, return to the PnP BIOS indicating that we are
+ * a boot-capable device; the BIOS will call our boot execution vector
+ * if it wants to boot us. If it is not PnP, hook INT 19.
+ */
+init:
+ /* Preserve registers, clear direction flag, set %ds=%cs */
+ pushaw
+ pushw %ds
+ pushw %es
+ cld
+ pushw %cs
+ popw %ds
+ /* Print message as early as possible */
+ movw $init_message, %si
+ call print_message
+ /* Check for PnP BIOS */
+ testw $0x0f, %di /* PnP signature must be aligned - bochs */
+ jnz hook_int19 /* uses unalignment to indicate 'fake' PnP. */
+ cmpl $PNP_SIGNATURE, %es:0(%di)
+ jne hook_int19
+ /* Is PnP: print PnP message */
+ movw $init_message_pnp, %si
+ call print_message
+ xchgw %bx, %bx
+ /* Check for BBS */
+ pushw %es:0x1b(%di) /* Real-mode data segment */
+ pushw %ds /* &(bbs_version) */
+ pushw $bbs_version
+ pushw $PNP_GET_BBS_VERSION
+ lcall *%es:0xd(%di)
+ addw $8, %sp
+ testw %ax, %ax
+ jne hook_int19
+ movw $init_message_bbs, %si
+ call print_message
+ jmp hook_bbs
+ /* Not BBS-compliant - must hook INT 19 */
+hook_int19:
+ movw $init_message_int19, %si
+ call print_message
+ xorw %ax, %ax
+ movw %ax, %es
+ pushw %cs
+ pushw $int19_entry
+ popl %es:( 0x19 * 4 )
+hook_bbs:
+ /* Check for PMM */
+ movw $( 0xe000 - 1 ), %di
+pmm_scan:
+ incw %di
+ jz no_pmm
+ movw %di, %es
+ cmpl $PMM_SIGNATURE, %es:0
+ jne pmm_scan
+ xorw %bx, %bx
+ xorw %si, %si
+ movzbw %es:5, %cx
+1: es lodsb
+ addb %al, %bl
+ loop 1b
+ jnz pmm_scan
+ /* PMM found: print PMM message */
+ movw $init_message_pmm, %si
+ call print_message
+ /* Try to allocate 2MB block via PMM */
+ pushw $0x0006 /* Aligned, extended memory */
+ pushl $0xffffffff /* No handle */
+ pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */
+ pushw $0x0000 /* pmmAllocate */
+ lcall *%es:7
+ addw $12, %sp
+ testw %dx, %dx /* %ax==0 even on success, since align=2MB */
+ jnz gotpmm
+ movw $init_message_pmm_failed, %si
+ call print_message
+ jmp no_pmm
+gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */
+ pushal /* PMM presence implies 1kB stack */
+ movw %ax, %es /* %ax=0 already - see above */
+ pushw %dx
+ pushw %ax
+ popl %edi
+ movl %edi, image_source
+ xorl %esi, %esi
+ movzbl romheader_size, %ecx
+ shll $9, %ecx
+ addr32 rep movsb /* PMM presence implies flat real mode */
+ movl %edi, decompress_to
+ /* Shrink ROM and update checksum */
+ xorw %bx, %bx
+ xorw %si, %si
+ movw $_prefix_size_sect, %cx
+ movb %cl, romheader_size
+ shlw $9, %cx
+1: lodsb
+ addb %al, %bl
+ loop 1b
+ subb %bl, checksum
+ popal
+no_pmm:
+ /* Print CRLF to terminate messages */
+ movw $'\n', %ax
+ call print_character
+ /* Restore registers */
+ popw %es
+ popw %ds
+ popaw
+ /* Indicate boot capability to PnP BIOS, if present */
+ movw $0x20, %ax
+ lret
+ .size init, . - init
+
+init_message:
+ .asciz "gPXE (http://etherboot.org) -"
+ .size init_message, . - init_message
+init_message_pnp:
+ .asciz " PnP"
+ .size init_message_pnp, . - init_message_pnp
+init_message_bbs:
+ .asciz " BBS"
+ .size init_message_bbs, . - init_message_bbs
+init_message_pmm:
+ .asciz " PMM"
+ .size init_message_pmm, . - init_message_pmm
+init_message_pmm_failed:
+ .asciz "(failed)"
+ .size init_message_pmm_failed, . - init_message_pmm_failed
+init_message_int19:
+ .asciz " INT19"
+ .size init_message_int19, . - init_message_int19
+
+/* ROM image location
+ *
+ * May be either within option ROM space, or within PMM-allocated block.
+ */
+image_source:
+ .long 0
+ .size image_source, . - image_source
+
+/* Temporary decompression area
+ *
+ * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
+ */
+decompress_to:
+ .long HIGHMEM_LOADPOINT
+ .size decompress_to, . - decompress_to
+
+/* BBS version
+ *
+ * Filled in by BBS BIOS. We ignore the value.
+ */
+bbs_version:
+ .word 0
+
+/* Boot Execution Vector entry point
+ *
+ * Called by the PnP BIOS when it wants to boot us.
+ */
+bev_entry:
+ pushw %cs
+ call exec
+ lret
+ .size bev_entry, . - bev_entry
+
+/* INT19 entry point
+ *
+ * Called via the hooked INT 19 if we detected a non-PnP BIOS.
+ */
+int19_entry:
+ pushw %cs
+ call exec
+ /* No real way to return from INT19 */
+ int $0x18
+ .size int19_entry, . - int19_entry
+
+/* Execute as a boot device
+ *
+ */
+exec: /* Set %ds = %cs */
+ pushw %cs
+ popw %ds
+
+ /* Print message as soon as possible */
+ movw $exec_message, %si
+ call print_message
+
+ /* Store magic word on BIOS stack and remember BIOS %ss:sp */
+ pushl $STACK_MAGIC
+ movw %ss, %dx
+ movw %sp, %bp
+
+ /* Obtain a reasonably-sized temporary stack */
+ xorw %ax, %ax
+ movw %ax, %ss
+ movw $0x7c00, %sp
+
+ /* Install gPXE */
+ movl image_source, %esi
+ movl decompress_to, %edi
+ call alloc_basemem
+ call install_prealloc
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1: /* Call main() */
+ pushl $main
+ pushw %cs
+ call prot_call
+ /* No need to clean up stack; we are about to reload %ss:sp */
+
+ /* Restore BIOS stack */
+ movw %dx, %ss
+ movw %bp, %sp
+
+ /* Check magic word on BIOS stack */
+ popl %eax
+ cmpl $STACK_MAGIC, %eax
+ jne 1f
+ /* BIOS stack OK: return to caller */
+ lret
+1: /* BIOS stack corrupt: use INT 18 */
+ int $0x18
+ .previous
+
+exec_message:
+ .asciz "gPXE starting boot\n"
+ .size exec_message, . - exec_message
+
+/* UNDI loader
+ *
+ * Called by an external program to load our PXE stack.
+ */
+undiloader:
+ /* Save registers */
+ pushl %esi
+ pushl %edi
+ pushw %es
+ pushw %bx
+ /* UNDI loader parameter structure address into %es:%di */
+ movw %sp, %bx
+ movw %ss:12(%bx), %di
+ movw %ss:14(%bx), %es
+ /* Install to specified real-mode addresses */
+ pushw %di
+ movw %es:12(%di), %bx
+ movw %es:14(%di), %ax
+ movl %cs:image_source, %esi
+ movl %cs:decompress_to, %edi
+ call install_prealloc
+ popw %di
+ /* Call UNDI loader C code */
+ pushl $pxe_loader_call
+ pushw %cs
+ pushw $1f
+ pushw %ax
+ pushw $prot_call
+ lret
+1: popw %bx /* discard */
+ popw %bx /* discard */
+ /* Restore registers and return */
+ popw %bx
+ popw %es
+ popl %edi
+ popl %esi
+ lret
+ .size undiloader, . - undiloader
diff --git a/gpxe/src/arch/i386/prefix/unnrv2b.S b/gpxe/src/arch/i386/prefix/unnrv2b.S
new file mode 100644
index 00000000..70167a14
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/unnrv2b.S
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ *
+ * This file 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.
+ *
+ * Originally this code was part of ucl the data compression library
+ * for upx the ``Ultimate Packer of eXecutables''.
+ *
+ * - Converted to gas assembly, and refitted to work with etherboot.
+ * Eric Biederman 20 Aug 2002
+ *
+ * - Structure modified to be a subroutine call rather than an
+ * executable prefix.
+ * Michael Brown 30 Mar 2004
+ *
+ * - Modified to be compilable as either 16-bit or 32-bit code.
+ * Michael Brown 9 Mar 2005
+ */
+
+/****************************************************************************
+ * This file provides the decompress() and decompress16() functions
+ * which can be called in order to decompress an image compressed with
+ * the nrv2b utility in src/util.
+ *
+ * These functions are designed to be called by the prefix. They are
+ * position-independent code.
+ *
+ * The same basic assembly code is used to compile both
+ * decompress() and decompress16().
+ ****************************************************************************
+ */
+
+ .text
+ .arch i386
+ .section ".prefix.lib", "ax", @progbits
+
+#ifdef CODE16
+/****************************************************************************
+ * decompress16 (real-mode near call, position independent)
+ *
+ * Decompress data in 16-bit mode
+ *
+ * Parameters (passed via registers):
+ * %ds:%esi - Start of compressed input data
+ * %es:%edi - Start of output buffer
+ * Returns:
+ * %ds:%esi - End of compressed input data
+ * %es:%edi - End of decompressed output data
+ * All other registers are preserved
+ *
+ * NOTE: It would be possible to build a smaller version of the
+ * decompression code for -DKEEP_IT_REAL by using
+ * #define REG(x) x
+ * to use 16-bit registers where possible. This would impose limits
+ * that the compressed data size must be in the range [1,65533-%si]
+ * and the uncompressed data size must be in the range [1,65536-%di]
+ * (where %si and %di are the input values for those registers). Note
+ * particularly that the lower limit is 1, not 0, and that the upper
+ * limit on the input (compressed) data really is 65533, since the
+ * algorithm may read up to three bytes beyond the end of the input
+ * data, since it reads dwords.
+ ****************************************************************************
+ */
+
+#define REG(x) e ## x
+#define ADDR32 addr32
+
+ .code16
+ .globl decompress16
+decompress16:
+
+#else /* CODE16 */
+
+/****************************************************************************
+ * decompress (32-bit protected-mode near call, position independent)
+ *
+ * Parameters (passed via registers):
+ * %ds:%esi - Start of compressed input data
+ * %es:%edi - Start of output buffer
+ * Returns:
+ * %ds:%esi - End of compressed input data
+ * %es:%edi - End of decompressed output data
+ * All other registers are preserved
+ ****************************************************************************
+ */
+
+#define REG(x) e ## x
+#define ADDR32
+
+ .code32
+ .globl decompress
+decompress:
+
+#endif /* CODE16 */
+
+#define xAX REG(ax)
+#define xCX REG(cx)
+#define xBP REG(bp)
+#define xSI REG(si)
+#define xDI REG(di)
+
+ /* Save registers */
+ push %xAX
+ pushl %ebx
+ push %xCX
+ push %xBP
+ /* Do the decompression */
+ cld
+ xor %xBP, %xBP
+ dec %xBP /* last_m_off = -1 */
+ jmp dcl1_n2b
+
+decompr_literals_n2b:
+ ADDR32 movsb
+decompr_loop_n2b:
+ addl %ebx, %ebx
+ jnz dcl2_n2b
+dcl1_n2b:
+ call getbit32
+dcl2_n2b:
+ jc decompr_literals_n2b
+ xor %xAX, %xAX
+ inc %xAX /* m_off = 1 */
+loop1_n2b:
+ call getbit1
+ adc %xAX, %xAX /* m_off = m_off*2 + getbit() */
+ call getbit1
+ jnc loop1_n2b /* while(!getbit()) */
+ sub $3, %xAX
+ jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */
+ shl $8, %xAX
+ ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */
+ inc %xSI
+ xor $-1, %xAX
+ jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */
+ mov %xAX, %xBP /* last_m_off = m_off ?*/
+decompr_ebpeax_n2b:
+ xor %xCX, %xCX
+ call getbit1
+ adc %xCX, %xCX /* m_len = getbit() */
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */
+ jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
+ inc %xCX /* m_len++ */
+loop2_n2b:
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit() */
+ call getbit1
+ jnc loop2_n2b /* while(!getbit()) */
+ inc %xCX
+ inc %xCX /* m_len += 2 */
+decompr_got_mlen_n2b:
+ cmp $-0xd00, %xBP
+ adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+ push %xSI
+ ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */
+ rep
+ es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ pop %xSI
+ jmp decompr_loop_n2b
+
+
+getbit1:
+ addl %ebx, %ebx
+ jnz 1f
+getbit32:
+ ADDR32 movl (%xSI), %ebx
+ sub $-4, %xSI /* sets carry flag */
+ adcl %ebx, %ebx
+1:
+ ret
+
+decompr_end_n2b:
+ /* Restore registers and return */
+ pop %xBP
+ pop %xCX
+ popl %ebx
+ pop %xAX
+ ret
diff --git a/gpxe/src/arch/i386/prefix/usbdisk.S b/gpxe/src/arch/i386/prefix/usbdisk.S
new file mode 100644
index 00000000..fa7d1956
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/usbdisk.S
@@ -0,0 +1,23 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+#include "mbr.S"
+
+/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */
+ .org 446
+ .space 16
+ .space 16
+ .space 16
+ .byte 0x80, 0x01, 0x01, 0x00
+ .byte 0xeb, 0x3f, 0x20, 0x01
+ .long 0x00000020
+ .long 0x00000fe0
+
+ .org 510
+ .byte 0x55, 0xaa
+
+/* Skip to start of partition */
+ .org 32 * 512