summaryrefslogtreecommitdiff
path: root/roms/ipxe/src/arch/i386
diff options
context:
space:
mode:
Diffstat (limited to 'roms/ipxe/src/arch/i386')
-rw-r--r--roms/ipxe/src/arch/i386/Makefile28
-rw-r--r--roms/ipxe/src/arch/i386/Makefile.efi4
-rw-r--r--roms/ipxe/src/arch/i386/Makefile.pcbios100
-rw-r--r--roms/ipxe/src/arch/i386/core/basemem_packet.c37
-rw-r--r--roms/ipxe/src/arch/i386/core/cachedhcp.c175
-rw-r--r--roms/ipxe/src/arch/i386/core/dumpregs.c23
-rw-r--r--roms/ipxe/src/arch/i386/core/gdbidt.S28
-rw-r--r--roms/ipxe/src/arch/i386/core/gdbmach.c184
-rw-r--r--roms/ipxe/src/arch/i386/core/patch_cf.S42
-rw-r--r--roms/ipxe/src/arch/i386/core/pci_autoboot.c48
-rw-r--r--roms/ipxe/src/arch/i386/core/rdtsc_timer.c94
-rw-r--r--roms/ipxe/src/arch/i386/core/relocate.c138
-rw-r--r--roms/ipxe/src/arch/i386/core/runtime.c269
-rw-r--r--roms/ipxe/src/arch/i386/core/stack.S15
-rw-r--r--roms/ipxe/src/arch/i386/core/stack16.S15
-rw-r--r--roms/ipxe/src/arch/i386/core/video_subr.c113
-rw-r--r--roms/ipxe/src/arch/i386/core/virtaddr.S145
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undi.c146
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undiisr.S87
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undiload.c184
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undinet.c822
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undionly.c142
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undipreload.c42
-rw-r--r--roms/ipxe/src/arch/i386/drivers/net/undirom.c235
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c51
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c395
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S589
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c98
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c235
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c343
-rw-r--r--roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c114
-rw-r--r--roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c117
-rw-r--r--roms/ipxe/src/arch/i386/image/bootsector.c141
-rw-r--r--roms/ipxe/src/arch/i386/image/bzimage.c669
-rw-r--r--roms/ipxe/src/arch/i386/image/com32.c292
-rw-r--r--roms/ipxe/src/arch/i386/image/comboot.c327
-rw-r--r--roms/ipxe/src/arch/i386/image/elfboot.c145
-rw-r--r--roms/ipxe/src/arch/i386/image/initrd.c304
-rw-r--r--roms/ipxe/src/arch/i386/image/multiboot.c492
-rw-r--r--roms/ipxe/src/arch/i386/image/nbi.c427
-rw-r--r--roms/ipxe/src/arch/i386/image/pxe_image.c171
-rw-r--r--roms/ipxe/src/arch/i386/image/sdi.c140
-rw-r--r--roms/ipxe/src/arch/i386/include/basemem.h35
-rw-r--r--roms/ipxe/src/arch/i386/include/basemem_packet.h15
-rw-r--r--roms/ipxe/src/arch/i386/include/bios.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bios_disks.h69
-rw-r--r--roms/ipxe/src/arch/i386/include/biosint.h33
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/compiler.h2
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/entropy.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/hyperv.h23
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/nap.h15
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/reboot.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/sanboot.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/smbios.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/time.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/timer.h15
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/uaccess.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bits/umalloc.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bochs.h34
-rw-r--r--roms/ipxe/src/arch/i386/include/bootsector.h14
-rw-r--r--roms/ipxe/src/arch/i386/include/bzimage.h142
-rw-r--r--roms/ipxe/src/arch/i386/include/comboot.h132
-rw-r--r--roms/ipxe/src/arch/i386/include/fakee820.h9
-rw-r--r--roms/ipxe/src/arch/i386/include/gdbmach.h10
-rw-r--r--roms/ipxe/src/arch/i386/include/initrd.h30
-rw-r--r--roms/ipxe/src/arch/i386/include/int13.h333
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h29
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h44
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h115
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h68
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h39
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h62
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/vesafb.h210
-rw-r--r--roms/ipxe/src/arch/i386/include/ipxe/vmware.h81
-rw-r--r--roms/ipxe/src/arch/i386/include/kir.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/libkir.h233
-rw-r--r--roms/ipxe/src/arch/i386/include/librm.h267
-rw-r--r--roms/ipxe/src/arch/i386/include/memsizes.h19
-rw-r--r--roms/ipxe/src/arch/i386/include/multiboot.h149
-rw-r--r--roms/ipxe/src/arch/i386/include/pnpbios.h17
-rw-r--r--roms/ipxe/src/arch/i386/include/pxe.h199
-rw-r--r--roms/ipxe/src/arch/i386/include/pxe_api.h1823
-rw-r--r--roms/ipxe/src/arch/i386/include/pxe_call.h43
-rw-r--r--roms/ipxe/src/arch/i386/include/pxe_error.h123
-rw-r--r--roms/ipxe/src/arch/i386/include/pxe_types.h127
-rw-r--r--roms/ipxe/src/arch/i386/include/pxeparent.h11
-rw-r--r--roms/ipxe/src/arch/i386/include/realmode.h139
-rw-r--r--roms/ipxe/src/arch/i386/include/registers.h198
-rw-r--r--roms/ipxe/src/arch/i386/include/rtc.h83
-rw-r--r--roms/ipxe/src/arch/i386/include/sdi.h39
-rw-r--r--roms/ipxe/src/arch/i386/include/setjmp.h20
-rw-r--r--roms/ipxe/src/arch/i386/include/undi.h106
-rw-r--r--roms/ipxe/src/arch/i386/include/undiload.h35
-rw-r--r--roms/ipxe/src/arch/i386/include/undinet.h17
-rw-r--r--roms/ipxe/src/arch/i386/include/undipreload.h18
-rw-r--r--roms/ipxe/src/arch/i386/include/undirom.h53
-rw-r--r--roms/ipxe/src/arch/i386/include/vga.h228
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/apm.c112
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c16
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c52
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c65
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c70
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/biosint.c92
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/int13.c1993
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/int13con.c284
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c182
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c123
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c203
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c142
-rw-r--r--roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c542
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c354
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S221
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c65
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c346
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c55
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c387
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c597
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c474
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c1084
-rw-r--r--roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c283
-rw-r--r--roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c191
-rw-r--r--roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S97
-rw-r--r--roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c717
-rw-r--r--roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c61
-rw-r--r--roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c271
-rw-r--r--roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c332
-rw-r--r--roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c138
-rw-r--r--roms/ipxe/src/arch/i386/interface/vmware/vmware.c62
-rw-r--r--roms/ipxe/src/arch/i386/prefix/bootpart.S218
-rw-r--r--roms/ipxe/src/arch/i386/prefix/dskprefix.S383
-rw-r--r--roms/ipxe/src/arch/i386/prefix/exeprefix.S161
-rw-r--r--roms/ipxe/src/arch/i386/prefix/hdprefix.S111
-rw-r--r--roms/ipxe/src/arch/i386/prefix/isaromprefix.S29
-rw-r--r--roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S17
-rw-r--r--roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S11
-rw-r--r--roms/ipxe/src/arch/i386/prefix/kpxeprefix.S10
-rw-r--r--roms/ipxe/src/arch/i386/prefix/libprefix.S1016
-rw-r--r--roms/ipxe/src/arch/i386/prefix/lkrnprefix.S236
-rw-r--r--roms/ipxe/src/arch/i386/prefix/mbr.S15
-rw-r--r--roms/ipxe/src/arch/i386/prefix/mromprefix.S516
-rw-r--r--roms/ipxe/src/arch/i386/prefix/nbiprefix.S84
-rw-r--r--roms/ipxe/src/arch/i386/prefix/nullprefix.S15
-rw-r--r--roms/ipxe/src/arch/i386/prefix/pciromprefix.S29
-rw-r--r--roms/ipxe/src/arch/i386/prefix/pxeprefix.S862
-rw-r--r--roms/ipxe/src/arch/i386/prefix/romprefix.S895
-rw-r--r--roms/ipxe/src/arch/i386/prefix/undiloader.S54
-rw-r--r--roms/ipxe/src/arch/i386/prefix/unlzma.S942
-rw-r--r--roms/ipxe/src/arch/i386/prefix/unlzma16.S9
-rw-r--r--roms/ipxe/src/arch/i386/prefix/usbdisk.S34
-rw-r--r--roms/ipxe/src/arch/i386/scripts/i386.lds254
-rw-r--r--roms/ipxe/src/arch/i386/tests/gdbstub_test.S54
-rwxr-xr-xroms/ipxe/src/arch/i386/tests/gdbstub_test.gdb116
-rw-r--r--roms/ipxe/src/arch/i386/transitions/liba20.S313
-rw-r--r--roms/ipxe/src/arch/i386/transitions/libkir.S256
-rw-r--r--roms/ipxe/src/arch/i386/transitions/libpm.S0
-rw-r--r--roms/ipxe/src/arch/i386/transitions/librm.S671
-rw-r--r--roms/ipxe/src/arch/i386/transitions/librm_mgmt.c158
-rw-r--r--roms/ipxe/src/arch/i386/transitions/librm_test.c122
163 files changed, 30902 insertions, 192 deletions
diff --git a/roms/ipxe/src/arch/i386/Makefile b/roms/ipxe/src/arch/i386/Makefile
index fe3adc9ce..99f875314 100644
--- a/roms/ipxe/src/arch/i386/Makefile
+++ b/roms/ipxe/src/arch/i386/Makefile
@@ -80,10 +80,34 @@ PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie')
WORKAROUND_CFLAGS += $(PIE_FLAGS)
endif
+# Define version string for lkrnprefix.S
+#
+CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\""
+
+# Locations of utilities
+#
+ISOLINUX_BIN_LIST := \
+ $(ISOLINUX_BIN) \
+ /usr/lib/syslinux/isolinux.bin \
+ /usr/lib/syslinux/bios/isolinux.bin \
+ /usr/share/syslinux/isolinux.bin \
+ /usr/share/syslinux/bios/isolinux.bin \
+ /usr/local/share/syslinux/isolinux.bin \
+ /usr/local/share/syslinux/bios/isolinux.bin \
+ /usr/lib/ISOLINUX/isolinux.bin
+ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST)))
+
# i386-specific directories containing source files
#
-SRCDIRS += arch/i386/core
-SRCDIRS += arch/i386/tests
+SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
+SRCDIRS += arch/i386/firmware/pcbios
+SRCDIRS += arch/i386/image
+SRCDIRS += arch/i386/interface/pcbios
+SRCDIRS += arch/i386/interface/pxe
+SRCDIRS += arch/i386/interface/pxeparent
+SRCDIRS += arch/i386/interface/syslinux
+SRCDIRS += arch/i386/interface/vmware
+SRCDIRS += arch/i386/hci/commands
# Include common x86 Makefile
#
diff --git a/roms/ipxe/src/arch/i386/Makefile.efi b/roms/ipxe/src/arch/i386/Makefile.efi
index 37ede65ac..aa809eb5d 100644
--- a/roms/ipxe/src/arch/i386/Makefile.efi
+++ b/roms/ipxe/src/arch/i386/Makefile.efi
@@ -8,10 +8,6 @@ ELF2EFI = $(ELF2EFI32)
#
CFLAGS += -malign-double
-# Specify EFI boot file
-#
-EFI_BOOT_FILE = bootia32.efi
-
# Include generic EFI Makefile
#
MAKEDEPS += arch/x86/Makefile.efi
diff --git a/roms/ipxe/src/arch/i386/Makefile.pcbios b/roms/ipxe/src/arch/i386/Makefile.pcbios
index dfb8db0a0..ff823737d 100644
--- a/roms/ipxe/src/arch/i386/Makefile.pcbios
+++ b/roms/ipxe/src/arch/i386/Makefile.pcbios
@@ -1,6 +1,100 @@
# -*- makefile -*- : Force emacs to use Makefile mode
-# Include generic BIOS Makefile
+# The i386 linker script
#
-MAKEDEPS += arch/x86/Makefile.pcbios
-include arch/x86/Makefile.pcbios
+LDSCRIPT = arch/i386/scripts/i386.lds
+
+# Stop ld from complaining about our customised linker script
+#
+LDFLAGS += -N --no-check-sections
+
+# pcbios specific drivers
+SRCDIRS += arch/i386/drivers
+SRCDIRS += arch/i386/drivers/net
+
+# Media types.
+#
+MEDIA += rom
+MEDIA += mrom
+MEDIA += pcirom
+MEDIA += isarom
+MEDIA += pxe
+MEDIA += kpxe
+MEDIA += kkpxe
+MEDIA += kkkpxe
+MEDIA += lkrn
+MEDIA += dsk
+MEDIA += nbi
+MEDIA += hd
+MEDIA += raw
+MEDIA += exe
+
+# Padding rules
+#
+PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff
+PAD_mrom = $(PAD_rom)
+PAD_pcirom = $(PAD_rom)
+PAD_isarom = $(PAD_rom)
+PAD_dsk = $(PERL) $(PADIMG) --blksize=512
+PAD_hd = $(PERL) $(PADIMG) --blksize=32768
+PAD_exe = $(PERL) $(PADIMG) --blksize=512
+
+# Finalisation rules
+#
+FINALISE_rom = $(PERL) $(FIXROM)
+FINALISE_mrom = $(FINALISE_rom)
+FINALISE_pcirom = $(FINALISE_rom)
+FINALISE_isarom = $(FINALISE_rom)
+
+# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc.
+#
+LIST_NAME_rom := ROMS
+LIST_NAME_mrom := ROMS
+LIST_NAME_pcirom := ROMS
+LIST_NAME_isarom := ROMS
+
+# rule to make a non-emulation ISO boot image
+NON_AUTO_MEDIA += iso
+%iso: %lkrn util/geniso
+ $(QM)$(ECHO) " [GENISO] $@"
+ $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) VERSION="$(VERSION)" bash util/geniso -o $@ $<
+
+# rule to make a floppy emulation ISO boot image
+NON_AUTO_MEDIA += liso
+%liso: %lkrn util/geniso
+ $(QM)$(ECHO) " [GENISO] $@"
+ $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $<
+
+# rule to make a syslinux floppy image (mountable, bootable)
+NON_AUTO_MEDIA += sdsk
+%sdsk: %lkrn util/gensdsk
+ $(QM)$(ECHO) " [GENSDSK] $@"
+ $(Q)bash util/gensdsk $@ $<
+
+# rule to write disk images to /dev/fd0
+NON_AUTO_MEDIA += fd0
+%fd0 : %dsk
+ $(QM)$(ECHO) " [DD] $@"
+ $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0
+ $(Q)sync
+
+# Special target for building Master Boot Record binary
+$(BIN)/mbr.bin : $(BIN)/mbr.o
+ $(QM)$(ECHO) " [OBJCOPY] $@"
+ $(Q)$(OBJCOPY) -O binary $< $@
+
+# rule to make a USB disk image
+$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o
+ $(QM)$(ECHO) " [OBJCOPY] $@"
+ $(Q)$(OBJCOPY) -O binary $< $@
+
+NON_AUTO_MEDIA += usb
+%usb: $(BIN)/usbdisk.bin %hd
+ $(QM)$(ECHO) " [FINISH] $@"
+ $(Q)cat $^ > $@
+
+# Padded floppy image (e.g. for iLO)
+NON_AUTO_MEDIA += pdsk
+%pdsk : %dsk
+ $(Q)cp $< $@
+ $(Q)$(PADIMG) --blksize=1474560 $@
diff --git a/roms/ipxe/src/arch/i386/core/basemem_packet.c b/roms/ipxe/src/arch/i386/core/basemem_packet.c
new file mode 100644
index 000000000..9f5fbf330
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/basemem_packet.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Packet buffer in base memory. Used by various components which
+ * need to pass packets to and from external real-mode code.
+ *
+ */
+
+#include <basemem_packet.h>
+
+#undef basemem_packet
+char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );
diff --git a/roms/ipxe/src/arch/i386/core/cachedhcp.c b/roms/ipxe/src/arch/i386/core/cachedhcp.c
new file mode 100644
index 000000000..a5c624035
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/cachedhcp.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <ipxe/dhcppkt.h>
+#include <ipxe/init.h>
+#include <ipxe/netdevice.h>
+#include <realmode.h>
+#include <pxe_api.h>
+
+/** @file
+ *
+ * Cached DHCP packet
+ *
+ */
+
+/** Cached DHCPACK physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( cached_dhcpack_phys );
+#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
+
+/** Colour for debug messages */
+#define colour &cached_dhcpack_phys
+
+/** Cached DHCPACK */
+static struct dhcp_packet *cached_dhcpack;
+
+/**
+ * Cached DHCPACK startup function
+ *
+ */
+static void cachedhcp_init ( void ) {
+ struct dhcp_packet *dhcppkt;
+ struct dhcp_packet *tmp;
+ struct dhcphdr *dhcphdr;
+ size_t len;
+
+ /* Do nothing if no cached DHCPACK is present */
+ if ( ! cached_dhcpack_phys ) {
+ DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
+ return;
+ }
+
+ /* No reliable way to determine length before parsing packet;
+ * start by assuming maximum length permitted by PXE.
+ */
+ len = sizeof ( BOOTPLAYER_t );
+
+ /* Allocate and populate DHCP packet */
+ dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
+ if ( ! dhcppkt ) {
+ DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
+ return;
+ }
+ dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
+ copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
+ len );
+ dhcppkt_init ( dhcppkt, dhcphdr, len );
+
+ /* Resize packet to required length. If reallocation fails,
+ * just continue to use the original packet.
+ */
+ len = dhcppkt_len ( dhcppkt );
+ tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
+ if ( tmp )
+ dhcppkt = tmp;
+
+ /* Reinitialise packet at new address */
+ dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
+ dhcppkt_init ( dhcppkt, dhcphdr, len );
+
+ /* Store as cached DHCPACK, and mark original copy as consumed */
+ DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
+ cached_dhcpack_phys, len );
+ cached_dhcpack = dhcppkt;
+ cached_dhcpack_phys = 0;
+}
+
+/**
+ * Cached DHCPACK startup function
+ *
+ */
+static void cachedhcp_startup ( void ) {
+
+ /* If cached DHCP packet was not claimed by any network device
+ * during startup, then free it.
+ */
+ if ( cached_dhcpack ) {
+ DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
+ dhcppkt_put ( cached_dhcpack );
+ cached_dhcpack = NULL;
+ }
+}
+
+/** Cached DHCPACK initialisation function */
+struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = cachedhcp_init,
+};
+
+/** Cached DHCPACK startup function */
+struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
+ .startup = cachedhcp_startup,
+};
+
+/**
+ * Apply cached DHCPACK to network device, if applicable
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int cachedhcp_probe ( struct net_device *netdev ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ int rc;
+
+ /* Do nothing unless we have a cached DHCPACK */
+ if ( ! cached_dhcpack )
+ return 0;
+
+ /* Do nothing unless cached DHCPACK's MAC address matches this
+ * network device.
+ */
+ if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
+ ll_protocol->ll_addr_len ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
+ netdev->name );
+ return 0;
+ }
+ DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
+
+ /* Register as DHCP settings for this network device */
+ if ( ( rc = register_settings ( &cached_dhcpack->settings,
+ netdev_settings ( netdev ),
+ DHCP_SETTINGS_NAME ) ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Claim cached DHCPACK */
+ dhcppkt_put ( cached_dhcpack );
+ cached_dhcpack = NULL;
+
+ return 0;
+}
+
+/** Cached DHCP packet network device driver */
+struct net_driver cachedhcp_driver __net_driver = {
+ .name = "cachedhcp",
+ .probe = cachedhcp_probe,
+};
diff --git a/roms/ipxe/src/arch/i386/core/dumpregs.c b/roms/ipxe/src/arch/i386/core/dumpregs.c
new file mode 100644
index 000000000..82dc21847
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/dumpregs.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <realmode.h>
+
+void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) {
+
+ __asm__ __volatile__ (
+ TEXT16_CODE ( ".globl dump_regs\n\t"
+ "\ndump_regs:\n\t"
+ "pushl $_dump_regs\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ "addr32 leal 4(%%esp), %%esp\n\t"
+ "ret\n\t" ) : : );
+
+ printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
+ "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
+ "CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n",
+ ix86->regs.eax, ix86->regs.ebx, ix86->regs.ecx,
+ ix86->regs.edx, ix86->regs.esi, ix86->regs.edi,
+ ix86->regs.ebp, ix86->regs.esp,
+ ix86->segs.cs, ix86->segs.ss, ix86->segs.ds,
+ ix86->segs.es, ix86->segs.fs, ix86->segs.gs );
+}
diff --git a/roms/ipxe/src/arch/i386/core/gdbidt.S b/roms/ipxe/src/arch/i386/core/gdbidt.S
index 666ecce3c..a1e309d7c 100644
--- a/roms/ipxe/src/arch/i386/core/gdbidt.S
+++ b/roms/ipxe/src/arch/i386/core/gdbidt.S
@@ -15,29 +15,41 @@
/* POSIX signal numbers for reporting traps to GDB */
#define SIGILL 4
#define SIGTRAP 5
+#define SIGBUS 7
#define SIGFPE 8
+#define SIGSEGV 11
#define SIGSTKFLT 16
- .globl gdbmach_sigfpe
-gdbmach_sigfpe:
+ .globl gdbmach_nocode_sigfpe
+gdbmach_nocode_sigfpe:
pushl $SIGFPE
jmp gdbmach_interrupt
- .globl gdbmach_sigtrap
-gdbmach_sigtrap:
+ .globl gdbmach_nocode_sigtrap
+gdbmach_nocode_sigtrap:
pushl $SIGTRAP
jmp gdbmach_interrupt
- .globl gdbmach_sigstkflt
-gdbmach_sigstkflt:
+ .globl gdbmach_nocode_sigstkflt
+gdbmach_nocode_sigstkflt:
pushl $SIGSTKFLT
jmp gdbmach_interrupt
- .globl gdbmach_sigill
-gdbmach_sigill:
+ .globl gdbmach_nocode_sigill
+gdbmach_nocode_sigill:
pushl $SIGILL
jmp gdbmach_interrupt
+ .globl gdbmach_withcode_sigbus
+gdbmach_withcode_sigbus:
+ movl $SIGBUS, (%esp)
+ jmp gdbmach_interrupt
+
+ .globl gdbmach_withcode_sigsegv
+gdbmach_withcode_sigsegv:
+ movl $SIGSEGV, (%esp)
+ jmp gdbmach_interrupt
+
/* When invoked, the stack contains: eflags, cs, eip, signo. */
#define IH_OFFSET_GDB_REGS ( 0 )
#define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS )
diff --git a/roms/ipxe/src/arch/i386/core/gdbmach.c b/roms/ipxe/src/arch/i386/core/gdbmach.c
new file mode 100644
index 000000000..d92a4ac08
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/gdbmach.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <stdio.h>
+#include <assert.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/gdbstub.h>
+#include <librm.h>
+#include <gdbmach.h>
+
+/** @file
+ *
+ * GDB architecture-specific bits for i386
+ *
+ */
+
+enum {
+ DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */
+ DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */
+};
+
+/** Hardware breakpoint, fields stored in x86 bit pattern form */
+struct hwbp {
+ int type; /* type (1=write watchpoint, 3=access watchpoint) */
+ unsigned long addr; /* linear address */
+ size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */
+ int enabled;
+};
+
+static struct hwbp hwbps [ 4 ];
+static gdbreg_t dr7 = DR7_CLEAR;
+
+static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) {
+ struct hwbp *available = NULL;
+ unsigned int i;
+ for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) {
+ if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) {
+ return &hwbps [ i ];
+ }
+ if ( !hwbps [ i ].enabled ) {
+ available = &hwbps [ i ];
+ }
+ }
+ return available;
+}
+
+static void gdbmach_commit_hwbp ( struct hwbp *bp ) {
+ unsigned int regnum = bp - hwbps;
+
+ /* Set breakpoint address */
+ assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) );
+ switch ( regnum ) {
+ case 0:
+ __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) );
+ break;
+ case 1:
+ __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) );
+ break;
+ case 2:
+ __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) );
+ break;
+ case 3:
+ __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) );
+ break;
+ }
+
+ /* Set type */
+ dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) );
+ dr7 |= bp->type << ( 16 + 4 * regnum );
+
+ /* Set length */
+ dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) );
+ dr7 |= bp->len << ( 18 + 4 * regnum );
+
+ /* Set/clear local enable bit */
+ dr7 &= ~( 0x3 << 2 * regnum );
+ dr7 |= bp->enabled << 2 * regnum;
+}
+
+int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) {
+ struct hwbp *bp;
+
+ /* Check and convert breakpoint type to x86 type */
+ switch ( type ) {
+ case GDBMACH_WATCH:
+ type = 0x1;
+ break;
+ case GDBMACH_AWATCH:
+ type = 0x3;
+ break;
+ default:
+ return 0; /* unsupported breakpoint type */
+ }
+
+ /* Only lengths 1, 2, and 4 are supported */
+ if ( len != 2 && len != 4 ) {
+ len = 1;
+ }
+ len--; /* convert to x86 breakpoint length bit pattern */
+
+ /* Calculate linear address by adding segment base */
+ addr += virt_offset;
+
+ /* Set up the breakpoint */
+ bp = gdbmach_find_hwbp ( type, addr, len );
+ if ( !bp ) {
+ return 0; /* ran out of hardware breakpoints */
+ }
+ bp->type = type;
+ bp->addr = addr;
+ bp->len = len;
+ bp->enabled = enable;
+ gdbmach_commit_hwbp ( bp );
+ return 1;
+}
+
+static void gdbmach_disable_hwbps ( void ) {
+ /* Store and clear hardware breakpoints */
+ __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) );
+}
+
+static void gdbmach_enable_hwbps ( void ) {
+ /* Clear breakpoint status register */
+ __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) );
+
+ /* Restore hardware breakpoints */
+ __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) );
+}
+
+__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
+ gdbmach_disable_hwbps();
+ gdbstub_handler ( signo, regs );
+ gdbmach_enable_hwbps();
+}
+
+static void * gdbmach_interrupt_vectors[] = {
+ gdbmach_nocode_sigfpe, /* Divide by zero */
+ gdbmach_nocode_sigtrap, /* Debug trap */
+ NULL, /* Non-maskable interrupt */
+ gdbmach_nocode_sigtrap, /* Breakpoint */
+ gdbmach_nocode_sigstkflt, /* Overflow */
+ gdbmach_nocode_sigstkflt, /* Bound range exceeded */
+ gdbmach_nocode_sigill, /* Invalid opcode */
+ NULL, /* Device not available */
+ gdbmach_withcode_sigbus, /* Double fault */
+ NULL, /* Coprocessor segment overrun */
+ gdbmach_withcode_sigsegv, /* Invalid TSS */
+ gdbmach_withcode_sigsegv, /* Segment not present */
+ gdbmach_withcode_sigsegv, /* Stack segment fault */
+ gdbmach_withcode_sigsegv, /* General protection fault */
+ gdbmach_withcode_sigsegv, /* Page fault */
+};
+
+void gdbmach_init ( void ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) /
+ sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) {
+ set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] );
+ }
+}
diff --git a/roms/ipxe/src/arch/i386/core/patch_cf.S b/roms/ipxe/src/arch/i386/core/patch_cf.S
new file mode 100644
index 000000000..4365563fe
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/patch_cf.S
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 H. Peter Anvin <hpa@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .arch i386
+ .code16
+
+/****************************************************************************
+ * Set/clear CF on the stack as appropriate, assumes stack is as it should
+ * be immediately before IRET
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .globl patch_cf
+patch_cf:
+ pushw %bp
+ movw %sp, %bp
+ setc 8(%bp) /* Set/reset CF; clears PF, AF, ZF, SF */
+ popw %bp
+ ret
+ .size patch_cf, . - patch_cf
diff --git a/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/roms/ipxe/src/arch/i386/core/pci_autoboot.c
new file mode 100644
index 000000000..337598091
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/pci_autoboot.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Red Hat Inc.
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/device.h>
+#include <ipxe/init.h>
+#include <realmode.h>
+#include <usr/autoboot.h>
+
+uint16_t __bss16 ( autoboot_busdevfn );
+#define autoboot_busdevfn __use_data16 ( autoboot_busdevfn )
+
+/**
+ * Initialise PCI autoboot device
+ */
+static void pci_autoboot_init ( void ) {
+
+ if ( autoboot_busdevfn )
+ set_autoboot_busloc ( BUS_TYPE_PCI, autoboot_busdevfn );
+}
+
+/** PCI autoboot device initialisation function */
+struct init_fn pci_autoboot_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = pci_autoboot_init,
+};
diff --git a/roms/ipxe/src/arch/i386/core/rdtsc_timer.c b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c
new file mode 100644
index 000000000..e720a239c
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * RDTSC timer
+ *
+ */
+
+#include <assert.h>
+#include <ipxe/timer.h>
+#include <ipxe/pit8254.h>
+
+/**
+ * Number of TSC ticks per microsecond
+ *
+ * This is calibrated on the first use of the timer.
+ */
+static unsigned long rdtsc_ticks_per_usec;
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static void rdtsc_udelay ( unsigned long usecs ) {
+ unsigned long start;
+ unsigned long elapsed;
+
+ /* Sanity guard, since we may divide by this */
+ if ( ! usecs )
+ usecs = 1;
+
+ start = currticks();
+ if ( rdtsc_ticks_per_usec ) {
+ /* Already calibrated; busy-wait until done */
+ do {
+ elapsed = ( currticks() - start );
+ } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) );
+ } else {
+ /* Not yet calibrated; use 8254 PIT and calibrate
+ * based on result.
+ */
+ pit8254_udelay ( usecs );
+ elapsed = ( currticks() - start );
+ rdtsc_ticks_per_usec = ( elapsed / usecs );
+ DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs "
+ "(%ld MHz)\n", elapsed, usecs,
+ ( rdtsc_ticks_per_usec << TSC_SHIFT ) );
+ }
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static unsigned long rdtsc_ticks_per_sec ( void ) {
+
+ /* Calibrate timer, if not already done */
+ if ( ! rdtsc_ticks_per_usec )
+ udelay ( 1 );
+
+ /* Sanity check */
+ assert ( rdtsc_ticks_per_usec != 0 );
+
+ return ( rdtsc_ticks_per_usec * 1000 * 1000 );
+}
+
+PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay );
+PROVIDE_TIMER_INLINE ( rdtsc, currticks );
+PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec );
diff --git a/roms/ipxe/src/arch/i386/core/relocate.c b/roms/ipxe/src/arch/i386/core/relocate.c
new file mode 100644
index 000000000..54ad387e4
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/relocate.c
@@ -0,0 +1,138 @@
+#include <ipxe/io.h>
+#include <registers.h>
+
+/*
+ * Originally by Eric Biederman
+ *
+ * Heavily modified by Michael Brown
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/*
+ * The linker passes in the symbol _max_align, which is the alignment
+ * that we must preserve, in bytes.
+ *
+ */
+extern char _max_align[];
+#define max_align ( ( unsigned int ) _max_align )
+
+/* Linker symbols */
+extern char _textdata[];
+extern char _etextdata[];
+
+/* within 1MB of 4GB is too close.
+ * MAX_ADDR is the maximum address we can easily do DMA to.
+ *
+ * Not sure where this constraint comes from, but kept it from Eric's
+ * old code - mcb30
+ */
+#define MAX_ADDR (0xfff00000UL)
+
+/**
+ * Relocate iPXE
+ *
+ * @v ebp Maximum address to use for relocation
+ * @ret esi Current physical address
+ * @ret edi New physical address
+ * @ret ecx Length to copy
+ *
+ * This finds a suitable location for iPXE near the top of 32-bit
+ * address space, and returns the physical address of the new location
+ * to the prefix in %edi.
+ */
+__asmcall void relocate ( struct i386_all_regs *ix86 ) {
+ struct memory_map memmap;
+ unsigned long start, end, size, padded_size, max;
+ unsigned long new_start, new_end;
+ unsigned i;
+
+ /* Get memory map and current location */
+ get_memmap ( &memmap );
+ start = virt_to_phys ( _textdata );
+ end = virt_to_phys ( _etextdata );
+ size = ( end - start );
+ padded_size = ( size + max_align - 1 );
+
+ DBG ( "Relocate: currently at [%lx,%lx)\n"
+ "...need %lx bytes for %d-byte alignment\n",
+ start, end, padded_size, max_align );
+
+ /* Determine maximum usable address */
+ max = MAX_ADDR;
+ if ( ix86->regs.ebp < max ) {
+ max = ix86->regs.ebp;
+ DBG ( "Limiting relocation to [0,%lx)\n", max );
+ }
+
+ /* Walk through the memory map and find the highest address
+ * below 4GB that iPXE will fit into.
+ */
+ new_end = end;
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ struct memory_region *region = &memmap.regions[i];
+ unsigned long r_start, r_end;
+
+ DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
+
+ /* Truncate block to maximum address. This will be
+ * less than 4GB, which means that we can get away
+ * with using just 32-bit arithmetic after this stage.
+ */
+ if ( region->start > max ) {
+ DBG ( "...starts after max=%lx\n", max );
+ continue;
+ }
+ r_start = region->start;
+ if ( region->end > max ) {
+ DBG ( "...end truncated to max=%lx\n", max );
+ r_end = max;
+ } else {
+ r_end = region->end;
+ }
+ DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
+
+ /* If we have rounded down r_end below r_ start, skip
+ * this block.
+ */
+ if ( r_end < r_start ) {
+ DBG ( "...truncated to negative size\n" );
+ continue;
+ }
+
+ /* Check that there is enough space to fit in iPXE */
+ if ( ( r_end - r_start ) < size ) {
+ DBG ( "...too small (need %lx bytes)\n", size );
+ continue;
+ }
+
+ /* If the start address of the iPXE we would
+ * place in this block is higher than the end address
+ * of the current highest block, use this block.
+ *
+ * Note that this avoids overlaps with the current
+ * iPXE, as well as choosing the highest of all viable
+ * blocks.
+ */
+ if ( ( r_end - size ) > new_end ) {
+ new_end = r_end;
+ DBG ( "...new best block found.\n" );
+ }
+ }
+
+ /* Calculate new location of iPXE, and align it to the
+ * required alignemnt.
+ */
+ new_start = new_end - padded_size;
+ new_start += ( start - new_start ) & ( max_align - 1 );
+ new_end = new_start + size;
+
+ DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
+ start, end, new_start, new_end );
+
+ /* Let prefix know what to copy */
+ ix86->regs.esi = start;
+ ix86->regs.edi = new_start;
+ ix86->regs.ecx = size;
+}
diff --git a/roms/ipxe/src/arch/i386/core/runtime.c b/roms/ipxe/src/arch/i386/core/runtime.c
new file mode 100644
index 000000000..d160fee04
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/runtime.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Command line and initrd passed to iPXE at runtime
+ *
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/init.h>
+#include <ipxe/image.h>
+#include <ipxe/script.h>
+#include <ipxe/umalloc.h>
+#include <realmode.h>
+
+/** Command line physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( cmdline_phys );
+#define cmdline_phys __use_data16 ( cmdline_phys )
+
+/** initrd physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( initrd_phys );
+#define initrd_phys __use_data16 ( initrd_phys )
+
+/** initrd length
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( initrd_len );
+#define initrd_len __use_data16 ( initrd_len )
+
+/** Internal copy of the command line */
+static char *cmdline_copy;
+
+/** Free command line image */
+static void cmdline_image_free ( struct refcnt *refcnt ) {
+ struct image *image = container_of ( refcnt, struct image, refcnt );
+
+ DBGC ( image, "RUNTIME freeing command line\n" );
+ free ( cmdline_copy );
+}
+
+/** Embedded script representing the command line */
+static struct image cmdline_image = {
+ .refcnt = REF_INIT ( cmdline_image_free ),
+ .name = "<CMDLINE>",
+ .type = &script_image_type,
+};
+
+/** Colour for debug messages */
+#define colour &cmdline_image
+
+/**
+ * Strip unwanted cruft from command line
+ *
+ * @v cmdline Command line
+ * @v cruft Initial substring of cruft to strip
+ */
+static void cmdline_strip ( char *cmdline, const char *cruft ) {
+ char *strip;
+ char *strip_end;
+
+ /* Find unwanted cruft, if present */
+ if ( ! ( strip = strstr ( cmdline, cruft ) ) )
+ return;
+
+ /* Strip unwanted cruft */
+ strip_end = strchr ( strip, ' ' );
+ if ( strip_end ) {
+ *strip_end = '\0';
+ DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
+ strcpy ( strip, ( strip_end + 1 ) );
+ } else {
+ DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
+ *strip = '\0';
+ }
+}
+
+/**
+ * Initialise command line
+ *
+ * @ret rc Return status code
+ */
+static int cmdline_init ( void ) {
+ userptr_t cmdline_user;
+ char *cmdline;
+ size_t len;
+ int rc;
+
+ /* Do nothing if no command line was specified */
+ if ( ! cmdline_phys ) {
+ DBGC ( colour, "RUNTIME found no command line\n" );
+ return 0;
+ }
+ cmdline_user = phys_to_user ( cmdline_phys );
+ len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
+
+ /* Allocate and copy command line */
+ cmdline_copy = malloc ( len );
+ if ( ! cmdline_copy ) {
+ DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
+ "command line\n", len );
+ rc = -ENOMEM;
+ goto err_alloc_cmdline_copy;
+ }
+ cmdline = cmdline_copy;
+ copy_from_user ( cmdline, cmdline_user, 0, len );
+ DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
+ cmdline, cmdline_phys );
+
+ /* Mark command line as consumed */
+ cmdline_phys = 0;
+
+ /* Strip unwanted cruft from the command line */
+ cmdline_strip ( cmdline, "BOOT_IMAGE=" );
+ cmdline_strip ( cmdline, "initrd=" );
+ while ( isspace ( *cmdline ) )
+ cmdline++;
+ DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );
+
+ /* Prepare and register image */
+ cmdline_image.data = virt_to_user ( cmdline );
+ cmdline_image.len = strlen ( cmdline );
+ if ( cmdline_image.len ) {
+ if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
+ DBGC ( colour, "RUNTIME could not register command "
+ "line: %s\n", strerror ( rc ) );
+ goto err_register_image;
+ }
+ }
+
+ /* Drop our reference to the image */
+ image_put ( &cmdline_image );
+
+ return 0;
+
+ err_register_image:
+ image_put ( &cmdline_image );
+ err_alloc_cmdline_copy:
+ return rc;
+}
+
+/**
+ * Initialise initrd
+ *
+ * @ret rc Return status code
+ */
+static int initrd_init ( void ) {
+ struct image *image;
+ int rc;
+
+ /* Do nothing if no initrd was specified */
+ if ( ! initrd_phys ) {
+ DBGC ( colour, "RUNTIME found no initrd\n" );
+ return 0;
+ }
+ if ( ! initrd_len ) {
+ DBGC ( colour, "RUNTIME found empty initrd\n" );
+ return 0;
+ }
+ DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n",
+ initrd_phys, ( initrd_phys + initrd_len ) );
+
+ /* Allocate image */
+ image = alloc_image ( NULL );
+ if ( ! image ) {
+ DBGC ( colour, "RUNTIME could not allocate image for "
+ "initrd\n" );
+ rc = -ENOMEM;
+ goto err_alloc_image;
+ }
+ if ( ( rc = image_set_name ( image, "<INITRD>" ) ) != 0 ) {
+ DBGC ( colour, "RUNTIME could not set image name: %s\n",
+ strerror ( rc ) );
+ goto err_set_name;
+ }
+
+ /* Allocate and copy initrd content */
+ image->data = umalloc ( initrd_len );
+ if ( ! image->data ) {
+ DBGC ( colour, "RUNTIME could not allocate %d bytes for "
+ "initrd\n", initrd_len );
+ rc = -ENOMEM;
+ goto err_umalloc;
+ }
+ image->len = initrd_len;
+ memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
+ initrd_len );
+
+ /* Mark initrd as consumed */
+ initrd_phys = 0;
+
+ /* Register image */
+ if ( ( rc = register_image ( image ) ) != 0 ) {
+ DBGC ( colour, "RUNTIME could not register initrd: %s\n",
+ strerror ( rc ) );
+ goto err_register_image;
+ }
+
+ /* Drop our reference to the image */
+ image_put ( image );
+
+ return 0;
+
+ err_register_image:
+ err_umalloc:
+ err_set_name:
+ image_put ( image );
+ err_alloc_image:
+ return rc;
+}
+
+/**
+ * Initialise command line and initrd
+ *
+ */
+static void runtime_init ( void ) {
+ int rc;
+
+ /* Initialise command line */
+ if ( ( rc = cmdline_init() ) != 0 ) {
+ /* No way to report failure */
+ return;
+ }
+
+ /* Initialise initrd */
+ if ( ( rc = initrd_init() ) != 0 ) {
+ /* No way to report failure */
+ return;
+ }
+}
+
+/** Command line and initrd initialisation function */
+struct startup_fn runtime_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+ .startup = runtime_init,
+};
diff --git a/roms/ipxe/src/arch/i386/core/stack.S b/roms/ipxe/src/arch/i386/core/stack.S
new file mode 100644
index 000000000..98f1cd9b9
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/stack.S
@@ -0,0 +1,15 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .arch i386
+
+/****************************************************************************
+ * Internal stack
+ ****************************************************************************
+ */
+ .section ".stack", "aw", @nobits
+ .align 8
+ .globl _stack
+_stack:
+ .space 4096
+ .globl _estack
+_estack:
diff --git a/roms/ipxe/src/arch/i386/core/stack16.S b/roms/ipxe/src/arch/i386/core/stack16.S
new file mode 100644
index 000000000..4bc6f081a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/stack16.S
@@ -0,0 +1,15 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .arch i386
+
+/****************************************************************************
+ * Internal stack
+ ****************************************************************************
+ */
+ .section ".stack16", "aw", @nobits
+ .align 8
+ .globl _stack16
+_stack16:
+ .space 4096
+ .globl _estack16
+_estack16:
diff --git a/roms/ipxe/src/arch/i386/core/video_subr.c b/roms/ipxe/src/arch/i386/core/video_subr.c
new file mode 100644
index 000000000..3f701bd96
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/video_subr.c
@@ -0,0 +1,113 @@
+/*
+ *
+ * modified from linuxbios code
+ * by Cai Qiang <rimy2000@hotmail.com>
+ *
+ */
+
+#include "stddef.h"
+#include "string.h"
+#include <ipxe/io.h>
+#include <ipxe/console.h>
+#include <ipxe/init.h>
+#include "vga.h"
+#include <config/console.h>
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_DIRECT_VGA ) && \
+ CONSOLE_EXPLICIT ( CONSOLE_DIRECT_VGA ) )
+#undef CONSOLE_DIRECT_VGA
+#define CONSOLE_DIRECT_VGA ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+struct console_driver vga_console __console_driver;
+
+static char *vidmem; /* The video buffer */
+static int video_line, video_col;
+
+#define VIDBUFFER 0xB8000
+
+static void memsetw(void *s, int c, unsigned int n)
+{
+ unsigned int i;
+ u16 *ss = (u16 *) s;
+
+ for (i = 0; i < n; i++) {
+ ss[i] = ( u16 ) c;
+ }
+}
+
+static void video_init(void)
+{
+ static int inited=0;
+
+ vidmem = (char *)phys_to_virt(VIDBUFFER);
+
+ if (!inited) {
+ video_line = 0;
+ video_col = 0;
+
+ memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
+
+ inited=1;
+ }
+}
+
+static void video_scroll(void)
+{
+ int i;
+
+ memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
+ for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
+ vidmem[i] = ' ';
+}
+
+static void vga_putc(int byte)
+{
+ if (byte == '\n') {
+ video_line++;
+ video_col = 0;
+
+ } else if (byte == '\r') {
+ video_col = 0;
+
+ } else if (byte == '\b') {
+ video_col--;
+
+ } else if (byte == '\t') {
+ video_col += 4;
+
+ } else if (byte == '\a') {
+ //beep
+ //beep(500);
+
+ } else {
+ vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
+ vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
+ video_col++;
+ }
+ if (video_col < 0) {
+ video_col = 0;
+ }
+ if (video_col >= COLS) {
+ video_line++;
+ video_col = 0;
+ }
+ if (video_line >= LINES) {
+ video_scroll();
+ video_line--;
+ }
+ // move the cursor
+ write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
+ write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
+}
+
+struct console_driver vga_console __console_driver = {
+ .putchar = vga_putc,
+ .disabled = CONSOLE_DISABLED,
+ .usage = CONSOLE_DIRECT_VGA,
+};
+
+struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = video_init,
+};
diff --git a/roms/ipxe/src/arch/i386/core/virtaddr.S b/roms/ipxe/src/arch/i386/core/virtaddr.S
new file mode 100644
index 000000000..425591570
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/core/virtaddr.S
@@ -0,0 +1,145 @@
+/*
+ * Functions to support the virtual addressing method of relocation
+ * that Etherboot uses.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#include "librm.h"
+
+ .arch i386
+ .text
+ .code32
+
+/****************************************************************************
+ * _virt_to_phys (virtual addressing)
+ *
+ * Switch from virtual to flat physical addresses. %esp is adjusted
+ * to a physical value. Segment registers are set to flat physical
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _virt_to_phys
+_virt_to_phys:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Change return address to a physical address */
+ movl virt_offset, %ebp
+ addl %ebp, 12(%esp)
+
+ /* Switch to physical code segment */
+ cli
+ pushl $PHYSICAL_CS
+ leal 1f(%ebp), %eax
+ pushl %eax
+ lret
+1:
+ /* Reload other segment registers and adjust %esp */
+ movl $PHYSICAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+ movl %eax, %ss
+ addl %ebp, %esp
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
+
+/****************************************************************************
+ * _phys_to_virt (flat physical addressing)
+ *
+ * Switch from flat physical to virtual addresses. %esp is adjusted
+ * to a virtual value. Segment registers are set to virtual
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _phys_to_virt
+_phys_to_virt:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Switch to virtual code segment */
+ cli
+ ljmp $VIRTUAL_CS, $1f
+1:
+ /* Reload data segment registers */
+ movl $VIRTUAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Reload stack segment and adjust %esp */
+ movl virt_offset, %ebp
+ movl %eax, %ss
+ subl %ebp, %esp
+
+ /* Change the return address to a virtual address */
+ subl %ebp, 12(%esp)
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
+
+/****************************************************************************
+ * _intr_to_virt (virtual code segment, virtual or physical stack segment)
+ *
+ * Switch from virtual code segment with either a virtual or physical
+ * stack segment to using virtual addressing. %esp is adjusted if
+ * necessary to a virtual value. Segment registers are set to virtual
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _intr_to_virt
+_intr_to_virt:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Check whether stack segment is physical or virtual */
+ movl %ss, %eax
+ cmpw $VIRTUAL_DS, %ax
+ movl $VIRTUAL_DS, %eax
+
+ /* Reload data segment registers */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Reload stack segment and adjust %esp if necessary */
+ je 1f
+ movl virt_offset, %ebp
+ movl %eax, %ss
+ subl %ebp, %esp
+1:
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undi.c b/roms/ipxe/src/arch/i386/drivers/net/undi.c
new file mode 100644
index 000000000..9820cf629
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undi.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ipxe/pci.h>
+#include <undi.h>
+#include <undirom.h>
+#include <undiload.h>
+#include <undinet.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * UNDI PCI driver
+ *
+ */
+
+/**
+ * Find UNDI ROM for PCI device
+ *
+ * @v pci PCI device
+ * @ret undirom UNDI ROM, or NULL
+ *
+ * Try to find a driver for this device. Try an exact match on the
+ * ROM address first, then fall back to a vendor/device ID match only
+ */
+static struct undi_rom * undipci_find_rom ( struct pci_device *pci ) {
+ struct undi_rom *undirom;
+ unsigned long rombase;
+
+ rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS );
+ undirom = undirom_find_pci ( pci->vendor, pci->device, rombase );
+ if ( ! undirom )
+ undirom = undirom_find_pci ( pci->vendor, pci->device, 0 );
+ return undirom;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int undipci_probe ( struct pci_device *pci ) {
+ struct undi_device *undi;
+ struct undi_rom *undirom;
+ int rc;
+
+ /* Allocate UNDI device structure */
+ undi = zalloc ( sizeof ( *undi ) );
+ if ( ! undi )
+ return -ENOMEM;
+ pci_set_drvdata ( pci, undi );
+
+ /* Find/create our pixie */
+ if ( preloaded_undi.pci_busdevfn == pci->busdevfn ) {
+ /* Claim preloaded UNDI device */
+ DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi );
+ memcpy ( undi, &preloaded_undi, sizeof ( *undi ) );
+ memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) );
+ } else {
+ /* Find UNDI ROM for PCI device */
+ if ( ! ( undirom = undipci_find_rom ( pci ) ) ) {
+ rc = -ENODEV;
+ goto err_find_rom;
+ }
+
+ /* Call UNDI ROM loader to create pixie */
+ if ( ( rc = undi_load_pci ( undi, undirom,
+ pci->busdevfn ) ) != 0 ) {
+ goto err_load_pci;
+ }
+ }
+
+ /* Add to device hierarchy */
+ snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
+ "UNDI-%s", pci->dev.name );
+ memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) );
+ undi->dev.parent = &pci->dev;
+ INIT_LIST_HEAD ( &undi->dev.children );
+ list_add ( &undi->dev.siblings, &pci->dev.children );
+
+ /* Create network device */
+ if ( ( rc = undinet_probe ( undi ) ) != 0 )
+ goto err_undinet_probe;
+
+ return 0;
+
+ err_undinet_probe:
+ undi_unload ( undi );
+ list_del ( &undi->dev.siblings );
+ err_find_rom:
+ err_load_pci:
+ free ( undi );
+ pci_set_drvdata ( pci, NULL );
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void undipci_remove ( struct pci_device *pci ) {
+ struct undi_device *undi = pci_get_drvdata ( pci );
+
+ undinet_remove ( undi );
+ undi_unload ( undi );
+ list_del ( &undi->dev.siblings );
+ free ( undi );
+ pci_set_drvdata ( pci, NULL );
+}
+
+static struct pci_device_id undipci_nics[] = {
+ PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ),
+};
+
+struct pci_driver undipci_driver __pci_driver_fallback = {
+ .ids = undipci_nics,
+ .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ),
+ .class = PCI_CLASS_ID ( PCI_CLASS_NETWORK, PCI_ANY_ID, PCI_ANY_ID ),
+ .probe = undipci_probe,
+ .remove = undipci_remove,
+};
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undiisr.S b/roms/ipxe/src/arch/i386/drivers/net/undiisr.S
new file mode 100644
index 000000000..b27effe1d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undiisr.S
@@ -0,0 +1,87 @@
+FILE_LICENCE ( GPL2_OR_LATER )
+
+#define PXENV_UNDI_ISR 0x0014
+#define PXENV_UNDI_ISR_IN_START 1
+#define PXENV_UNDI_ISR_OUT_OURS 0
+#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+
+#define IRQ_PIC_CUTOFF 8
+#define ICR_EOI_NON_SPECIFIC 0x20
+#define PIC1_ICR 0x20
+#define PIC2_ICR 0xa0
+
+ .text
+ .arch i386
+ .code16
+
+ .section ".text16", "ax", @progbits
+ .globl undiisr
+undiisr:
+
+ /* Preserve registers */
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ pushfl
+ pushal
+
+ /* Set up our segment registers */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+
+ /* Check that we have an UNDI entry point */
+ cmpw $0, pxeparent_entry_point
+ je chain
+
+ /* Issue UNDI API call */
+ movw %ax, %es
+ movw $undinet_params, %di
+ movw $PXENV_UNDI_ISR, %bx
+ movw $PXENV_UNDI_ISR_IN_START, funcflag
+ pushw %es
+ pushw %di
+ pushw %bx
+ lcall *pxeparent_entry_point
+ cli /* Just in case */
+ addw $6, %sp
+ cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag
+ jne eoi
+
+trig: /* Record interrupt occurence */
+ incb undiisr_trigger_count
+
+eoi: /* Send EOI */
+ movb $ICR_EOI_NON_SPECIFIC, %al
+ cmpb $IRQ_PIC_CUTOFF, undiisr_irq
+ jb 1f
+ outb %al, $PIC2_ICR
+1: outb %al, $PIC1_ICR
+ jmp exit
+
+chain: /* Chain to next handler */
+ pushfw
+ lcall *undiisr_next_handler
+
+exit: /* Restore registers and return */
+ cli
+ popal
+ movzwl %sp, %esp
+ addr32 movl -20(%esp), %esp /* %esp isn't restored by popal */
+ popfl
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ iret
+
+ .section ".data16", "aw", @progbits
+undinet_params:
+status: .word 0
+funcflag: .word 0
+bufferlength: .word 0
+framelength: .word 0
+frameheaderlength: .word 0
+frame: .word 0, 0
+prottype: .byte 0
+pkttype: .byte 0
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undiload.c b/roms/ipxe/src/arch/i386/drivers/net/undiload.c
new file mode 100644
index 000000000..7160ee384
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undiload.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <bios.h>
+#include <pnpbios.h>
+#include <basemem.h>
+#include <ipxe/pci.h>
+#include <undi.h>
+#include <undirom.h>
+#include <undiload.h>
+
+/** @file
+ *
+ * UNDI load/unload
+ *
+ */
+
+/* Disambiguate the various error causes */
+#define EINFO_EUNDILOAD \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "UNDI loader error" )
+#define EUNDILOAD( status ) EPLATFORM ( EINFO_EUNDILOAD, status )
+
+/** Parameter block for calling UNDI loader */
+static struct s_UNDI_LOADER __bss16 ( undi_loader );
+#define undi_loader __use_data16 ( undi_loader )
+
+/** UNDI loader entry point */
+static SEGOFF16_t __bss16 ( undi_loader_entry );
+#define undi_loader_entry __use_data16 ( undi_loader_entry )
+
+/**
+ * Call UNDI loader to create a pixie
+ *
+ * @v undi UNDI device
+ * @v undirom UNDI ROM
+ * @ret rc Return status code
+ */
+int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
+ struct s_PXE ppxe;
+ unsigned int fbms_seg;
+ uint16_t exit;
+ int rc;
+
+ /* Only one UNDI instance may be loaded at any given time */
+ if ( undi_loader_entry.segment ) {
+ DBG ( "UNDI %p cannot load multiple instances\n", undi );
+ return -EBUSY;
+ }
+
+ /* Set up START_UNDI parameters */
+ memset ( &undi_loader, 0, sizeof ( undi_loader ) );
+ undi_loader.AX = undi->pci_busdevfn;
+ undi_loader.BX = undi->isapnp_csn;
+ undi_loader.DX = undi->isapnp_read_port;
+ undi_loader.ES = BIOS_SEG;
+ undi_loader.DI = find_pnp_bios();
+
+ /* Allocate base memory for PXE stack */
+ undi->restore_fbms = get_fbms();
+ fbms_seg = ( undi->restore_fbms << 6 );
+ fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 );
+ undi_loader.UNDI_CS = fbms_seg;
+ fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 );
+ undi_loader.UNDI_DS = fbms_seg;
+
+ /* Debug info */
+ DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ",
+ undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS );
+ if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
+ unsigned int bus = ( undi->pci_busdevfn >> 8 );
+ unsigned int devfn = ( undi->pci_busdevfn & 0xff );
+ DBGC ( undi, "PCI %02x:%02x.%x\n",
+ bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) );
+ }
+ if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
+ DBGC ( undi, "ISAPnP(%04x) CSN %04x\n",
+ undi->isapnp_read_port, undi->isapnp_csn );
+ }
+
+ /* Call loader */
+ undi_loader_entry = undirom->loader_entry;
+ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "pushw %%ds\n\t"
+ "pushw %%ax\n\t"
+ "lcall *undi_loader_entry\n\t"
+ "popl %%ebp\n\t" /* discard */
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( exit )
+ : "a" ( __from_data16 ( &undi_loader ) )
+ : "ebx", "ecx", "edx", "esi", "edi" );
+
+ if ( exit != PXENV_EXIT_SUCCESS ) {
+ /* Clear entry point */
+ memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
+
+ rc = -EUNDILOAD ( undi_loader.Status );
+ DBGC ( undi, "UNDI %p loader failed: %s\n",
+ undi, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Populate PXE device structure */
+ undi->pxenv = undi_loader.PXENVptr;
+ undi->ppxe = undi_loader.PXEptr;
+ copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset,
+ sizeof ( ppxe ) );
+ undi->entry = ppxe.EntryPointSP;
+ DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x "
+ "entry %04x:%04x\n", undi, undi->pxenv.segment,
+ undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset,
+ undi->entry.segment, undi->entry.offset );
+
+ /* Update free base memory counter */
+ undi->fbms = ( fbms_seg >> 6 );
+ set_fbms ( undi->fbms );
+ DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+
+ return 0;
+}
+
+/**
+ * Unload a pixie
+ *
+ * @v undi UNDI device
+ * @ret rc Return status code
+ *
+ * Erases the PXENV+ and !PXE signatures, and frees the used base
+ * memory (if possible).
+ */
+int undi_unload ( struct undi_device *undi ) {
+ static uint32_t dead = 0xdeaddead;
+
+ DBGC ( undi, "UNDI %p unloading\n", undi );
+
+ /* Clear entry point */
+ memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
+
+ /* Erase signatures */
+ if ( undi->pxenv.segment )
+ put_real ( dead, undi->pxenv.segment, undi->pxenv.offset );
+ if ( undi->ppxe.segment )
+ put_real ( dead, undi->ppxe.segment, undi->ppxe.offset );
+
+ /* Free base memory, if possible */
+ if ( undi->fbms == get_fbms() ) {
+ DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+ set_fbms ( undi->restore_fbms );
+ return 0;
+ } else {
+ DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+ return -EBUSY;
+ }
+}
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undinet.c b/roms/ipxe/src/arch/i386/drivers/net/undinet.c
new file mode 100644
index 000000000..6450665ff
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undinet.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <pic8259.h>
+#include <biosint.h>
+#include <pnpbios.h>
+#include <basemem_packet.h>
+#include <ipxe/io.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/profile.h>
+#include <undi.h>
+#include <undinet.h>
+#include <pxeparent.h>
+
+/** @file
+ *
+ * UNDI network device driver
+ *
+ */
+
+/** An UNDI NIC */
+struct undi_nic {
+ /** Device supports IRQs */
+ int irq_supported;
+ /** Assigned IRQ number */
+ unsigned int irq;
+ /** Currently processing ISR */
+ int isr_processing;
+ /** Bug workarounds */
+ int hacks;
+};
+
+/**
+ * @defgroup undi_hacks UNDI workarounds
+ * @{
+ */
+
+/** Work around Etherboot 5.4 bugs */
+#define UNDI_HACK_EB54 0x0001
+
+/** @} */
+
+/** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
+#define UNDI_INITIALIZE_RETRY_MAX 10
+
+/** Delay between retries of PXENV_UNDI_INITIALIZE */
+#define UNDI_INITIALIZE_RETRY_DELAY_MS 200
+
+/** Maximum number of received packets per poll */
+#define UNDI_RX_QUOTA 4
+
+/** Alignment of received frame payload */
+#define UNDI_RX_ALIGN 16
+
+static void undinet_close ( struct net_device *netdev );
+
+/** Address of UNDI entry point */
+static SEGOFF16_t undinet_entry;
+
+/** Transmit profiler */
+static struct profiler undinet_tx_profiler __profiler =
+ { .name = "undinet.tx" };
+
+/** Transmit call profiler */
+static struct profiler undinet_tx_call_profiler __profiler =
+ { .name = "undinet.tx_call" };
+
+/** IRQ profiler */
+static struct profiler undinet_irq_profiler __profiler =
+ { .name = "undinet.irq" };
+
+/** ISR call profiler */
+static struct profiler undinet_isr_call_profiler __profiler =
+ { .name = "undinet.isr_call" };
+
+/** Receive profiler */
+static struct profiler undinet_rx_profiler __profiler =
+ { .name = "undinet.rx" };
+
+/*****************************************************************************
+ *
+ * UNDI interrupt service routine
+ *
+ *****************************************************************************
+ */
+
+/**
+ * UNDI interrupt service routine
+ *
+ * The UNDI ISR increments a counter (@c trigger_count) and exits.
+ */
+extern void undiisr ( void );
+
+/** IRQ number */
+uint8_t __data16 ( undiisr_irq );
+#define undiisr_irq __use_data16 ( undiisr_irq )
+
+/** IRQ chain vector */
+struct segoff __data16 ( undiisr_next_handler );
+#define undiisr_next_handler __use_data16 ( undiisr_next_handler )
+
+/** IRQ trigger count */
+volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
+#define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
+
+/** Last observed trigger count */
+static unsigned int last_trigger_count = 0;
+
+/**
+ * Hook UNDI interrupt service routine
+ *
+ * @v irq IRQ number
+ */
+static void undinet_hook_isr ( unsigned int irq ) {
+
+ assert ( irq <= IRQ_MAX );
+ assert ( undiisr_irq == 0 );
+
+ undiisr_irq = irq;
+ hook_bios_interrupt ( IRQ_INT ( irq ),
+ ( ( unsigned int ) undiisr ),
+ &undiisr_next_handler );
+}
+
+/**
+ * Unhook UNDI interrupt service routine
+ *
+ * @v irq IRQ number
+ */
+static void undinet_unhook_isr ( unsigned int irq ) {
+
+ assert ( irq <= IRQ_MAX );
+
+ unhook_bios_interrupt ( IRQ_INT ( irq ),
+ ( ( unsigned int ) undiisr ),
+ &undiisr_next_handler );
+ undiisr_irq = 0;
+}
+
+/**
+ * Test to see if UNDI ISR has been triggered
+ *
+ * @ret triggered ISR has been triggered since last check
+ */
+static int undinet_isr_triggered ( void ) {
+ unsigned int this_trigger_count;
+
+ /* Read trigger_count. Do this only once; it is volatile */
+ this_trigger_count = undiisr_trigger_count;
+
+ if ( this_trigger_count == last_trigger_count ) {
+ /* Not triggered */
+ return 0;
+ } else {
+ /* Triggered */
+ last_trigger_count = this_trigger_count;
+ return 1;
+ }
+}
+
+/*****************************************************************************
+ *
+ * UNDI network device interface
+ *
+ *****************************************************************************
+ */
+
+/** UNDI transmit buffer descriptor */
+static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
+#define undinet_tbd __use_data16 ( undinet_tbd )
+
+/** UNDI transmit destination address */
+static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
+#define undinet_destaddr __use_data16 ( undinet_destaddr )
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int undinet_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_TRANSMIT undi_transmit;
+ const void *ll_dest;
+ const void *ll_source;
+ uint16_t net_proto;
+ unsigned int flags;
+ uint8_t protocol;
+ size_t len;
+ int rc;
+
+ /* Start profiling */
+ profile_start ( &undinet_tx_profiler );
+
+ /* Technically, we ought to make sure that the previous
+ * transmission has completed before we re-use the buffer.
+ * However, many PXE stacks (including at least some Intel PXE
+ * stacks and Etherboot 5.4) fail to generate TX completions.
+ * In practice this won't be a problem, since our TX datapath
+ * has a very low packet volume and we can get away with
+ * assuming that a TX will be complete by the time we want to
+ * transmit the next packet.
+ */
+
+ /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
+ * always try to prepend a link-layer header. Work around
+ * these stacks by stripping the existing link-layer header
+ * and allowing the PXE stack to (re)construct the link-layer
+ * header itself.
+ */
+ if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
+ &net_proto, &flags ) ) != 0 ) {
+ DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
+ "%s\n", undinic, strerror ( rc ) );
+ return rc;
+ }
+ memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
+ switch ( net_proto ) {
+ case htons ( ETH_P_IP ) :
+ protocol = P_IP;
+ break;
+ case htons ( ETH_P_ARP ) :
+ protocol = P_ARP;
+ break;
+ case htons ( ETH_P_RARP ) :
+ protocol = P_RARP;
+ break;
+ default:
+ /* Unknown protocol; restore the original link-layer header */
+ iob_push ( iobuf, sizeof ( struct ethhdr ) );
+ protocol = P_UNKNOWN;
+ break;
+ }
+
+ /* Copy packet to UNDI I/O buffer */
+ len = iob_len ( iobuf );
+ if ( len > sizeof ( basemem_packet ) )
+ len = sizeof ( basemem_packet );
+ memcpy ( &basemem_packet, iobuf->data, len );
+
+ /* Create PXENV_UNDI_TRANSMIT data structure */
+ memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
+ undi_transmit.Protocol = protocol;
+ undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
+ XMT_BROADCAST : XMT_DESTADDR );
+ undi_transmit.DestAddr.segment = rm_ds;
+ undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
+ undi_transmit.TBD.segment = rm_ds;
+ undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
+
+ /* Create PXENV_UNDI_TBD data structure */
+ undinet_tbd.ImmedLength = len;
+ undinet_tbd.Xmit.segment = rm_ds;
+ undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
+
+ /* Issue PXE API call */
+ profile_start ( &undinet_tx_call_profiler );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
+ &undi_transmit,
+ sizeof ( undi_transmit ) ) ) != 0 )
+ goto done;
+ profile_stop ( &undinet_tx_call_profiler );
+
+ /* Free I/O buffer */
+ netdev_tx_complete ( netdev, iobuf );
+ profile_stop ( &undinet_tx_profiler );
+ done:
+ return rc;
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ *
+ * Fun, fun, fun. UNDI drivers don't use polling; they use
+ * interrupts. We therefore cheat and pretend that an interrupt has
+ * occurred every time undinet_poll() is called. This isn't too much
+ * of a hack; PCI devices share IRQs and so the first thing that a
+ * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
+ * not the UNDI NIC generated the interrupt; there is no harm done by
+ * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
+ * handling them any more rapidly than the usual rate of
+ * undinet_poll() being called even if we did implement a full ISR.
+ * So it should work. Ha!
+ *
+ * Addendum (21/10/03). Some cards don't play nicely with this trick,
+ * so instead of doing it the easy way we have to go to all the hassle
+ * of installing a genuine interrupt service routine and dealing with
+ * the wonderful 8259 Programmable Interrupt Controller. Joy.
+ *
+ * Addendum (10/07/07). When doing things such as iSCSI boot, in
+ * which we have to co-operate with a running OS, we can't get away
+ * with the "ISR-just-increments-a-counter-and-returns" trick at all,
+ * because it involves tying up the PIC for far too long, and other
+ * interrupt-dependent components (e.g. local disks) start breaking.
+ * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
+ * from within interrupt context in order to deassert the device
+ * interrupt, and sends EOI if applicable.
+ */
+static void undinet_poll ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct io_buffer *iobuf = NULL;
+ unsigned int quota = UNDI_RX_QUOTA;
+ size_t len;
+ size_t reserve_len;
+ size_t frag_len;
+ size_t max_frag_len;
+ int rc;
+
+ if ( ! undinic->isr_processing ) {
+ /* Allow interrupt to occur. Do this even if
+ * interrupts are not known to be supported, since
+ * some cards erroneously report that they do not
+ * support interrupts.
+ */
+ if ( ! undinet_isr_triggered() ) {
+ /* Allow interrupt to occur */
+ profile_start ( &undinet_irq_profiler );
+ __asm__ __volatile__ ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" );
+ profile_stop ( &undinet_irq_profiler );
+
+ /* If interrupts are known to be supported,
+ * then do nothing on this poll; wait for the
+ * interrupt to be triggered.
+ */
+ if ( undinic->irq_supported )
+ return;
+ }
+
+ /* Start ISR processing */
+ undinic->isr_processing = 1;
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ } else {
+ /* Continue ISR processing */
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ }
+
+ /* Run through the ISR loop */
+ while ( quota ) {
+ profile_start ( &undinet_isr_call_profiler );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
+ &undi_isr,
+ sizeof ( undi_isr ) ) ) != 0 ) {
+ netdev_rx_err ( netdev, NULL, rc );
+ break;
+ }
+ profile_stop ( &undinet_isr_call_profiler );
+ switch ( undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* We don't care about transmit completions */
+ break;
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Packet fragment received */
+ profile_start ( &undinet_rx_profiler );
+ len = undi_isr.FrameLength;
+ frag_len = undi_isr.BufferLength;
+ reserve_len = ( -undi_isr.FrameHeaderLength &
+ ( UNDI_RX_ALIGN - 1 ) );
+ if ( ( len == 0 ) || ( len < frag_len ) ) {
+ /* Don't laugh. VMWare does it. */
+ DBGC ( undinic, "UNDINIC %p reported insane "
+ "fragment (%zd of %zd bytes)\n",
+ undinic, frag_len, len );
+ netdev_rx_err ( netdev, NULL, -EINVAL );
+ break;
+ }
+ if ( ! iobuf ) {
+ iobuf = alloc_iob ( reserve_len + len );
+ if ( ! iobuf ) {
+ DBGC ( undinic, "UNDINIC %p could not "
+ "allocate %zd bytes for RX "
+ "buffer\n", undinic, len );
+ /* Fragment will be dropped */
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ goto done;
+ }
+ iob_reserve ( iobuf, reserve_len );
+ }
+ max_frag_len = iob_tailroom ( iobuf );
+ if ( frag_len > max_frag_len ) {
+ DBGC ( undinic, "UNDINIC %p fragment too big "
+ "(%zd+%zd does not fit into %zd)\n",
+ undinic, iob_len ( iobuf ), frag_len,
+ ( iob_len ( iobuf ) + max_frag_len ) );
+ frag_len = max_frag_len;
+ }
+ copy_from_real ( iob_put ( iobuf, frag_len ),
+ undi_isr.Frame.segment,
+ undi_isr.Frame.offset, frag_len );
+ if ( iob_len ( iobuf ) == len ) {
+ /* Whole packet received; deliver it */
+ netdev_rx ( netdev, iob_disown ( iobuf ) );
+ quota--;
+ /* Etherboot 5.4 fails to return all packets
+ * under mild load; pretend it retriggered.
+ */
+ if ( undinic->hacks & UNDI_HACK_EB54 )
+ --last_trigger_count;
+ }
+ profile_stop ( &undinet_rx_profiler );
+ break;
+ case PXENV_UNDI_ISR_OUT_DONE:
+ /* Processing complete */
+ undinic->isr_processing = 0;
+ goto done;
+ default:
+ /* Should never happen. VMWare does it routinely. */
+ DBGC ( undinic, "UNDINIC %p ISR returned invalid "
+ "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
+ undinic->isr_processing = 0;
+ goto done;
+ }
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ }
+
+ done:
+ if ( iobuf ) {
+ DBGC ( undinic, "UNDINIC %p returned incomplete packet "
+ "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
+ ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
+ netdev_rx_err ( netdev, iobuf, -EINVAL );
+ }
+}
+
+/**
+ * Open NIC
+ *
+ * @v netdev Net device
+ * @ret rc Return status code
+ */
+static int undinet_open ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
+ struct s_PXENV_UNDI_OPEN undi_open;
+ int rc;
+
+ /* Hook interrupt service routine and enable interrupt if applicable */
+ if ( undinic->irq ) {
+ undinet_hook_isr ( undinic->irq );
+ enable_irq ( undinic->irq );
+ send_eoi ( undinic->irq );
+ }
+
+ /* Set station address. Required for some PXE stacks; will
+ * spuriously fail on others. Ignore failures. We only ever
+ * use it to set the MAC address to the card's permanent value
+ * anyway.
+ */
+ memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
+ sizeof ( undi_set_address.StationAddress ) );
+ pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
+ &undi_set_address, sizeof ( undi_set_address ) );
+
+ /* Open NIC. We ask for promiscuous operation, since it's the
+ * only way to ask for all multicast addresses. On any
+ * switched network, it shouldn't really make a difference to
+ * performance.
+ */
+ memset ( &undi_open, 0, sizeof ( undi_open ) );
+ undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
+ &undi_open, sizeof ( undi_open ) ) ) != 0 )
+ goto err;
+
+ DBGC ( undinic, "UNDINIC %p opened\n", undinic );
+ return 0;
+
+ err:
+ undinet_close ( netdev );
+ return rc;
+}
+
+/**
+ * Close NIC
+ *
+ * @v netdev Net device
+ */
+static void undinet_close ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct s_PXENV_UNDI_CLOSE undi_close;
+ int rc;
+
+ /* Ensure ISR has exited cleanly */
+ while ( undinic->isr_processing ) {
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
+ &undi_isr,
+ sizeof ( undi_isr ) ) ) != 0 )
+ break;
+ switch ( undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Continue draining */
+ break;
+ default:
+ /* Stop processing */
+ undinic->isr_processing = 0;
+ break;
+ }
+ }
+
+ /* Close NIC */
+ pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
+ &undi_close, sizeof ( undi_close ) );
+
+ /* Disable interrupt and unhook ISR if applicable */
+ if ( undinic->irq ) {
+ disable_irq ( undinic->irq );
+ undinet_unhook_isr ( undinic->irq );
+ }
+
+ DBGC ( undinic, "UNDINIC %p closed\n", undinic );
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev Net device
+ * @v enable Interrupts should be enabled
+ */
+static void undinet_irq ( struct net_device *netdev, int enable ) {
+ struct undi_nic *undinic = netdev->priv;
+
+ /* Cannot support interrupts yet */
+ DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
+ undinic, ( enable ? "enable" : "disable" ) );
+}
+
+/** UNDI network device operations */
+static struct net_device_operations undinet_operations = {
+ .open = undinet_open,
+ .close = undinet_close,
+ .transmit = undinet_transmit,
+ .poll = undinet_poll,
+ .irq = undinet_irq,
+};
+
+/** A device with broken support for generating interrupts */
+struct undinet_irq_broken {
+ /** PCI vendor ID */
+ uint16_t pci_vendor;
+ /** PCI device ID */
+ uint16_t pci_device;
+};
+
+/**
+ * List of devices with broken support for generating interrupts
+ *
+ * Some PXE stacks are known to claim that IRQs are supported, but
+ * then never generate interrupts. No satisfactory solution has been
+ * found to this problem; the workaround is to add the PCI vendor and
+ * device IDs to this list. This is something of a hack, since it
+ * will generate false positives for identical devices with a working
+ * PXE stack (e.g. those that have been reflashed with iPXE), but it's
+ * an improvement on the current situation.
+ */
+static const struct undinet_irq_broken undinet_irq_broken_list[] = {
+ /* HP XX70x laptops */
+ { .pci_vendor = 0x8086, .pci_device = 0x1502 },
+ { .pci_vendor = 0x8086, .pci_device = 0x1503 },
+};
+
+/**
+ * Check for devices with broken support for generating interrupts
+ *
+ * @v undi UNDI device
+ * @ret irq_is_broken Interrupt support is broken; no interrupts are generated
+ */
+static int undinet_irq_is_broken ( struct undi_device *undi ) {
+ const struct undinet_irq_broken *broken;
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
+ sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
+ broken = &undinet_irq_broken_list[i];
+ if ( ( undi->dev.desc.bus_type == BUS_TYPE_PCI ) &&
+ ( undi->dev.desc.vendor == broken->pci_vendor ) &&
+ ( undi->dev.desc.device == broken->pci_device ) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Probe UNDI device
+ *
+ * @v undi UNDI device
+ * @ret rc Return status code
+ */
+int undinet_probe ( struct undi_device *undi ) {
+ struct net_device *netdev;
+ struct undi_nic *undinic;
+ struct s_PXENV_START_UNDI start_undi;
+ struct s_PXENV_UNDI_STARTUP undi_startup;
+ struct s_PXENV_UNDI_INITIALIZE undi_init;
+ struct s_PXENV_UNDI_GET_INFORMATION undi_info;
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_STOP_UNDI stop_undi;
+ unsigned int retry;
+ int rc;
+
+ /* Allocate net device */
+ netdev = alloc_etherdev ( sizeof ( *undinic ) );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &undinet_operations );
+ undinic = netdev->priv;
+ undi_set_drvdata ( undi, netdev );
+ netdev->dev = &undi->dev;
+ memset ( undinic, 0, sizeof ( *undinic ) );
+ undinet_entry = undi->entry;
+ DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
+
+ /* Hook in UNDI stack */
+ if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
+ memset ( &start_undi, 0, sizeof ( start_undi ) );
+ start_undi.AX = undi->pci_busdevfn;
+ start_undi.BX = undi->isapnp_csn;
+ start_undi.DX = undi->isapnp_read_port;
+ start_undi.ES = BIOS_SEG;
+ start_undi.DI = find_pnp_bios();
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
+ &start_undi,
+ sizeof ( start_undi ) ) ) != 0 )
+ goto err_start_undi;
+ }
+ undi->flags |= UNDI_FL_STARTED;
+
+ /* Bring up UNDI stack */
+ if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
+ memset ( &undi_startup, 0, sizeof ( undi_startup ) );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
+ &undi_startup,
+ sizeof ( undi_startup ) ) ) != 0 )
+ goto err_undi_startup;
+ /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
+ * due to a transient condition (e.g. media test
+ * failing because the link has only just come out of
+ * reset). We may therefore need to retry this call
+ * several times.
+ */
+ for ( retry = 0 ; ; ) {
+ memset ( &undi_init, 0, sizeof ( undi_init ) );
+ if ( ( rc = pxeparent_call ( undinet_entry,
+ PXENV_UNDI_INITIALIZE,
+ &undi_init,
+ sizeof ( undi_init ))) ==0)
+ break;
+ if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
+ goto err_undi_initialize;
+ DBGC ( undinic, "UNDINIC %p retrying "
+ "PXENV_UNDI_INITIALIZE (retry %d)\n",
+ undinic, retry );
+ /* Delay to allow link to settle if necessary */
+ mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
+ }
+ }
+ undi->flags |= UNDI_FL_INITIALIZED;
+
+ /* Get device information */
+ memset ( &undi_info, 0, sizeof ( undi_info ) );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
+ &undi_info, sizeof ( undi_info ) ) ) != 0 )
+ goto err_undi_get_information;
+ memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
+ memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
+ undinic->irq = undi_info.IntNumber;
+ if ( undinic->irq > IRQ_MAX ) {
+ DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
+ undinic, undinic->irq );
+ rc = -EINVAL;
+ goto err_bad_irq;
+ }
+ DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
+ undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
+
+ /* Get interface information */
+ memset ( &undi_iface, 0, sizeof ( undi_iface ) );
+ if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
+ &undi_iface,
+ sizeof ( undi_iface ) ) ) != 0 )
+ goto err_undi_get_iface_info;
+ DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
+ undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
+ undi_iface.ServiceFlags );
+ if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
+ ( undinic->irq != 0 ) ) {
+ undinic->irq_supported = 1;
+ }
+ DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
+ ( undinic->irq_supported ? "interrupt" : "polling" ) );
+ if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
+ sizeof ( undi_iface.IfaceType ) ) == 0 ) {
+ DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
+ undinic );
+ undinic->hacks |= UNDI_HACK_EB54;
+ }
+ if ( undinet_irq_is_broken ( undi ) ) {
+ DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
+ "broken interrupts\n", undinic );
+ undinic->irq_supported = 0;
+ }
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ /* Mark as link up; we don't handle link state */
+ netdev_link_up ( netdev );
+
+ DBGC ( undinic, "UNDINIC %p added\n", undinic );
+ return 0;
+
+ err_register:
+ err_undi_get_iface_info:
+ err_bad_irq:
+ err_undi_get_information:
+ err_undi_initialize:
+ /* Shut down UNDI stack */
+ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
+ pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
+ sizeof ( undi_shutdown ) );
+ memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
+ pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
+ sizeof ( undi_cleanup ) );
+ undi->flags &= ~UNDI_FL_INITIALIZED;
+ err_undi_startup:
+ /* Unhook UNDI stack */
+ memset ( &stop_undi, 0, sizeof ( stop_undi ) );
+ pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
+ sizeof ( stop_undi ) );
+ undi->flags &= ~UNDI_FL_STARTED;
+ err_start_undi:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ undi_set_drvdata ( undi, NULL );
+ return rc;
+}
+
+/**
+ * Remove UNDI device
+ *
+ * @v undi UNDI device
+ */
+void undinet_remove ( struct undi_device *undi ) {
+ struct net_device *netdev = undi_get_drvdata ( undi );
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_STOP_UNDI stop_undi;
+
+ /* Unregister net device */
+ unregister_netdev ( netdev );
+
+ /* If we are preparing for an OS boot, or if we cannot exit
+ * via the PXE stack, then shut down the PXE stack.
+ */
+ if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
+
+ /* Shut down UNDI stack */
+ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
+ pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
+ &undi_shutdown, sizeof ( undi_shutdown ) );
+ memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
+ pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
+ &undi_cleanup, sizeof ( undi_cleanup ) );
+ undi->flags &= ~UNDI_FL_INITIALIZED;
+
+ /* Unhook UNDI stack */
+ memset ( &stop_undi, 0, sizeof ( stop_undi ) );
+ pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
+ sizeof ( stop_undi ) );
+ undi->flags &= ~UNDI_FL_STARTED;
+ }
+
+ /* Clear entry point */
+ memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
+
+ /* Free network device */
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+
+ DBGC ( undinic, "UNDINIC %p removed\n", undinic );
+}
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undionly.c b/roms/ipxe/src/arch/i386/drivers/net/undionly.c
new file mode 100644
index 000000000..70dbe4bfd
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undionly.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ipxe/device.h>
+#include <ipxe/init.h>
+#include <ipxe/pci.h>
+#include <undi.h>
+#include <undinet.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * "Pure" UNDI driver
+ *
+ * This is the UNDI driver without explicit support for PCI or any
+ * other bus type. It is capable only of using the preloaded UNDI
+ * device. It must not be combined in an image with any other
+ * drivers.
+ *
+ * If you want a PXE-loadable image that contains only the UNDI
+ * driver, build "bin/undionly.kpxe".
+ *
+ * If you want any other image format, or any other drivers in
+ * addition to the UNDI driver, build e.g. "bin/undi.dsk".
+ */
+
+/**
+ * Probe UNDI root bus
+ *
+ * @v rootdev UNDI bus root device
+ *
+ * Scans the UNDI bus for devices and registers all devices it can
+ * find.
+ */
+static int undibus_probe ( struct root_device *rootdev ) {
+ struct undi_device *undi = &preloaded_undi;
+ int rc;
+
+ /* Check for a valie preloaded UNDI device */
+ if ( ! undi->entry.segment ) {
+ DBG ( "No preloaded UNDI device found!\n" );
+ return -ENODEV;
+ }
+
+ /* Add to device hierarchy */
+ undi->dev.driver_name = "undionly";
+ if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
+ undi->dev.desc.bus_type = BUS_TYPE_PCI;
+ undi->dev.desc.location = undi->pci_busdevfn;
+ undi->dev.desc.vendor = undi->pci_vendor;
+ undi->dev.desc.device = undi->pci_device;
+ snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
+ "UNDI-PCI%02x:%02x.%x",
+ PCI_BUS ( undi->pci_busdevfn ),
+ PCI_SLOT ( undi->pci_busdevfn ),
+ PCI_FUNC ( undi->pci_busdevfn ) );
+ } else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
+ undi->dev.desc.bus_type = BUS_TYPE_ISAPNP;
+ snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
+ "UNDI-ISAPNP" );
+ }
+ undi->dev.parent = &rootdev->dev;
+ list_add ( &undi->dev.siblings, &rootdev->dev.children);
+ INIT_LIST_HEAD ( &undi->dev.children );
+
+ /* Create network device */
+ if ( ( rc = undinet_probe ( undi ) ) != 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ list_del ( &undi->dev.siblings );
+ return rc;
+}
+
+/**
+ * Remove UNDI root bus
+ *
+ * @v rootdev UNDI bus root device
+ */
+static void undibus_remove ( struct root_device *rootdev __unused ) {
+ struct undi_device *undi = &preloaded_undi;
+
+ undinet_remove ( undi );
+ list_del ( &undi->dev.siblings );
+}
+
+/** UNDI bus root device driver */
+static struct root_driver undi_root_driver = {
+ .probe = undibus_probe,
+ .remove = undibus_remove,
+};
+
+/** UNDI bus root device */
+struct root_device undi_root_device __root_device = {
+ .dev = { .name = "UNDI" },
+ .driver = &undi_root_driver,
+};
+
+/**
+ * Prepare for exit
+ *
+ * @v booting System is shutting down for OS boot
+ */
+static void undionly_shutdown ( int booting ) {
+ /* If we are shutting down to boot an OS, clear the "keep PXE
+ * stack" flag.
+ */
+ if ( booting )
+ preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL;
+}
+
+struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = {
+ .shutdown = undionly_shutdown,
+};
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undipreload.c b/roms/ipxe/src/arch/i386/drivers/net/undipreload.c
new file mode 100644
index 000000000..fca771843
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undipreload.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <realmode.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * Preloaded UNDI stack
+ *
+ */
+
+/**
+ * Preloaded UNDI device
+ *
+ * This is the UNDI device that was present when Etherboot started
+ * execution (i.e. when loading a .kpxe image). The first driver to
+ * claim this device must zero out this data structure.
+ */
+struct undi_device __data16 ( preloaded_undi );
diff --git a/roms/ipxe/src/arch/i386/drivers/net/undirom.c b/roms/ipxe/src/arch/i386/drivers/net/undirom.c
new file mode 100644
index 000000000..b54c6170f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/drivers/net/undirom.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <undirom.h>
+
+/** @file
+ *
+ * UNDI expansion ROMs
+ *
+ */
+
+/** List of all UNDI ROMs */
+static LIST_HEAD ( undiroms );
+
+/**
+ * Parse PXE ROM ID structure
+ *
+ * @v undirom UNDI ROM
+ * @v pxeromid Offset within ROM to PXE ROM ID structure
+ * @ret rc Return status code
+ */
+static int undirom_parse_pxeromid ( struct undi_rom *undirom,
+ unsigned int pxeromid ) {
+ struct undi_rom_id undi_rom_id;
+ unsigned int undiloader;
+
+ DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom,
+ undirom->rom_segment, pxeromid );
+
+ /* Read PXE ROM ID structure and verify */
+ copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid,
+ sizeof ( undi_rom_id ) );
+ if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
+ DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature "
+ "%08x\n", undirom, undi_rom_id.Signature );
+ return -EINVAL;
+ }
+
+ /* Check for UNDI loader */
+ undiloader = undi_rom_id.UNDILoader;
+ if ( ! undiloader ) {
+ DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom );
+ return -EINVAL;
+ }
+
+ /* Fill in UNDI ROM loader fields */
+ undirom->loader_entry.segment = undirom->rom_segment;
+ undirom->loader_entry.offset = undiloader;
+ undirom->code_size = undi_rom_id.CodeSize;
+ undirom->data_size = undi_rom_id.DataSize;
+
+ DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x "
+ "(code %04zx data %04zx)\n", undirom,
+ undirom->loader_entry.segment, undirom->loader_entry.offset,
+ undirom->code_size, undirom->data_size );
+ return 0;
+}
+
+/**
+ * Parse PCI expansion header
+ *
+ * @v undirom UNDI ROM
+ * @v pcirheader Offset within ROM to PCI expansion header
+ */
+static int undirom_parse_pcirheader ( struct undi_rom *undirom,
+ unsigned int pcirheader ) {
+ struct pcir_header pcir_header;
+
+ DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n",
+ undirom, undirom->rom_segment, pcirheader );
+
+ /* Read PCI expansion header and verify */
+ copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader,
+ sizeof ( pcir_header ) );
+ if ( pcir_header.signature != PCIR_SIGNATURE ) {
+ DBGC ( undirom, "UNDIROM %p has bad PCI expansion header "
+ "signature %08x\n", undirom, pcir_header.signature );
+ return -EINVAL;
+ }
+
+ /* Fill in UNDI ROM PCI device fields */
+ undirom->bus_type = PCI_NIC;
+ undirom->bus_id.pci.vendor_id = pcir_header.vendor_id;
+ undirom->bus_id.pci.device_id = pcir_header.device_id;
+
+ DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom,
+ undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id );
+ return 0;
+
+}
+
+/**
+ * Probe UNDI ROM
+ *
+ * @v rom_segment ROM segment address
+ * @ret rc Return status code
+ */
+static int undirom_probe ( unsigned int rom_segment ) {
+ struct undi_rom *undirom = NULL;
+ struct undi_rom_header romheader;
+ size_t rom_len;
+ unsigned int pxeromid;
+ unsigned int pcirheader;
+ int rc;
+
+ /* Read expansion ROM header and verify */
+ copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) );
+ if ( romheader.Signature != ROM_SIGNATURE ) {
+ rc = -EINVAL;
+ goto err;
+ }
+ rom_len = ( romheader.ROMLength * 512 );
+
+ /* Allocate memory for UNDI ROM */
+ undirom = zalloc ( sizeof ( *undirom ) );
+ if ( ! undirom ) {
+ DBG ( "Could not allocate UNDI ROM structure\n" );
+ rc = -ENOMEM;
+ goto err;
+ }
+ DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 "
+ "(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) );
+ undirom->rom_segment = rom_segment;
+
+ /* Check for and parse PXE ROM ID */
+ pxeromid = romheader.PXEROMID;
+ if ( ! pxeromid ) {
+ DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom );
+ rc = -EINVAL;
+ goto err;
+ }
+ if ( pxeromid > rom_len ) {
+ DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n",
+ undirom );
+ rc = -EINVAL;
+ goto err;
+ }
+ if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 )
+ goto err;
+
+ /* Parse PCIR header, if present */
+ pcirheader = romheader.PCIRHeader;
+ if ( pcirheader )
+ undirom_parse_pcirheader ( undirom, pcirheader );
+
+ /* Add to UNDI ROM list and return */
+ DBGC ( undirom, "UNDIROM %p registered\n", undirom );
+ list_add ( &undirom->list, &undiroms );
+ return 0;
+
+ err:
+ free ( undirom );
+ return rc;
+}
+
+/**
+ * Create UNDI ROMs for all possible expansion ROMs
+ *
+ * @ret
+ */
+static void undirom_probe_all_roms ( void ) {
+ static int probed = 0;
+ unsigned int rom_segment;
+
+ /* Perform probe only once */
+ if ( probed )
+ return;
+
+ DBG ( "Scanning for PXE expansion ROMs\n" );
+
+ /* Scan through expansion ROM region at 512 byte intervals */
+ for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
+ rom_segment += 0x20 ) {
+ undirom_probe ( rom_segment );
+ }
+
+ probed = 1;
+}
+
+/**
+ * Find UNDI ROM for PCI device
+ *
+ * @v vendor_id PCI vendor ID
+ * @v device_id PCI device ID
+ * @v rombase ROM base address, or 0 for any
+ * @ret undirom UNDI ROM, or NULL
+ */
+struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
+ unsigned int device_id,
+ unsigned int rombase ) {
+ struct undi_rom *undirom;
+
+ undirom_probe_all_roms();
+
+ list_for_each_entry ( undirom, &undiroms, list ) {
+ if ( undirom->bus_type != PCI_NIC )
+ continue;
+ if ( undirom->bus_id.pci.vendor_id != vendor_id )
+ continue;
+ if ( undirom->bus_id.pci.device_id != device_id )
+ continue;
+ if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) )
+ continue;
+ DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n",
+ undirom, vendor_id, device_id, rombase );
+ return undirom;
+ }
+
+ DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n",
+ vendor_id, device_id, rombase );
+ return NULL;
+}
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c b/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c
new file mode 100644
index 000000000..6a46081aa
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <realmode.h>
+#include <bios.h>
+#include <basemem.h>
+#include <ipxe/hidemem.h>
+
+/** @file
+ *
+ * Base memory allocation
+ *
+ */
+
+/**
+ * Set the BIOS free base memory counter
+ *
+ * @v new_fbms New free base memory counter (in kB)
+ */
+void set_fbms ( unsigned int new_fbms ) {
+ uint16_t fbms = new_fbms;
+
+ /* Update the BIOS memory counter */
+ put_real ( fbms, BDA_SEG, BDA_FBMS );
+
+ /* Update our hidden memory region map */
+ hide_basemem();
+}
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c
new file mode 100644
index 000000000..63413cdc1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c
@@ -0,0 +1,395 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <realmode.h>
+#include <bios.h>
+#include <ipxe/console.h>
+#include <ipxe/ansiesc.h>
+#include <ipxe/keymap.h>
+#include <config/console.h>
+
+#define ATTR_BOLD 0x08
+
+#define ATTR_FCOL_MASK 0x07
+#define ATTR_FCOL_BLACK 0x00
+#define ATTR_FCOL_BLUE 0x01
+#define ATTR_FCOL_GREEN 0x02
+#define ATTR_FCOL_CYAN 0x03
+#define ATTR_FCOL_RED 0x04
+#define ATTR_FCOL_MAGENTA 0x05
+#define ATTR_FCOL_YELLOW 0x06
+#define ATTR_FCOL_WHITE 0x07
+
+#define ATTR_BLINK 0x80
+
+#define ATTR_BCOL_MASK 0x70
+#define ATTR_BCOL_BLACK 0x00
+#define ATTR_BCOL_BLUE 0x10
+#define ATTR_BCOL_GREEN 0x20
+#define ATTR_BCOL_CYAN 0x30
+#define ATTR_BCOL_RED 0x40
+#define ATTR_BCOL_MAGENTA 0x50
+#define ATTR_BCOL_YELLOW 0x60
+#define ATTR_BCOL_WHITE 0x70
+
+#define ATTR_DEFAULT ATTR_FCOL_WHITE
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
+#undef CONSOLE_PCBIOS
+#define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Current character attribute */
+static unsigned int bios_attr = ATTR_DEFAULT;
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params[0] Row (1 is top)
+ * @v params[1] Column (1 is left)
+ */
+static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused, int params[] ) {
+ int cx = ( params[1] - 1 );
+ int cy = ( params[0] - 1 );
+
+ if ( cx < 0 )
+ cx = 0;
+ if ( cy < 0 )
+ cy = 0;
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0200 ), "b" ( 1 ),
+ "d" ( ( cy << 8 ) | cx ) );
+}
+
+/**
+ * Handle ANSI ED (erase in page)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params[0] Region to erase
+ */
+static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+ /* We assume that we always clear the whole screen */
+ assert ( params[0] == ANSIESC_ED_ALL );
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
+ "c" ( 0 ),
+ "d" ( ( ( console_height - 1 ) << 8 ) |
+ ( console_width - 1 ) ) );
+}
+
+/**
+ * Handle ANSI SGR (set graphics rendition)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
+ unsigned int count, int params[] ) {
+ static const uint8_t bios_attr_fcols[10] = {
+ ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
+ ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
+ ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
+ ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
+ };
+ static const uint8_t bios_attr_bcols[10] = {
+ ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
+ ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
+ ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
+ ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
+ };
+ unsigned int i;
+ int aspect;
+
+ for ( i = 0 ; i < count ; i++ ) {
+ aspect = params[i];
+ if ( aspect == 0 ) {
+ bios_attr = ATTR_DEFAULT;
+ } else if ( aspect == 1 ) {
+ bios_attr |= ATTR_BOLD;
+ } else if ( aspect == 5 ) {
+ bios_attr |= ATTR_BLINK;
+ } else if ( aspect == 22 ) {
+ bios_attr &= ~ATTR_BOLD;
+ } else if ( aspect == 25 ) {
+ bios_attr &= ~ATTR_BLINK;
+ } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
+ bios_attr &= ~ATTR_FCOL_MASK;
+ bios_attr |= bios_attr_fcols[ aspect - 30 ];
+ } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
+ bios_attr &= ~ATTR_BCOL_MASK;
+ bios_attr |= bios_attr_bcols[ aspect - 40 ];
+ }
+ }
+}
+
+/**
+ * Handle ANSI DECTCEM set (show cursor)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+ uint8_t height;
+
+ /* Get character height */
+ get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0100 ),
+ "c" ( ( ( height - 2 ) << 8 ) |
+ ( height - 1 ) ) );
+}
+
+/**
+ * Handle ANSI DECTCEM reset (hide cursor)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0100 ), "c" ( 0x2000 ) );
+}
+
+/** BIOS console ANSI escape sequence handlers */
+static struct ansiesc_handler bios_ansiesc_handlers[] = {
+ { ANSIESC_CUP, bios_handle_cup },
+ { ANSIESC_ED, bios_handle_ed },
+ { ANSIESC_SGR, bios_handle_sgr },
+ { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
+ { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
+ { 0, NULL }
+};
+
+/** BIOS console ANSI escape sequence context */
+static struct ansiesc_context bios_ansiesc_ctx = {
+ .handlers = bios_ansiesc_handlers,
+};
+
+/**
+ * Print a character to BIOS console
+ *
+ * @v character Character to be printed
+ */
+static void bios_putchar ( int character ) {
+ int discard_a, discard_b, discard_c;
+
+ /* Intercept ANSI escape sequences */
+ character = ansiesc_process ( &bios_ansiesc_ctx, character );
+ if ( character < 0 )
+ return;
+
+ /* Print character with attribute */
+ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "sti\n\t"
+ /* Skip non-printable characters */
+ "cmpb $0x20, %%al\n\t"
+ "jb 1f\n\t"
+ /* Read attribute */
+ "movb %%al, %%cl\n\t"
+ "movb $0x08, %%ah\n\t"
+ "int $0x10\n\t"
+ "xchgb %%al, %%cl\n\t"
+ /* Skip if attribute matches */
+ "cmpb %%ah, %%bl\n\t"
+ "je 1f\n\t"
+ /* Set attribute */
+ "movw $0x0001, %%cx\n\t"
+ "movb $0x09, %%ah\n\t"
+ "int $0x10\n\t"
+ "\n1:\n\t"
+ /* Print character */
+ "xorw %%bx, %%bx\n\t"
+ "movb $0x0e, %%ah\n\t"
+ "int $0x10\n\t"
+ "cli\n\t"
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( discard_a ), "=b" ( discard_b ),
+ "=c" ( discard_c )
+ : "a" ( character ), "b" ( bios_attr ) );
+}
+
+/**
+ * Pointer to current ANSI output sequence
+ *
+ * While we are in the middle of returning an ANSI sequence for a
+ * special key, this will point to the next character to return. When
+ * not in the middle of such a sequence, this will point to a NUL
+ * (note: not "will be NULL").
+ */
+static const char *ansi_input = "";
+
+/** A mapping from a BIOS scan code to an ANSI escape sequence */
+#define BIOS_KEY( key, ansi ) key ansi "\0"
+
+/** Mapping from BIOS scan codes to ANSI escape sequences */
+static const char ansi_sequences[] = {
+ BIOS_KEY ( "\x53", "[3~" ) /* Delete */
+ BIOS_KEY ( "\x48", "[A" ) /* Up arrow */
+ BIOS_KEY ( "\x50", "[B" ) /* Down arrow */
+ BIOS_KEY ( "\x4b", "[D" ) /* Left arrow */
+ BIOS_KEY ( "\x4d", "[C" ) /* Right arrow */
+ BIOS_KEY ( "\x47", "[H" ) /* Home */
+ BIOS_KEY ( "\x4f", "[F" ) /* End */
+ BIOS_KEY ( "\x49", "[5~" ) /* Page up */
+ BIOS_KEY ( "\x51", "[6~" ) /* Page down */
+ BIOS_KEY ( "\x3f", "[15~" ) /* F5 */
+ BIOS_KEY ( "\x40", "[17~" ) /* F6 */
+ BIOS_KEY ( "\x41", "[18~" ) /* F7 */
+ BIOS_KEY ( "\x42", "[19~" ) /* F8 (required for PXE) */
+ BIOS_KEY ( "\x43", "[20~" ) /* F9 */
+ BIOS_KEY ( "\x44", "[21~" ) /* F10 */
+ BIOS_KEY ( "\x85", "[23~" ) /* F11 */
+ BIOS_KEY ( "\x86", "[24~" ) /* F12 */
+};
+
+/**
+ * Get ANSI escape sequence corresponding to BIOS scancode
+ *
+ * @v scancode BIOS scancode
+ * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
+ */
+static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
+ const char *seq = ansi_sequences;
+
+ while ( *seq ) {
+ if ( *(seq++) == ( ( char ) scancode ) )
+ return seq;
+ seq += ( strlen ( seq ) + 1 );
+ }
+ DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
+ return NULL;
+}
+
+/**
+ * Map a key
+ *
+ * @v character Character read from console
+ * @ret character Mapped character
+ */
+static int bios_keymap ( unsigned int character ) {
+ struct key_mapping *mapping;
+
+ for_each_table_entry ( mapping, KEYMAP ) {
+ if ( mapping->from == character )
+ return mapping->to;
+ }
+ return character;
+}
+
+/**
+ * Get character from BIOS console
+ *
+ * @ret character Character read from console
+ */
+static int bios_getchar ( void ) {
+ uint16_t keypress;
+ unsigned int character;
+ const char *ansi_seq;
+
+ /* If we are mid-sequence, pass out the next byte */
+ if ( ( character = *ansi_input ) ) {
+ ansi_input++;
+ return character;
+ }
+
+ /* Read character from real BIOS console */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x16\n\t"
+ "cli\n\t" )
+ : "=a" ( keypress ) : "a" ( 0x1000 ) );
+ character = ( keypress & 0xff );
+
+ /* If it's a normal character, just map and return it */
+ if ( character && ( character < 0x80 ) )
+ return bios_keymap ( character );
+
+ /* Otherwise, check for a special key that we know about */
+ if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) {
+ /* Start of escape sequence: return ESC (0x1b) */
+ ansi_input = ansi_seq;
+ return 0x1b;
+ }
+
+ return 0;
+}
+
+/**
+ * Check for character ready to read from BIOS console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int bios_iskey ( void ) {
+ unsigned int discard_a;
+ unsigned int flags;
+
+ /* If we are mid-sequence, we are always ready */
+ if ( *ansi_input )
+ return 1;
+
+ /* Otherwise check the real BIOS console */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x16\n\t"
+ "pushfw\n\t"
+ "popw %w0\n\t"
+ "cli\n\t" )
+ : "=r" ( flags ), "=a" ( discard_a )
+ : "a" ( 0x1100 ) );
+ return ( ! ( flags & ZF ) );
+}
+
+struct console_driver bios_console __console_driver = {
+ .putchar = bios_putchar,
+ .getchar = bios_getchar,
+ .iskey = bios_iskey,
+ .usage = CONSOLE_PCBIOS,
+};
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S
new file mode 100644
index 000000000..d5d97b482
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -0,0 +1,589 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .arch i386
+ .code16
+
+#define SMAP 0x534d4150
+
+/* Most documentation refers to the E820 buffer as being 20 bytes, and
+ * the API makes it perfectly legitimate to pass only a 20-byte buffer
+ * and expect to get valid data. However, some morons at ACPI decided
+ * to extend the data structure by adding an extra "extended
+ * attributes" field and by including critical information within this
+ * field, such as whether or not the region is enabled. A caller who
+ * passes in only a 20-byte buffer therefore risks getting very, very
+ * misleading information.
+ *
+ * I have personally witnessed an HP BIOS that returns a value of
+ * 0x0009 in the extended attributes field. If we don't pass this
+ * value through to the caller, 32-bit WinPE will die, usually with a
+ * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
+ *
+ * Allow a ridiculously large maximum value (64 bytes) for the E820
+ * buffer as a guard against insufficiently creative idiots in the
+ * future.
+ */
+#define E820MAXSIZE 64
+
+/****************************************************************************
+ *
+ * Allowed memory windows
+ *
+ * There are two ways to view this list. The first is as a list of
+ * (non-overlapping) allowed memory regions, sorted by increasing
+ * address. The second is as a list of (non-overlapping) hidden
+ * memory regions, again sorted by increasing address. The second
+ * view is offset by half an entry from the first: think about this
+ * for a moment and it should make sense.
+ *
+ * xxx_memory_window is used to indicate an "allowed region"
+ * structure, hidden_xxx_memory is used to indicate a "hidden region"
+ * structure. Each structure is 16 bytes in length.
+ *
+ ****************************************************************************
+ */
+ .section ".data16", "aw", @progbits
+ .align 16
+ .globl hidemem_base
+ .globl hidemem_umalloc
+ .globl hidemem_textdata
+memory_windows:
+base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
+
+hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
+ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
+
+hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
+ .long 0xffffffff, 0xffffffff /* Changes at runtime */
+
+hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
+ .long 0xffffffff, 0xffffffff /* Changes at runtime */
+
+ .long 0xffffffff, 0xffffffff /* End of memory */
+memory_windows_end:
+
+/****************************************************************************
+ * Truncate region to memory window
+ *
+ * Parameters:
+ * %edx:%eax Start of region
+ * %ecx:%ebx Length of region
+ * %si Memory window
+ * Returns:
+ * %edx:%eax Start of windowed region
+ * %ecx:%ebx Length of windowed region
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+window_region:
+ /* Convert (start,len) to (start, end) */
+ addl %eax, %ebx
+ adcl %edx, %ecx
+ /* Truncate to window start */
+ cmpl 4(%si), %edx
+ jne 1f
+ cmpl 0(%si), %eax
+1: jae 2f
+ movl 4(%si), %edx
+ movl 0(%si), %eax
+2: /* Truncate to window end */
+ cmpl 12(%si), %ecx
+ jne 1f
+ cmpl 8(%si), %ebx
+1: jbe 2f
+ movl 12(%si), %ecx
+ movl 8(%si), %ebx
+2: /* Convert (start, end) back to (start, len) */
+ subl %eax, %ebx
+ sbbl %edx, %ecx
+ /* If length is <0, set length to 0 */
+ jae 1f
+ xorl %ebx, %ebx
+ xorl %ecx, %ecx
+ ret
+ .size window_region, . - window_region
+
+/****************************************************************************
+ * Patch "memory above 1MB" figure
+ *
+ * Parameters:
+ * %ax Memory above 1MB, in 1kB blocks
+ * Returns:
+ * %ax Modified memory above 1M in 1kB blocks
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+patch_1m:
+ pushal
+ /* Convert to (start,len) format and call truncate */
+ xorl %ecx, %ecx
+ movzwl %ax, %ebx
+ shll $10, %ebx
+ xorl %edx, %edx
+ movl $0x100000, %eax
+ movw $ext_memory_window, %si
+ call window_region
+ /* Convert back to "memory above 1MB" format and return via %ax */
+ pushfw
+ shrl $10, %ebx
+ popfw
+ movw %sp, %bp
+ movw %bx, 28(%bp)
+ popal
+ ret
+ .size patch_1m, . - patch_1m
+
+/****************************************************************************
+ * Patch "memory above 16MB" figure
+ *
+ * Parameters:
+ * %bx Memory above 16MB, in 64kB blocks
+ * Returns:
+ * %bx Modified memory above 16M in 64kB blocks
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+patch_16m:
+ pushal
+ /* Convert to (start,len) format and call truncate */
+ xorl %ecx, %ecx
+ shll $16, %ebx
+ xorl %edx, %edx
+ movl $0x1000000, %eax
+ movw $ext_memory_window, %si
+ call window_region
+ /* Convert back to "memory above 16MB" format and return via %bx */
+ pushfw
+ shrl $16, %ebx
+ popfw
+ movw %sp, %bp
+ movw %bx, 16(%bp)
+ popal
+ ret
+ .size patch_16m, . - patch_16m
+
+/****************************************************************************
+ * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
+ *
+ * Parameters:
+ * %ax Memory between 1MB and 16MB, in 1kB blocks
+ * %bx Memory above 16MB, in 64kB blocks
+ * Returns:
+ * %ax Modified memory between 1MB and 16MB, in 1kB blocks
+ * %bx Modified memory above 16MB, in 64kB blocks
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+patch_1m_16m:
+ call patch_1m
+ call patch_16m
+ /* If 1M region is no longer full-length, kill off the 16M region */
+ cmpw $( 15 * 1024 ), %ax
+ je 1f
+ xorw %bx, %bx
+1: ret
+ .size patch_1m_16m, . - patch_1m_16m
+
+/****************************************************************************
+ * Get underlying e820 memory region to underlying_e820 buffer
+ *
+ * Parameters:
+ * As for INT 15,e820
+ * Returns:
+ * As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that the continuation
+ * value (%ebx) is a 16-bit simple sequence counter (with the high 16
+ * bits ignored), and termination is always via CF=1 rather than
+ * %ebx=0.
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+get_underlying_e820:
+
+ /* If the requested region is in the cache, return it */
+ cmpw %bx, underlying_e820_index
+ jne 2f
+ pushw %di
+ pushw %si
+ movw $underlying_e820_cache, %si
+ cmpl underlying_e820_cache_size, %ecx
+ jbe 1f
+ movl underlying_e820_cache_size, %ecx
+1: pushl %ecx
+ rep movsb
+ popl %ecx
+ popw %si
+ popw %di
+ incw %bx
+ movl %edx, %eax
+ clc
+ ret
+2:
+ /* If the requested region is earlier than the cached region,
+ * invalidate the cache.
+ */
+ cmpw %bx, underlying_e820_index
+ jbe 1f
+ movw $0xffff, underlying_e820_index
+1:
+ /* If the cache is invalid, reset the underlying %ebx */
+ cmpw $0xffff, underlying_e820_index
+ jne 1f
+ andl $0, underlying_e820_ebx
+1:
+ /* If the cache is valid but the continuation value is zero,
+ * this means that the previous underlying call returned with
+ * %ebx=0. Return with CF=1 in this case.
+ */
+ cmpw $0xffff, underlying_e820_index
+ je 1f
+ cmpl $0, underlying_e820_ebx
+ jne 1f
+ stc
+ ret
+1:
+ /* Get the next region into the cache */
+ pushl %eax
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %esi /* Some implementations corrupt %esi, so we */
+ pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
+ pushl %ebp
+ pushw %es
+ pushw %ds
+ popw %es
+ movw $underlying_e820_cache, %di
+ cmpl $E820MAXSIZE, %ecx
+ jbe 1f
+ movl $E820MAXSIZE, %ecx
+1: movl underlying_e820_ebx, %ebx
+ stc
+ pushfw
+ lcall *%cs:int15_vector
+ popw %es
+ popl %ebp
+ popl %edi
+ popl %esi
+ /* Check for error return from underlying e820 call */
+ jc 2f /* CF set: error */
+ cmpl $SMAP, %eax
+ je 3f /* 'SMAP' missing: error */
+2: /* An error occurred: return values returned by underlying e820 call */
+ stc /* Force CF set if SMAP was missing */
+ addr32 leal 16(%esp), %esp /* avoid changing other flags */
+ ret
+3: /* No error occurred */
+ movl %ebx, underlying_e820_ebx
+ movl %ecx, underlying_e820_cache_size
+ popl %edx
+ popl %ecx
+ popl %ebx
+ popl %eax
+ /* Mark cache as containing this result */
+ incw underlying_e820_index
+
+ /* Loop until found */
+ jmp get_underlying_e820
+ .size get_underlying_e820, . - get_underlying_e820
+
+ .section ".data16", "aw", @progbits
+underlying_e820_index:
+ .word 0xffff /* Initialise to an invalid value */
+ .size underlying_e820_index, . - underlying_e820_index
+
+ .section ".bss16", "aw", @nobits
+underlying_e820_ebx:
+ .long 0
+ .size underlying_e820_ebx, . - underlying_e820_ebx
+
+ .section ".bss16", "aw", @nobits
+underlying_e820_cache:
+ .space E820MAXSIZE
+ .size underlying_e820_cache, . - underlying_e820_cache
+
+ .section ".bss16", "aw", @nobits
+underlying_e820_cache_size:
+ .long 0
+ .size underlying_e820_cache_size, . - underlying_e820_cache_size
+
+/****************************************************************************
+ * Get windowed e820 region, without empty region stripping
+ *
+ * Parameters:
+ * As for INT 15,e820
+ * Returns:
+ * As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that each underlying
+ * region is returned N times, windowed to fit within N visible-memory
+ * windows. Termination is always via CF=1.
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+get_windowed_e820:
+
+ /* Preserve registers */
+ pushl %esi
+ pushw %bp
+
+ /* Split %ebx into %si:%bx, store original %bx in %bp */
+ pushl %ebx
+ popw %bp
+ popw %si
+
+ /* %si == 0 => start of memory_windows list */
+ testw %si, %si
+ jne 1f
+ movw $memory_windows, %si
+1:
+ /* Get (cached) underlying e820 region to buffer */
+ call get_underlying_e820
+ jc 99f /* Abort on error */
+
+ /* Preserve registers */
+ pushal
+ /* start => %edx:%eax, len => %ecx:%ebx */
+ movl %es:0(%di), %eax
+ movl %es:4(%di), %edx
+ movl %es:8(%di), %ebx
+ movl %es:12(%di), %ecx
+ /* Truncate region to current window */
+ call window_region
+1: /* Store modified values in e820 map entry */
+ movl %eax, %es:0(%di)
+ movl %edx, %es:4(%di)
+ movl %ebx, %es:8(%di)
+ movl %ecx, %es:12(%di)
+ /* Restore registers */
+ popal
+
+ /* Derive continuation value for next call */
+ addw $16, %si
+ cmpw $memory_windows_end, %si
+ jne 1f
+ /* End of memory windows: reset %si and allow %bx to continue */
+ xorw %si, %si
+ jmp 2f
+1: /* More memory windows to go: restore original %bx */
+ movw %bp, %bx
+2: /* Construct %ebx from %si:%bx */
+ pushw %si
+ pushw %bx
+ popl %ebx
+
+98: /* Clear CF */
+ clc
+99: /* Restore registers and return */
+ popw %bp
+ popl %esi
+ ret
+ .size get_windowed_e820, . - get_windowed_e820
+
+/****************************************************************************
+ * Get windowed e820 region, with empty region stripping
+ *
+ * Parameters:
+ * As for INT 15,e820
+ * Returns:
+ * As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that each underlying
+ * region is returned up to N times, windowed to fit within N
+ * visible-memory windows. Empty windows are never returned.
+ * Termination is always via CF=1.
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+get_nonempty_e820:
+
+ /* Record entry parameters */
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+
+ /* Get next windowed region */
+ call get_windowed_e820
+ jc 99f /* abort on error */
+
+ /* If region is non-empty, finish here */
+ cmpl $0, %es:8(%di)
+ jne 98f
+ cmpl $0, %es:12(%di)
+ jne 98f
+
+ /* Region was empty: restore entry parameters and go to next region */
+ popl %edx
+ popl %ecx
+ popl %eax
+ jmp get_nonempty_e820
+
+98: /* Clear CF */
+ clc
+99: /* Return values from underlying call */
+ addr32 leal 12(%esp), %esp /* avoid changing flags */
+ ret
+ .size get_nonempty_e820, . - get_nonempty_e820
+
+/****************************************************************************
+ * Get mangled e820 region, with empty region stripping
+ *
+ * Parameters:
+ * As for INT 15,e820
+ * Returns:
+ * As for INT 15,e820
+ *
+ * Wraps the underlying INT 15,e820 call so that underlying regions
+ * are windowed to the allowed memory regions. Empty regions are
+ * stripped from the map. Termination is always via %ebx=0.
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+get_mangled_e820:
+
+ /* Get a nonempty region */
+ call get_nonempty_e820
+ jc 99f /* Abort on error */
+
+ /* Peek ahead to see if there are any further nonempty regions */
+ pushal
+ pushw %es
+ movw %sp, %bp
+ subw %cx, %sp
+ movl $0xe820, %eax
+ movl $SMAP, %edx
+ pushw %ss
+ popw %es
+ movw %sp, %di
+ call get_nonempty_e820
+ movw %bp, %sp
+ popw %es
+ popal
+ jnc 99f /* There are further nonempty regions */
+
+ /* No futher nonempty regions: zero %ebx and clear CF */
+ xorl %ebx, %ebx
+
+99: /* Return */
+ ret
+ .size get_mangled_e820, . - get_mangled_e820
+
+/****************************************************************************
+ * INT 15,e820 handler
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+int15_e820:
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ call get_mangled_e820
+ popw %ds
+ call patch_cf
+ iret
+ .size int15_e820, . - int15_e820
+
+/****************************************************************************
+ * INT 15,e801 handler
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+int15_e801:
+ /* Call previous handler */
+ pushfw
+ lcall *%cs:int15_vector
+ call patch_cf
+ /* Edit result */
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ call patch_1m_16m
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ call patch_1m_16m
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ popw %ds
+ iret
+ .size int15_e801, . - int15_e801
+
+/****************************************************************************
+ * INT 15,88 handler
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+int15_88:
+ /* Call previous handler */
+ pushfw
+ lcall *%cs:int15_vector
+ call patch_cf
+ /* Edit result */
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ call patch_1m
+ popw %ds
+ iret
+ .size int15_88, . - int15_88
+
+/****************************************************************************
+ * INT 15 handler
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .globl int15
+int15:
+ /* See if we want to intercept this call */
+ pushfw
+ cmpw $0xe820, %ax
+ jne 1f
+ cmpl $SMAP, %edx
+ jne 1f
+ popfw
+ jmp int15_e820
+1: cmpw $0xe801, %ax
+ jne 2f
+ popfw
+ jmp int15_e801
+2: cmpb $0x88, %ah
+ jne 3f
+ popfw
+ jmp int15_88
+3: popfw
+ ljmp *%cs:int15_vector
+ .size int15, . - int15
+
+ .section ".text16.data", "aw", @progbits
+ .globl int15_vector
+int15_vector:
+ .long 0
+ .size int15_vector, . - int15_vector
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c b/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c
new file mode 100644
index 000000000..15f4d772f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <realmode.h>
+#include <biosint.h>
+
+/** Assembly routine in inline asm */
+extern void int15_fakee820();
+
+/** Original INT 15 handler */
+static struct segoff __text16 ( real_int15_vector );
+#define real_int15_vector __use_text16 ( real_int15_vector )
+
+/** An INT 15,e820 memory map entry */
+struct e820_entry {
+ /** Start of region */
+ uint64_t start;
+ /** Length of region */
+ uint64_t len;
+ /** Type of region */
+ uint32_t type;
+} __attribute__ (( packed ));
+
+#define E820_TYPE_RAM 1 /**< Normal memory */
+#define E820_TYPE_RSVD 2 /**< Reserved and unavailable */
+#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
+#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
+
+/** Fake e820 map */
+static struct e820_entry __text16_array ( e820map, [] ) __used = {
+ { 0x00000000ULL, ( 0x000a0000ULL - 0x00000000ULL ), E820_TYPE_RAM },
+ { 0x00100000ULL, ( 0xcfb50000ULL - 0x00100000ULL ), E820_TYPE_RAM },
+ { 0xcfb50000ULL, ( 0xcfb64000ULL - 0xcfb50000ULL ), E820_TYPE_RSVD },
+ { 0xcfb64000ULL, ( 0xcfb66000ULL - 0xcfb64000ULL ), E820_TYPE_RSVD },
+ { 0xcfb66000ULL, ( 0xcfb85c00ULL - 0xcfb66000ULL ), E820_TYPE_ACPI },
+ { 0xcfb85c00ULL, ( 0xd0000000ULL - 0xcfb85c00ULL ), E820_TYPE_RSVD },
+ { 0xe0000000ULL, ( 0xf0000000ULL - 0xe0000000ULL ), E820_TYPE_RSVD },
+ { 0xfe000000ULL, (0x100000000ULL - 0xfe000000ULL ), E820_TYPE_RSVD },
+ {0x100000000ULL, (0x230000000ULL -0x100000000ULL ), E820_TYPE_RAM },
+};
+#define e820map __use_text16 ( e820map )
+
+void fake_e820 ( void ) {
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint15_fakee820:\n\t"
+ "pushfw\n\t"
+ "cmpl $0xe820, %%eax\n\t"
+ "jne 99f\n\t"
+ "cmpl $0x534d4150, %%edx\n\t"
+ "jne 99f\n\t"
+ "pushaw\n\t"
+ "movw %%sp, %%bp\n\t"
+ "andb $~0x01, 22(%%bp)\n\t" /* Clear return CF */
+ "leaw e820map(%%bx), %%si\n\t"
+ "cs rep movsb\n\t"
+ "popaw\n\t"
+ "movl %%edx, %%eax\n\t"
+ "addl $20, %%ebx\n\t"
+ "cmpl %0, %%ebx\n\t"
+ "jne 1f\n\t"
+ "xorl %%ebx,%%ebx\n\t"
+ "\n1:\n\t"
+ "popfw\n\t"
+ "iret\n\t"
+ "\n99:\n\t"
+ "popfw\n\t"
+ "ljmp *%%cs:real_int15_vector\n\t" )
+ : : "i" ( sizeof ( e820map ) ) );
+
+ hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820,
+ &real_int15_vector );
+}
+
+void unfake_e820 ( void ) {
+ unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820,
+ &real_int15_vector );
+}
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c
new file mode 100644
index 000000000..253c601ff
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c
@@ -0,0 +1,235 @@
+/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <basemem.h>
+#include <fakee820.h>
+#include <ipxe/init.h>
+#include <ipxe/io.h>
+#include <ipxe/hidemem.h>
+
+/** Set to true if you want to test a fake E820 map */
+#define FAKE_E820 0
+
+/** Alignment for hidden memory regions */
+#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */
+
+/**
+ * A hidden region of iPXE
+ *
+ * This represents a region that will be edited out of the system's
+ * memory map.
+ *
+ * This structure is accessed by assembly code, so must not be
+ * changed.
+ */
+struct hidden_region {
+ /** Physical start address */
+ uint64_t start;
+ /** Physical end address */
+ uint64_t end;
+};
+
+/** Hidden base memory */
+extern struct hidden_region __data16 ( hidemem_base );
+#define hidemem_base __use_data16 ( hidemem_base )
+
+/** Hidden umalloc memory */
+extern struct hidden_region __data16 ( hidemem_umalloc );
+#define hidemem_umalloc __use_data16 ( hidemem_umalloc )
+
+/** Hidden text memory */
+extern struct hidden_region __data16 ( hidemem_textdata );
+#define hidemem_textdata __use_data16 ( hidemem_textdata )
+
+/** Assembly routine in e820mangler.S */
+extern void int15();
+
+/** Vector for storing original INT 15 handler */
+extern struct segoff __text16 ( int15_vector );
+#define int15_vector __use_text16 ( int15_vector )
+
+/* The linker defines these symbols for us */
+extern char _textdata[];
+extern char _etextdata[];
+extern char _text16_memsz[];
+#define _text16_memsz ( ( unsigned int ) _text16_memsz )
+extern char _data16_memsz[];
+#define _data16_memsz ( ( unsigned int ) _data16_memsz )
+
+/**
+ * Hide region of memory from system memory map
+ *
+ * @v region Hidden memory region
+ * @v start Start of region
+ * @v end End of region
+ */
+static void hide_region ( struct hidden_region *region,
+ physaddr_t start, physaddr_t end ) {
+
+ /* Some operating systems get a nasty shock if a region of the
+ * E820 map seems to start on a non-page boundary. Make life
+ * safer by rounding out our edited region.
+ */
+ region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
+ region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
+
+ DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
+}
+
+/**
+ * Hide used base memory
+ *
+ */
+void hide_basemem ( void ) {
+ /* Hide from the top of free base memory to 640kB. Don't use
+ * hide_region(), because we don't want this rounded to the
+ * nearest page boundary.
+ */
+ hidemem_base.start = ( get_fbms() * 1024 );
+}
+
+/**
+ * Hide umalloc() region
+ *
+ */
+void hide_umalloc ( physaddr_t start, physaddr_t end ) {
+ assert ( end <= virt_to_phys ( _textdata ) );
+ hide_region ( &hidemem_umalloc, start, end );
+}
+
+/**
+ * Hide .text and .data
+ *
+ */
+void hide_textdata ( void ) {
+ hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
+ virt_to_phys ( _etextdata ) );
+}
+
+/**
+ * Hide Etherboot
+ *
+ * Installs an INT 15 handler to edit Etherboot out of the memory map
+ * returned by the BIOS.
+ */
+static void hide_etherboot ( void ) {
+ struct memory_map memmap;
+ unsigned int rm_ds_top;
+ unsigned int rm_cs_top;
+ unsigned int fbms;
+
+ /* Dump memory map before mangling */
+ DBG ( "Hiding iPXE from system memory map\n" );
+ get_memmap ( &memmap );
+
+ /* Hook in fake E820 map, if we're testing one */
+ if ( FAKE_E820 ) {
+ DBG ( "Hooking in fake E820 map\n" );
+ fake_e820();
+ get_memmap ( &memmap );
+ }
+
+ /* Initialise the hidden regions */
+ hide_basemem();
+ hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
+ hide_textdata();
+
+ /* Some really moronic BIOSes bring up the PXE stack via the
+ * UNDI loader entry point and then don't bother to unload it
+ * before overwriting the code and data segments. If this
+ * happens, we really don't want to leave INT 15 hooked,
+ * because that will cause any loaded OS to die horribly as
+ * soon as it attempts to fetch the system memory map.
+ *
+ * We use a heuristic to guess whether or not we are being
+ * loaded sensibly.
+ */
+ rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
+ rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
+ fbms = get_fbms();
+ if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
+ DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
+ "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
+ DBG ( "Disabling INT 15 memory hiding\n" );
+ return;
+ }
+
+ /* Hook INT 15 */
+ hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+ &int15_vector );
+
+ /* Dump memory map after mangling */
+ DBG ( "Hidden iPXE from system memory map\n" );
+ get_memmap ( &memmap );
+}
+
+/**
+ * Unhide Etherboot
+ *
+ * Uninstalls the INT 15 handler installed by hide_etherboot(), if
+ * possible.
+ */
+static void unhide_etherboot ( int flags __unused ) {
+ struct memory_map memmap;
+ int rc;
+
+ /* If we have more than one hooked interrupt at this point, it
+ * means that some other vector is still hooked, in which case
+ * we can't safely unhook INT 15 because we need to keep our
+ * memory protected. (We expect there to be at least one
+ * hooked interrupt, because INT 15 itself is still hooked).
+ */
+ if ( hooked_bios_interrupts > 1 ) {
+ DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
+ hooked_bios_interrupts );
+ return;
+ }
+
+ /* Try to unhook INT 15 */
+ if ( ( rc = unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+ &int15_vector ) ) != 0 ) {
+ DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) );
+ /* Leave it hooked; there's nothing else we can do,
+ * and it should be intrinsically safe (though
+ * wasteful of RAM).
+ */
+ }
+
+ /* Unhook fake E820 map, if used */
+ if ( FAKE_E820 )
+ unfake_e820();
+
+ /* Dump memory map after unhiding */
+ DBG ( "Unhidden iPXE from system memory map\n" );
+ get_memmap ( &memmap );
+}
+
+/** Hide Etherboot startup function */
+struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+ .startup = hide_etherboot,
+ .shutdown = unhide_etherboot,
+};
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c b/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c
new file mode 100644
index 000000000..bcacecd6a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c
@@ -0,0 +1,343 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <errno.h>
+#include <realmode.h>
+#include <bios.h>
+#include <memsizes.h>
+#include <ipxe/io.h>
+
+/**
+ * @file
+ *
+ * Memory mapping
+ *
+ */
+
+/** Magic value for INT 15,e820 calls */
+#define SMAP ( 0x534d4150 )
+
+/** An INT 15,e820 memory map entry */
+struct e820_entry {
+ /** Start of region */
+ uint64_t start;
+ /** Length of region */
+ uint64_t len;
+ /** Type of region */
+ uint32_t type;
+ /** Extended attributes (optional) */
+ uint32_t attrs;
+} __attribute__ (( packed ));
+
+#define E820_TYPE_RAM 1 /**< Normal memory */
+#define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */
+#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
+#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
+
+#define E820_ATTR_ENABLED 0x00000001UL
+#define E820_ATTR_NONVOLATILE 0x00000002UL
+#define E820_ATTR_UNKNOWN 0xfffffffcUL
+
+#define E820_MIN_SIZE 20
+
+/** Buffer for INT 15,e820 calls */
+static struct e820_entry __bss16 ( e820buf );
+#define e820buf __use_data16 ( e820buf )
+
+/** We are running during POST; inhibit INT 15,e820 and INT 15,e801 */
+uint8_t __bss16 ( memmap_post );
+#define memmap_post __use_data16 ( memmap_post )
+
+/**
+ * Get size of extended memory via INT 15,e801
+ *
+ * @ret extmem Extended memory size, in kB, or 0
+ */
+static unsigned int extmemsize_e801 ( void ) {
+ uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
+ uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
+ unsigned int flags;
+ unsigned int extmem;
+
+ /* Inhibit INT 15,e801 during POST */
+ if ( memmap_post ) {
+ DBG ( "INT 15,e801 not available during POST\n" );
+ return 0;
+ }
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "popw %w0\n\t" )
+ : "=r" ( flags ),
+ "=a" ( extmem_1m_to_16m_k ),
+ "=b" ( extmem_16m_plus_64k ),
+ "=c" ( confmem_1m_to_16m_k ),
+ "=d" ( confmem_16m_plus_64k )
+ : "a" ( 0xe801 ) );
+
+ if ( flags & CF ) {
+ DBG ( "INT 15,e801 failed with CF set\n" );
+ return 0;
+ }
+
+ if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) {
+ DBG ( "INT 15,e801 extmem=0, using confmem\n" );
+ extmem_1m_to_16m_k = confmem_1m_to_16m_k;
+ extmem_16m_plus_64k = confmem_16m_plus_64k;
+ }
+
+ extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) );
+ DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB "
+ "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k,
+ extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) );
+
+ /* Sanity check. Some BIOSes report the entire 4GB address
+ * space as available, which cannot be correct (since that
+ * would leave no address space available for 32-bit PCI
+ * BARs).
+ */
+ if ( extmem == ( 0x400000 - 0x400 ) ) {
+ DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" );
+ return 0;
+ }
+
+ return extmem;
+}
+
+/**
+ * Get size of extended memory via INT 15,88
+ *
+ * @ret extmem Extended memory size, in kB
+ */
+static unsigned int extmemsize_88 ( void ) {
+ uint16_t extmem;
+
+ /* Ignore CF; it is not reliable for this call */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
+ : "=a" ( extmem ) : "a" ( 0x8800 ) );
+
+ DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n",
+ extmem, ( 0x100000 + ( extmem * 1024 ) ) );
+ return extmem;
+}
+
+/**
+ * Get size of extended memory
+ *
+ * @ret extmem Extended memory size, in kB
+ *
+ * Note that this is only an approximation; for an accurate picture,
+ * use the E820 memory map obtained via get_memmap();
+ */
+unsigned int extmemsize ( void ) {
+ unsigned int extmem_e801;
+ unsigned int extmem_88;
+
+ /* Try INT 15,e801 first, then fall back to INT 15,88 */
+ extmem_88 = extmemsize_88();
+ extmem_e801 = extmemsize_e801();
+ return ( extmem_e801 ? extmem_e801 : extmem_88 );
+}
+
+/**
+ * Get e820 memory map
+ *
+ * @v memmap Memory map to fill in
+ * @ret rc Return status code
+ */
+static int meme820 ( struct memory_map *memmap ) {
+ struct memory_region *region = memmap->regions;
+ struct memory_region *prev_region = NULL;
+ uint32_t next = 0;
+ uint32_t smap;
+ size_t size;
+ unsigned int flags;
+ unsigned int discard_D;
+
+ /* Inhibit INT 15,e820 during POST */
+ if ( memmap_post ) {
+ DBG ( "INT 15,e820 not available during POST\n" );
+ return -ENOTTY;
+ }
+
+ /* Clear the E820 buffer. Do this once before starting,
+ * rather than on each call; some BIOSes rely on the contents
+ * being preserved between calls.
+ */
+ memset ( &e820buf, 0, sizeof ( e820buf ) );
+
+ do {
+ /* Some BIOSes corrupt %esi for fun. Guard against
+ * this by telling gcc that all non-output registers
+ * may be corrupted.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t"
+ "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "popw %%dx\n\t"
+ "popl %%ebp\n\t" )
+ : "=a" ( smap ), "=b" ( next ),
+ "=c" ( size ), "=d" ( flags ),
+ "=D" ( discard_D )
+ : "a" ( 0xe820 ), "b" ( next ),
+ "D" ( __from_data16 ( &e820buf ) ),
+ "c" ( sizeof ( e820buf ) ),
+ "d" ( SMAP )
+ : "esi", "memory" );
+
+ if ( smap != SMAP ) {
+ DBG ( "INT 15,e820 failed SMAP signature check\n" );
+ return -ENOTSUP;
+ }
+
+ if ( size < E820_MIN_SIZE ) {
+ DBG ( "INT 15,e820 returned only %zd bytes\n", size );
+ return -EINVAL;
+ }
+
+ if ( flags & CF ) {
+ DBG ( "INT 15,e820 terminated on CF set\n" );
+ break;
+ }
+
+ /* If first region is not RAM, assume map is invalid */
+ if ( ( memmap->count == 0 ) &&
+ ( e820buf.type != E820_TYPE_RAM ) ) {
+ DBG ( "INT 15,e820 failed, first entry not RAM\n" );
+ return -EINVAL;
+ }
+
+ DBG ( "INT 15,e820 region [%llx,%llx) type %d",
+ e820buf.start, ( e820buf.start + e820buf.len ),
+ ( int ) e820buf.type );
+ if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
+ DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED )
+ ? "enabled" : "disabled" ) );
+ if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
+ DBG ( ", non-volatile" );
+ if ( e820buf.attrs & E820_ATTR_UNKNOWN )
+ DBG ( ", other [%08x]", e820buf.attrs );
+ DBG ( ")" );
+ }
+ DBG ( "\n" );
+
+ /* Discard non-RAM regions */
+ if ( e820buf.type != E820_TYPE_RAM )
+ continue;
+
+ /* Check extended attributes, if present */
+ if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
+ if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) )
+ continue;
+ if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
+ continue;
+ }
+
+ region->start = e820buf.start;
+ region->end = e820buf.start + e820buf.len;
+
+ /* Check for adjacent regions and merge them */
+ if ( prev_region && ( region->start == prev_region->end ) ) {
+ prev_region->end = region->end;
+ } else {
+ prev_region = region;
+ region++;
+ memmap->count++;
+ }
+
+ if ( memmap->count >= ( sizeof ( memmap->regions ) /
+ sizeof ( memmap->regions[0] ) ) ) {
+ DBG ( "INT 15,e820 too many regions returned\n" );
+ /* Not a fatal error; what we've got so far at
+ * least represents valid regions of memory,
+ * even if we couldn't get them all.
+ */
+ break;
+ }
+ } while ( next != 0 );
+
+ /* Sanity checks. Some BIOSes report complete garbage via INT
+ * 15,e820 (especially at POST time), despite passing the
+ * signature checks. We currently check for a base memory
+ * region (starting at 0) and at least one high memory region
+ * (starting at 0x100000).
+ */
+ if ( memmap->count < 2 ) {
+ DBG ( "INT 15,e820 returned only %d regions; assuming "
+ "insane\n", memmap->count );
+ return -EINVAL;
+ }
+ if ( memmap->regions[0].start != 0 ) {
+ DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); "
+ "assuming insane\n", memmap->regions[0].start );
+ return -EINVAL;
+ }
+ if ( memmap->regions[1].start != 0x100000 ) {
+ DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); "
+ "assuming insane\n", memmap->regions[0].start );
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Get memory map
+ *
+ * @v memmap Memory map to fill in
+ */
+void x86_get_memmap ( struct memory_map *memmap ) {
+ unsigned int basemem, extmem;
+ int rc;
+
+ DBG ( "Fetching system memory map\n" );
+
+ /* Clear memory map */
+ memset ( memmap, 0, sizeof ( *memmap ) );
+
+ /* Get base and extended memory sizes */
+ basemem = basememsize();
+ DBG ( "FBMS base memory size %d kB [0,%x)\n",
+ basemem, ( basemem * 1024 ) );
+ extmem = extmemsize();
+
+ /* Try INT 15,e820 first */
+ if ( ( rc = meme820 ( memmap ) ) == 0 ) {
+ DBG ( "Obtained system memory map via INT 15,e820\n" );
+ return;
+ }
+
+ /* Fall back to constructing a map from basemem and extmem sizes */
+ DBG ( "INT 15,e820 failed; constructing map\n" );
+ memmap->regions[0].end = ( basemem * 1024 );
+ memmap->regions[1].start = 0x100000;
+ memmap->regions[1].end = 0x100000 + ( extmem * 1024 );
+ memmap->count = 2;
+}
+
+PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap );
diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c b/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c
new file mode 100644
index 000000000..20ec35d75
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <realmode.h>
+#include <pnpbios.h>
+
+/** @file
+ *
+ * PnP BIOS
+ *
+ */
+
+/** PnP BIOS structure */
+struct pnp_bios {
+ /** Signature
+ *
+ * Must be equal to @c PNP_BIOS_SIGNATURE
+ */
+ uint32_t signature;
+ /** Version as BCD (e.g. 1.0 is 0x10) */
+ uint8_t version;
+ /** Length of this structure */
+ uint8_t length;
+ /** System capabilities */
+ uint16_t control;
+ /** Checksum */
+ uint8_t checksum;
+} __attribute__ (( packed ));
+
+/** Signature for a PnP BIOS structure */
+#define PNP_BIOS_SIGNATURE \
+ ( ( '$' << 0 ) + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
+
+/**
+ * Test address for PnP BIOS structure
+ *
+ * @v offset Offset within BIOS segment to test
+ * @ret rc Return status code
+ */
+static int is_pnp_bios ( unsigned int offset ) {
+ union {
+ struct pnp_bios pnp_bios;
+ uint8_t bytes[256]; /* 256 is maximum length possible */
+ } u;
+ size_t len;
+ unsigned int i;
+ uint8_t sum = 0;
+
+ /* Read start of header and verify signature */
+ copy_from_real ( &u.pnp_bios, BIOS_SEG, offset, sizeof ( u.pnp_bios ));
+ if ( u.pnp_bios.signature != PNP_BIOS_SIGNATURE )
+ return -EINVAL;
+
+ /* Read whole header and verify checksum */
+ len = u.pnp_bios.length;
+ copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
+ for ( i = 0 ; i < len ; i++ ) {
+ sum += u.bytes[i];
+ }
+ if ( sum != 0 )
+ return -EINVAL;
+
+ DBG ( "Found PnP BIOS at %04x:%04x\n", BIOS_SEG, offset );
+
+ return 0;
+}
+
+/**
+ * Locate Plug-and-Play BIOS
+ *
+ * @ret pnp_offset Offset of PnP BIOS structure within BIOS segment
+ *
+ * The PnP BIOS structure will be at BIOS_SEG:pnp_offset. If no PnP
+ * BIOS is found, -1 is returned.
+ */
+int find_pnp_bios ( void ) {
+ static int pnp_offset = 0;
+
+ if ( pnp_offset )
+ return pnp_offset;
+
+ for ( pnp_offset = 0 ; pnp_offset < 0x10000 ; pnp_offset += 0x10 ) {
+ if ( is_pnp_bios ( pnp_offset ) == 0 )
+ return pnp_offset;
+ }
+
+ pnp_offset = -1;
+ return pnp_offset;
+}
diff --git a/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c b/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c
new file mode 100644
index 000000000..473b97f97
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+#include <ipxe/netdevice.h>
+#include <ipxe/command.h>
+#include <ipxe/parseopt.h>
+#include <hci/ifmgmt_cmd.h>
+#include <pxe_call.h>
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * PXE commands
+ *
+ */
+
+/** "startpxe" options */
+struct startpxe_options {};
+
+/** "startpxe" option list */
+static struct option_descriptor startpxe_opts[] = {};
+
+/**
+ * "startpxe" payload
+ *
+ * @v netdev Network device
+ * @v opts Command options
+ * @ret rc Return status code
+ */
+static int startpxe_payload ( struct net_device *netdev,
+ struct startpxe_options *opts __unused ) {
+
+ if ( netdev_is_open ( netdev ) )
+ pxe_activate ( netdev );
+
+ return 0;
+}
+
+/** "startpxe" command descriptor */
+static struct ifcommon_command_descriptor startpxe_cmd =
+ IFCOMMON_COMMAND_DESC ( struct startpxe_options, startpxe_opts,
+ 0, MAX_ARGUMENTS, "[<interface>]",
+ startpxe_payload, 0 );
+
+/**
+ * The "startpxe" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+static int startpxe_exec ( int argc, char **argv ) {
+ return ifcommon_exec ( argc, argv, &startpxe_cmd );
+}
+
+/** "stoppxe" options */
+struct stoppxe_options {};
+
+/** "stoppxe" option list */
+static struct option_descriptor stoppxe_opts[] = {};
+
+/** "stoppxe" command descriptor */
+static struct command_descriptor stoppxe_cmd =
+ COMMAND_DESC ( struct stoppxe_options, stoppxe_opts, 0, 0, NULL );
+
+/**
+ * The "stoppxe" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+static int stoppxe_exec ( int argc __unused, char **argv __unused ) {
+ struct stoppxe_options opts;
+ int rc;
+
+ /* Parse options */
+ if ( ( rc = parse_options ( argc, argv, &stoppxe_cmd, &opts ) ) != 0 )
+ return rc;
+
+ pxe_deactivate();
+
+ return 0;
+}
+
+/** PXE commands */
+struct command pxe_commands[] __command = {
+ {
+ .name = "startpxe",
+ .exec = startpxe_exec,
+ },
+ {
+ .name = "stoppxe",
+ .exec = stoppxe_exec,
+ },
+};
diff --git a/roms/ipxe/src/arch/i386/image/bootsector.c b/roms/ipxe/src/arch/i386/image/bootsector.c
new file mode 100644
index 000000000..dba87613c
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/bootsector.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * x86 bootsector image format
+ *
+ */
+
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <ipxe/console.h>
+
+/** Vector for storing original INT 18 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int18_vector;
+
+/** Vector for storing original INT 19 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int19_vector;
+
+/** Restart point for INT 18 or 19 */
+extern void bootsector_exec_fail ( void );
+
+/**
+ * Jump to preloaded bootsector
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @v drive Drive number to pass to boot sector
+ * @ret rc Return status code
+ */
+int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive ) {
+ int discard_b, discard_D, discard_d;
+
+ /* Reset console, since boot sector will probably use it */
+ console_reset();
+
+ DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset );
+
+ /* Hook INTs 18 and 19 to capture failure paths */
+ hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ /* Boot the loaded sector
+ *
+ * We assume that the boot sector may completely destroy our
+ * real-mode stack, so we preserve everything we need in
+ * static storage.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
+ "popw %%cs:saved_retaddr\n\t"
+ /* Save stack pointer */
+ "movw %%ss, %%ax\n\t"
+ "movw %%ax, %%cs:saved_ss\n\t"
+ "movw %%sp, %%cs:saved_sp\n\t"
+ /* Save frame pointer (gcc bug) */
+ "movl %%ebp, %%cs:saved_ebp\n\t"
+ /* Prepare jump to boot sector */
+ "pushw %%bx\n\t"
+ "pushw %%di\n\t"
+ /* Clear all registers */
+ "xorl %%eax, %%eax\n\t"
+ "xorl %%ebx, %%ebx\n\t"
+ "xorl %%ecx, %%ecx\n\t"
+ /* %edx contains drive number */
+ "xorl %%esi, %%esi\n\t"
+ "xorl %%edi, %%edi\n\t"
+ "xorl %%ebp, %%ebp\n\t"
+ "movw %%ax, %%ds\n\t"
+ "movw %%ax, %%es\n\t"
+ "movw %%ax, %%fs\n\t"
+ "movw %%ax, %%gs\n\t"
+ /* Jump to boot sector */
+ "sti\n\t"
+ "lret\n\t"
+ /* Preserved variables */
+ "\nsaved_ebp: .long 0\n\t"
+ "\nsaved_ss: .word 0\n\t"
+ "\nsaved_sp: .word 0\n\t"
+ "\nsaved_retaddr: .word 0\n\t"
+ /* Boot failure return point */
+ "\nbootsector_exec_fail:\n\t"
+ /* Restore frame pointer (gcc bug) */
+ "movl %%cs:saved_ebp, %%ebp\n\t"
+ /* Restore stack pointer */
+ "movw %%cs:saved_ss, %%ax\n\t"
+ "movw %%ax, %%ss\n\t"
+ "movw %%cs:saved_sp, %%sp\n\t"
+ /* Return via saved address */
+ "jmp *%%cs:saved_retaddr\n\t" )
+ : "=b" ( discard_b ), "=D" ( discard_D ),
+ "=d" ( discard_d )
+ : "b" ( segment ), "D" ( offset ),
+ "d" ( drive )
+ : "eax", "ecx", "esi" );
+
+ DBG ( "Booted disk returned via INT 18 or 19\n" );
+
+ /* Unhook INTs 18 and 19 */
+ unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ return -ECANCELED;
+}
diff --git a/roms/ipxe/src/arch/i386/image/bzimage.c b/roms/ipxe/src/arch/i386/image/bzimage.c
new file mode 100644
index 000000000..a64206cd3
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/bzimage.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Linux bzImage image format
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <bzimage.h>
+#include <initrd.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/cpio.h>
+#include <ipxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
+
+/**
+ * bzImage context
+ */
+struct bzimage_context {
+ /** Boot protocol version */
+ unsigned int version;
+ /** Real-mode kernel portion load segment address */
+ unsigned int rm_kernel_seg;
+ /** Real-mode kernel portion load address */
+ userptr_t rm_kernel;
+ /** Real-mode kernel portion file size */
+ size_t rm_filesz;
+ /** Real-mode heap top (offset from rm_kernel) */
+ size_t rm_heap;
+ /** Command line (offset from rm_kernel) */
+ size_t rm_cmdline;
+ /** Command line maximum length */
+ size_t cmdline_size;
+ /** Real-mode kernel portion total memory size */
+ size_t rm_memsz;
+ /** Non-real-mode kernel portion load address */
+ userptr_t pm_kernel;
+ /** Non-real-mode kernel portion file and memory size */
+ size_t pm_sz;
+ /** Video mode */
+ unsigned int vid_mode;
+ /** Memory limit */
+ uint64_t mem_limit;
+ /** Initrd address */
+ physaddr_t ramdisk_image;
+ /** Initrd size */
+ physaddr_t ramdisk_size;
+
+ /** Command line magic block */
+ struct bzimage_cmdline cmdline_magic;
+ /** bzImage header */
+ struct bzimage_header bzhdr;
+};
+
+/**
+ * Parse bzImage header
+ *
+ * @v image bzImage file
+ * @v bzimg bzImage context
+ * @v src bzImage to parse
+ * @ret rc Return status code
+ */
+static int bzimage_parse_header ( struct image *image,
+ struct bzimage_context *bzimg,
+ userptr_t src ) {
+ unsigned int syssize;
+ int is_bzimage;
+
+ /* Sanity check */
+ if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
+ DBGC ( image, "bzImage %p too short for kernel header\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Read in header structures */
+ memset ( bzimg, 0, sizeof ( *bzimg ) );
+ copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
+ sizeof ( bzimg->cmdline_magic ) );
+ copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
+ sizeof ( bzimg->bzhdr ) );
+
+ /* Calculate size of real-mode portion */
+ bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ?
+ bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 );
+ if ( bzimg->rm_filesz > image->len ) {
+ DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
+ image, bzimg->rm_filesz );
+ return -ENOEXEC;
+ }
+ bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
+
+ /* Calculate size of protected-mode portion */
+ bzimg->pm_sz = ( image->len - bzimg->rm_filesz );
+ syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
+
+ /* Check for signatures and determine version */
+ if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
+ DBGC ( image, "bzImage %p missing 55AA signature\n", image );
+ return -ENOEXEC;
+ }
+ if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
+ /* 2.00+ */
+ bzimg->version = bzimg->bzhdr.version;
+ } else {
+ /* Pre-2.00. Check that the syssize field is correct,
+ * as a guard against accepting arbitrary binary data,
+ * since the 55AA check is pretty lax. Note that the
+ * syssize field is unreliable for protocols between
+ * 2.00 and 2.03 inclusive, so we should not always
+ * check this field.
+ */
+ bzimg->version = 0x0100;
+ if ( bzimg->bzhdr.syssize != syssize ) {
+ DBGC ( image, "bzImage %p bad syssize %x (expected "
+ "%x)\n", image, bzimg->bzhdr.syssize, syssize );
+ return -ENOEXEC;
+ }
+ }
+
+ /* Determine image type */
+ is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
+ ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
+
+ /* Calculate load address of real-mode portion */
+ bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
+ bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
+
+ /* Allow space for the stack and heap */
+ bzimg->rm_memsz += BZI_STACK_SIZE;
+ bzimg->rm_heap = bzimg->rm_memsz;
+
+ /* Allow space for the command line */
+ bzimg->rm_cmdline = bzimg->rm_memsz;
+ bzimg->rm_memsz += BZI_CMDLINE_SIZE;
+
+ /* Calculate load address of protected-mode portion */
+ bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
+ : BZI_LOAD_LOW_ADDR );
+
+ /* Extract video mode */
+ bzimg->vid_mode = bzimg->bzhdr.vid_mode;
+
+ /* Extract memory limit */
+ bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
+ bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
+
+ /* Extract command line size */
+ bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
+ bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
+
+ DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
+ "cmdlen %zd\n", image, bzimg->version,
+ user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
+ user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
+ bzimg->cmdline_size );
+
+ return 0;
+}
+
+/**
+ * Update bzImage header in loaded kernel
+ *
+ * @v image bzImage file
+ * @v bzimg bzImage context
+ * @v dst bzImage to update
+ */
+static void bzimage_update_header ( struct image *image,
+ struct bzimage_context *bzimg,
+ userptr_t dst ) {
+
+ /* Set loader type */
+ if ( bzimg->version >= 0x0200 )
+ bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE;
+
+ /* Set heap end pointer */
+ if ( bzimg->version >= 0x0201 ) {
+ bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
+ bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
+ }
+
+ /* Set command line */
+ if ( bzimg->version >= 0x0202 ) {
+ bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
+ bzimg->rm_cmdline );
+ } else {
+ bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
+ bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
+ if ( bzimg->version >= 0x0200 )
+ bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
+ }
+
+ /* Set video mode */
+ bzimg->bzhdr.vid_mode = bzimg->vid_mode;
+
+ /* Set initrd address */
+ if ( bzimg->version >= 0x0200 ) {
+ bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
+ bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
+ }
+
+ /* Write out header structures */
+ copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
+ sizeof ( bzimg->cmdline_magic ) );
+ copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
+ sizeof ( bzimg->bzhdr ) );
+
+ DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
+}
+
+/**
+ * Parse kernel command line for bootloader parameters
+ *
+ * @v image bzImage file
+ * @v bzimg bzImage context
+ * @v cmdline Kernel command line
+ * @ret rc Return status code
+ */
+static int bzimage_parse_cmdline ( struct image *image,
+ struct bzimage_context *bzimg,
+ const char *cmdline ) {
+ char *vga;
+ char *mem;
+
+ /* Look for "vga=" */
+ if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
+ vga += 4;
+ if ( strcmp ( vga, "normal" ) == 0 ) {
+ bzimg->vid_mode = BZI_VID_MODE_NORMAL;
+ } else if ( strcmp ( vga, "ext" ) == 0 ) {
+ bzimg->vid_mode = BZI_VID_MODE_EXT;
+ } else if ( strcmp ( vga, "ask" ) == 0 ) {
+ bzimg->vid_mode = BZI_VID_MODE_ASK;
+ } else {
+ bzimg->vid_mode = strtoul ( vga, &vga, 0 );
+ if ( *vga && ( *vga != ' ' ) ) {
+ DBGC ( image, "bzImage %p strange \"vga=\""
+ "terminator '%c'\n", image, *vga );
+ }
+ }
+ }
+
+ /* Look for "mem=" */
+ if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
+ mem += 4;
+ bzimg->mem_limit = strtoul ( mem, &mem, 0 );
+ switch ( *mem ) {
+ case 'G':
+ case 'g':
+ bzimg->mem_limit <<= 10;
+ case 'M':
+ case 'm':
+ bzimg->mem_limit <<= 10;
+ case 'K':
+ case 'k':
+ bzimg->mem_limit <<= 10;
+ break;
+ case '\0':
+ case ' ':
+ break;
+ default:
+ DBGC ( image, "bzImage %p strange \"mem=\" "
+ "terminator '%c'\n", image, *mem );
+ break;
+ }
+ bzimg->mem_limit -= 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Set command line
+ *
+ * @v image bzImage image
+ * @v bzimg bzImage context
+ * @v cmdline Kernel command line
+ */
+static void bzimage_set_cmdline ( struct image *image,
+ struct bzimage_context *bzimg,
+ const char *cmdline ) {
+ size_t cmdline_len;
+
+ /* Copy command line down to real-mode portion */
+ cmdline_len = ( strlen ( cmdline ) + 1 );
+ if ( cmdline_len > bzimg->cmdline_size )
+ cmdline_len = bzimg->cmdline_size;
+ copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
+ cmdline, cmdline_len );
+ DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
+}
+
+/**
+ * Parse standalone image command line for cpio parameters
+ *
+ * @v image bzImage file
+ * @v cpio CPIO header
+ * @v cmdline Command line
+ */
+static void bzimage_parse_cpio_cmdline ( struct image *image,
+ struct cpio_header *cpio,
+ const char *cmdline ) {
+ char *arg;
+ char *end;
+ unsigned int mode;
+
+ /* Look for "mode=" */
+ if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
+ arg += 5;
+ mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
+ if ( *end && ( *end != ' ' ) ) {
+ DBGC ( image, "bzImage %p strange \"mode=\""
+ "terminator '%c'\n", image, *end );
+ }
+ cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) );
+ }
+}
+
+/**
+ * Align initrd length
+ *
+ * @v len Length
+ * @ret len Length rounded up to INITRD_ALIGN
+ */
+static inline size_t bzimage_align ( size_t len ) {
+
+ return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
+}
+
+/**
+ * Load initrd
+ *
+ * @v image bzImage image
+ * @v initrd initrd image
+ * @v address Address at which to load, or UNULL
+ * @ret len Length of loaded image, excluding zero-padding
+ */
+static size_t bzimage_load_initrd ( struct image *image,
+ struct image *initrd,
+ userptr_t address ) {
+ char *filename = initrd->cmdline;
+ char *cmdline;
+ struct cpio_header cpio;
+ size_t offset;
+ size_t name_len;
+ size_t pad_len;
+
+ /* Do not include kernel image itself as an initrd */
+ if ( initrd == image )
+ return 0;
+
+ /* Create cpio header for non-prebuilt images */
+ if ( filename && filename[0] ) {
+ cmdline = strchr ( filename, ' ' );
+ name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
+ : strlen ( filename ) ) + 1 /* NUL */ );
+ memset ( &cpio, '0', sizeof ( cpio ) );
+ memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) );
+ cpio_set_field ( cpio.c_mode, 0100644 );
+ cpio_set_field ( cpio.c_nlink, 1 );
+ cpio_set_field ( cpio.c_filesize, initrd->len );
+ cpio_set_field ( cpio.c_namesize, name_len );
+ if ( cmdline ) {
+ bzimage_parse_cpio_cmdline ( image, &cpio,
+ ( cmdline + 1 /* ' ' */ ));
+ }
+ offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 );
+ } else {
+ offset = 0;
+ name_len = 0;
+ }
+
+ /* Copy in initrd image body (and cpio header if applicable) */
+ if ( address ) {
+ memmove_user ( address, offset, initrd->data, 0, initrd->len );
+ if ( offset ) {
+ memset_user ( address, 0, 0, offset );
+ copy_to_user ( address, 0, &cpio, sizeof ( cpio ) );
+ copy_to_user ( address, sizeof ( cpio ), filename,
+ ( name_len - 1 /* NUL (or space) */ ) );
+ }
+ DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
+ "%s%s\n", image, initrd, user_to_phys ( address, 0 ),
+ user_to_phys ( address, offset ),
+ user_to_phys ( address, ( offset + initrd->len ) ),
+ ( filename ? " " : "" ), ( filename ? filename : "" ) );
+ DBGC2_MD5A ( image, user_to_phys ( address, offset ),
+ user_to_virt ( address, offset ), initrd->len );
+ }
+ offset += initrd->len;
+
+ /* Zero-pad to next INITRD_ALIGN boundary */
+ pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
+ if ( address )
+ memset_user ( address, offset, 0, pad_len );
+
+ return offset;
+}
+
+/**
+ * Check that initrds can be loaded
+ *
+ * @v image bzImage image
+ * @v bzimg bzImage context
+ * @ret rc Return status code
+ */
+static int bzimage_check_initrds ( struct image *image,
+ struct bzimage_context *bzimg ) {
+ struct image *initrd;
+ userptr_t bottom;
+ size_t len = 0;
+ int rc;
+
+ /* Calculate total loaded length of initrds */
+ for_each_image ( initrd ) {
+
+ /* Skip kernel */
+ if ( initrd == image )
+ continue;
+
+ /* Calculate length */
+ len += bzimage_load_initrd ( image, initrd, UNULL );
+ len = bzimage_align ( len );
+
+ DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n",
+ image, initrd, user_to_phys ( initrd->data, 0 ),
+ user_to_phys ( initrd->data, initrd->len ),
+ ( initrd->cmdline ? " " : "" ),
+ ( initrd->cmdline ? initrd->cmdline : "" ) );
+ DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ),
+ user_to_virt ( initrd->data, 0 ), initrd->len );
+ }
+
+ /* Calculate lowest usable address */
+ bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz );
+
+ /* Check that total length fits within space available for
+ * reshuffling. This is a conservative check, since CPIO
+ * headers are not present during reshuffling, but this
+ * doesn't hurt and keeps the code simple.
+ */
+ if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) {
+ DBGC ( image, "bzImage %p failed reshuffle check: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check that total length fits within kernel's memory limit */
+ if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) {
+ DBGC ( image, "bzImage %p not enough space for initrds\n",
+ image );
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+/**
+ * Load initrds, if any
+ *
+ * @v image bzImage image
+ * @v bzimg bzImage context
+ */
+static void bzimage_load_initrds ( struct image *image,
+ struct bzimage_context *bzimg ) {
+ struct image *initrd;
+ struct image *highest = NULL;
+ struct image *other;
+ userptr_t top;
+ userptr_t dest;
+ size_t offset;
+ size_t len;
+
+ /* Reshuffle initrds into desired order */
+ initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) );
+
+ /* Find highest initrd */
+ for_each_image ( initrd ) {
+ if ( ( highest == NULL ) ||
+ ( userptr_sub ( initrd->data, highest->data ) > 0 ) ) {
+ highest = initrd;
+ }
+ }
+
+ /* Do nothing if there are no initrds */
+ if ( ! highest )
+ return;
+
+ /* Find highest usable address */
+ top = userptr_add ( highest->data, bzimage_align ( highest->len ) );
+ if ( user_to_phys ( top, 0 ) > bzimg->mem_limit )
+ top = phys_to_user ( bzimg->mem_limit );
+ DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n",
+ image, user_to_phys ( top, 0 ) );
+
+ /* Load initrds in order */
+ for_each_image ( initrd ) {
+
+ /* Calculate cumulative length of following
+ * initrds (including padding).
+ */
+ offset = 0;
+ for_each_image ( other ) {
+ if ( other == initrd )
+ offset = 0;
+ offset += bzimage_load_initrd ( image, other, UNULL );
+ offset = bzimage_align ( offset );
+ }
+
+ /* Load initrd at this address */
+ dest = userptr_add ( top, -offset );
+ len = bzimage_load_initrd ( image, initrd, dest );
+
+ /* Record initrd location */
+ if ( ! bzimg->ramdisk_image )
+ bzimg->ramdisk_image = user_to_phys ( dest, 0 );
+ bzimg->ramdisk_size = ( user_to_phys ( dest, len ) -
+ bzimg->ramdisk_image );
+ }
+ DBGC ( image, "bzImage %p initrds at [%#08lx,%#08lx)\n",
+ image, bzimg->ramdisk_image,
+ ( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
+}
+
+/**
+ * Execute bzImage image
+ *
+ * @v image bzImage image
+ * @ret rc Return status code
+ */
+static int bzimage_exec ( struct image *image ) {
+ struct bzimage_context bzimg;
+ const char *cmdline = ( image->cmdline ? image->cmdline : "" );
+ int rc;
+
+ /* Read and parse header from image */
+ if ( ( rc = bzimage_parse_header ( image, &bzimg,
+ image->data ) ) != 0 )
+ return rc;
+
+ /* Prepare segments */
+ if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
+ bzimg.rm_memsz ) ) != 0 ) {
+ DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+ if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
+ bzimg.pm_sz ) ) != 0 ) {
+ DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Parse command line for bootloader parameters */
+ if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
+ return rc;
+
+ /* Check that initrds can be loaded */
+ if ( ( rc = bzimage_check_initrds ( image, &bzimg ) ) != 0 )
+ return rc;
+
+ /* Remove kernel from image list (without invalidating image pointer) */
+ unregister_image ( image_get ( image ) );
+
+ /* Load segments */
+ memcpy_user ( bzimg.rm_kernel, 0, image->data,
+ 0, bzimg.rm_filesz );
+ memcpy_user ( bzimg.pm_kernel, 0, image->data,
+ bzimg.rm_filesz, bzimg.pm_sz );
+
+ /* Store command line */
+ bzimage_set_cmdline ( image, &bzimg, cmdline );
+
+ /* Prepare for exiting. Must do this before loading initrds,
+ * since loading the initrds will corrupt the external heap.
+ */
+ shutdown_boot();
+
+ /* Load any initrds */
+ bzimage_load_initrds ( image, &bzimg );
+
+ /* Update kernel header */
+ bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
+
+ DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
+ "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
+ bzimg.rm_kernel_seg, bzimg.rm_heap );
+
+ /* Jump to the kernel */
+ __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
+ "movw %w0, %%es\n\t"
+ "movw %w0, %%fs\n\t"
+ "movw %w0, %%gs\n\t"
+ "movw %w0, %%ss\n\t"
+ "movw %w1, %%sp\n\t"
+ "pushw %w2\n\t"
+ "pushw $0\n\t"
+ "lret\n\t" )
+ : : "r" ( bzimg.rm_kernel_seg ),
+ "r" ( bzimg.rm_heap ),
+ "r" ( bzimg.rm_kernel_seg + 0x20 ) );
+
+ /* There is no way for the image to return, since we provide
+ * no return address.
+ */
+ assert ( 0 );
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+}
+
+/**
+ * Probe bzImage image
+ *
+ * @v image bzImage file
+ * @ret rc Return status code
+ */
+int bzimage_probe ( struct image *image ) {
+ struct bzimage_context bzimg;
+ int rc;
+
+ /* Read and parse header from image */
+ if ( ( rc = bzimage_parse_header ( image, &bzimg,
+ image->data ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/** Linux bzImage image type */
+struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "bzImage",
+ .probe = bzimage_probe,
+ .exec = bzimage_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/com32.c b/roms/ipxe/src/arch/i386/image/com32.c
new file mode 100644
index 000000000..c12ffb684
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/com32.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * SYSLINUX COM32 image format
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <basemem.h>
+#include <comboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/io.h>
+
+/**
+ * Execute COMBOOT image
+ *
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_exec_loop ( struct image *image ) {
+ struct memory_map memmap;
+ unsigned int i;
+ int state;
+ uint32_t avail_mem_top;
+
+ state = rmsetjmp ( comboot_return );
+
+ switch ( state ) {
+ case 0: /* First time through; invoke COM32 program */
+
+ /* Get memory map */
+ get_memmap ( &memmap );
+
+ /* Find end of block covering COM32 image loading area */
+ for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
+ if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
+ (memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
+ avail_mem_top = memmap.regions[i].end;
+ break;
+ }
+ }
+
+ DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
+ image, avail_mem_top );
+
+ assert ( avail_mem_top != 0 );
+
+ com32_external_esp = phys_to_virt ( avail_mem_top );
+
+ /* Hook COMBOOT API interrupts */
+ hook_comboot_interrupts();
+
+ /* Unregister image, so that a "boot" command doesn't
+ * throw us into an execution loop. We never
+ * reregister ourselves; COMBOOT images expect to be
+ * removed on exit.
+ */
+ unregister_image ( image );
+
+ __asm__ __volatile__ (
+ "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */
+ "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */
+ "call _virt_to_phys\n\t" /* Switch to flat physical address space */
+ "sti\n\t" /* Enable interrupts */
+ "pushl %0\n\t" /* Pointer to CDECL helper function */
+ "pushl %1\n\t" /* Pointer to FAR call helper function */
+ "pushl %2\n\t" /* Size of low memory bounce buffer */
+ "pushl %3\n\t" /* Pointer to low memory bounce buffer */
+ "pushl %4\n\t" /* Pointer to INT call helper function */
+ "pushl %5\n\t" /* Pointer to the command line arguments */
+ "pushl $6\n\t" /* Number of additional arguments */
+ "call *%6\n\t" /* Execute image */
+ "cli\n\t" /* Disable interrupts */
+ "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */
+ "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
+ :
+ :
+ /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
+ /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
+ /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ),
+ /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ),
+ /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
+ /* %5 */ "r" ( virt_to_phys ( image->cmdline ?
+ image->cmdline : "" ) ),
+ /* %6 */ "r" ( COM32_START_PHYS )
+ :
+ "memory" );
+ DBGC ( image, "COM32 %p: returned\n", image );
+ break;
+
+ case COMBOOT_EXIT:
+ DBGC ( image, "COM32 %p: exited\n", image );
+ break;
+
+ case COMBOOT_EXIT_RUN_KERNEL:
+ assert ( image->replacement );
+ DBGC ( image, "COM32 %p: exited to run kernel %s\n",
+ image, image->replacement->name );
+ break;
+
+ case COMBOOT_EXIT_COMMAND:
+ DBGC ( image, "COM32 %p: exited after executing command\n",
+ image );
+ break;
+
+ default:
+ assert ( 0 );
+ break;
+ }
+
+ unhook_comboot_interrupts();
+ comboot_force_text_mode();
+
+ return 0;
+}
+
+/**
+ * Check image name extension
+ *
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_identify ( struct image *image ) {
+ const char *ext;
+ static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
+ uint8_t buf[5];
+
+ if ( image->len >= 5 ) {
+ /* Check for magic number
+ * mov eax,21cd4cffh
+ * B8 FF 4C CD 21
+ */
+ copy_from_user ( buf, image->data, 0, sizeof(buf) );
+ if ( ! memcmp ( buf, magic, sizeof(buf) ) ) {
+ DBGC ( image, "COM32 %p: found magic number\n",
+ image );
+ return 0;
+ }
+ }
+
+ /* Magic number not found; check filename extension */
+
+ ext = strrchr( image->name, '.' );
+
+ if ( ! ext ) {
+ DBGC ( image, "COM32 %p: no extension\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ ++ext;
+
+ if ( strcasecmp( ext, "c32" ) ) {
+ DBGC ( image, "COM32 %p: unrecognized extension %s\n",
+ image, ext );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Load COM32 image into memory
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_load_image ( struct image *image ) {
+ size_t filesz, memsz;
+ userptr_t buffer;
+ int rc;
+
+ filesz = image->len;
+ memsz = filesz;
+ buffer = phys_to_user ( COM32_START_PHYS );
+ if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, 0, filesz );
+
+ return 0;
+}
+
+/**
+ * Prepare COM32 low memory bounce buffer
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_prepare_bounce_buffer ( struct image * image ) {
+ unsigned int seg;
+ userptr_t seg_userptr;
+ size_t filesz, memsz;
+ int rc;
+
+ seg = COM32_BOUNCE_SEG;
+ seg_userptr = real_to_user ( seg, 0 );
+
+ /* Ensure the entire 64k segment is free */
+ memsz = 0xFFFF;
+ filesz = 0;
+
+ /* Prepare, verify, and load the real-mode segment */
+ if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Probe COM32 image
+ *
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_probe ( struct image *image ) {
+ int rc;
+
+ DBGC ( image, "COM32 %p: name '%s'\n", image, image->name );
+
+ /* Check if this is a COMBOOT image */
+ if ( ( rc = com32_identify ( image ) ) != 0 ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute COMBOOT image
+ *
+ * @v image COM32 image
+ * @ret rc Return status code
+ */
+static int com32_exec ( struct image *image ) {
+ int rc;
+
+ /* Load image */
+ if ( ( rc = com32_load_image ( image ) ) != 0 ) {
+ return rc;
+ }
+
+ /* Prepare bounce buffer segment */
+ if ( ( rc = com32_prepare_bounce_buffer ( image ) ) != 0 ) {
+ return rc;
+ }
+
+ return com32_exec_loop ( image );
+}
+
+/** SYSLINUX COM32 image type */
+struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "COM32",
+ .probe = com32_probe,
+ .exec = com32_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/comboot.c b/roms/ipxe/src/arch/i386/image/comboot.c
new file mode 100644
index 000000000..1ec02331d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/comboot.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * SYSLINUX COMBOOT (16-bit) image format
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <basemem.h>
+#include <comboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
+
+/**
+ * COMBOOT PSP, copied to offset 0 of code segment
+ */
+struct comboot_psp {
+ /** INT 20 instruction, executed if COMBOOT image returns with RET */
+ uint16_t int20;
+ /** Segment of first non-free paragraph of memory */
+ uint16_t first_non_free_para;
+};
+
+/** Offset in PSP of command line */
+#define COMBOOT_PSP_CMDLINE_OFFSET 0x81
+
+/** Maximum length of command line in PSP
+ * (127 bytes minus space and CR) */
+#define COMBOOT_MAX_CMDLINE_LEN 125
+
+
+/**
+ * Copy command line to PSP
+ *
+ * @v image COMBOOT image
+ */
+static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
+ const char *cmdline = ( image->cmdline ? image->cmdline : "" );
+ int cmdline_len = strlen ( cmdline );
+ if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
+ cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
+ uint8_t len_byte = cmdline_len;
+ char spc = ' ', cr = '\r';
+
+ /* Copy length to byte before command line */
+ copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
+ &len_byte, 1 );
+
+ /* Command line starts with space */
+ copy_to_user ( seg_userptr,
+ COMBOOT_PSP_CMDLINE_OFFSET,
+ &spc, 1 );
+
+ /* Copy command line */
+ copy_to_user ( seg_userptr,
+ COMBOOT_PSP_CMDLINE_OFFSET + 1,
+ cmdline, cmdline_len );
+
+ /* Command line ends with CR */
+ copy_to_user ( seg_userptr,
+ COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
+ &cr, 1 );
+}
+
+/**
+ * Initialize PSP
+ *
+ * @v image COMBOOT image
+ * @v seg_userptr segment to initialize
+ */
+static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
+ struct comboot_psp psp;
+
+ /* Fill PSP */
+
+ /* INT 20h instruction, byte order reversed */
+ psp.int20 = 0x20CD;
+
+ /* get_fbms() returns BIOS free base memory counter, which is in
+ * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
+ psp.first_non_free_para = get_fbms() << 6;
+
+ DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
+ image, psp.first_non_free_para );
+
+ /* Copy the PSP to offset 0 of segment.
+ * The rest of the PSP was already zeroed by
+ * comboot_prepare_segment. */
+ copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
+
+ /* Copy the command line to the PSP */
+ comboot_copy_cmdline ( image, seg_userptr );
+}
+
+/**
+ * Execute COMBOOT image
+ *
+ * @v image COMBOOT image
+ * @ret rc Return status code
+ */
+static int comboot_exec_loop ( struct image *image ) {
+ userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
+ int state;
+
+ state = rmsetjmp ( comboot_return );
+
+ switch ( state ) {
+ case 0: /* First time through; invoke COMBOOT program */
+
+ /* Initialize PSP */
+ comboot_init_psp ( image, seg_userptr );
+
+ /* Hook COMBOOT API interrupts */
+ hook_comboot_interrupts();
+
+ DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
+ COMBOOT_PSP_SEG );
+
+ /* Unregister image, so that a "boot" command doesn't
+ * throw us into an execution loop. We never
+ * reregister ourselves; COMBOOT images expect to be
+ * removed on exit.
+ */
+ unregister_image ( image );
+
+ /* Store stack segment at 0x38 and stack pointer at 0x3A
+ * in the PSP and jump to the image */
+ __asm__ __volatile__ (
+ REAL_CODE ( /* Save return address with segment on old stack */
+ "popw %%ax\n\t"
+ "pushw %%cs\n\t"
+ "pushw %%ax\n\t"
+ /* Set DS=ES=segment with image */
+ "movw %w0, %%ds\n\t"
+ "movw %w0, %%es\n\t"
+ /* Set SS:SP to new stack (end of image segment) */
+ "movw %w0, %%ss\n\t"
+ "xor %%sp, %%sp\n\t"
+ "pushw $0\n\t"
+ "pushw %w0\n\t"
+ "pushw $0x100\n\t"
+ /* Zero registers (some COM files assume GP regs are 0) */
+ "xorw %%ax, %%ax\n\t"
+ "xorw %%bx, %%bx\n\t"
+ "xorw %%cx, %%cx\n\t"
+ "xorw %%dx, %%dx\n\t"
+ "xorw %%si, %%si\n\t"
+ "xorw %%di, %%di\n\t"
+ "xorw %%bp, %%bp\n\t"
+ "lret\n\t" )
+ : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
+ DBGC ( image, "COMBOOT %p: returned\n", image );
+ break;
+
+ case COMBOOT_EXIT:
+ DBGC ( image, "COMBOOT %p: exited\n", image );
+ break;
+
+ case COMBOOT_EXIT_RUN_KERNEL:
+ assert ( image->replacement );
+ DBGC ( image, "COMBOOT %p: exited to run kernel %s\n",
+ image, image->replacement->name );
+ break;
+
+ case COMBOOT_EXIT_COMMAND:
+ DBGC ( image, "COMBOOT %p: exited after executing command\n",
+ image );
+ break;
+
+ default:
+ assert ( 0 );
+ break;
+ }
+
+ unhook_comboot_interrupts();
+ comboot_force_text_mode();
+
+ return 0;
+}
+
+/**
+ * Check image name extension
+ *
+ * @v image COMBOOT image
+ * @ret rc Return status code
+ */
+static int comboot_identify ( struct image *image ) {
+ const char *ext;
+
+ ext = strrchr( image->name, '.' );
+
+ if ( ! ext ) {
+ DBGC ( image, "COMBOOT %p: no extension\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ ++ext;
+
+ if ( strcasecmp( ext, "cbt" ) ) {
+ DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
+ image, ext );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Load COMBOOT image into memory, preparing a segment and returning it
+ * @v image COMBOOT image
+ * @ret rc Return status code
+ */
+static int comboot_prepare_segment ( struct image *image )
+{
+ userptr_t seg_userptr;
+ size_t filesz, memsz;
+ int rc;
+
+ /* Load image in segment */
+ seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
+
+ /* Allow etra 0x100 bytes before image for PSP */
+ filesz = image->len + 0x100;
+
+ /* Ensure the entire 64k segment is free */
+ memsz = 0xFFFF;
+
+ /* Prepare, verify, and load the real-mode segment */
+ if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Zero PSP */
+ memset_user ( seg_userptr, 0, 0, 0x100 );
+
+ /* Copy image to segment:0100 */
+ memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
+
+ return 0;
+}
+
+/**
+ * Probe COMBOOT image
+ *
+ * @v image COMBOOT image
+ * @ret rc Return status code
+ */
+static int comboot_probe ( struct image *image ) {
+ int rc;
+
+ DBGC ( image, "COMBOOT %p: name '%s'\n",
+ image, image->name );
+
+ /* Check if this is a COMBOOT image */
+ if ( ( rc = comboot_identify ( image ) ) != 0 ) {
+
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute COMBOOT image
+ *
+ * @v image COMBOOT image
+ * @ret rc Return status code
+ */
+static int comboot_exec ( struct image *image ) {
+ int rc;
+
+ /* Sanity check for filesize */
+ if( image->len >= 0xFF00 ) {
+ DBGC( image, "COMBOOT %p: image too large\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Prepare segment and load image */
+ if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
+ return rc;
+ }
+
+ return comboot_exec_loop ( image );
+}
+
+/** SYSLINUX COMBOOT (16-bit) image type */
+struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "COMBOOT",
+ .probe = comboot_probe,
+ .exec = comboot_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/elfboot.c b/roms/ipxe/src/arch/i386/image/elfboot.c
new file mode 100644
index 000000000..dc3568929
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/elfboot.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <errno.h>
+#include <elf.h>
+#include <ipxe/image.h>
+#include <ipxe/elf.h>
+#include <ipxe/features.h>
+#include <ipxe/init.h>
+
+/**
+ * @file
+ *
+ * ELF bootable image
+ *
+ */
+
+FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 );
+
+/**
+ * Execute ELF image
+ *
+ * @v image ELF image
+ * @ret rc Return status code
+ */
+static int elfboot_exec ( struct image *image ) {
+ physaddr_t entry;
+ physaddr_t max;
+ int rc;
+
+ /* Load the image using core ELF support */
+ if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) {
+ DBGC ( image, "ELF %p could not load: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* An ELF image has no callback interface, so we need to shut
+ * down before invoking it.
+ */
+ shutdown_boot();
+
+ /* Jump to OS with flat physical addressing */
+ DBGC ( image, "ELF %p starting execution at %lx\n", image, entry );
+ __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "call *%%edi\n\t"
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : : "D" ( entry )
+ : "eax", "ebx", "ecx", "edx", "esi", "memory" );
+
+ DBGC ( image, "ELF %p returned\n", image );
+
+ /* It isn't safe to continue after calling shutdown() */
+ while ( 1 ) {}
+
+ return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
+}
+
+/**
+ * Check that ELF segment uses flat physical addressing
+ *
+ * @v image ELF file
+ * @v phdr ELF program header
+ * @v dest Destination address
+ * @ret rc Return status code
+ */
+static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr,
+ physaddr_t dest ) {
+
+ /* Check that ELF segment uses flat physical addressing */
+ if ( phdr->p_vaddr != dest ) {
+ DBGC ( image, "ELF %p uses virtual addressing (phys %x, "
+ "virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Probe ELF image
+ *
+ * @v image ELF file
+ * @ret rc Return status code
+ */
+static int elfboot_probe ( struct image *image ) {
+ Elf32_Ehdr ehdr;
+ static const uint8_t e_ident[] = {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELFCLASS32,
+ [EI_DATA] = ELFDATA2LSB,
+ [EI_VERSION] = EV_CURRENT,
+ };
+ physaddr_t entry;
+ physaddr_t max;
+ int rc;
+
+ /* Read ELF header */
+ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
+ if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
+ DBGC ( image, "Invalid ELF identifier\n" );
+ return -ENOEXEC;
+ }
+
+ /* Check that this image uses flat physical addressing */
+ if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment,
+ &entry, &max ) ) != 0 ) {
+ DBGC ( image, "Unloadable ELF image\n" );
+ return rc;
+ }
+
+ return 0;
+}
+
+/** ELF image type */
+struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "ELF",
+ .probe = elfboot_probe,
+ .exec = elfboot_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/initrd.c b/roms/ipxe/src/arch/i386/image/initrd.c
new file mode 100644
index 000000000..80c197417
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/initrd.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 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 (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <errno.h>
+#include <initrd.h>
+#include <ipxe/image.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/init.h>
+#include <ipxe/memblock.h>
+
+/** @file
+ *
+ * Initial ramdisk (initrd) reshuffling
+ *
+ */
+
+/** Maximum address available for initrd */
+userptr_t initrd_top;
+
+/** Minimum address available for initrd */
+userptr_t initrd_bottom;
+
+/**
+ * Squash initrds as high as possible in memory
+ *
+ * @v top Highest possible address
+ * @ret used Lowest address used by initrds
+ */
+static userptr_t initrd_squash_high ( userptr_t top ) {
+ userptr_t current = top;
+ struct image *initrd;
+ struct image *highest;
+ size_t len;
+
+ /* Squash up any initrds already within or below the region */
+ while ( 1 ) {
+
+ /* Find the highest image not yet in its final position */
+ highest = NULL;
+ for_each_image ( initrd ) {
+ if ( ( userptr_sub ( initrd->data, current ) < 0 ) &&
+ ( ( highest == NULL ) ||
+ ( userptr_sub ( initrd->data,
+ highest->data ) > 0 ) ) ) {
+ highest = initrd;
+ }
+ }
+ if ( ! highest )
+ break;
+
+ /* Move this image to its final position */
+ len = ( ( highest->len + INITRD_ALIGN - 1 ) &
+ ~( INITRD_ALIGN - 1 ) );
+ current = userptr_sub ( current, len );
+ DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
+ "[%#08lx,%#08lx)\n", highest->name,
+ user_to_phys ( highest->data, 0 ),
+ user_to_phys ( highest->data, highest->len ),
+ user_to_phys ( current, 0 ),
+ user_to_phys ( current, highest->len ) );
+ memmove_user ( current, 0, highest->data, 0, highest->len );
+ highest->data = current;
+ }
+
+ /* Copy any remaining initrds (e.g. embedded images) to the region */
+ for_each_image ( initrd ) {
+ if ( userptr_sub ( initrd->data, top ) >= 0 ) {
+ len = ( ( initrd->len + INITRD_ALIGN - 1 ) &
+ ~( INITRD_ALIGN - 1 ) );
+ current = userptr_sub ( current, len );
+ DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->"
+ "[%#08lx,%#08lx)\n", initrd->name,
+ user_to_phys ( initrd->data, 0 ),
+ user_to_phys ( initrd->data, initrd->len ),
+ user_to_phys ( current, 0 ),
+ user_to_phys ( current, initrd->len ) );
+ memcpy_user ( current, 0, initrd->data, 0,
+ initrd->len );
+ initrd->data = current;
+ }
+ }
+
+ return current;
+}
+
+/**
+ * Swap position of two adjacent initrds
+ *
+ * @v low Lower initrd
+ * @v high Higher initrd
+ * @v free Free space
+ * @v free_len Length of free space
+ */
+static void initrd_swap ( struct image *low, struct image *high,
+ userptr_t free, size_t free_len ) {
+ size_t len = 0;
+ size_t frag_len;
+ size_t new_len;
+
+ DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
+ "%s\n", low->name, user_to_phys ( low->data, 0 ),
+ user_to_phys ( low->data, low->len ),
+ user_to_phys ( high->data, 0 ),
+ user_to_phys ( high->data, high->len ), high->name );
+
+ /* Round down length of free space */
+ free_len &= ~( INITRD_ALIGN - 1 );
+ assert ( free_len > 0 );
+
+ /* Swap image data */
+ while ( len < high->len ) {
+
+ /* Calculate maximum fragment length */
+ frag_len = ( high->len - len );
+ if ( frag_len > free_len )
+ frag_len = free_len;
+ new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
+ ~( INITRD_ALIGN - 1 ) );
+
+ /* Swap fragments */
+ memcpy_user ( free, 0, high->data, len, frag_len );
+ memmove_user ( low->data, new_len, low->data, len, low->len );
+ memcpy_user ( low->data, len, free, 0, frag_len );
+ len = new_len;
+ }
+
+ /* Adjust data pointers */
+ high->data = low->data;
+ low->data = userptr_add ( low->data, len );
+}
+
+/**
+ * Swap position of any two adjacent initrds not currently in the correct order
+ *
+ * @v free Free space
+ * @v free_len Length of free space
+ * @ret swapped A pair of initrds was swapped
+ */
+static int initrd_swap_any ( userptr_t free, size_t free_len ) {
+ struct image *low;
+ struct image *high;
+ size_t padded_len;
+ userptr_t adjacent;
+
+ /* Find any pair of initrds that can be swapped */
+ for_each_image ( low ) {
+
+ /* Calculate location of adjacent image (if any) */
+ padded_len = ( ( low->len + INITRD_ALIGN - 1 ) &
+ ~( INITRD_ALIGN - 1 ) );
+ adjacent = userptr_add ( low->data, padded_len );
+
+ /* Search for adjacent image */
+ for_each_image ( high ) {
+
+ /* If we have found the adjacent image, swap and exit */
+ if ( high->data == adjacent ) {
+ initrd_swap ( low, high, free, free_len );
+ return 1;
+ }
+
+ /* Stop search if all remaining potential
+ * adjacent images are already in the correct
+ * order.
+ */
+ if ( high == low )
+ break;
+ }
+ }
+
+ /* Nothing swapped */
+ return 0;
+}
+
+/**
+ * Dump initrd locations (for debug)
+ *
+ */
+static void initrd_dump ( void ) {
+ struct image *initrd;
+
+ /* Do nothing unless debugging is enabled */
+ if ( ! DBG_LOG )
+ return;
+
+ /* Dump initrd locations */
+ for_each_image ( initrd ) {
+ DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
+ initrd->name, user_to_phys ( initrd->data, 0 ),
+ user_to_phys ( initrd->data, initrd->len ) );
+ DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ),
+ user_to_virt ( initrd->data, 0 ), initrd->len );
+ }
+}
+
+/**
+ * Reshuffle initrds into desired order at top of memory
+ *
+ * @v bottom Lowest address available for initrds
+ *
+ * After this function returns, the initrds have been rearranged in
+ * memory and the external heap structures will have been corrupted.
+ * Reshuffling must therefore take place immediately prior to jumping
+ * to the loaded OS kernel; no further execution within iPXE is
+ * permitted.
+ */
+void initrd_reshuffle ( userptr_t bottom ) {
+ userptr_t top;
+ userptr_t used;
+ userptr_t free;
+ size_t free_len;
+
+ /* Calculate limits of available space for initrds */
+ top = initrd_top;
+ if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
+ bottom = initrd_bottom;
+
+ /* Debug */
+ DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n",
+ user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) );
+ initrd_dump();
+
+ /* Squash initrds as high as possible in memory */
+ used = initrd_squash_high ( top );
+
+ /* Calculate available free space */
+ free = bottom;
+ free_len = userptr_sub ( used, free );
+
+ /* Bubble-sort initrds into desired order */
+ while ( initrd_swap_any ( free, free_len ) ) {}
+
+ /* Debug */
+ initrd_dump();
+}
+
+/**
+ * Check that there is enough space to reshuffle initrds
+ *
+ * @v len Total length of initrds (including padding)
+ * @v bottom Lowest address available for initrds
+ * @ret rc Return status code
+ */
+int initrd_reshuffle_check ( size_t len, userptr_t bottom ) {
+ userptr_t top;
+ size_t available;
+
+ /* Calculate limits of available space for initrds */
+ top = initrd_top;
+ if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
+ bottom = initrd_bottom;
+ available = userptr_sub ( top, bottom );
+
+ /* Allow for a sensible minimum amount of free space */
+ len += INITRD_MIN_FREE_LEN;
+
+ /* Check for available space */
+ return ( ( len < available ) ? 0 : -ENOBUFS );
+}
+
+/**
+ * initrd startup function
+ *
+ */
+static void initrd_startup ( void ) {
+ size_t len;
+
+ /* Record largest memory block available. Do this after any
+ * allocations made during driver startup (e.g. large host
+ * memory blocks for Infiniband devices, which may still be in
+ * use at the time of rearranging if a SAN device is hooked)
+ * but before any allocations for downloaded images (which we
+ * can safely reuse when rearranging).
+ */
+ len = largest_memblock ( &initrd_bottom );
+ initrd_top = userptr_add ( initrd_bottom, len );
+}
+
+/** initrd startup function */
+struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
+ .startup = initrd_startup,
+};
diff --git a/roms/ipxe/src/arch/i386/image/multiboot.c b/roms/ipxe/src/arch/i386/image/multiboot.c
new file mode 100644
index 000000000..0c85df708
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/multiboot.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Multiboot image format
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <multiboot.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/io.h>
+#include <ipxe/elf.h>
+#include <ipxe/init.h>
+#include <ipxe/features.h>
+#include <ipxe/uri.h>
+#include <ipxe/version.h>
+
+FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 );
+
+/**
+ * Maximum number of modules we will allow for
+ *
+ * If this has bitten you: sorry. I did have a perfect scheme with a
+ * dynamically allocated list of modules on the protected-mode stack,
+ * but it was incompatible with some broken OSes that can only access
+ * low memory at boot time (even though we kindly set up 4GB flat
+ * physical addressing as per the multiboot specification.
+ *
+ */
+#define MAX_MODULES 8
+
+/**
+ * Maximum combined length of command lines
+ *
+ * Again; sorry. Some broken OSes zero out any non-base memory that
+ * isn't part of the loaded module set, so we can't just use
+ * virt_to_phys(cmdline) to point to the command lines, even though
+ * this would comply with the Multiboot spec.
+ */
+#define MB_MAX_CMDLINE 512
+
+/** Multiboot flags that we support */
+#define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
+ MB_FLAG_VIDMODE | MB_FLAG_RAW )
+
+/** Compulsory feature multiboot flags */
+#define MB_COMPULSORY_FLAGS 0x0000ffff
+
+/** Optional feature multiboot flags */
+#define MB_OPTIONAL_FLAGS 0xffff0000
+
+/**
+ * Multiboot flags that we don't support
+ *
+ * We only care about the compulsory feature flags (bits 0-15); we are
+ * allowed to ignore the optional feature flags.
+ */
+#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
+
+/** A multiboot header descriptor */
+struct multiboot_header_info {
+ /** The actual multiboot header */
+ struct multiboot_header mb;
+ /** Offset of header within the multiboot image */
+ size_t offset;
+};
+
+/** Multiboot module command lines */
+static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
+#define mb_cmdlines __use_data16 ( mb_cmdlines )
+
+/** Offset within module command lines */
+static unsigned int mb_cmdline_offset;
+
+/**
+ * Build multiboot memory map
+ *
+ * @v image Multiboot image
+ * @v mbinfo Multiboot information structure
+ * @v mbmemmap Multiboot memory map
+ * @v limit Maxmimum number of memory map entries
+ */
+static void multiboot_build_memmap ( struct image *image,
+ struct multiboot_info *mbinfo,
+ struct multiboot_memory_map *mbmemmap,
+ unsigned int limit ) {
+ struct memory_map memmap;
+ unsigned int i;
+
+ /* Get memory map */
+ get_memmap ( &memmap );
+
+ /* Translate into multiboot format */
+ memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ if ( i >= limit ) {
+ DBGC ( image, "MULTIBOOT %p limit of %d memmap "
+ "entries reached\n", image, limit );
+ break;
+ }
+ mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
+ sizeof ( mbmemmap[i].size ) );
+ mbmemmap[i].base_addr = memmap.regions[i].start;
+ mbmemmap[i].length = ( memmap.regions[i].end -
+ memmap.regions[i].start );
+ mbmemmap[i].type = MBMEM_RAM;
+ mbinfo->mmap_length += sizeof ( mbmemmap[i] );
+ if ( memmap.regions[i].start == 0 )
+ mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
+ if ( memmap.regions[i].start == 0x100000 )
+ mbinfo->mem_upper = ( ( memmap.regions[i].end -
+ 0x100000 ) / 1024 );
+ }
+}
+
+/**
+ * Add command line in base memory
+ *
+ * @v image Image
+ * @ret physaddr Physical address of command line
+ */
+static physaddr_t multiboot_add_cmdline ( struct image *image ) {
+ char *mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
+ size_t remaining = ( sizeof ( mb_cmdlines ) - mb_cmdline_offset );
+ char *buf = mb_cmdline;
+ size_t len;
+
+ /* Copy image URI to base memory buffer as start of command line */
+ len = ( format_uri ( image->uri, buf, remaining ) + 1 /* NUL */ );
+ if ( len > remaining )
+ len = remaining;
+ mb_cmdline_offset += len;
+ buf += len;
+ remaining -= len;
+
+ /* Copy command line to base memory buffer, if present */
+ if ( image->cmdline ) {
+ mb_cmdline_offset--; /* Strip NUL */
+ buf--;
+ remaining++;
+ len = ( snprintf ( buf, remaining, " %s",
+ image->cmdline ) + 1 /* NUL */ );
+ if ( len > remaining )
+ len = remaining;
+ mb_cmdline_offset += len;
+ }
+
+ return virt_to_phys ( mb_cmdline );
+}
+
+/**
+ * Add multiboot modules
+ *
+ * @v image Multiboot image
+ * @v start Start address for modules
+ * @v mbinfo Multiboot information structure
+ * @v modules Multiboot module list
+ * @ret rc Return status code
+ */
+static int multiboot_add_modules ( struct image *image, physaddr_t start,
+ struct multiboot_info *mbinfo,
+ struct multiboot_module *modules,
+ unsigned int limit ) {
+ struct image *module_image;
+ struct multiboot_module *module;
+ int rc;
+
+ /* Add each image as a multiboot module */
+ for_each_image ( module_image ) {
+
+ if ( mbinfo->mods_count >= limit ) {
+ DBGC ( image, "MULTIBOOT %p limit of %d modules "
+ "reached\n", image, limit );
+ break;
+ }
+
+ /* Do not include kernel image itself as a module */
+ if ( module_image == image )
+ continue;
+
+ /* Page-align the module */
+ start = ( ( start + 0xfff ) & ~0xfff );
+
+ /* Prepare segment */
+ if ( ( rc = prep_segment ( phys_to_user ( start ),
+ module_image->len,
+ module_image->len ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p could not prepare module "
+ "%s: %s\n", image, module_image->name,
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy module */
+ memcpy_user ( phys_to_user ( start ), 0,
+ module_image->data, 0, module_image->len );
+
+ /* Add module to list */
+ module = &modules[mbinfo->mods_count++];
+ module->mod_start = start;
+ module->mod_end = ( start + module_image->len );
+ module->string = multiboot_add_cmdline ( module_image );
+ module->reserved = 0;
+ DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n",
+ image, module_image->name, module->mod_start,
+ module->mod_end );
+ start += module_image->len;
+ }
+
+ return 0;
+}
+
+/**
+ * The multiboot information structure
+ *
+ * Kept in base memory because some OSes won't find it elsewhere,
+ * along with the other structures belonging to the Multiboot
+ * information table.
+ */
+static struct multiboot_info __bss16 ( mbinfo );
+#define mbinfo __use_data16 ( mbinfo )
+
+/** The multiboot bootloader name */
+static char __bss16_array ( mb_bootloader_name, [32] );
+#define mb_bootloader_name __use_data16 ( mb_bootloader_name )
+
+/** The multiboot memory map */
+static struct multiboot_memory_map
+ __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
+#define mbmemmap __use_data16 ( mbmemmap )
+
+/** The multiboot module list */
+static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
+#define mbmodules __use_data16 ( mbmodules )
+
+/**
+ * Find multiboot header
+ *
+ * @v image Multiboot file
+ * @v hdr Multiboot header descriptor to fill in
+ * @ret rc Return status code
+ */
+static int multiboot_find_header ( struct image *image,
+ struct multiboot_header_info *hdr ) {
+ uint32_t buf[64];
+ size_t offset;
+ unsigned int buf_idx;
+ uint32_t checksum;
+
+ /* Scan through first 8kB of image file 256 bytes at a time.
+ * (Use the buffering to avoid the overhead of a
+ * copy_from_user() for every dword.)
+ */
+ for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
+ /* Check for end of image */
+ if ( offset > image->len )
+ break;
+ /* Refill buffer if applicable */
+ buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
+ if ( buf_idx == 0 ) {
+ copy_from_user ( buf, image->data, offset,
+ sizeof ( buf ) );
+ }
+ /* Check signature */
+ if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
+ continue;
+ /* Copy header and verify checksum */
+ copy_from_user ( &hdr->mb, image->data, offset,
+ sizeof ( hdr->mb ) );
+ checksum = ( hdr->mb.magic + hdr->mb.flags +
+ hdr->mb.checksum );
+ if ( checksum != 0 )
+ continue;
+ /* Record offset of multiboot header and return */
+ hdr->offset = offset;
+ return 0;
+ }
+
+ /* No multiboot header found */
+ return -ENOEXEC;
+}
+
+/**
+ * Load raw multiboot image into memory
+ *
+ * @v image Multiboot file
+ * @v hdr Multiboot header descriptor
+ * @ret entry Entry point
+ * @ret max Maximum used address
+ * @ret rc Return status code
+ */
+static int multiboot_load_raw ( struct image *image,
+ struct multiboot_header_info *hdr,
+ physaddr_t *entry, physaddr_t *max ) {
+ size_t offset;
+ size_t filesz;
+ size_t memsz;
+ userptr_t buffer;
+ int rc;
+
+ /* Sanity check */
+ if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
+ DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
+ image );
+ return -EINVAL;
+ }
+
+ /* Verify and prepare segment */
+ offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
+ filesz = ( hdr->mb.load_end_addr ?
+ ( hdr->mb.load_end_addr - hdr->mb.load_addr ) :
+ ( image->len - offset ) );
+ memsz = ( hdr->mb.bss_end_addr ?
+ ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz );
+ buffer = phys_to_user ( hdr->mb.load_addr );
+ if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, offset, filesz );
+
+ /* Record execution entry point and maximum used address */
+ *entry = hdr->mb.entry_addr;
+ *max = ( hdr->mb.load_addr + memsz );
+
+ return 0;
+}
+
+/**
+ * Load ELF multiboot image into memory
+ *
+ * @v image Multiboot file
+ * @ret entry Entry point
+ * @ret max Maximum used address
+ * @ret rc Return status code
+ */
+static int multiboot_load_elf ( struct image *image, physaddr_t *entry,
+ physaddr_t *max ) {
+ int rc;
+
+ /* Load ELF image*/
+ if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute multiboot image
+ *
+ * @v image Multiboot image
+ * @ret rc Return status code
+ */
+static int multiboot_exec ( struct image *image ) {
+ struct multiboot_header_info hdr;
+ physaddr_t entry;
+ physaddr_t max;
+ int rc;
+
+ /* Locate multiboot header, if present */
+ if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
+ image );
+ return rc;
+ }
+
+ /* Abort if we detect flags that we cannot support */
+ if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
+ DBGC ( image, "MULTIBOOT %p flags %08x not supported\n",
+ image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
+ return -ENOTSUP;
+ }
+
+ /* There is technically a bit MB_FLAG_RAW to indicate whether
+ * this is an ELF or a raw image. In practice, grub will use
+ * the ELF header if present, and Solaris relies on this
+ * behaviour.
+ */
+ if ( ( ( rc = multiboot_load_elf ( image, &entry, &max ) ) != 0 ) &&
+ ( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 ))
+ return rc;
+
+ /* Populate multiboot information structure */
+ memset ( &mbinfo, 0, sizeof ( mbinfo ) );
+ mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
+ MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
+ mb_cmdline_offset = 0;
+ mbinfo.cmdline = multiboot_add_cmdline ( image );
+ mbinfo.mods_addr = virt_to_phys ( mbmodules );
+ mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
+ snprintf ( mb_bootloader_name, sizeof ( mb_bootloader_name ),
+ "iPXE %s", product_version );
+ mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
+ if ( ( rc = multiboot_add_modules ( image, max, &mbinfo, mbmodules,
+ ( sizeof ( mbmodules ) /
+ sizeof ( mbmodules[0] ) ) ) ) !=0)
+ return rc;
+
+ /* Multiboot images may not return and have no callback
+ * interface, so shut everything down prior to booting the OS.
+ */
+ shutdown_boot();
+
+ /* Build memory map after unhiding bootloader memory regions as part of
+ * shutting everything down.
+ */
+ multiboot_build_memmap ( image, &mbinfo, mbmemmap,
+ ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
+
+ /* Jump to OS with flat physical addressing */
+ DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
+ image, entry );
+ __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t"
+ "call *%%edi\n\t"
+ "popl %%ebp\n\t" )
+ : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
+ "b" ( virt_to_phys ( &mbinfo ) ),
+ "D" ( entry )
+ : "ecx", "edx", "esi", "memory" );
+
+ DBGC ( image, "MULTIBOOT %p returned\n", image );
+
+ /* It isn't safe to continue after calling shutdown() */
+ while ( 1 ) {}
+
+ return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
+}
+
+/**
+ * Probe multiboot image
+ *
+ * @v image Multiboot file
+ * @ret rc Return status code
+ */
+static int multiboot_probe ( struct image *image ) {
+ struct multiboot_header_info hdr;
+ int rc;
+
+ /* Locate multiboot header, if present */
+ if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
+ image );
+ return rc;
+ }
+ DBGC ( image, "MULTIBOOT %p found header with flags %08x\n",
+ image, hdr.mb.flags );
+
+ return 0;
+}
+
+/** Multiboot image type */
+struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = {
+ .name = "Multiboot",
+ .probe = multiboot_probe,
+ .exec = multiboot_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/nbi.c b/roms/ipxe/src/arch/i386/image/nbi.c
new file mode 100644
index 000000000..99046144d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/nbi.c
@@ -0,0 +1,427 @@
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <memsizes.h>
+#include <basemem_packet.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/segment.h>
+#include <ipxe/init.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/fakedhcp.h>
+#include <ipxe/image.h>
+#include <ipxe/features.h>
+#include <ipxe/version.h>
+
+/** @file
+ *
+ * NBI image format.
+ *
+ * The Net Boot Image format is defined by the "Draft Net Boot Image
+ * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now
+ * considered to be a legacy format, but it still included because a
+ * large amount of software (e.g. nymph, LTSP) makes use of NBI files.
+ *
+ * Etherboot does not implement the INT 78 callback interface
+ * described by the NBI specification. For a callback interface on
+ * x86 architecture, use PXE.
+ *
+ */
+
+FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 );
+
+/**
+ * An NBI image header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
+struct imgheader {
+ unsigned long magic; /**< Magic number (NBI_MAGIC) */
+ union {
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned long flags; /**< Image flags */
+ };
+ segoff_t location; /**< 16-bit seg:off header location */
+ union {
+ segoff_t segoff; /**< 16-bit seg:off entry point */
+ unsigned long linear; /**< 32-bit entry point */
+ } execaddr;
+} __attribute__ (( packed ));
+
+/** NBI magic number */
+#define NBI_MAGIC 0x1B031336UL
+
+/* Interpretation of the "length" fields */
+#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 )
+#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 )
+#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) )
+
+/* Interpretation of the "flags" fields */
+#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) )
+#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) )
+
+/** NBI header length */
+#define NBI_HEADER_LENGTH 512
+
+/**
+ * An NBI segment header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
+struct segheader {
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned char vendortag; /**< Vendor-defined private tag */
+ unsigned char reserved;
+ unsigned char flags; /**< Segment flags */
+ unsigned long loadaddr; /**< Load address */
+ unsigned long imglength; /**< Segment length in NBI file */
+ unsigned long memlength; /**< Segment length in memory */
+};
+
+/* Interpretation of the "flags" fields */
+#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 )
+#define NBI_LOADADDR_ABS 0x00
+#define NBI_LOADADDR_AFTER 0x01
+#define NBI_LOADADDR_END 0x02
+#define NBI_LOADADDR_BEFORE 0x03
+#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) )
+
+/* Define a type for passing info to a loaded program */
+struct ebinfo {
+ uint8_t major, minor; /* Version */
+ uint16_t flags; /* Bit flags */
+};
+
+/**
+ * Prepare a segment for an NBI image
+ *
+ * @v image NBI image
+ * @v offset Offset within NBI image
+ * @v filesz Length of initialised-data portion of the segment
+ * @v memsz Total length of the segment
+ * @v src Source for initialised data
+ * @ret rc Return status code
+ */
+static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
+ userptr_t dest, size_t filesz, size_t memsz ){
+ int rc;
+
+ if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "NBI %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Load a segment for an NBI image
+ *
+ * @v image NBI image
+ * @v offset Offset within NBI image
+ * @v filesz Length of initialised-data portion of the segment
+ * @v memsz Total length of the segment
+ * @v src Source for initialised data
+ * @ret rc Return status code
+ */
+static int nbi_load_segment ( struct image *image, size_t offset,
+ userptr_t dest, size_t filesz,
+ size_t memsz __unused ) {
+ memcpy_user ( dest, 0, image->data, offset, filesz );
+ return 0;
+}
+
+/**
+ * Process segments of an NBI image
+ *
+ * @v image NBI image
+ * @v imgheader Image header information
+ * @v process Function to call for each segment
+ * @ret rc Return status code
+ */
+static int nbi_process_segments ( struct image *image,
+ struct imgheader *imgheader,
+ int ( * process ) ( struct image *image,
+ size_t offset,
+ userptr_t dest,
+ size_t filesz,
+ size_t memsz ) ) {
+ struct segheader sh;
+ size_t offset = 0;
+ size_t sh_off;
+ userptr_t dest;
+ size_t filesz;
+ size_t memsz;
+ int rc;
+
+ /* Copy image header to target location */
+ dest = real_to_user ( imgheader->location.segment,
+ imgheader->location.offset );
+ filesz = memsz = NBI_HEADER_LENGTH;
+ if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 )
+ return rc;
+ offset += filesz;
+
+ /* Process segments in turn */
+ sh_off = NBI_LENGTH ( imgheader->length );
+ do {
+ /* Read segment header */
+ copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) );
+ if ( sh.length == 0 ) {
+ /* Avoid infinite loop? */
+ DBGC ( image, "NBI %p invalid segheader length 0\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Calculate segment load address */
+ switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) {
+ case NBI_LOADADDR_ABS:
+ dest = phys_to_user ( sh.loadaddr );
+ break;
+ case NBI_LOADADDR_AFTER:
+ dest = userptr_add ( dest, memsz + sh.loadaddr );
+ break;
+ case NBI_LOADADDR_BEFORE:
+ dest = userptr_add ( dest, -sh.loadaddr );
+ break;
+ case NBI_LOADADDR_END:
+ /* Not correct according to the spec, but
+ * maintains backwards compatibility with
+ * previous versions of Etherboot.
+ */
+ dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024
+ - sh.loadaddr );
+ break;
+ default:
+ /* Cannot be reached */
+ assert ( 0 );
+ }
+
+ /* Process this segment */
+ filesz = sh.imglength;
+ memsz = sh.memlength;
+ if ( ( offset + filesz ) > image->len ) {
+ DBGC ( image, "NBI %p segment outside file\n", image );
+ return -ENOEXEC;
+ }
+ if ( ( rc = process ( image, offset, dest,
+ filesz, memsz ) ) != 0 ) {
+ return rc;
+ }
+ offset += filesz;
+
+ /* Next segheader */
+ sh_off += NBI_LENGTH ( sh.length );
+ if ( sh_off >= NBI_HEADER_LENGTH ) {
+ DBGC ( image, "NBI %p header overflow\n", image );
+ return -ENOEXEC;
+ }
+
+ } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) );
+
+ if ( offset != image->len ) {
+ DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n",
+ image, image->len, offset );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Boot a 16-bit NBI image
+ *
+ * @v imgheader Image header information
+ * @ret rc Return status code, if image returns
+ */
+static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
+ int discard_D, discard_S, discard_b;
+ int rc;
+
+ DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image,
+ imgheader->execaddr.segoff.segment,
+ imgheader->execaddr.segoff.offset );
+
+ __asm__ __volatile__ (
+ REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "pushw %%ds\n\t" /* far pointer to bootp data */
+ "pushw %%bx\n\t"
+ "pushl %%esi\n\t" /* location */
+ "pushw %%cs\n\t" /* lcall execaddr */
+ "call 1f\n\t"
+ "jmp 2f\n\t"
+ "\n1:\n\t"
+ "pushl %%edi\n\t"
+ "lret\n\t"
+ "\n2:\n\t"
+ "addw $8,%%sp\n\t" /* clean up stack */
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
+ "=b" ( discard_b )
+ : "D" ( imgheader->execaddr.segoff ),
+ "S" ( imgheader->location ),
+ "b" ( __from_data16 ( basemem_packet ) )
+ : "ecx", "edx" );
+
+ return rc;
+}
+
+/**
+ * Boot a 32-bit NBI image
+ *
+ * @v imgheader Image header information
+ * @ret rc Return status code, if image returns
+ */
+static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
+ struct ebinfo loaderinfo = {
+ product_major_version, product_minor_version,
+ 0
+ };
+ int discard_D, discard_S, discard_b;
+ int rc;
+
+ DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
+ image, imgheader->execaddr.linear );
+
+ /* Jump to OS with flat physical addressing */
+ __asm__ __volatile__ (
+ PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "pushl %%ebx\n\t" /* bootp data */
+ "pushl %%esi\n\t" /* imgheader */
+ "pushl %%eax\n\t" /* loaderinfo */
+ "call *%%edi\n\t"
+ "addl $12, %%esp\n\t" /* clean up stack */
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
+ "=b" ( discard_b )
+ : "D" ( imgheader->execaddr.linear ),
+ "S" ( ( imgheader->location.segment << 4 ) +
+ imgheader->location.offset ),
+ "b" ( virt_to_phys ( basemem_packet ) ),
+ "a" ( virt_to_phys ( &loaderinfo ) )
+ : "ecx", "edx", "memory" );
+
+ return rc;
+}
+
+/**
+ * Prepare DHCP parameter block for NBI image
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_prepare_dhcp ( struct image *image ) {
+ struct net_device *boot_netdev;
+ int rc;
+
+ boot_netdev = last_opened_netdev();
+ if ( ! boot_netdev ) {
+ DBGC ( image, "NBI %p could not identify a network device\n",
+ image );
+ return -ENODEV;
+ }
+
+ if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet,
+ sizeof ( basemem_packet ) ) ) != 0 ) {
+ DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute a loaded NBI image
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_exec ( struct image *image ) {
+ struct imgheader imgheader;
+ int may_return;
+ int rc;
+
+ /* Retrieve image header */
+ copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
+
+ DBGC ( image, "NBI %p placing header at %hx:%hx\n", image,
+ imgheader.location.segment, imgheader.location.offset );
+
+ /* NBI files can have overlaps between segments; the bss of
+ * one segment may overlap the initialised data of another. I
+ * assume this is a design flaw, but there are images out
+ * there that we need to work with. We therefore do two
+ * passes: first to initialise the segments, then to copy the
+ * data. This avoids zeroing out already-copied data.
+ */
+ if ( ( rc = nbi_process_segments ( image, &imgheader,
+ nbi_prepare_segment ) ) != 0 )
+ return rc;
+ if ( ( rc = nbi_process_segments ( image, &imgheader,
+ nbi_load_segment ) ) != 0 )
+ return rc;
+
+ /* Prepare DHCP option block */
+ if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 )
+ return rc;
+
+ /* Shut down now if NBI image will not return */
+ may_return = NBI_PROGRAM_RETURNS ( imgheader.flags );
+ if ( ! may_return )
+ shutdown_boot();
+
+ /* Execute NBI image */
+ if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) {
+ rc = nbi_boot32 ( image, &imgheader );
+ } else {
+ rc = nbi_boot16 ( image, &imgheader );
+ }
+
+ if ( ! may_return ) {
+ /* Cannot continue after shutdown() called */
+ DBGC ( image, "NBI %p returned %d from non-returnable image\n",
+ image, rc );
+ while ( 1 ) {}
+ }
+
+ DBGC ( image, "NBI %p returned %d\n", image, rc );
+
+ return rc;
+}
+
+/**
+ * Probe NBI image
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_probe ( struct image *image ) {
+ struct imgheader imgheader;
+
+ /* If we don't have enough data give up */
+ if ( image->len < NBI_HEADER_LENGTH ) {
+ DBGC ( image, "NBI %p too short for an NBI image\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Check image header */
+ copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
+ if ( imgheader.magic != NBI_MAGIC ) {
+ DBGC ( image, "NBI %p has no NBI signature\n", image );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/** NBI image type */
+struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "NBI",
+ .probe = nbi_probe,
+ .exec = nbi_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/image/pxe_image.c b/roms/ipxe/src/arch/i386/image/pxe_image.c
new file mode 100644
index 000000000..5b0f6eb89
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/pxe_image.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * PXE image format
+ *
+ */
+
+#include <pxe.h>
+#include <pxe_call.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+#include <ipxe/segment.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/features.h>
+#include <ipxe/console.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/IndustryStandard/PeImage.h>
+
+FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 );
+
+/** PXE command line */
+const char *pxe_cmdline;
+
+/**
+ * Execute PXE image
+ *
+ * @v image PXE image
+ * @ret rc Return status code
+ */
+static int pxe_exec ( struct image *image ) {
+ userptr_t buffer = real_to_user ( 0, 0x7c00 );
+ struct net_device *netdev;
+ int rc;
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, image->len, image->len ) ) != 0 ) {
+ DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, 0, image->len );
+
+ /* Arbitrarily pick the most recently opened network device */
+ if ( ( netdev = last_opened_netdev() ) == NULL ) {
+ DBGC ( image, "IMAGE %p could not locate PXE net device\n",
+ image );
+ return -ENODEV;
+ }
+ netdev_get ( netdev );
+
+ /* Activate PXE */
+ pxe_activate ( netdev );
+
+ /* Set PXE command line */
+ pxe_cmdline = image->cmdline;
+
+ /* Reset console since PXE NBP will probably use it */
+ console_reset();
+
+ /* Start PXE NBP */
+ rc = pxe_start_nbp();
+
+ /* Clear PXE command line */
+ pxe_cmdline = NULL;
+
+ /* Deactivate PXE */
+ pxe_deactivate();
+
+ /* Try to reopen network device. Ignore errors, since the NBP
+ * may have called PXENV_STOP_UNDI.
+ */
+ netdev_open ( netdev );
+ netdev_put ( netdev );
+
+ return rc;
+}
+
+/**
+ * Probe PXE image
+ *
+ * @v image PXE file
+ * @ret rc Return status code
+ */
+int pxe_probe ( struct image *image ) {
+
+ /* Images too large to fit in base memory cannot be PXE
+ * images. We include this check to help prevent unrecognised
+ * images from being marked as PXE images, since PXE images
+ * have no signature we can check against.
+ */
+ if ( image->len > ( 0xa0000 - 0x7c00 ) )
+ return -ENOEXEC;
+
+ /* Rejecting zero-length images is also useful, since these
+ * end up looking to the user like bugs in iPXE.
+ */
+ if ( ! image->len )
+ return -ENOEXEC;
+
+ return 0;
+}
+
+/**
+ * Probe PXE image (with rejection of potential EFI images)
+ *
+ * @v image PXE file
+ * @ret rc Return status code
+ */
+int pxe_probe_no_mz ( struct image *image ) {
+ uint16_t magic;
+ int rc;
+
+ /* Probe PXE image */
+ if ( ( rc = pxe_probe ( image ) ) != 0 )
+ return rc;
+
+ /* Reject image with an "MZ" signature which may indicate an
+ * EFI image incorrectly handed out to a BIOS system.
+ */
+ if ( image->len >= sizeof ( magic ) ) {
+ copy_from_user ( &magic, image->data, 0, sizeof ( magic ) );
+ if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) {
+ DBGC ( image, "IMAGE %p may be an EFI image\n",
+ image );
+ return -ENOTTY;
+ }
+ }
+
+ return 0;
+}
+
+/** PXE image type */
+struct image_type pxe_image_type[] __image_type ( PROBE_PXE ) = {
+ {
+ .name = "PXE-NBP",
+ .probe = pxe_probe_no_mz,
+ .exec = pxe_exec,
+ },
+ {
+ .name = "PXE-NBP (may be EFI?)",
+ .probe = pxe_probe,
+ .exec = pxe_exec,
+ },
+};
diff --git a/roms/ipxe/src/arch/i386/image/sdi.c b/roms/ipxe/src/arch/i386/image/sdi.c
new file mode 100644
index 000000000..fa2d0b73f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/image/sdi.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 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 (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <realmode.h>
+#include <sdi.h>
+#include <ipxe/image.h>
+#include <ipxe/features.h>
+
+/** @file
+ *
+ * System Deployment Image (SDI)
+ *
+ * Based on the MSDN article "RAM boot using SDI in Windows XP
+ * Embedded with Service Pack 1", available at the time of writing
+ * from:
+ *
+ * http://msdn.microsoft.com/en-us/library/ms838543.aspx
+ */
+
+FEATURE ( FEATURE_IMAGE, "SDI", DHCP_EB_FEATURE_SDI, 1 );
+
+/**
+ * Parse SDI image header
+ *
+ * @v image SDI file
+ * @v sdi SDI header to fill in
+ * @ret rc Return status code
+ */
+static int sdi_parse_header ( struct image *image, struct sdi_header *sdi ) {
+
+ /* Sanity check */
+ if ( image->len < sizeof ( *sdi ) ) {
+ DBGC ( image, "SDI %p too short for SDI header\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Read in header */
+ copy_from_user ( sdi, image->data, 0, sizeof ( *sdi ) );
+
+ /* Check signature */
+ if ( sdi->magic != SDI_MAGIC ) {
+ DBGC ( image, "SDI %p is not an SDI image\n", image );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute SDI image
+ *
+ * @v image SDI file
+ * @ret rc Return status code
+ */
+static int sdi_exec ( struct image *image ) {
+ struct sdi_header sdi;
+ uint32_t sdiptr;
+ int rc;
+
+ /* Parse image header */
+ if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
+ return rc;
+
+ /* Check that image is bootable */
+ if ( sdi.boot_size == 0 ) {
+ DBGC ( image, "SDI %p is not bootable\n", image );
+ return -ENOTTY;
+ }
+ DBGC ( image, "SDI %p image at %08lx+%08zx\n",
+ image, user_to_phys ( image->data, 0 ), image->len );
+ DBGC ( image, "SDI %p boot code at %08lx+%llx\n", image,
+ user_to_phys ( image->data, sdi.boot_offset ), sdi.boot_size );
+
+ /* Copy boot code */
+ memcpy_user ( real_to_user ( SDI_BOOT_SEG, SDI_BOOT_OFF ), 0,
+ image->data, sdi.boot_offset, sdi.boot_size );
+
+ /* Jump to boot code */
+ sdiptr = ( user_to_phys ( image->data, 0 ) | SDI_WTF );
+ __asm__ __volatile__ ( REAL_CODE ( "ljmp %0, %1\n\t" )
+ : : "i" ( SDI_BOOT_SEG ),
+ "i" ( SDI_BOOT_OFF ),
+ "d" ( sdiptr ) );
+
+ /* There is no way for the image to return, since we provide
+ * no return address.
+ */
+ assert ( 0 );
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+}
+
+/**
+ * Probe SDI image
+ *
+ * @v image SDI file
+ * @ret rc Return status code
+ */
+static int sdi_probe ( struct image *image ) {
+ struct sdi_header sdi;
+ int rc;
+
+ /* Parse image */
+ if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/** SDI image type */
+struct image_type sdi_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "SDI",
+ .probe = sdi_probe,
+ .exec = sdi_exec,
+};
diff --git a/roms/ipxe/src/arch/i386/include/basemem.h b/roms/ipxe/src/arch/i386/include/basemem.h
new file mode 100644
index 000000000..01c2ea917
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/basemem.h
@@ -0,0 +1,35 @@
+#ifndef _BASEMEM_H
+#define _BASEMEM_H
+
+/** @file
+ *
+ * Base memory allocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <realmode.h>
+#include <bios.h>
+
+/**
+ * Read the BIOS free base memory counter
+ *
+ * @ret fbms Free base memory counter (in kB)
+ */
+static inline unsigned int get_fbms ( void ) {
+ uint16_t fbms;
+
+ get_real ( fbms, BDA_SEG, BDA_FBMS );
+ return fbms;
+}
+
+extern void set_fbms ( unsigned int new_fbms );
+
+/* Actually in hidemem.c, but putting it here avoids polluting the
+ * architecture-independent include/hidemem.h.
+ */
+extern void hide_basemem ( void );
+
+#endif /* _BASEMEM_H */
diff --git a/roms/ipxe/src/arch/i386/include/basemem_packet.h b/roms/ipxe/src/arch/i386/include/basemem_packet.h
new file mode 100644
index 000000000..def6dee31
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/basemem_packet.h
@@ -0,0 +1,15 @@
+#ifndef BASEMEM_PACKET_H
+#define BASEMEM_PACKET_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <realmode.h>
+
+/** Maximum length of base memory packet buffer */
+#define BASEMEM_PACKET_LEN 1514
+
+/** Base memory packet buffer */
+extern char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );
+#define basemem_packet __use_data16 ( basemem_packet )
+
+#endif /* BASEMEM_PACKET_H */
diff --git a/roms/ipxe/src/arch/i386/include/bios.h b/roms/ipxe/src/arch/i386/include/bios.h
new file mode 100644
index 000000000..988bbc62b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bios.h
@@ -0,0 +1,14 @@
+#ifndef BIOS_H
+#define BIOS_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#define BDA_SEG 0x0040
+#define BDA_EQUIPMENT_WORD 0x0010
+#define BDA_FBMS 0x0013
+#define BDA_REBOOT 0x0072
+#define BDA_REBOOT_WARM 0x1234
+#define BDA_NUM_DRIVES 0x0075
+#define BDA_CHAR_HEIGHT 0x0085
+
+#endif /* BIOS_H */
diff --git a/roms/ipxe/src/arch/i386/include/bios_disks.h b/roms/ipxe/src/arch/i386/include/bios_disks.h
new file mode 100644
index 000000000..0dd7c4ebb
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bios_disks.h
@@ -0,0 +1,69 @@
+#ifndef BIOS_DISKS_H
+#define BIOS_DISKS_H
+
+#include "dev.h"
+
+/*
+ * Constants
+ *
+ */
+
+#define BIOS_DISK_MAX_NAME_LEN 6
+
+struct bios_disk_sector {
+ char data[512];
+};
+
+/*
+ * The location of a BIOS disk
+ *
+ */
+struct bios_disk_loc {
+ uint8_t drive;
+};
+
+/*
+ * A physical BIOS disk device
+ *
+ */
+struct bios_disk_device {
+ char name[BIOS_DISK_MAX_NAME_LEN];
+ uint8_t drive;
+ uint8_t type;
+};
+
+/*
+ * A BIOS disk driver, with a valid device ID range and naming
+ * function.
+ *
+ */
+struct bios_disk_driver {
+ void ( *fill_drive_name ) ( char *buf, uint8_t drive );
+ uint8_t min_drive;
+ uint8_t max_drive;
+};
+
+/*
+ * Define a BIOS disk driver
+ *
+ */
+#define BIOS_DISK_DRIVER( _name, _fill_drive_name, _min_drive, _max_drive ) \
+ static struct bios_disk_driver _name = { \
+ .fill_drive_name = _fill_drive_name, \
+ .min_drive = _min_drive, \
+ .max_drive = _max_drive, \
+ }
+
+/*
+ * Functions in bios_disks.c
+ *
+ */
+
+
+/*
+ * bios_disk bus global definition
+ *
+ */
+extern struct bus_driver bios_disk_driver;
+
+#endif /* BIOS_DISKS_H */
diff --git a/roms/ipxe/src/arch/i386/include/biosint.h b/roms/ipxe/src/arch/i386/include/biosint.h
new file mode 100644
index 000000000..67d6a3811
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/biosint.h
@@ -0,0 +1,33 @@
+#ifndef BIOSINT_H
+#define BIOSINT_H
+
+/**
+ * @file BIOS interrupts
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <realmode.h>
+
+struct segoff;
+
+/**
+ * Hooked interrupt count
+ *
+ * At exit, after unhooking all possible interrupts, this counter
+ * should be examined. If it is non-zero, it means that we failed to
+ * unhook at least one interrupt vector, and so must not free up the
+ * memory we are using. (Note that this also implies that we should
+ * re-hook INT 15 in order to hide ourselves from the memory map).
+ */
+extern uint16_t __text16 ( hooked_bios_interrupts );
+#define hooked_bios_interrupts __use_text16 ( hooked_bios_interrupts )
+
+extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector );
+extern int unhook_bios_interrupt ( unsigned int interrupt,
+ unsigned int handler,
+ struct segoff *chain_vector );
+
+#endif /* BIOSINT_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/compiler.h b/roms/ipxe/src/arch/i386/include/bits/compiler.h
index 7c4a09396..87201135f 100644
--- a/roms/ipxe/src/arch/i386/include/bits/compiler.h
+++ b/roms/ipxe/src/arch/i386/include/bits/compiler.h
@@ -9,7 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
/** Declare a function with standard calling conventions */
-#define __asmcall __attribute__ (( used, cdecl, regparm(0) ))
+#define __asmcall __attribute__ (( cdecl, regparm(0) ))
/**
* Declare a function with libgcc implicit linkage
diff --git a/roms/ipxe/src/arch/i386/include/bits/entropy.h b/roms/ipxe/src/arch/i386/include/bits/entropy.h
new file mode 100644
index 000000000..bfeb5e3b5
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/entropy.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_ENTROPY_H
+#define _BITS_ENTROPY_H
+
+/** @file
+ *
+ * i386-specific entropy API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/rtc_entropy.h>
+
+#endif /* _BITS_ENTROPY_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/hyperv.h b/roms/ipxe/src/arch/i386/include/bits/hyperv.h
index 0ba58afb7..3565c8a83 100644
--- a/roms/ipxe/src/arch/i386/include/bits/hyperv.h
+++ b/roms/ipxe/src/arch/i386/include/bits/hyperv.h
@@ -46,4 +46,27 @@ hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in,
return result;
}
+/**
+ * Set bit atomically
+ *
+ * @v bits Bit field
+ * @v bit Bit to set
+ */
+static inline __attribute__ (( always_inline )) void
+hv_set_bit ( void *bits, unsigned int bit ) {
+ struct {
+ uint32_t dword[ ( bit / 32 ) + 1 ];
+ } *dwords = bits;
+
+ /* Set bit using "lock bts". Inform compiler that any memory
+ * from the start of the bit field up to and including the
+ * dword containing this bit may be modified. (This is
+ * overkill but shouldn't matter in practice since we're
+ * unlikely to subsequently read other bits from the same bit
+ * field.)
+ */
+ __asm__ __volatile__ ( "lock bts %1, %0"
+ : "+m" ( *dwords ) : "Ir" ( bit ) );
+}
+
#endif /* _BITS_HYPERV_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/nap.h b/roms/ipxe/src/arch/i386/include/bits/nap.h
new file mode 100644
index 000000000..e8bcfd13b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/nap.h
@@ -0,0 +1,15 @@
+#ifndef _BITS_NAP_H
+#define _BITS_NAP_H
+
+/** @file
+ *
+ * i386-specific CPU sleeping API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/bios_nap.h>
+#include <ipxe/efi/efix86_nap.h>
+
+#endif /* _BITS_MAP_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/reboot.h b/roms/ipxe/src/arch/i386/include/bits/reboot.h
new file mode 100644
index 000000000..803dacfe4
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/reboot.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_REBOOT_H
+#define _BITS_REBOOT_H
+
+/** @file
+ *
+ * i386-specific reboot API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/bios_reboot.h>
+
+#endif /* _BITS_REBOOT_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/sanboot.h b/roms/ipxe/src/arch/i386/include/bits/sanboot.h
new file mode 100644
index 000000000..f02d2e649
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/sanboot.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_SANBOOT_H
+#define _BITS_SANBOOT_H
+
+/** @file
+ *
+ * i386-specific sanboot API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/bios_sanboot.h>
+
+#endif /* _BITS_SANBOOT_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/smbios.h b/roms/ipxe/src/arch/i386/include/bits/smbios.h
new file mode 100644
index 000000000..2ab31e74b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/smbios.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_SMBIOS_H
+#define _BITS_SMBIOS_H
+
+/** @file
+ *
+ * i386-specific SMBIOS API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/bios_smbios.h>
+
+#endif /* _BITS_SMBIOS_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/time.h b/roms/ipxe/src/arch/i386/include/bits/time.h
new file mode 100644
index 000000000..6a5d63d32
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/time.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_TIME_H
+#define _BITS_TIME_H
+
+/** @file
+ *
+ * i386-specific time API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/rtc_time.h>
+
+#endif /* _BITS_TIME_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/timer.h b/roms/ipxe/src/arch/i386/include/bits/timer.h
new file mode 100644
index 000000000..f7d86d78c
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/timer.h
@@ -0,0 +1,15 @@
+#ifndef _BITS_TIMER_H
+#define _BITS_TIMER_H
+
+/** @file
+ *
+ * i386-specific timer API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/bios_timer.h>
+#include <ipxe/rdtsc_timer.h>
+
+#endif /* _BITS_TIMER_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/uaccess.h b/roms/ipxe/src/arch/i386/include/bits/uaccess.h
new file mode 100644
index 000000000..aac09ba95
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/uaccess.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_UACCESS_H
+#define _BITS_UACCESS_H
+
+/** @file
+ *
+ * i386-specific user access API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <librm.h>
+
+#endif /* _BITS_UACCESS_H */
diff --git a/roms/ipxe/src/arch/i386/include/bits/umalloc.h b/roms/ipxe/src/arch/i386/include/bits/umalloc.h
new file mode 100644
index 000000000..113f16fd1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bits/umalloc.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_UMALLOC_H
+#define _BITS_UMALLOC_H
+
+/** @file
+ *
+ * i386-specific user memory allocation API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/memtop_umalloc.h>
+
+#endif /* _BITS_UMALLOC_H */
diff --git a/roms/ipxe/src/arch/i386/include/bochs.h b/roms/ipxe/src/arch/i386/include/bochs.h
new file mode 100644
index 000000000..9d090fc12
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bochs.h
@@ -0,0 +1,34 @@
+#ifndef BOCHS_H
+#define BOCHS_H
+
+/** @file
+ *
+ * bochs breakpoints
+ *
+ * This file defines @c bochsbp, the magic breakpoint instruction that
+ * is incredibly useful when debugging under bochs. This file should
+ * never be included in production code.
+ *
+ * Use the pseudo-instruction @c bochsbp in assembly code, or the
+ * bochsbp() function in C code.
+ *
+ */
+
+#ifdef ASSEMBLY
+
+/* Breakpoint for when debugging under bochs */
+#define bochsbp xchgw %bx, %bx
+#define BOCHSBP bochsbp
+
+#else /* ASSEMBLY */
+
+/** Breakpoint for when debugging under bochs */
+static inline void bochsbp ( void ) {
+ __asm__ __volatile__ ( "xchgw %bx, %bx" );
+}
+
+#endif /* ASSEMBLY */
+
+#warning "bochs.h should not be included into production code"
+
+#endif /* BOCHS_H */
diff --git a/roms/ipxe/src/arch/i386/include/bootsector.h b/roms/ipxe/src/arch/i386/include/bootsector.h
new file mode 100644
index 000000000..c5d35aae3
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bootsector.h
@@ -0,0 +1,14 @@
+#ifndef _BOOTSECTOR_H
+#define _BOOTSECTOR_H
+
+/** @file
+ *
+ * x86 bootsector image format
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive );
+
+#endif /* _BOOTSECTOR_H */
diff --git a/roms/ipxe/src/arch/i386/include/bzimage.h b/roms/ipxe/src/arch/i386/include/bzimage.h
new file mode 100644
index 000000000..4933ce5b1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/bzimage.h
@@ -0,0 +1,142 @@
+#ifndef _BZIMAGE_H
+#define _BZIMAGE_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * A bzImage header
+ *
+ * As documented in Documentation/i386/boot.txt
+ */
+struct bzimage_header {
+ /** The size of the setup in sectors
+ *
+ * If this field contains 0, assume it contains 4.
+ */
+ uint8_t setup_sects;
+ /** If set, the root is mounted readonly */
+ uint16_t root_flags;
+ /** DO NOT USE - for bootsect.S use only */
+ uint16_t syssize;
+ /** DO NOT USE - obsolete */
+ uint16_t swap_dev;
+ /** DO NOT USE - for bootsect.S use only */
+ uint16_t ram_size;
+ /** Video mode control */
+ uint16_t vid_mode;
+ /** Default root device number */
+ uint16_t root_dev;
+ /** 0xAA55 magic number */
+ uint16_t boot_flag;
+ /** Jump instruction */
+ uint16_t jump;
+ /** Magic signature "HdrS" */
+ uint32_t header;
+ /** Boot protocol version supported */
+ uint16_t version;
+ /** Boot loader hook (see below) */
+ uint32_t realmode_swtch;
+ /** The load-low segment (0x1000) (obsolete) */
+ uint16_t start_sys;
+ /** Pointer to kernel version string */
+ uint16_t kernel_version;
+ /** Boot loader identifier */
+ uint8_t type_of_loader;
+ /** Boot protocol option flags */
+ uint8_t loadflags;
+ /** Move to high memory size (used with hooks) */
+ uint16_t setup_move_size;
+ /** Boot loader hook (see below) */
+ uint32_t code32_start;
+ /** initrd load address (set by boot loader) */
+ uint32_t ramdisk_image;
+ /** initrd size (set by boot loader) */
+ uint32_t ramdisk_size;
+ /** DO NOT USE - for bootsect.S use only */
+ uint32_t bootsect_kludge;
+ /** Free memory after setup end */
+ uint16_t heap_end_ptr;
+ /** Unused */
+ uint16_t pad1;
+ /** 32-bit pointer to the kernel command line */
+ uint32_t cmd_line_ptr;
+ /** Highest legal initrd address */
+ uint32_t initrd_addr_max;
+ /** Physical addr alignment required for kernel */
+ uint32_t kernel_alignment;
+ /** Whether kernel is relocatable or not */
+ uint8_t relocatable_kernel;
+ /** Unused */
+ uint8_t pad2[3];
+ /** Maximum size of the kernel command line */
+ uint32_t cmdline_size;
+} __attribute__ (( packed ));
+
+/** Offset of bzImage header within kernel image */
+#define BZI_HDR_OFFSET 0x1f1
+
+/** bzImage boot flag value */
+#define BZI_BOOT_FLAG 0xaa55
+
+/** bzImage magic signature value */
+#define BZI_SIGNATURE 0x53726448
+
+/** bzImage boot loader identifier for Etherboot */
+#define BZI_LOADER_TYPE_ETHERBOOT 0x40
+
+/** bzImage boot loader identifier for iPXE
+ *
+ * We advertise ourselves as Etherboot version 6.
+ */
+#define BZI_LOADER_TYPE_IPXE ( BZI_LOADER_TYPE_ETHERBOOT | 0x06 )
+
+/** bzImage "load high" flag */
+#define BZI_LOAD_HIGH 0x01
+
+/** Load address for high-loaded kernels */
+#define BZI_LOAD_HIGH_ADDR 0x100000
+
+/** Load address for low-loaded kernels */
+#define BZI_LOAD_LOW_ADDR 0x10000
+
+/** bzImage "kernel can use heap" flag */
+#define BZI_CAN_USE_HEAP 0x80
+
+/** bzImage special video mode "normal" */
+#define BZI_VID_MODE_NORMAL 0xffff
+
+/** bzImage special video mode "ext" */
+#define BZI_VID_MODE_EXT 0xfffe
+
+/** bzImage special video mode "ask" */
+#define BZI_VID_MODE_ASK 0xfffd
+
+/** bzImage maximum initrd address for versions < 2.03 */
+#define BZI_INITRD_MAX 0x37ffffff
+
+/** bzImage command-line structure used by older kernels */
+struct bzimage_cmdline {
+ /** Magic signature */
+ uint16_t magic;
+ /** Offset to command line */
+ uint16_t offset;
+} __attribute__ (( packed ));
+
+/** Offset of bzImage command-line structure within kernel image */
+#define BZI_CMDLINE_OFFSET 0x20
+
+/** bzImage command line present magic marker value */
+#define BZI_CMDLINE_MAGIC 0xa33f
+
+/** Assumed size of real-mode portion (including .bss) */
+#define BZI_ASSUMED_RM_SIZE 0x8000
+
+/** Amount of stack space to provide */
+#define BZI_STACK_SIZE 0x1000
+
+/** Maximum size of command line */
+#define BZI_CMDLINE_SIZE 0x7ff
+
+#endif /* _BZIMAGE_H */
diff --git a/roms/ipxe/src/arch/i386/include/comboot.h b/roms/ipxe/src/arch/i386/include/comboot.h
new file mode 100644
index 000000000..2d2f04fe1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/comboot.h
@@ -0,0 +1,132 @@
+#ifndef COMBOOT_H
+#define COMBOOT_H
+
+/**
+ * @file
+ *
+ * SYSLINUX COMBOOT
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <setjmp.h>
+#include <ipxe/in.h>
+
+/** Segment used for COMBOOT PSP and image */
+#define COMBOOT_PSP_SEG 0x07C0
+
+/** Entry point address of COM32 images */
+#define COM32_START_PHYS 0x101000
+
+/** COM32 bounce buffer segment */
+#define COM32_BOUNCE_SEG 0x07C0
+
+/** Size of SYSLINUX file block in bytes */
+#define COMBOOT_FILE_BLOCKSZ 512
+
+/** COMBOOT feature flags (INT 22h AX=15h) */
+#define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0)
+#define COMBOOT_FEATURE_IDLE_LOOP (1 << 1)
+
+/** Maximum number of shuffle descriptors for
+ * shuffle and boot functions
+ * (INT 22h AX=0012h, 001Ah, 001Bh)
+ */
+#define COMBOOT_MAX_SHUFFLE_DESCRIPTORS 682
+
+typedef union {
+ uint32_t l;
+ uint16_t w[2];
+ uint8_t b[4];
+} com32_reg32_t;
+
+typedef struct {
+ uint16_t gs; /* Offset 0 */
+ uint16_t fs; /* Offset 2 */
+ uint16_t es; /* Offset 4 */
+ uint16_t ds; /* Offset 6 */
+
+ com32_reg32_t edi; /* Offset 8 */
+ com32_reg32_t esi; /* Offset 12 */
+ com32_reg32_t ebp; /* Offset 16 */
+ com32_reg32_t _unused_esp; /* Offset 20 */
+ com32_reg32_t ebx; /* Offset 24 */
+ com32_reg32_t edx; /* Offset 28 */
+ com32_reg32_t ecx; /* Offset 32 */
+ com32_reg32_t eax; /* Offset 36 */
+
+ com32_reg32_t eflags; /* Offset 40 */
+} com32sys_t;
+
+typedef struct {
+ uint32_t eax; /* Offset 0 */
+ uint32_t ecx; /* Offset 4 */
+ uint32_t edx; /* Offset 8 */
+ uint32_t ebx; /* Offset 12 */
+ uint32_t esp; /* Offset 16 */
+ uint32_t ebp; /* Offset 20 */
+ uint32_t esi; /* Offset 24 */
+ uint32_t edi; /* Offset 28 */
+
+ uint32_t eip; /* Offset 32 */
+} syslinux_pm_regs;
+
+typedef struct {
+ uint16_t es; /* Offset 0 */
+ uint16_t _unused_cs; /* Offset 2 */
+ uint16_t ds; /* Offset 4 */
+ uint16_t ss; /* Offset 6 */
+ uint16_t fs; /* Offset 8 */
+ uint16_t gs; /* Offset 10 */
+
+ uint32_t eax; /* Offset 12 */
+ uint32_t ecx; /* Offset 16 */
+ uint32_t edx; /* Offset 20 */
+ uint32_t ebx; /* Offset 24 */
+ uint32_t esp; /* Offset 28 */
+ uint32_t ebp; /* Offset 32 */
+ uint32_t esi; /* Offset 36 */
+ uint32_t edi; /* Offset 40 */
+
+ uint16_t ip; /* Offset 44 */
+ uint16_t cs; /* Offset 46 */
+} syslinux_rm_regs;
+
+typedef struct {
+ uint32_t dest;
+ uint32_t src;
+ uint32_t len;
+} comboot_shuffle_descriptor;
+
+extern void hook_comboot_interrupts ( );
+extern void unhook_comboot_interrupts ( );
+
+/* These are not the correct prototypes, but it doens't matter,
+ * as we only ever get the address of these functions;
+ * they are only called from COM32 code running in PHYS_CODE
+ */
+extern void com32_intcall_wrapper ( );
+extern void com32_farcall_wrapper ( );
+extern void com32_cfarcall_wrapper ( );
+
+/* Resolve a hostname to an (IPv4) address */
+extern int comboot_resolv ( const char *name, struct in_addr *address );
+
+/* setjmp/longjmp context buffer used to return after loading an image */
+extern rmjmp_buf comboot_return;
+
+extern void *com32_external_esp;
+
+#define COMBOOT_EXIT 1
+#define COMBOOT_EXIT_RUN_KERNEL 2
+#define COMBOOT_EXIT_COMMAND 3
+
+extern void comboot_force_text_mode ( void );
+
+#define COMBOOT_VIDEO_GRAPHICS 0x01
+#define COMBOOT_VIDEO_NONSTANDARD 0x02
+#define COMBOOT_VIDEO_VESA 0x04
+#define COMBOOT_VIDEO_NOTEXT 0x08
+
+#endif
diff --git a/roms/ipxe/src/arch/i386/include/fakee820.h b/roms/ipxe/src/arch/i386/include/fakee820.h
new file mode 100644
index 000000000..552b1e48d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/fakee820.h
@@ -0,0 +1,9 @@
+#ifndef _FAKEE820_H
+#define _FAKEE820_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern void fake_e820 ( void );
+extern void unfake_e820 ( void );
+
+#endif /* _FAKEE820_H */
diff --git a/roms/ipxe/src/arch/i386/include/gdbmach.h b/roms/ipxe/src/arch/i386/include/gdbmach.h
index 52cce7833..416ae341a 100644
--- a/roms/ipxe/src/arch/i386/include/gdbmach.h
+++ b/roms/ipxe/src/arch/i386/include/gdbmach.h
@@ -47,10 +47,12 @@ enum {
};
/* Interrupt vectors */
-extern void gdbmach_sigfpe ( void );
-extern void gdbmach_sigtrap ( void );
-extern void gdbmach_sigstkflt ( void );
-extern void gdbmach_sigill ( void );
+extern void gdbmach_nocode_sigfpe ( void );
+extern void gdbmach_nocode_sigtrap ( void );
+extern void gdbmach_nocode_sigstkflt ( void );
+extern void gdbmach_nocode_sigill ( void );
+extern void gdbmach_withcode_sigbus ( void );
+extern void gdbmach_withcode_sigsegv ( void );
static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
regs [ GDBMACH_EIP ] = pc;
diff --git a/roms/ipxe/src/arch/i386/include/initrd.h b/roms/ipxe/src/arch/i386/include/initrd.h
new file mode 100644
index 000000000..ddb3e5a45
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/initrd.h
@@ -0,0 +1,30 @@
+#ifndef _INITRD_H
+#define _INITRD_H
+
+/** @file
+ *
+ * Initial ramdisk (initrd) reshuffling
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/uaccess.h>
+
+/** Minimum alignment for initrds
+ *
+ * Some versions of Linux complain about initrds that are not
+ * page-aligned.
+ */
+#define INITRD_ALIGN 4096
+
+/** Minimum free space required to reshuffle initrds
+ *
+ * Chosen to avoid absurdly long reshuffling times
+ */
+#define INITRD_MIN_FREE_LEN ( 512 * 1024 )
+
+extern void initrd_reshuffle ( userptr_t bottom );
+extern int initrd_reshuffle_check ( size_t len, userptr_t bottom );
+
+#endif /* _INITRD_H */
diff --git a/roms/ipxe/src/arch/i386/include/int13.h b/roms/ipxe/src/arch/i386/include/int13.h
new file mode 100644
index 000000000..f82a583c6
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/int13.h
@@ -0,0 +1,333 @@
+#ifndef INT13_H
+#define INT13_H
+
+/** @file
+ *
+ * INT 13 emulation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/list.h>
+#include <ipxe/edd.h>
+#include <realmode.h>
+
+/**
+ * @defgroup int13ops INT 13 operation codes
+ * @{
+ */
+
+/** Reset disk system */
+#define INT13_RESET 0x00
+/** Get status of last operation */
+#define INT13_GET_LAST_STATUS 0x01
+/** Read sectors */
+#define INT13_READ_SECTORS 0x02
+/** Write sectors */
+#define INT13_WRITE_SECTORS 0x03
+/** Get drive parameters */
+#define INT13_GET_PARAMETERS 0x08
+/** Get disk type */
+#define INT13_GET_DISK_TYPE 0x15
+/** Extensions installation check */
+#define INT13_EXTENSION_CHECK 0x41
+/** Extended read */
+#define INT13_EXTENDED_READ 0x42
+/** Extended write */
+#define INT13_EXTENDED_WRITE 0x43
+/** Verify sectors */
+#define INT13_EXTENDED_VERIFY 0x44
+/** Extended seek */
+#define INT13_EXTENDED_SEEK 0x47
+/** Get extended drive parameters */
+#define INT13_GET_EXTENDED_PARAMETERS 0x48
+/** Get CD-ROM status / terminate emulation */
+#define INT13_CDROM_STATUS_TERMINATE 0x4b
+/** Read CD-ROM boot catalog */
+#define INT13_CDROM_READ_BOOT_CATALOG 0x4d
+
+/** @} */
+
+/**
+ * @defgroup int13status INT 13 status codes
+ * @{
+ */
+
+/** Operation completed successfully */
+#define INT13_STATUS_SUCCESS 0x00
+/** Invalid function or parameter */
+#define INT13_STATUS_INVALID 0x01
+/** Read error */
+#define INT13_STATUS_READ_ERROR 0x04
+/** Reset failed */
+#define INT13_STATUS_RESET_FAILED 0x05
+/** Write error */
+#define INT13_STATUS_WRITE_ERROR 0xcc
+
+/** @} */
+
+/** Block size for non-extended INT 13 calls */
+#define INT13_BLKSIZE 512
+
+/** @defgroup int13fddtype INT 13 floppy disk drive types
+ * @{
+ */
+
+/** 360K */
+#define INT13_FDD_TYPE_360K 0x01
+/** 1.2M */
+#define INT13_FDD_TYPE_1M2 0x02
+/** 720K */
+#define INT13_FDD_TYPE_720K 0x03
+/** 1.44M */
+#define INT13_FDD_TYPE_1M44 0x04
+
+/** An INT 13 disk address packet */
+struct int13_disk_address {
+ /** Size of the packet, in bytes */
+ uint8_t bufsize;
+ /** Reserved */
+ uint8_t reserved_a;
+ /** Block count */
+ uint8_t count;
+ /** Reserved */
+ uint8_t reserved_b;
+ /** Data buffer */
+ struct segoff buffer;
+ /** Starting block number */
+ uint64_t lba;
+ /** Data buffer (EDD 3.0+ only) */
+ uint64_t buffer_phys;
+ /** Block count (EDD 4.0+ only) */
+ uint32_t long_count;
+ /** Reserved */
+ uint32_t reserved_c;
+} __attribute__ (( packed ));
+
+/** INT 13 disk parameters */
+struct int13_disk_parameters {
+ /** Size of this structure */
+ uint16_t bufsize;
+ /** Flags */
+ uint16_t flags;
+ /** Number of cylinders */
+ uint32_t cylinders;
+ /** Number of heads */
+ uint32_t heads;
+ /** Number of sectors per track */
+ uint32_t sectors_per_track;
+ /** Total number of sectors on drive */
+ uint64_t sectors;
+ /** Bytes per sector */
+ uint16_t sector_size;
+ /** Device parameter table extension */
+ struct segoff dpte;
+ /** Device path information */
+ struct edd_device_path_information dpi;
+} __attribute__ (( packed ));
+
+/**
+ * @defgroup int13types INT 13 disk types
+ * @{
+ */
+
+/** No such drive */
+#define INT13_DISK_TYPE_NONE 0x00
+/** Floppy without change-line support */
+#define INT13_DISK_TYPE_FDD 0x01
+/** Floppy with change-line support */
+#define INT13_DISK_TYPE_FDD_CL 0x02
+/** Hard disk */
+#define INT13_DISK_TYPE_HDD 0x03
+
+/** @} */
+
+/**
+ * @defgroup int13flags INT 13 disk parameter flags
+ * @{
+ */
+
+/** DMA boundary errors handled transparently */
+#define INT13_FL_DMA_TRANSPARENT 0x01
+/** CHS information is valid */
+#define INT13_FL_CHS_VALID 0x02
+/** Removable drive */
+#define INT13_FL_REMOVABLE 0x04
+/** Write with verify supported */
+#define INT13_FL_VERIFIABLE 0x08
+/** Has change-line supported (valid only for removable drives) */
+#define INT13_FL_CHANGE_LINE 0x10
+/** Drive can be locked (valid only for removable drives) */
+#define INT13_FL_LOCKABLE 0x20
+/** CHS is max possible, not current media (valid only for removable drives) */
+#define INT13_FL_CHS_MAX 0x40
+
+/** @} */
+
+/**
+ * @defgroup int13exts INT 13 extension flags
+ * @{
+ */
+
+/** Extended disk access functions supported */
+#define INT13_EXTENSION_LINEAR 0x01
+/** Removable drive functions supported */
+#define INT13_EXTENSION_REMOVABLE 0x02
+/** EDD functions supported */
+#define INT13_EXTENSION_EDD 0x04
+/** 64-bit extensions are present */
+#define INT13_EXTENSION_64BIT 0x08
+
+/** @} */
+
+/**
+ * @defgroup int13vers INT 13 extension versions
+ * @{
+ */
+
+/** INT13 extensions version 1.x */
+#define INT13_EXTENSION_VER_1_X 0x01
+/** INT13 extensions version 2.0 (EDD-1.0) */
+#define INT13_EXTENSION_VER_2_0 0x20
+/** INT13 extensions version 2.1 (EDD-1.1) */
+#define INT13_EXTENSION_VER_2_1 0x21
+/** INT13 extensions version 3.0 (EDD-3.0) */
+#define INT13_EXTENSION_VER_3_0 0x30
+
+/** @} */
+
+/** Maximum number of sectors for which CHS geometry is allowed to be valid
+ *
+ * This number is taken from the EDD specification.
+ */
+#define INT13_MAX_CHS_SECTORS 15482880
+
+/** Bootable CD-ROM specification packet */
+struct int13_cdrom_specification {
+ /** Size of packet in bytes */
+ uint8_t size;
+ /** Boot media type */
+ uint8_t media_type;
+ /** Drive number */
+ uint8_t drive;
+ /** CD-ROM controller number */
+ uint8_t controller;
+ /** LBA of disk image to emulate */
+ uint32_t lba;
+ /** Device specification */
+ uint16_t device;
+ /** Segment of 3K buffer for caching CD-ROM reads */
+ uint16_t cache_segment;
+ /** Load segment for initial boot image */
+ uint16_t load_segment;
+ /** Number of 512-byte sectors to load */
+ uint16_t load_sectors;
+ /** Low 8 bits of cylinder number */
+ uint8_t cyl;
+ /** Sector number, plus high 2 bits of cylinder number */
+ uint8_t cyl_sector;
+ /** Head number */
+ uint8_t head;
+} __attribute__ (( packed ));
+
+/** Bootable CD-ROM boot catalog command packet */
+struct int13_cdrom_boot_catalog_command {
+ /** Size of packet in bytes */
+ uint8_t size;
+ /** Number of sectors of boot catalog to read */
+ uint8_t count;
+ /** Buffer for boot catalog */
+ uint32_t buffer;
+ /** First sector in boot catalog to transfer */
+ uint16_t start;
+} __attribute__ (( packed ));
+
+/** A C/H/S address within a partition table entry */
+struct partition_chs {
+ /** Head number */
+ uint8_t head;
+ /** Sector number, plus high 2 bits of cylinder number */
+ uint8_t cyl_sector;
+ /** Low 8 bits of cylinder number */
+ uint8_t cyl;
+} __attribute__ (( packed ));
+
+#define PART_HEAD(chs) ( (chs).head )
+#define PART_SECTOR(chs) ( (chs).cyl_sector & 0x3f )
+#define PART_CYLINDER(chs) ( (chs).cyl | ( ( (chs).cyl_sector & 0xc0 ) << 2 ) )
+
+/** A partition table entry within the MBR */
+struct partition_table_entry {
+ /** Bootable flag */
+ uint8_t bootable;
+ /** C/H/S start address */
+ struct partition_chs chs_start;
+ /** System indicator (partition type) */
+ uint8_t type;
+ /** C/H/S end address */
+ struct partition_chs chs_end;
+ /** Linear start address */
+ uint32_t start;
+ /** Linear length */
+ uint32_t length;
+} __attribute__ (( packed ));
+
+/** A Master Boot Record */
+struct master_boot_record {
+ /** Code area */
+ uint8_t code[440];
+ /** Disk signature */
+ uint32_t signature;
+ /** Padding */
+ uint8_t pad[2];
+ /** Partition table */
+ struct partition_table_entry partitions[4];
+ /** 0x55aa MBR signature */
+ uint16_t magic;
+} __attribute__ (( packed ));
+
+/** MBR magic signature */
+#define INT13_MBR_MAGIC 0xaa55
+
+/** A floppy disk geometry */
+struct int13_fdd_geometry {
+ /** Number of tracks */
+ uint8_t tracks;
+ /** Number of heads and sectors per track */
+ uint8_t heads_spt;
+};
+
+/** Define a floppy disk geometry */
+#define INT13_FDD_GEOMETRY( cylinders, heads, sectors ) \
+ { \
+ .tracks = (cylinders), \
+ .heads_spt = ( ( (heads) << 6 ) | (sectors) ), \
+ }
+
+/** Get floppy disk number of cylinders */
+#define INT13_FDD_CYLINDERS( geometry ) ( (geometry)->tracks )
+
+/** Get floppy disk number of heads */
+#define INT13_FDD_HEADS( geometry ) ( (geometry)->heads_spt >> 6 )
+
+/** Get floppy disk number of sectors per track */
+#define INT13_FDD_SECTORS( geometry ) ( (geometry)->heads_spt & 0x3f )
+
+/** A floppy drive parameter table */
+struct int13_fdd_parameters {
+ uint8_t step_rate__head_unload;
+ uint8_t head_load__ndma;
+ uint8_t motor_off_delay;
+ uint8_t bytes_per_sector;
+ uint8_t sectors_per_track;
+ uint8_t gap_length;
+ uint8_t data_length;
+ uint8_t format_gap_length;
+ uint8_t format_filler;
+ uint8_t head_settle_time;
+ uint8_t motor_start_time;
+} __attribute__ (( packed ));
+
+#endif /* INT13_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h
new file mode 100644
index 000000000..c9b82c1e5
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_BIOS_NAP_H
+#define _IPXE_BIOS_NAP_H
+
+/** @file
+ *
+ * BIOS CPU sleeping
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef NAP_PCBIOS
+#define NAP_PREFIX_pcbios
+#else
+#define NAP_PREFIX_pcbios __pcbios_
+#endif
+
+#endif /* _IPXE_BIOS_NAP_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h
new file mode 100644
index 000000000..3f6df9073
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_BIOS_REBOOT_H
+#define _IPXE_BIOS_REBOOT_H
+
+/** @file
+ *
+ * Standard PC-BIOS reboot mechanism
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef REBOOT_PCBIOS
+#define REBOOT_PREFIX_pcbios
+#else
+#define REBOOT_PREFIX_pcbios __pcbios_
+#endif
+
+#endif /* _IPXE_BIOS_REBOOT_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h
new file mode 100644
index 000000000..1a86b7d57
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h
@@ -0,0 +1,29 @@
+#ifndef _IPXE_BIOS_SANBOOT_H
+#define _IPXE_BIOS_SANBOOT_H
+
+/** @file
+ *
+ * Standard PC-BIOS sanboot interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef SANBOOT_PCBIOS
+#define SANBOOT_PREFIX_pcbios
+#else
+#define SANBOOT_PREFIX_pcbios __pcbios_
+#endif
+
+/**
+ * Get default SAN drive number
+ *
+ * @ret drive Default drive number
+ */
+static inline __always_inline unsigned int
+SANBOOT_INLINE ( pcbios, san_default_drive ) ( void ) {
+ /* Default to booting from first hard disk */
+ return 0x80;
+}
+
+#endif /* _IPXE_BIOS_SANBOOT_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h
new file mode 100644
index 000000000..9f7f9c8ff
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_BIOS_SMBIOS_H
+#define _IPXE_BIOS_SMBIOS_H
+
+/** @file
+ *
+ * Standard PC-BIOS SMBIOS interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef SMBIOS_PCBIOS
+#define SMBIOS_PREFIX_pcbios
+#else
+#define SMBIOS_PREFIX_pcbios __pcbios_
+#endif
+
+#endif /* _IPXE_BIOS_SMBIOS_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h
new file mode 100644
index 000000000..6b88a623c
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h
@@ -0,0 +1,44 @@
+#ifndef _IPXE_BIOS_TIMER_H
+#define _IPXE_BIOS_TIMER_H
+
+/** @file
+ *
+ * BIOS timer
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef TIMER_PCBIOS
+#define TIMER_PREFIX_pcbios
+#else
+#define TIMER_PREFIX_pcbios __pcbios_
+#endif
+
+#include <ipxe/pit8254.h>
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static inline __always_inline void
+TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) {
+ /* BIOS timer is not high-resolution enough for udelay(), so
+ * we use the 8254 Programmable Interval Timer.
+ */
+ pit8254_udelay ( usecs );
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static inline __always_inline unsigned long
+TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) {
+ /* BIOS timer ticks over at 18.2 ticks per second */
+ return 18;
+}
+
+#endif /* _IPXE_BIOS_TIMER_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h b/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h
new file mode 100644
index 000000000..6312adaa4
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h
@@ -0,0 +1,115 @@
+#ifndef _IPXE_ERRNO_PCBIOS_H
+#define _IPXE_ERRNO_PCBIOS_H
+
+/**
+ * @file
+ *
+ * PC-BIOS platform error codes
+ *
+ * We use the PXE-specified error codes as the platform error codes
+ * for the PC-BIOS platform.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <pxe_error.h>
+
+/**
+ * Convert platform error code to platform component of iPXE error code
+ *
+ * @v platform Platform error code
+ * @ret errno Platform component of iPXE error code
+ */
+#define PLATFORM_TO_ERRNO( platform ) ( (platform) & 0xff )
+
+/**
+ * Convert iPXE error code to platform error code
+ *
+ * @v errno iPXE error code
+ * @ret platform Platform error code
+ */
+#define ERRNO_TO_PLATFORM( errno ) ( (errno) & 0xff )
+
+/* Platform-specific error codes */
+#define PLATFORM_ENOERR PXENV_STATUS_SUCCESS
+#define PLATFORM_E2BIG PXENV_STATUS_BAD_FUNC
+#define PLATFORM_EACCES PXENV_STATUS_TFTP_ACCESS_VIOLATION
+#define PLATFORM_EADDRINUSE PXENV_STATUS_UDP_OPEN
+#define PLATFORM_EADDRNOTAVAIL PXENV_STATUS_UDP_OPEN
+#define PLATFORM_EAFNOSUPPORT PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_EAGAIN PXENV_STATUS_FAILURE
+#define PLATFORM_EALREADY PXENV_STATUS_UDP_OPEN
+#define PLATFORM_EBADF PXENV_STATUS_TFTP_CLOSED
+#define PLATFORM_EBADMSG PXENV_STATUS_FAILURE
+#define PLATFORM_EBUSY PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ECANCELED PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE
+#define PLATFORM_ECHILD PXENV_STATUS_TFTP_FILE_NOT_FOUND
+#define PLATFORM_ECONNABORTED PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
+#define PLATFORM_ECONNREFUSED PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION
+#define PLATFORM_ECONNRESET PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
+#define PLATFORM_EDEADLK PXENV_STATUS_FAILURE
+#define PLATFORM_EDESTADDRREQ PXENV_STATUS_BAD_FUNC
+#define PLATFORM_EDOM PXENV_STATUS_FAILURE
+#define PLATFORM_EDQUOT PXENV_STATUS_FAILURE
+#define PLATFORM_EEXIST PXENV_STATUS_FAILURE
+#define PLATFORM_EFAULT PXENV_STATUS_MCOPY_PROBLEM
+#define PLATFORM_EFBIG PXENV_STATUS_MCOPY_PROBLEM
+#define PLATFORM_EHOSTUNREACH PXENV_STATUS_ARP_TIMEOUT
+#define PLATFORM_EIDRM PXENV_STATUS_FAILURE
+#define PLATFORM_EILSEQ PXENV_STATUS_FAILURE
+#define PLATFORM_EINPROGRESS PXENV_STATUS_FAILURE
+#define PLATFORM_EINTR PXENV_STATUS_FAILURE
+#define PLATFORM_EINVAL PXENV_STATUS_BAD_FUNC
+#define PLATFORM_EIO PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
+#define PLATFORM_EISCONN PXENV_STATUS_UDP_OPEN
+#define PLATFORM_EISDIR PXENV_STATUS_FAILURE
+#define PLATFORM_ELOOP PXENV_STATUS_FAILURE
+#define PLATFORM_EMFILE PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_EMLINK PXENV_STATUS_FAILURE
+#define PLATFORM_EMSGSIZE PXENV_STATUS_BAD_FUNC
+#define PLATFORM_EMULTIHOP PXENV_STATUS_FAILURE
+#define PLATFORM_ENAMETOOLONG PXENV_STATUS_FAILURE
+#define PLATFORM_ENETDOWN PXENV_STATUS_ARP_TIMEOUT
+#define PLATFORM_ENETRESET PXENV_STATUS_FAILURE
+#define PLATFORM_ENETUNREACH PXENV_STATUS_ARP_TIMEOUT
+#define PLATFORM_ENFILE PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ENOBUFS PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ENODATA PXENV_STATUS_FAILURE
+#define PLATFORM_ENODEV PXENV_STATUS_TFTP_FILE_NOT_FOUND
+#define PLATFORM_ENOENT PXENV_STATUS_TFTP_FILE_NOT_FOUND
+#define PLATFORM_ENOEXEC PXENV_STATUS_FAILURE
+#define PLATFORM_ENOLCK PXENV_STATUS_FAILURE
+#define PLATFORM_ENOLINK PXENV_STATUS_FAILURE
+#define PLATFORM_ENOMEM PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ENOMSG PXENV_STATUS_FAILURE
+#define PLATFORM_ENOPROTOOPT PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_ENOSPC PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ENOSR PXENV_STATUS_OUT_OF_RESOURCES
+#define PLATFORM_ENOSTR PXENV_STATUS_FAILURE
+#define PLATFORM_ENOSYS PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_ENOTCONN PXENV_STATUS_FAILURE
+#define PLATFORM_ENOTDIR PXENV_STATUS_FAILURE
+#define PLATFORM_ENOTEMPTY PXENV_STATUS_FAILURE
+#define PLATFORM_ENOTSOCK PXENV_STATUS_FAILURE
+#define PLATFORM_ENOTSUP PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_ENOTTY PXENV_STATUS_FAILURE
+#define PLATFORM_ENXIO PXENV_STATUS_TFTP_FILE_NOT_FOUND
+#define PLATFORM_EOPNOTSUPP PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_EOVERFLOW PXENV_STATUS_FAILURE
+#define PLATFORM_EPERM PXENV_STATUS_TFTP_ACCESS_VIOLATION
+#define PLATFORM_EPIPE PXENV_STATUS_FAILURE
+#define PLATFORM_EPROTO PXENV_STATUS_FAILURE
+#define PLATFORM_EPROTONOSUPPORT PXENV_STATUS_UNSUPPORTED
+#define PLATFORM_EPROTOTYPE PXENV_STATUS_FAILURE
+#define PLATFORM_ERANGE PXENV_STATUS_FAILURE
+#define PLATFORM_EROFS PXENV_STATUS_FAILURE
+#define PLATFORM_ESPIPE PXENV_STATUS_FAILURE
+#define PLATFORM_ESRCH PXENV_STATUS_TFTP_FILE_NOT_FOUND
+#define PLATFORM_ESTALE PXENV_STATUS_FAILURE
+#define PLATFORM_ETIME PXENV_STATUS_FAILURE
+#define PLATFORM_ETIMEDOUT PXENV_STATUS_TFTP_READ_TIMEOUT
+#define PLATFORM_ETXTBSY PXENV_STATUS_FAILURE
+#define PLATFORM_EWOULDBLOCK PXENV_STATUS_TFTP_OPEN
+#define PLATFORM_EXDEV PXENV_STATUS_FAILURE
+
+#endif /* _IPXE_ERRNO_PCBIOS_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h b/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h
new file mode 100644
index 000000000..bc3d85506
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h
@@ -0,0 +1,68 @@
+#ifndef _IPXE_GUESTRPC_H
+#define _IPXE_GUESTRPC_H
+
+/** @file
+ *
+ * VMware GuestRPC mechanism
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/vmware.h>
+
+/** GuestRPC magic number */
+#define GUESTRPC_MAGIC 0x49435052 /* "RPCI" */
+
+/** Open RPC channel */
+#define GUESTRPC_OPEN 0x00
+
+/** Open RPC channel success status */
+#define GUESTRPC_OPEN_SUCCESS 0x00010000
+
+/** Send RPC command length */
+#define GUESTRPC_COMMAND_LEN 0x01
+
+/** Send RPC command length success status */
+#define GUESTRPC_COMMAND_LEN_SUCCESS 0x00810000
+
+/** Send RPC command data */
+#define GUESTRPC_COMMAND_DATA 0x02
+
+/** Send RPC command data success status */
+#define GUESTRPC_COMMAND_DATA_SUCCESS 0x00010000
+
+/** Receive RPC reply length */
+#define GUESTRPC_REPLY_LEN 0x03
+
+/** Receive RPC reply length success status */
+#define GUESTRPC_REPLY_LEN_SUCCESS 0x00830000
+
+/** Receive RPC reply data */
+#define GUESTRPC_REPLY_DATA 0x04
+
+/** Receive RPC reply data success status */
+#define GUESTRPC_REPLY_DATA_SUCCESS 0x00010000
+
+/** Finish receiving RPC reply */
+#define GUESTRPC_REPLY_FINISH 0x05
+
+/** Finish receiving RPC reply success status */
+#define GUESTRPC_REPLY_FINISH_SUCCESS 0x00010000
+
+/** Close RPC channel */
+#define GUESTRPC_CLOSE 0x06
+
+/** Close RPC channel success status */
+#define GUESTRPC_CLOSE_SUCCESS 0x00010000
+
+/** RPC command success status */
+#define GUESTRPC_SUCCESS 0x2031 /* "1 " */
+
+extern int guestrpc_open ( void );
+extern void guestrpc_close ( int channel );
+extern int guestrpc_command ( int channel, const char *command, char *reply,
+ size_t reply_len );
+
+#endif /* _IPXE_GUESTRPC_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h b/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h
new file mode 100644
index 000000000..dee055d16
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_MEMTOP_UMALLOC_H
+#define _IPXE_MEMTOP_UMALLOC_H
+
+/** @file
+ *
+ * External memory allocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef UMALLOC_MEMTOP
+#define UMALLOC_PREFIX_memtop
+#else
+#define UMALLOC_PREFIX_memtop __memtop_
+#endif
+
+#endif /* _IPXE_MEMTOP_UMALLOC_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h b/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h
new file mode 100644
index 000000000..598f4bb08
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h
@@ -0,0 +1,39 @@
+#ifndef _IPXE_RDTSC_TIMER_H
+#define _IPXE_RDTSC_TIMER_H
+
+/** @file
+ *
+ * RDTSC timer
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef TIMER_RDTSC
+#define TIMER_PREFIX_rdtsc
+#else
+#define TIMER_PREFIX_rdtsc __rdtsc_
+#endif
+
+/**
+ * RDTSC values can easily overflow an unsigned long. We discard the
+ * low-order bits in order to obtain sensibly-scaled values.
+ */
+#define TSC_SHIFT 8
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ */
+static inline __always_inline unsigned long
+TIMER_INLINE ( rdtsc, currticks ) ( void ) {
+ unsigned long ticks;
+
+ __asm__ __volatile__ ( "rdtsc\n\t"
+ "shrdl %1, %%edx, %%eax\n\t"
+ : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" );
+ return ticks;
+}
+
+#endif /* _IPXE_RDTSC_TIMER_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h b/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h
new file mode 100644
index 000000000..e214745d0
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h
@@ -0,0 +1,62 @@
+#ifndef _IPXE_RTC_ENTROPY_H
+#define _IPXE_RTC_ENTROPY_H
+
+/** @file
+ *
+ * RTC-based entropy source
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+#ifdef ENTROPY_RTC
+#define ENTROPY_PREFIX_rtc
+#else
+#define ENTROPY_PREFIX_rtc __rtc_
+#endif
+
+/**
+ * min-entropy per sample
+ *
+ * @ret min_entropy min-entropy of each sample
+ */
+static inline __always_inline double
+ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) {
+
+ /* The min-entropy has been measured on several platforms
+ * using the entropy_sample test code. Modelling the samples
+ * as independent, and using a confidence level of 99.99%, the
+ * measurements were as follows:
+ *
+ * qemu-kvm : 7.38 bits
+ * VMware : 7.46 bits
+ * Physical hardware : 2.67 bits
+ *
+ * We choose the lowest of these (2.67 bits) and apply a 50%
+ * safety margin to allow for some potential non-independence
+ * of samples.
+ */
+ return 1.3;
+}
+
+extern uint8_t rtc_sample ( void );
+
+/**
+ * Get noise sample
+ *
+ * @ret noise Noise sample
+ * @ret rc Return status code
+ */
+static inline __always_inline int
+ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) {
+
+ /* Get sample */
+ *noise = rtc_sample();
+
+ /* Always successful */
+ return 0;
+}
+
+#endif /* _IPXE_RTC_ENTROPY_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h b/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h
new file mode 100644
index 000000000..cb8c7f49e
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_RTC_TIME_H
+#define _IPXE_RTC_TIME_H
+
+/** @file
+ *
+ * RTC-based time source
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef TIME_RTC
+#define TIME_PREFIX_rtc
+#else
+#define TIME_PREFIX_rtc __rtc_
+#endif
+
+#endif /* _IPXE_RTC_TIME_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h b/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h
new file mode 100644
index 000000000..efc8f2cb8
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h
@@ -0,0 +1,210 @@
+#ifndef _IPXE_VESAFB_H
+#define _IPXE_VESAFB_H
+
+/** @file
+ *
+ * VESA frame buffer console
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <realmode.h>
+
+/** INT 10,4f00: return controller information */
+#define VBE_CONTROLLER_INFO 0x4f00
+
+/** VBE controller information */
+struct vbe_controller_info {
+ /** VBE signature */
+ uint32_t vbe_signature;
+ /** VBE minor version */
+ uint8_t vbe_minor_version;
+ /** VBE major version */
+ uint8_t vbe_major_version;
+ /** Pointer to OEM string */
+ struct segoff oem_string_ptr;
+ /** Capabilities of graphics controller */
+ uint32_t capabilities;
+ /** Pointer to video mode list */
+ struct segoff video_mode_ptr;
+ /** Number of 64kB memory blocks */
+ uint16_t total_memory;
+ /** VBE implementation software revision */
+ uint16_t oem_software_rev;
+ /** Pointer to vendor name string */
+ struct segoff oem_vendor_name_ptr;
+ /** Pointer to product name string */
+ struct segoff oem_product_name_ptr;
+ /** Pointer to product revision string */
+ struct segoff oem_product_rev_ptr;
+ /** Reserved for VBE implementation scratch area */
+ uint8_t reserved[222];
+ /* VBE2.0 defines an additional 256-byte data area for
+ * including the OEM strings inline within the VBE information
+ * block; we omit this to reduce the amount of base memory
+ * required for VBE calls.
+ */
+} __attribute__ (( packed ));
+
+/** VBE controller information signature */
+#define VBE_CONTROLLER_SIGNATURE \
+ ( ( 'V' << 0 ) | ( 'E' << 8 ) | ( 'S' << 16 ) | ( 'A' << 24 ) )
+
+/** VBE mode list end marker */
+#define VBE_MODE_END 0xffff
+
+/** INT 10,4f01: return VBE mode information */
+#define VBE_MODE_INFO 0x4f01
+
+/** VBE mode information */
+struct vbe_mode_info {
+ /** Mode attributes */
+ uint16_t mode_attributes;
+ /** Window A attributes */
+ uint8_t win_a_attributes;
+ /** Window B attributes */
+ uint8_t win_b_attributes;
+ /** Window granularity */
+ uint16_t win_granularity;
+ /** Window size */
+ uint16_t win_size;
+ /** Window A start segment */
+ uint16_t win_a_segment;
+ /** Window B start segment */
+ uint16_t win_b_segment;
+ /** Pointer to window function */
+ struct segoff win_func_ptr;
+ /** Bytes per scan line */
+ uint16_t bytes_per_scan_line;
+ /** Horizontal resolution in pixels or characters */
+ uint16_t x_resolution;
+ /** Vertical resolution in pixels or characters */
+ uint16_t y_resolution;
+ /** Character cell width in pixels */
+ uint8_t x_char_size;
+ /** Character cell height in pixels */
+ uint8_t y_char_size;
+ /** Number of memory planes */
+ uint8_t number_of_planes;
+ /** Bits per pixel */
+ uint8_t bits_per_pixel;
+ /** Number of banks */
+ uint8_t number_of_banks;
+ /** Memory model type */
+ uint8_t memory_model;
+ /** Bank size in kB */
+ uint8_t bank_size;
+ /** Number of images */
+ uint8_t number_of_image_pages;
+ /** Reserved for page function */
+ uint8_t reserved_1;
+ /** Size of direct colour red mask in bits */
+ uint8_t red_mask_size;
+ /** Bit position of LSB of red mask */
+ uint8_t red_field_position;
+ /** Size of direct colour green mask in bits */
+ uint8_t green_mask_size;
+ /** Bit position of LSB of green mask */
+ uint8_t green_field_position;
+ /** Size of direct colour blue mask in bits */
+ uint8_t blue_mask_size;
+ /** Bit position of LSB of blue mask */
+ uint8_t blue_field_position;
+ /** Size of direct colour reserved mask in bits */
+ uint8_t rsvd_mask_size;
+ /** Bit position of LSB of reserved mask */
+ uint8_t rsvd_field_position;
+ /** Direct colour mode attributes */
+ uint8_t direct_colour_mode_info;
+ /** Physical address for flat memory frame buffer */
+ uint32_t phys_base_ptr;
+ /** Pointer to start of off-screen memory */
+ uint32_t off_screen_mem_offset;
+ /** Amount of off-screen memory in 1kB units */
+ uint16_t off_screen_mem_size;
+ /** Reserved */
+ uint8_t reserved_2[206];
+} __attribute__ (( packed ));
+
+/** VBE mode attributes */
+enum vbe_mode_attributes {
+ /** Mode supported in hardware */
+ VBE_MODE_ATTR_SUPPORTED = 0x0001,
+ /** TTY output functions supported by BIOS */
+ VBE_MODE_ATTR_TTY = 0x0004,
+ /** Colour mode */
+ VBE_MODE_ATTR_COLOUR = 0x0008,
+ /** Graphics mode */
+ VBE_MODE_ATTR_GRAPHICS = 0x0010,
+ /** Not a VGA compatible mode */
+ VBE_MODE_ATTR_NOT_VGA = 0x0020,
+ /** VGA compatible windowed memory mode is not available */
+ VBE_MODE_ATTR_NOT_WINDOWED = 0x0040,
+ /** Linear frame buffer mode is available */
+ VBE_MODE_ATTR_LINEAR = 0x0080,
+ /** Double scan mode is available */
+ VBE_MODE_ATTR_DOUBLE = 0x0100,
+ /** Interlaced mode is available */
+ VBE_MODE_ATTR_INTERLACED = 0x0200,
+ /** Hardware triple buffering support */
+ VBE_MODE_ATTR_TRIPLE_BUF = 0x0400,
+ /** Hardware stereoscopic display support */
+ VBE_MODE_ATTR_STEREO = 0x0800,
+ /** Dual display start address support */
+ VBE_MODE_ATTR_DUAL = 0x1000,
+};
+
+/** VBE mode memory models */
+enum vbe_mode_memory_model {
+ /** Text mode */
+ VBE_MODE_MODEL_TEXT = 0x00,
+ /** CGA graphics mode */
+ VBE_MODE_MODEL_CGA = 0x01,
+ /** Hercules graphics mode */
+ VBE_MODE_MODEL_HERCULES = 0x02,
+ /** Planar mode */
+ VBE_MODE_MODEL_PLANAR = 0x03,
+ /** Packed pixel mode */
+ VBE_MODE_MODEL_PACKED_PIXEL = 0x04,
+ /** Non-chain 4, 256 colour mode */
+ VBE_MODE_MODEL_NON_CHAIN_4 = 0x05,
+ /** Direct colour mode */
+ VBE_MODE_MODEL_DIRECT_COLOUR = 0x06,
+ /** YUV mode */
+ VBE_MODE_MODEL_YUV = 0x07,
+};
+
+/** INT 10,4f02: set VBE mode */
+#define VBE_SET_MODE 0x4f02
+
+/** VBE linear frame buffer mode bit */
+#define VBE_MODE_LINEAR 0x4000
+
+/** INT 10,1130: get font information */
+#define VBE_GET_FONT 0x1130
+
+/** Font sets */
+enum vbe_font_set {
+ /** 8x14 character font */
+ VBE_FONT_8x14 = 0x0200,
+ /** 8x8 double dot font */
+ VBE_FONT_8x8_DOUBLE = 0x0300,
+ /** 8x8 double dot font (high 128 characters) */
+ VBE_FONT_8x8_DOUBLE_HIGH = 0x0400,
+ /** 9x14 alpha alternate font */
+ VBE_FONT_9x14_ALPHA_ALT = 0x0500,
+ /** 8x16 font */
+ VBE_FONT_8x16 = 0x0600,
+ /** 9x16 alternate font */
+ VBE_FONT_9x16_ALT = 0x0700,
+};
+
+/** INT 10,00: set VGA mode */
+#define VBE_SET_VGA_MODE 0x0000
+
+/** INT 10,0f: get VGA mode */
+#define VBE_GET_VGA_MODE 0x0f00
+
+#endif /* _IPXE_VESAFB_H */
diff --git a/roms/ipxe/src/arch/i386/include/ipxe/vmware.h b/roms/ipxe/src/arch/i386/include/ipxe/vmware.h
new file mode 100644
index 000000000..24f60a03a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/ipxe/vmware.h
@@ -0,0 +1,81 @@
+#ifndef _IPXE_VMWARE_H
+#define _IPXE_VMWARE_H
+
+/** @file
+ *
+ * VMware backdoor mechanism
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/** VMware backdoor I/O port */
+#define VMW_PORT 0x5658
+
+/** VMware backdoor magic value */
+#define VMW_MAGIC 0x564d5868 /* "VMXh" */
+
+/** VMware backdoor magic instruction */
+#define VMW_BACKDOOR "inl %%dx, %%eax"
+
+/** Get VMware version */
+#define VMW_CMD_GET_VERSION 0x0a
+
+/** Issue GuestRPC command */
+#define VMW_CMD_GUESTRPC 0x1e
+
+/**
+ * Get VMware version
+ *
+ * @ret version VMware version(?)
+ * @ret magic VMware magic number, if present
+ * @ret product_type VMware product type
+ */
+static inline __attribute__ (( always_inline )) void
+vmware_cmd_get_version ( uint32_t *version, uint32_t *magic,
+ uint32_t *product_type ) {
+ uint32_t discard_d;
+
+ /* Perform backdoor call */
+ __asm__ __volatile__ ( VMW_BACKDOOR
+ : "=a" ( *version ), "=b" ( *magic ),
+ "=c" ( *product_type ), "=d" ( discard_d )
+ : "0" ( VMW_MAGIC ), "1" ( 0 ),
+ "2" ( VMW_CMD_GET_VERSION ),
+ "3" ( VMW_PORT ) );
+}
+
+/**
+ * Issue GuestRPC command
+ *
+ * @v channel Channel number
+ * @v subcommand GuestRPC subcommand
+ * @v parameter Subcommand-specific parameter
+ * @ret edxhi Subcommand-specific result
+ * @ret ebx Subcommand-specific result
+ * @ret status Command status
+ */
+static inline __attribute__ (( always_inline )) uint32_t
+vmware_cmd_guestrpc ( int channel, uint16_t subcommand, uint32_t parameter,
+ uint16_t *edxhi, uint32_t *ebx ) {
+ uint32_t discard_a;
+ uint32_t status;
+ uint32_t edx;
+
+ /* Perform backdoor call */
+ __asm__ __volatile__ ( VMW_BACKDOOR
+ : "=a" ( discard_a ), "=b" ( *ebx ),
+ "=c" ( status ), "=d" ( edx )
+ : "0" ( VMW_MAGIC ), "1" ( parameter ),
+ "2" ( VMW_CMD_GUESTRPC | ( subcommand << 16 )),
+ "3" ( VMW_PORT | ( channel << 16 ) ) );
+ *edxhi = ( edx >> 16 );
+
+ return status;
+}
+
+extern int vmware_present ( void );
+
+#endif /* _IPXE_VMWARE_H */
diff --git a/roms/ipxe/src/arch/i386/include/kir.h b/roms/ipxe/src/arch/i386/include/kir.h
new file mode 100644
index 000000000..84633d26f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/kir.h
@@ -0,0 +1,18 @@
+#ifndef KIR_H
+#define KIR_H
+
+#ifndef KEEP_IT_REAL
+#error "kir.h can be used only with -DKEEP_IT_REAL"
+#endif
+
+#ifdef ASSEMBLY
+
+#define code32 code16gcc
+
+#else /* ASSEMBLY */
+
+__asm__ ( ".code16gcc" );
+
+#endif /* ASSEMBLY */
+
+#endif /* KIR_H */
diff --git a/roms/ipxe/src/arch/i386/include/libkir.h b/roms/ipxe/src/arch/i386/include/libkir.h
new file mode 100644
index 000000000..1f5b13504
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/libkir.h
@@ -0,0 +1,233 @@
+#ifndef LIBKIR_H
+#define LIBKIR_H
+
+#include "realmode.h"
+
+#ifndef ASSEMBLY
+
+/*
+ * Full API documentation for these functions is in realmode.h.
+ *
+ */
+
+/* Access to variables in .data16 and .text16 in a way compatible with librm */
+#define __data16( variable ) variable
+#define __data16_array( variable, array ) variable array
+#define __bss16( variable ) variable
+#define __bss16_array( variable, array ) variable array
+#define __text16( variable ) variable
+#define __text16_array( variable,array ) variable array
+#define __use_data16( variable ) variable
+#define __use_text16( variable ) variable
+#define __from_data16( pointer ) pointer
+#define __from_text16( pointer ) pointer
+
+/* Real-mode data and code segments */
+static inline __attribute__ (( always_inline )) unsigned int _rm_cs ( void ) {
+ uint16_t cs;
+ __asm__ __volatile__ ( "movw %%cs, %w0" : "=r" ( cs ) );
+ return cs;
+}
+
+static inline __attribute__ (( always_inline )) unsigned int _rm_ds ( void ) {
+ uint16_t ds;
+ __asm__ __volatile__ ( "movw %%ds, %w0" : "=r" ( ds ) );
+ return ds;
+}
+
+#define rm_cs ( _rm_cs() )
+#define rm_ds ( _rm_ds() )
+
+/* Copy to/from base memory */
+
+static inline void copy_to_real_libkir ( unsigned int dest_seg,
+ unsigned int dest_off,
+ const void *src, size_t n ) {
+ unsigned int discard_D, discard_S, discard_c;
+
+ __asm__ __volatile__ ( "pushw %%es\n\t"
+ "movw %3, %%es\n\t"
+ "rep movsb\n\t"
+ "popw %%es\n\t"
+ : "=D" ( discard_D ), "=S" ( discard_S ),
+ "=c" ( discard_c )
+ : "r" ( dest_seg ), "D" ( dest_off ),
+ "S" ( src ),
+ "c" ( n )
+ : "memory" );
+}
+
+static inline void copy_from_real_libkir ( void *dest,
+ unsigned int src_seg,
+ unsigned int src_off,
+ size_t n ) {
+ unsigned int discard_D, discard_S, discard_c;
+
+ __asm__ __volatile__ ( "pushw %%ds\n\t"
+ "movw %4, %%ds\n\t"
+ "rep movsb\n\t"
+ "popw %%ds\n\t"
+ : "=D" ( discard_D ), "=S" ( discard_S ),
+ "=c" ( discard_c )
+ : "D" ( dest ),
+ "r" ( src_seg ), "S" ( src_off ),
+ "c" ( n )
+ : "memory" );
+}
+
+#define copy_to_real copy_to_real_libkir
+#define copy_from_real copy_from_real_libkir
+
+/*
+ * Transfer individual values to/from base memory. There may well be
+ * a neater way to do this. We have two versions: one for constant
+ * offsets (where the mov instruction must be of the form "mov
+ * %es:123, %xx") and one for non-constant offsets (where the mov
+ * instruction must be of the form "mov %es:(%xx), %yx". If it's
+ * possible to incorporate both forms into one __asm__ instruction, I
+ * don't know how to do it.
+ *
+ * Ideally, the mov instruction should be "mov%z0"; the "%z0" is meant
+ * to expand to either "b", "w" or "l" depending on the size of
+ * operand 0. This would remove the (minor) ambiguity in the mov
+ * instruction. However, gcc on at least my system barfs with an
+ * "internal compiler error" when confronted with %z0.
+ *
+ */
+
+#define put_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:%c2\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r,r" ( var ), "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define put_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:(%2)\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r" ( var ), "rm" ( seg ), "r" ( off ) \
+ )
+
+#define put_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ put_real_kir_const_off ( var, seg, off ); \
+ else \
+ put_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define get_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:%c2, %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r,r" ( var ) \
+ : "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define get_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:(%2), %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r" ( var ) \
+ : "rm" ( seg ), "r" ( off ) \
+ )
+
+#define get_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ get_real_kir_const_off ( var, seg, off ); \
+ else \
+ get_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define put_real put_real_kir
+#define get_real get_real_kir
+
+/**
+ * A pointer to a user buffer
+ *
+ * This is actually a struct segoff, but encoded as a uint32_t to
+ * ensure that gcc passes it around efficiently.
+ */
+typedef uint32_t userptr_t;
+
+/**
+ * Copy data to user buffer
+ *
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v src Source
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) {
+ copy_to_real ( ( buffer >> 16 ), ( ( buffer & 0xffff ) + offset ),
+ src, len );
+}
+
+/**
+ * Copy data from user buffer
+ *
+ * @v dest Destination
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
+ copy_from_real ( dest, ( buffer >> 16 ),
+ ( ( buffer & 0xffff ) + offset ), len );
+}
+
+/**
+ * Convert segment:offset address to user buffer
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @ret buffer User buffer
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+real_to_user ( unsigned int segment, unsigned int offset ) {
+ return ( ( segment << 16 ) | offset );
+}
+
+/**
+ * Convert virtual address to user buffer
+ *
+ * @v virtual Virtual address
+ * @ret buffer User buffer
+ *
+ * This constructs a user buffer from an ordinary pointer. Use it
+ * when you need to pass a pointer to an internal buffer to a function
+ * that expects a @c userptr_t.
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+virt_to_user ( void * virtual ) {
+ return real_to_user ( rm_ds, ( intptr_t ) virtual );
+}
+
+/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
+#define TEXT16_CODE( asm_code_str ) \
+ ".section \".text16\", \"ax\", @progbits\n\t" \
+ ".code16\n\t" \
+ ".arch i386\n\t" \
+ asm_code_str "\n\t" \
+ ".code16gcc\n\t" \
+ ".previous\n\t"
+
+/* REAL_CODE: declare a fragment of code that executes in real mode */
+#define REAL_CODE( asm_code_str ) \
+ ".code16\n\t" \
+ asm_code_str "\n\t" \
+ ".code16gcc\n\t"
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBKIR_H */
diff --git a/roms/ipxe/src/arch/i386/include/librm.h b/roms/ipxe/src/arch/i386/include/librm.h
new file mode 100644
index 000000000..a8a578a39
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/librm.h
@@ -0,0 +1,267 @@
+#ifndef LIBRM_H
+#define LIBRM_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/* Segment selectors as used in our protected-mode GDTs.
+ *
+ * Don't change these unless you really know what you're doing.
+ */
+
+#define VIRTUAL_CS 0x08
+#define VIRTUAL_DS 0x10
+#define PHYSICAL_CS 0x18
+#define PHYSICAL_DS 0x20
+#define REAL_CS 0x28
+#define REAL_DS 0x30
+#if 0
+#define LONG_CS 0x38
+#define LONG_DS 0x40
+#endif
+
+#ifndef ASSEMBLY
+
+#ifdef UACCESS_LIBRM
+#define UACCESS_PREFIX_librm
+#else
+#define UACCESS_PREFIX_librm __librm_
+#endif
+
+/* Variables in librm.S */
+extern unsigned long virt_offset;
+
+/**
+ * Convert physical address to user pointer
+ *
+ * @v phys_addr Physical address
+ * @ret userptr User pointer
+ */
+static inline __always_inline userptr_t
+UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) {
+ return ( phys_addr - virt_offset );
+}
+
+/**
+ * Convert user buffer to physical address
+ *
+ * @v userptr User pointer
+ * @v offset Offset from user pointer
+ * @ret phys_addr Physical address
+ */
+static inline __always_inline unsigned long
+UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) {
+ return ( userptr + offset + virt_offset );
+}
+
+static inline __always_inline userptr_t
+UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) {
+ return trivial_virt_to_user ( addr );
+}
+
+static inline __always_inline void *
+UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) {
+ return trivial_user_to_virt ( userptr, offset );
+}
+
+static inline __always_inline userptr_t
+UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) {
+ return trivial_userptr_add ( userptr, offset );
+}
+
+static inline __always_inline off_t
+UACCESS_INLINE ( librm, userptr_sub ) ( userptr_t userptr,
+ userptr_t subtrahend ) {
+ return trivial_userptr_sub ( userptr, subtrahend );
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off,
+ userptr_t src, off_t src_off,
+ size_t len ) {
+ trivial_memcpy_user ( dest, dest_off, src, src_off, len );
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off,
+ userptr_t src, off_t src_off,
+ size_t len ) {
+ trivial_memmove_user ( dest, dest_off, src, src_off, len );
+}
+
+static inline __always_inline int
+UACCESS_INLINE ( librm, memcmp_user ) ( userptr_t first, off_t first_off,
+ userptr_t second, off_t second_off,
+ size_t len ) {
+ return trivial_memcmp_user ( first, first_off, second, second_off, len);
+}
+
+static inline __always_inline void
+UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset,
+ int c, size_t len ) {
+ trivial_memset_user ( buffer, offset, c, len );
+}
+
+static inline __always_inline size_t
+UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) {
+ return trivial_strlen_user ( buffer, offset );
+}
+
+static inline __always_inline off_t
+UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset,
+ int c, size_t len ) {
+ return trivial_memchr_user ( buffer, offset, c, len );
+}
+
+
+/******************************************************************************
+ *
+ * Access to variables in .data16 and .text16
+ *
+ */
+
+extern char *data16;
+extern char *text16;
+
+#define __data16( variable ) \
+ __attribute__ (( section ( ".data16" ) )) \
+ _data16_ ## variable __asm__ ( #variable )
+
+#define __data16_array( variable, array ) \
+ __attribute__ (( section ( ".data16" ) )) \
+ _data16_ ## variable array __asm__ ( #variable )
+
+#define __bss16( variable ) \
+ __attribute__ (( section ( ".bss16" ) )) \
+ _data16_ ## variable __asm__ ( #variable )
+
+#define __bss16_array( variable, array ) \
+ __attribute__ (( section ( ".bss16" ) )) \
+ _data16_ ## variable array __asm__ ( #variable )
+
+#define __text16( variable ) \
+ __attribute__ (( section ( ".text16.data" ) )) \
+ _text16_ ## variable __asm__ ( #variable )
+
+#define __text16_array( variable, array ) \
+ __attribute__ (( section ( ".text16.data" ) )) \
+ _text16_ ## variable array __asm__ ( #variable )
+
+#define __use_data16( variable ) \
+ ( * ( ( typeof ( _data16_ ## variable ) * ) \
+ & ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) )
+
+#define __use_text16( variable ) \
+ ( * ( ( typeof ( _text16_ ## variable ) * ) \
+ & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) )
+
+#define __from_data16( pointer ) \
+ ( ( unsigned int ) \
+ ( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) )
+
+#define __from_text16( pointer ) \
+ ( ( unsigned int ) \
+ ( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) )
+
+/* Variables in librm.S, present in the normal data segment */
+extern uint16_t rm_sp;
+extern uint16_t rm_ss;
+extern uint16_t __text16 ( rm_cs );
+#define rm_cs __use_text16 ( rm_cs )
+extern uint16_t __text16 ( rm_ds );
+#define rm_ds __use_text16 ( rm_ds )
+
+extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );
+extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
+
+/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
+#define TEXT16_CODE( asm_code_str ) \
+ ".section \".text16\", \"ax\", @progbits\n\t" \
+ ".code16\n\t" \
+ asm_code_str "\n\t" \
+ ".code32\n\t" \
+ ".previous\n\t"
+
+/* REAL_CODE: declare a fragment of code that executes in real mode */
+#define REAL_CODE( asm_code_str ) \
+ "pushl $1f\n\t" \
+ "call real_call\n\t" \
+ "addl $4, %%esp\n\t" \
+ TEXT16_CODE ( "\n1:\n\t" \
+ asm_code_str \
+ "\n\t" \
+ "ret\n\t" )
+
+/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
+#define PHYS_CODE( asm_code_str ) \
+ "call _virt_to_phys\n\t" \
+ asm_code_str \
+ "call _phys_to_virt\n\t"
+
+/** Number of interrupts */
+#define NUM_INT 256
+
+/** An interrupt descriptor table register */
+struct idtr {
+ /** Limit */
+ uint16_t limit;
+ /** Base */
+ uint32_t base;
+} __attribute__ (( packed ));
+
+/** An interrupt descriptor table entry */
+struct interrupt_descriptor {
+ /** Low 16 bits of address */
+ uint16_t low;
+ /** Code segment */
+ uint16_t segment;
+ /** Unused */
+ uint8_t unused;
+ /** Type and attributes */
+ uint8_t attr;
+ /** High 16 bits of address */
+ uint16_t high;
+} __attribute__ (( packed ));
+
+/** Interrupt descriptor is present */
+#define IDTE_PRESENT 0x80
+
+/** Interrupt descriptor 32-bit interrupt gate type */
+#define IDTE_TYPE_IRQ32 0x0e
+
+/** An interrupt vector
+ *
+ * Each interrupt vector comprises an eight-byte fragment of code:
+ *
+ * 60 pushal
+ * b0 xx movb $INT, %al
+ * e9 xx xx xx xx jmp interrupt_wrapper
+ */
+struct interrupt_vector {
+ /** "pushal" instruction */
+ uint8_t pushal;
+ /** "movb" instruction */
+ uint8_t movb;
+ /** Interrupt number */
+ uint8_t intr;
+ /** "jmp" instruction */
+ uint8_t jmp;
+ /** Interrupt wrapper address offset */
+ uint32_t offset;
+ /** Next instruction after jump */
+ uint8_t next[0];
+} __attribute__ (( packed ));
+
+/** "pushal" instruction */
+#define PUSHAL_INSN 0x60
+
+/** "movb" instruction */
+#define MOVB_INSN 0xb0
+
+/** "jmp" instruction */
+#define JMP_INSN 0xe9
+
+extern void set_interrupt_vector ( unsigned int intr, void *vector );
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBRM_H */
diff --git a/roms/ipxe/src/arch/i386/include/memsizes.h b/roms/ipxe/src/arch/i386/include/memsizes.h
new file mode 100644
index 000000000..f115f7574
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/memsizes.h
@@ -0,0 +1,19 @@
+#ifndef _MEMSIZES_H
+#define _MEMSIZES_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <basemem.h>
+
+/**
+ * Get size of base memory from BIOS free base memory counter
+ *
+ * @ret basemem Base memory size, in kB
+ */
+static inline unsigned int basememsize ( void ) {
+ return get_fbms();
+}
+
+extern unsigned int extmemsize ( void );
+
+#endif /* _MEMSIZES_H */
diff --git a/roms/ipxe/src/arch/i386/include/multiboot.h b/roms/ipxe/src/arch/i386/include/multiboot.h
new file mode 100644
index 000000000..ae09df6c7
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/multiboot.h
@@ -0,0 +1,149 @@
+#ifndef _MULTIBOOT_H
+#define _MULTIBOOT_H
+
+/**
+ * @file
+ *
+ * Multiboot operating systems
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/** The magic number for the Multiboot header */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/** Boot modules must be page aligned */
+#define MB_FLAG_PGALIGN 0x00000001
+
+/** Memory map must be provided */
+#define MB_FLAG_MEMMAP 0x00000002
+
+/** Video mode information must be provided */
+#define MB_FLAG_VIDMODE 0x00000004
+
+/** Image is a raw multiboot image (not ELF) */
+#define MB_FLAG_RAW 0x00010000
+
+/**
+ * The magic number passed by a Multiboot-compliant boot loader
+ *
+ * Must be passed in register %eax when jumping to the Multiboot OS
+ * image.
+ */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+/** Multiboot information structure mem_* fields are valid */
+#define MBI_FLAG_MEM 0x00000001
+
+/** Multiboot information structure boot_device field is valid */
+#define MBI_FLAG_BOOTDEV 0x00000002
+
+/** Multiboot information structure cmdline field is valid */
+#define MBI_FLAG_CMDLINE 0x00000004
+
+/** Multiboot information structure module fields are valid */
+#define MBI_FLAG_MODS 0x00000008
+
+/** Multiboot information structure a.out symbol table is valid */
+#define MBI_FLAG_AOUT 0x00000010
+
+/** Multiboot information struture ELF section header table is valid */
+#define MBI_FLAG_ELF 0x00000020
+
+/** Multiboot information structure memory map is valid */
+#define MBI_FLAG_MMAP 0x00000040
+
+/** Multiboot information structure drive list is valid */
+#define MBI_FLAG_DRIVES 0x00000080
+
+/** Multiboot information structure ROM configuration field is valid */
+#define MBI_FLAG_CFGTBL 0x00000100
+
+/** Multiboot information structure boot loader name field is valid */
+#define MBI_FLAG_LOADER 0x00000200
+
+/** Multiboot information structure APM table is valid */
+#define MBI_FLAG_APM 0x00000400
+
+/** Multiboot information structure video information is valid */
+#define MBI_FLAG_VBE 0x00000800
+
+/** A multiboot header */
+struct multiboot_header {
+ uint32_t magic;
+ uint32_t flags;
+ uint32_t checksum;
+ uint32_t header_addr;
+ uint32_t load_addr;
+ uint32_t load_end_addr;
+ uint32_t bss_end_addr;
+ uint32_t entry_addr;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot a.out symbol table */
+struct multiboot_aout_symbol_table {
+ uint32_t tabsize;
+ uint32_t strsize;
+ uint32_t addr;
+ uint32_t reserved;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot ELF section header table */
+struct multiboot_elf_section_header_table {
+ uint32_t num;
+ uint32_t size;
+ uint32_t addr;
+ uint32_t shndx;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot information structure */
+struct multiboot_info {
+ uint32_t flags;
+ uint32_t mem_lower;
+ uint32_t mem_upper;
+ uint32_t boot_device;
+ uint32_t cmdline;
+ uint32_t mods_count;
+ uint32_t mods_addr;
+ union {
+ struct multiboot_aout_symbol_table aout_syms;
+ struct multiboot_elf_section_header_table elf_sections;
+ } syms;
+ uint32_t mmap_length;
+ uint32_t mmap_addr;
+ uint32_t drives_length;
+ uint32_t drives_addr;
+ uint32_t config_table;
+ uint32_t boot_loader_name;
+ uint32_t apm_table;
+ uint32_t vbe_control_info;
+ uint32_t vbe_mode_info;
+ uint16_t vbe_mode;
+ uint16_t vbe_interface_seg;
+ uint16_t vbe_interface_off;
+ uint16_t vbe_interface_len;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot module structure */
+struct multiboot_module {
+ uint32_t mod_start;
+ uint32_t mod_end;
+ uint32_t string;
+ uint32_t reserved;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot memory map entry */
+struct multiboot_memory_map {
+ uint32_t size;
+ uint64_t base_addr;
+ uint64_t length;
+ uint32_t type;
+} __attribute__ (( packed, may_alias ));
+
+/** Usable RAM */
+#define MBMEM_RAM 1
+
+#endif /* _MULTIBOOT_H */
diff --git a/roms/ipxe/src/arch/i386/include/pnpbios.h b/roms/ipxe/src/arch/i386/include/pnpbios.h
new file mode 100644
index 000000000..d14873700
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pnpbios.h
@@ -0,0 +1,17 @@
+#ifndef _PNPBIOS_H
+#define _PNPBIOS_H
+
+/** @file
+ *
+ * PnP BIOS
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/* BIOS segment address */
+#define BIOS_SEG 0xf000
+
+extern int find_pnp_bios ( void );
+
+#endif /* _PNPBIOS_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxe.h b/roms/ipxe/src/arch/i386/include/pxe.h
new file mode 100644
index 000000000..66d752683
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxe.h
@@ -0,0 +1,199 @@
+#ifndef PXE_H
+#define PXE_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "pxe_types.h"
+#include "pxe_error.h"
+#include "pxe_api.h"
+#include <ipxe/device.h>
+#include <ipxe/tables.h>
+
+/** PXE API invalid function code */
+#define PXENV_UNKNOWN 0xffff
+
+/** Parameter block for pxenv_unknown() */
+struct s_PXENV_UNKNOWN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNKNOWN PXENV_UNKNOWN_t;
+
+/* Union used for PXE API calls; we don't know the type of the
+ * structure until we interpret the opcode. Also, Status is available
+ * in the same location for any opcode, and it's convenient to have
+ * non-specific access to it.
+ */
+union u_PXENV_ANY {
+ /* Make it easy to read status for any operation */
+ PXENV_STATUS_t Status;
+ struct s_PXENV_UNKNOWN unknown;
+ struct s_PXENV_UNLOAD_STACK unload_stack;
+ struct s_PXENV_GET_CACHED_INFO get_cached_info;
+ struct s_PXENV_TFTP_READ_FILE restart_tftp;
+ struct s_PXENV_START_UNDI start_undi;
+ struct s_PXENV_STOP_UNDI stop_undi;
+ struct s_PXENV_START_BASE start_base;
+ struct s_PXENV_STOP_BASE stop_base;
+ struct s_PXENV_TFTP_OPEN tftp_open;
+ struct s_PXENV_TFTP_CLOSE tftp_close;
+ struct s_PXENV_TFTP_READ tftp_read;
+ struct s_PXENV_TFTP_READ_FILE tftp_read_file;
+ struct s_PXENV_TFTP_GET_FSIZE tftp_get_fsize;
+ struct s_PXENV_UDP_OPEN udp_open;
+ struct s_PXENV_UDP_CLOSE udp_close;
+ struct s_PXENV_UDP_WRITE udp_write;
+ struct s_PXENV_UDP_READ udp_read;
+ struct s_PXENV_UNDI_STARTUP undi_startup;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_UNDI_INITIALIZE undi_initialize;
+ struct s_PXENV_UNDI_RESET undi_reset_adapter;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_OPEN undi_open;
+ struct s_PXENV_UNDI_CLOSE undi_close;
+ struct s_PXENV_UNDI_TRANSMIT undi_transmit;
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address;
+ struct s_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter;
+ struct s_PXENV_UNDI_GET_INFORMATION undi_get_information;
+ struct s_PXENV_UNDI_GET_STATISTICS undi_get_statistics;
+ struct s_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics;
+ struct s_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags;
+ struct s_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt;
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address;
+ struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type;
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info;
+ struct s_PXENV_UNDI_GET_STATE undi_get_state;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct s_PXENV_FILE_OPEN file_open;
+ struct s_PXENV_FILE_CLOSE file_close;
+ struct s_PXENV_FILE_SELECT file_select;
+ struct s_PXENV_FILE_READ file_read;
+ struct s_PXENV_GET_FILE_SIZE get_file_size;
+ struct s_PXENV_FILE_EXEC file_exec;
+ struct s_PXENV_FILE_API_CHECK file_api_check;
+ struct s_PXENV_FILE_EXIT_HOOK file_exit_hook;
+};
+
+typedef union u_PXENV_ANY PXENV_ANY_t;
+
+/** A PXE API call */
+struct pxe_api_call {
+ /** Entry point
+ *
+ * @v params PXE API call parameters
+ * @ret exit PXE API call exit code
+ */
+ PXENV_EXIT_t ( * entry ) ( union u_PXENV_ANY *params );
+ /** Length of parameters */
+ uint16_t params_len;
+ /** Opcode */
+ uint16_t opcode;
+};
+
+/** PXE API call table */
+#define PXE_API_CALLS __table ( struct pxe_api_call, "pxe_api_calls" )
+
+/** Declare a PXE API call */
+#define __pxe_api_call __table_entry ( PXE_API_CALLS, 01 )
+
+/**
+ * Define a PXE API call
+ *
+ * @v _opcode Opcode
+ * @v _entry Entry point
+ * @v _params_type Type of parameter structure
+ * @ret call PXE API call
+ */
+#define PXE_API_CALL( _opcode, _entry, _params_type ) { \
+ .entry = ( ( ( ( PXENV_EXIT_t ( * ) ( _params_type *params ) ) NULL ) \
+ == ( ( typeof ( _entry ) * ) NULL ) ) \
+ ? ( ( PXENV_EXIT_t ( * ) \
+ ( union u_PXENV_ANY *params ) ) _entry ) \
+ : ( ( PXENV_EXIT_t ( * ) \
+ ( union u_PXENV_ANY *params ) ) _entry ) ), \
+ .params_len = sizeof ( _params_type ), \
+ .opcode = _opcode, \
+ }
+
+/** An UNDI expansion ROM header */
+struct undi_rom_header {
+ /** Signature
+ *
+ * Must be equal to @c ROM_SIGNATURE
+ */
+ UINT16_t Signature;
+ /** ROM length in 512-byte blocks */
+ UINT8_t ROMLength;
+ /** Unused */
+ UINT8_t unused[0x13];
+ /** Offset of the PXE ROM ID structure */
+ UINT16_t PXEROMID;
+ /** Offset of the PCI ROM structure */
+ UINT16_t PCIRHeader;
+} __attribute__ (( packed ));
+
+/** Signature for an expansion ROM */
+#define ROM_SIGNATURE 0xaa55
+
+/** An UNDI ROM ID structure */
+struct undi_rom_id {
+ /** Signature
+ *
+ * Must be equal to @c UNDI_ROM_ID_SIGNATURE
+ */
+ UINT32_t Signature;
+ /** Length of structure */
+ UINT8_t StructLength;
+ /** Checksum */
+ UINT8_t StructCksum;
+ /** Structure revision
+ *
+ * Must be zero.
+ */
+ UINT8_t StructRev;
+ /** UNDI revision
+ *
+ * Version 2.1.0 is encoded as the byte sequence 0x00, 0x01, 0x02.
+ */
+ UINT8_t UNDIRev[3];
+ /** Offset to UNDI loader */
+ UINT16_t UNDILoader;
+ /** Minimum required stack segment size */
+ UINT16_t StackSize;
+ /** Minimum required data segment size */
+ UINT16_t DataSize;
+ /** Minimum required code segment size */
+ UINT16_t CodeSize;
+} __attribute__ (( packed ));
+
+/** Signature for an UNDI ROM ID structure */
+#define UNDI_ROM_ID_SIGNATURE \
+ ( ( 'U' << 0 ) + ( 'N' << 8 ) + ( 'D' << 16 ) + ( 'I' << 24 ) )
+
+/** A PCI expansion header */
+struct pcir_header {
+ /** Signature
+ *
+ * Must be equal to @c PCIR_SIGNATURE
+ */
+ uint32_t signature;
+ /** PCI vendor ID */
+ uint16_t vendor_id;
+ /** PCI device ID */
+ uint16_t device_id;
+} __attribute__ (( packed ));
+
+/** Signature for an UNDI ROM ID structure */
+#define PCIR_SIGNATURE \
+ ( ( 'P' << 0 ) + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
+
+extern struct net_device *pxe_netdev;
+extern const char *pxe_cmdline;
+
+extern void pxe_set_netdev ( struct net_device *netdev );
+extern PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file );
+extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
+
+#endif /* PXE_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxe_api.h b/roms/ipxe/src/arch/i386/include/pxe_api.h
new file mode 100644
index 000000000..3110d26da
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxe_api.h
@@ -0,0 +1,1823 @@
+#ifndef PXE_API_H
+#define PXE_API_H
+
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ * As an alternative, at your option, you may use this file under the
+ * following terms, known as the "MIT license":
+ *
+ * Copyright (c) 2005-2009 Michael Brown <mbrown@fensystems.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/** @file
+ *
+ * Preboot eXecution Environment (PXE) API
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "pxe_types.h"
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_api_call PXE entry points
+ *
+ * PXE entry points and calling conventions
+ *
+ * @{
+ */
+
+/** The PXENV+ structure */
+struct s_PXENV {
+ /** Signature
+ *
+ * Contains the bytes 'P', 'X', 'E', 'N', 'V', '+'.
+ */
+ UINT8_t Signature[6];
+ /** PXE API version
+ *
+ * MSB is major version number, LSB is minor version number.
+ * If the API version number is 0x0201 or greater, the !PXE
+ * structure pointed to by #PXEPtr should be used instead of
+ * this data structure.
+ */
+ UINT16_t Version;
+ UINT8_t Length; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #Length) must be zero.
+ */
+ UINT8_t Checksum;
+ SEGOFF16_t RMEntry; /**< Real-mode PXENV+ entry point */
+ /** Protected-mode PXENV+ entry point offset
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ UINT32_t PMOffset;
+ /** Protected-mode PXENV+ entry point segment selector
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ SEGSEL_t PMSelector;
+ SEGSEL_t StackSeg; /**< Stack segment selector */
+ UINT16_t StackSize; /**< Stack segment size */
+ SEGSEL_t BC_CodeSeg; /**< Base-code code segment selector */
+ UINT16_t BC_CodeSize; /**< Base-code code segment size */
+ SEGSEL_t BC_DataSeg; /**< Base-code data segment selector */
+ UINT16_t BC_DataSize; /**< Base-code data segment size */
+ SEGSEL_t UNDIDataSeg; /**< UNDI data segment selector */
+ UINT16_t UNDIDataSize; /**< UNDI data segment size */
+ SEGSEL_t UNDICodeSeg; /**< UNDI code segment selector */
+ UINT16_t UNDICodeSize; /**< UNDI code segment size */
+ /** Address of the !PXE structure
+ *
+ * This field is present only if #Version is 0x0201 or
+ * greater. If present, it points to a struct s_PXE.
+ */
+ SEGOFF16_t PXEPtr;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV PXENV_t;
+
+/** The !PXE structure */
+struct s_PXE {
+ /** Signature
+ *
+ * Contains the bytes '!', 'P', 'X', 'E'.
+ */
+ UINT8_t Signature[4];
+ UINT8_t StructLength; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #StructLength) must be zero.
+ */
+ UINT8_t StructCksum;
+ /** Revision of this structure
+ *
+ * For PXE version 2.1, this field must be zero.
+ */
+ UINT8_t StructRev;
+ UINT8_t reserved_1; /**< Must be zero */
+ /** Address of the UNDI ROM ID structure
+ *
+ * This is a pointer to a struct s_UNDI_ROM_ID.
+ */
+ SEGOFF16_t UNDIROMID;
+ /** Address of the Base Code ROM ID structure
+ *
+ * This is a pointer to a struct s_BC_ROM_ID.
+ */
+ SEGOFF16_t BaseROMID;
+ /** 16-bit !PXE entry point
+ *
+ * This is the entry point for either real mode, or protected
+ * mode with a 16-bit stack segment.
+ */
+ SEGOFF16_t EntryPointSP;
+ /** 32-bit !PXE entry point
+ *
+ * This is the entry point for protected mode with a 32-bit
+ * stack segment.
+ */
+ SEGOFF16_t EntryPointESP;
+ /** Status call-out function
+ *
+ * @v 0 (if in a time-out loop)
+ * @v n Number of a received TFTP packet
+ * @ret 0 Continue operation
+ * @ret 1 Cancel operation
+ *
+ * This function will be called whenever the PXE stack is in
+ * protected mode, is waiting for an event (e.g. a DHCP reply)
+ * and wishes to allow the user to cancel the operation.
+ * Parameters are passed in register %ax; the return value
+ * must also be placed in register %ax. All other registers
+ * and flags @b must be preserved.
+ *
+ * In real mode, an internal function (that checks for a
+ * keypress) will be used.
+ *
+ * If this field is set to -1, no status call-out function
+ * will be used and consequently the user will not be allowed
+ * to interrupt operations.
+ *
+ * @note The PXE specification version 2.1 defines the
+ * StatusCallout field, mentions it 11 times, but nowhere
+ * defines what it actually does or how it gets called.
+ * Fortunately, the WfM specification version 1.1a deigns to
+ * inform us of such petty details.
+ */
+ SEGOFF16_t StatusCallout;
+ UINT8_t reserved_2; /**< Must be zero */
+ /** Number of segment descriptors
+ *
+ * If this number is greater than 7, the remaining descriptors
+ * follow immediately after #BC_CodeWrite.
+ */
+ UINT8_t SegDescCnt;
+ /** First protected-mode selector
+ *
+ * This is the segment selector value for the first segment
+ * assigned to PXE. Protected-mode selectors must be
+ * consecutive, according to the PXE 2.1 specification, though
+ * no reason is given. Each #SEGDESC_t includes a field for
+ * the segment selector, so this information is entirely
+ * redundant.
+ */
+ SEGSEL_t FirstSelector;
+ /** Stack segment descriptor */
+ SEGDESC_t Stack;
+ /** UNDI data segment descriptor */
+ SEGDESC_t UNDIData;
+ /** UNDI code segment descriptor */
+ SEGDESC_t UNDICode;
+ /** UNDI writable code segment descriptor */
+ SEGDESC_t UNDICodeWrite;
+ /** Base-code data segment descriptor */
+ SEGDESC_t BC_Data;
+ /** Base-code code segment descriptor */
+ SEGDESC_t BC_Code;
+ /** Base-code writable code segment descriptor */
+ SEGDESC_t BC_CodeWrite;
+} __attribute__ (( packed ));
+
+typedef struct s_PXE PXE_t;
+
+/** @} */ /* pxe_api_call */
+
+/** @defgroup pxe_preboot_api PXE Preboot API
+ *
+ * General high-level functions: #PXENV_UNLOAD_STACK, #PXENV_START_UNDI etc.
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_unload_stack PXENV_UNLOAD_STACK
+ *
+ * UNLOAD BASE CODE STACK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_unload_stack() */
+#define PXENV_UNLOAD_STACK 0x0070
+
+/** Parameter block for pxenv_unload_stack() */
+struct s_PXENV_UNLOAD_STACK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t reserved[10]; /**< Must be zero */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNLOAD_STACK PXENV_UNLOAD_STACK_t;
+
+/** @} */ /* pxenv_unload_stack */
+
+/** @defgroup pxenv_get_cached_info PXENV_GET_CACHED_INFO
+ *
+ * GET CACHED INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_get_cached_info() */
+#define PXENV_GET_CACHED_INFO 0x0071
+
+/** The client's DHCPDISCOVER packet */
+#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+
+/** The DHCP server's DHCPACK packet */
+#define PXENV_PACKET_TYPE_DHCP_ACK 2
+
+/** The Boot Server's Discover Reply packet
+ *
+ * This packet contains DHCP option 60 set to "PXEClient", a valid
+ * boot file name, and may or may not contain MTFTP options.
+ */
+#define PXENV_PACKET_TYPE_CACHED_REPLY 3
+
+/** Parameter block for pxenv_get_cached_info() */
+struct s_PXENV_GET_CACHED_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Packet type.
+ *
+ * Valid values are #PXENV_PACKET_TYPE_DHCP_DISCOVER,
+ * #PXENV_PACKET_TYPE_DHCP_ACK or #PXENV_PACKET_TYPE_CACHED_REPLY
+ */
+ UINT16_t PacketType;
+ UINT16_t BufferSize; /**< Buffer size */
+ SEGOFF16_t Buffer; /**< Buffer address */
+ UINT16_t BufferLimit; /**< Maximum buffer size */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_GET_CACHED_INFO PXENV_GET_CACHED_INFO_t;
+
+#define BOOTP_REQ 1 /**< A BOOTP request packet */
+#define BOOTP_REP 2 /**< A BOOTP reply packet */
+
+/** DHCP broadcast flag
+ *
+ * Request a broadcast response (DHCPOFFER or DHCPACK) from the DHCP
+ * server.
+ */
+#define BOOTP_BCAST 0x8000
+
+#define VM_RFC1048 0x63825363L /**< DHCP magic cookie */
+
+/** Maximum length of DHCP options */
+#define BOOTP_DHCPVEND 1024
+
+/** Format of buffer filled in by pxenv_get_cached_info()
+ *
+ * This somewhat convoluted data structure simply describes the layout
+ * of a DHCP packet. Refer to RFC2131 section 2 for a full
+ * description.
+ */
+struct bootph {
+ /** Message opcode.
+ *
+ * Valid values are #BOOTP_REQ and #BOOTP_REP.
+ */
+ UINT8_t opcode;
+ /** NIC hardware type.
+ *
+ * Valid values are as for s_PXENV_UNDI_GET_INFORMATION::HwType.
+ */
+ UINT8_t Hardware;
+ UINT8_t Hardlen; /**< MAC address length */
+ /** Gateway hops
+ *
+ * Zero in packets sent by the client. May be non-zero in
+ * replies from the DHCP server, if the reply comes via a DHCP
+ * relay agent.
+ */
+ UINT8_t Gatehops;
+ UINT32_t ident; /**< DHCP transaction id (xid) */
+ /** Elapsed time
+ *
+ * Number of seconds since the client began the DHCP
+ * transaction.
+ */
+ UINT16_t seconds;
+ /** Flags
+ *
+ * This is the bitwise-OR of any of the following values:
+ * #BOOTP_BCAST.
+ */
+ UINT16_t Flags;
+ /** Client IP address
+ *
+ * Set only if the client already has an IP address.
+ */
+ IP4_t cip;
+ /** Your IP address
+ *
+ * This is the IP address that the server assigns to the
+ * client.
+ */
+ IP4_t yip;
+ /** Server IP address
+ *
+ * This is the IP address of the BOOTP/DHCP server.
+ */
+ IP4_t sip;
+ /** Gateway IP address
+ *
+ * This is the IP address of the BOOTP/DHCP relay agent, if
+ * any. It is @b not (necessarily) the address of the default
+ * gateway for routing purposes.
+ */
+ IP4_t gip;
+ MAC_ADDR_t CAddr; /**< Client MAC address */
+ UINT8_t Sname[64]; /**< Server host name */
+ UINT8_t bootfile[128]; /**< Boot file name */
+ /** DHCP options
+ *
+ * Don't ask. Just laugh. Then burn a copy of the PXE
+ * specification and send Intel an e-mail asking them if
+ * they've figured out what a "union" does in C yet.
+ */
+ union bootph_vendor {
+ UINT8_t d[BOOTP_DHCPVEND]; /**< DHCP options */
+ /** DHCP options */
+ struct bootph_vendor_v {
+ /** DHCP magic cookie
+ *
+ * Should have the value #VM_RFC1048.
+ */
+ UINT8_t magic[4];
+ UINT32_t flags; /**< BOOTP flags/opcodes */
+ /** "End of BOOTP vendor extensions"
+ *
+ * Abandon hope, all ye who consider the
+ * purpose of this field.
+ */
+ UINT8_t pad[56];
+ } v;
+ } vendor;
+} __attribute__ (( packed ));
+
+typedef struct bootph BOOTPLAYER_t;
+
+/** @} */ /* pxenv_get_cached_info */
+
+/** @defgroup pxenv_restart_tftp PXENV_RESTART_TFTP
+ *
+ * RESTART TFTP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_restart_tftp() */
+#define PXENV_RESTART_TFTP 0x0073
+
+/** Parameter block for pxenv_restart_tftp() */
+struct s_PXENV_TFTP_READ_FILE;
+
+typedef struct s_PXENV_RESTART_TFTP PXENV_RESTART_TFTP_t;
+
+/** @} */ /* pxenv_restart_tftp */
+
+/** @defgroup pxenv_start_undi PXENV_START_UNDI
+ *
+ * START UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_undi() */
+#define PXENV_START_UNDI 0x0000
+
+/** Parameter block for pxenv_start_undi() */
+struct s_PXENV_START_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** %ax register as passed to the Option ROM initialisation routine.
+ *
+ * For a PCI device, this should contain the bus:dev:fn value
+ * that uniquely identifies the PCI device in the system. For
+ * a non-PCI device, this field is not defined.
+ */
+ UINT16_t AX;
+ /** %bx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the Card Select
+ * Number assigned to the ISAPnP card. For non-ISAPnP
+ * devices, this should contain 0xffff.
+ */
+ UINT16_t BX;
+ /** %dx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the ISAPnP Read
+ * Port address as currently set in all ISAPnP cards. If
+ * there are no ISAPnP cards, this should contain 0xffff. (If
+ * this is a non-ISAPnP device, but there are ISAPnP cards in
+ * the system, this value is not well defined.)
+ */
+ UINT16_t DX;
+ /** %di register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #OFF16_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #OFF16_t and #UINT16_t are
+ * equivalent anyway; for other architectures #OFF16_t makes
+ * more sense.
+ */
+ OFF16_t DI;
+ /** %es register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #SEGSEL_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t ES;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_START_UNDI PXENV_START_UNDI_t;
+
+/** @} */ /* pxenv_start_undi */
+
+/** @defgroup pxenv_stop_undi PXENV_STOP_UNDI
+ *
+ * STOP UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_undi() */
+#define PXENV_STOP_UNDI 0x0015
+
+/** Parameter block for pxenv_stop_undi() */
+struct s_PXENV_STOP_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_STOP_UNDI PXENV_STOP_UNDI_t;
+
+/** @} */ /* pxenv_stop_undi */
+
+/** @defgroup pxenv_start_base PXENV_START_BASE
+ *
+ * START BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_base() */
+#define PXENV_START_BASE 0x0075
+
+/** Parameter block for pxenv_start_base() */
+struct s_PXENV_START_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_START_BASE PXENV_START_BASE_t;
+
+/** @} */ /* pxenv_start_base */
+
+/** @defgroup pxenv_stop_base PXENV_STOP_BASE
+ *
+ * STOP BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_base() */
+#define PXENV_STOP_BASE 0x0076
+
+/** Parameter block for pxenv_stop_base() */
+struct s_PXENV_STOP_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_STOP_BASE PXENV_STOP_BASE_t;
+
+/** @} */ /* pxenv_stop_base */
+
+/** @} */ /* pxe_preboot_api */
+
+/** @defgroup pxe_tftp_api PXE TFTP API
+ *
+ * Download files via TFTP or MTFTP
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_tftp_open PXENV_TFTP_OPEN
+ *
+ * TFTP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_open() */
+#define PXENV_TFTP_OPEN 0x0020
+
+/** Parameter block for pxenv_tftp_open() */
+struct s_PXENV_TFTP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UDP_PORT_t TFTPPort; /**< TFTP server UDP port */
+ /** Requested size of TFTP packets
+ *
+ * This is the TFTP "blksize" option. This must be at least
+ * 512, since servers that do not support TFTP options cannot
+ * negotiate blocksizes smaller than this.
+ */
+ UINT16_t PacketSize;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_TFTP_OPEN PXENV_TFTP_OPEN_t;
+
+/** @} */ /* pxenv_tftp_open */
+
+/** @defgroup pxenv_tftp_close PXENV_TFTP_CLOSE
+ *
+ * TFTP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_close() */
+#define PXENV_TFTP_CLOSE 0x0021
+
+/** Parameter block for pxenv_tftp_close() */
+struct s_PXENV_TFTP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_TFTP_CLOSE PXENV_TFTP_CLOSE_t;
+
+/** @} */ /* pxenv_tftp_close */
+
+/** @defgroup pxenv_tftp_read PXENV_TFTP_READ
+ *
+ * TFTP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read() */
+#define PXENV_TFTP_READ 0x0022
+
+/** Parameter block for pxenv_tftp_read() */
+struct s_PXENV_TFTP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t PacketNumber; /**< TFTP packet number */
+ UINT16_t BufferSize; /**< Size of data buffer */
+ SEGOFF16_t Buffer; /**< Address of data buffer */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_TFTP_READ PXENV_TFTP_READ_t;
+
+/** @} */ /* pxenv_tftp_read */
+
+/** @defgroup pxenv_tftp_read_file PXENV_TFTP_READ_FILE
+ *
+ * TFTP/MTFTP READ FILE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read_file() */
+#define PXENV_TFTP_READ_FILE 0x0023
+
+/** Parameter block for pxenv_tftp_read_file() */
+struct s_PXENV_TFTP_READ_FILE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t BufferSize; /**< Size of data buffer */
+ ADDR32_t Buffer; /**< Address of data buffer */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ /** File multicast IP address */
+ IP4_t McastIPAddress;
+ /** Client multicast listening port */
+ UDP_PORT_t TFTPClntPort;
+ /** Server multicast listening port */
+ UDP_PORT_t TFTPSrvPort;
+ /** TFTP open timeout.
+ *
+ * This is the timeout for receiving the first DATA or ACK
+ * packets during the MTFTP Listen phase.
+ */
+ UINT16_t TFTPOpenTimeOut;
+ /** TFTP reopen timeout.
+ *
+ * This is the timeout for receiving an ACK packet while in
+ * the MTFTP Listen phase (when at least one ACK packet has
+ * already been seen).
+ */
+ UINT16_t TFTPReopenDelay;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_TFTP_READ_FILE PXENV_TFTP_READ_FILE_t;
+
+/** @} */ /* pxenv_tftp_read_file */
+
+/** @defgroup pxenv_tftp_get_fsize PXENV_TFTP_GET_FSIZE
+ *
+ * TFTP GET FILE SIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_get_fsize() */
+#define PXENV_TFTP_GET_FSIZE 0x0025
+
+/** Parameter block for pxenv_tftp_get_fsize() */
+struct s_PXENV_TFTP_GET_FSIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t FileSize; /**< Size of the file */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_TFTP_GET_FSIZE PXENV_TFTP_GET_FSIZE_t;
+
+/** @} */ /* pxenv_tftp_get_fsize */
+
+/** @} */ /* pxe_tftp_api */
+
+/** @defgroup pxe_udp_api PXE UDP API
+ *
+ * Transmit and receive UDP packets
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_udp_open PXENV_UDP_OPEN
+ *
+ * UDP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_open() */
+#define PXENV_UDP_OPEN 0x0030
+
+/** Parameter block for pxenv_udp_open() */
+struct s_PXENV_UDP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< IP address of this station */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UDP_OPEN PXENV_UDP_OPEN_t;
+
+/** @} */ /* pxenv_udp_open */
+
+/** @defgroup pxenv_udp_close PXENV_UDP_CLOSE
+ *
+ * UDP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_close() */
+#define PXENV_UDP_CLOSE 0x0031
+
+/** Parameter block for pxenv_udp_close() */
+struct s_PXENV_UDP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UDP_CLOSE PXENV_UDP_CLOSE_t;
+
+/** @} */ /* pxenv_udp_close */
+
+/** @defgroup pxenv_udp_write PXENV_UDP_WRITE
+ *
+ * UDP WRITE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_write() */
+#define PXENV_UDP_WRITE 0x0033
+
+/** Parameter block for pxenv_udp_write() */
+struct s_PXENV_UDP_WRITE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ip; /**< Destination IP address */
+ IP4_t gw; /**< Relay agent IP address */
+ UDP_PORT_t src_port; /**< Source UDP port */
+ UDP_PORT_t dst_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UDP_WRITE PXENV_UDP_WRITE_t;
+
+/** @} */ /* pxenv_udp_write */
+
+/** @defgroup pxenv_udp_read PXENV_UDP_READ
+ *
+ * UDP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_read() */
+#define PXENV_UDP_READ 0x0032
+
+/** Parameter block for pxenv_udp_read() */
+struct s_PXENV_UDP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< Source IP address */
+ IP4_t dest_ip; /**< Destination IP address */
+ UDP_PORT_t s_port; /**< Source UDP port */
+ UDP_PORT_t d_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UDP_READ PXENV_UDP_READ_t;
+
+/** @} */ /* pxenv_udp_read */
+
+/** @} */ /* pxe_udp_api */
+
+/** @defgroup pxe_undi_api PXE UNDI API
+ *
+ * Direct control of the network interface card
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_undi_startup PXENV_UNDI_STARTUP
+ *
+ * UNDI STARTUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_startup() */
+#define PXENV_UNDI_STARTUP 0x0001
+
+#define PXENV_BUS_ISA 0 /**< ISA bus type */
+#define PXENV_BUS_EISA 1 /**< EISA bus type */
+#define PXENV_BUS_MCA 2 /**< MCA bus type */
+#define PXENV_BUS_PCI 3 /**< PCI bus type */
+#define PXENV_BUS_VESA 4 /**< VESA bus type */
+#define PXENV_BUS_PCMCIA 5 /**< PCMCIA bus type */
+
+/** Parameter block for pxenv_undi_startup() */
+struct s_PXENV_UNDI_STARTUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_STARTUP PXENV_UNDI_STARTUP_t;
+
+/** @} */ /* pxenv_undi_startup */
+
+/** @defgroup pxenv_undi_cleanup PXENV_UNDI_CLEANUP
+ *
+ * UNDI CLEANUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_cleanup() */
+#define PXENV_UNDI_CLEANUP 0x0002
+
+/** Parameter block for pxenv_undi_cleanup() */
+struct s_PXENV_UNDI_CLEANUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_CLEANUP PXENV_UNDI_CLEANUP_t;
+
+/** @} */ /* pxenv_undi_cleanup */
+
+/** @defgroup pxenv_undi_initialize PXENV_UNDI_INITIALIZE
+ *
+ * UNDI INITIALIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initialize() */
+#define PXENV_UNDI_INITIALIZE 0x0003
+
+/** Parameter block for pxenv_undi_initialize() */
+struct s_PXENV_UNDI_INITIALIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NDIS 2.0 configuration information, or NULL
+ *
+ * This is a pointer to the data structure returned by the
+ * NDIS 2.0 GetProtocolManagerInfo() API call. The data
+ * structure is documented, in a rather haphazard way, in
+ * section 4-17 of the NDIS 2.0 specification.
+ */
+ ADDR32_t ProtocolIni;
+ UINT8_t reserved[8]; /**< Must be zero */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_INITIALIZE PXENV_UNDI_INITIALIZE_t;
+
+/** @} */ /* pxenv_undi_initialize */
+
+/** @defgroup pxenv_undi_reset_adapter PXENV_UNDI_RESET_ADAPTER
+ *
+ * UNDI RESET ADAPTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_reset_adapter() */
+#define PXENV_UNDI_RESET_ADAPTER 0x0004
+
+/** Maximum number of multicast MAC addresses */
+#define MAXNUM_MCADDR 8
+
+/** List of multicast MAC addresses */
+struct s_PXENV_UNDI_MCAST_ADDRESS {
+ /** Number of multicast MAC addresses */
+ UINT16_t MCastAddrCount;
+ /** List of up to #MAXNUM_MCADDR multicast MAC addresses */
+ MAC_ADDR_t McastAddr[MAXNUM_MCADDR];
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_MCAST_ADDRESS PXENV_UNDI_MCAST_ADDRESS_t;
+
+/** Parameter block for pxenv_undi_reset_adapter() */
+struct s_PXENV_UNDI_RESET {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_RESET PXENV_UNDI_RESET_t;
+
+/** @} */ /* pxenv_undi_reset_adapter */
+
+/** @defgroup pxenv_undi_shutdown PXENV_UNDI_SHUTDOWN
+ *
+ * UNDI SHUTDOWN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_shutdown() */
+#define PXENV_UNDI_SHUTDOWN 0x0005
+
+/** Parameter block for pxenv_undi_shutdown() */
+struct s_PXENV_UNDI_SHUTDOWN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_SHUTDOWN PXENV_UNDI_SHUTDOWN_t;
+
+/** @} */ /* pxenv_undi_shutdown */
+
+/** @defgroup pxenv_undi_open PXENV_UNDI_OPEN
+ *
+ * UNDI OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_open() */
+#define PXENV_UNDI_OPEN 0x0006
+
+/** Accept "directed" packets
+ *
+ * These are packets addresses to either this adapter's MAC address or
+ * to any of the configured multicast MAC addresses (see
+ * #s_PXENV_UNDI_MCAST_ADDRESS).
+ */
+#define FLTR_DIRECTED 0x0001
+/** Accept broadcast packets */
+#define FLTR_BRDCST 0x0002
+/** Accept all packets; listen in promiscuous mode */
+#define FLTR_PRMSCS 0x0004
+/** Accept source-routed packets */
+#define FLTR_SRC_RTG 0x0008
+
+/** Parameter block for pxenv_undi_open() */
+struct s_PXENV_UNDI_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Open flags as defined in NDIS 2.0
+ *
+ * This is the OpenOptions field as passed to the NDIS 2.0
+ * OpenAdapter() API call. It is defined to be "adapter
+ * specific", though 0 is guaranteed to be a valid value.
+ */
+ UINT16_t OpenFlag;
+ /** Receive packet filter
+ *
+ * This is the bitwise-OR of any of the following flags:
+ * #FLTR_DIRECTED, #FLTR_BRDCST, #FLTR_PRMSCS and
+ * #FLTR_SRC_RTG.
+ */
+ UINT16_t PktFilter;
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_OPEN PXENV_UNDI_OPEN_t;
+
+/** @} */ /* pxenv_undi_open */
+
+/** @defgroup pxenv_undi_close PXENV_UNDI_CLOSE
+ *
+ * UNDI CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_close() */
+#define PXENV_UNDI_CLOSE 0x0007
+
+/** Parameter block for pxenv_undi_close() */
+struct s_PXENV_UNDI_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_CLOSE PXENV_UNDI_CLOSE_t;
+
+/** @} */ /* pxenv_undi_close */
+
+/** @defgroup pxenv_undi_transmit PXENV_UNDI_TRANSMIT
+ *
+ * UNDI TRANSMIT PACKET
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_transmit() */
+#define PXENV_UNDI_TRANSMIT 0x0008
+
+#define P_UNKNOWN 0 /**< Media header already filled in */
+#define P_IP 1 /**< IP protocol */
+#define P_ARP 2 /**< ARP protocol */
+#define P_RARP 3 /**< RARP protocol */
+#define P_OTHER 4 /**< Other protocol */
+
+#define XMT_DESTADDR 0x0000 /**< Unicast packet */
+#define XMT_BROADCAST 0x0001 /**< Broadcast packet */
+
+/** Maximum number of data blocks in a transmit buffer descriptor */
+#define MAX_DATA_BLKS 8
+
+/** A transmit buffer descriptor, as pointed to by s_PXENV_UNDI_TRANSMIT::TBD
+ */
+struct s_PXENV_UNDI_TBD {
+ UINT16_t ImmedLength; /**< Length of the transmit buffer */
+ SEGOFF16_t Xmit; /**< Address of the transmit buffer */
+ UINT16_t DataBlkCount;
+ /** Array of up to #MAX_DATA_BLKS additional transmit buffers */
+ struct DataBlk {
+ /** Always 1
+ *
+ * A value of 0 would indicate that #TDDataPtr were an
+ * #ADDR32_t rather than a #SEGOFF16_t. The PXE
+ * specification version 2.1 explicitly states that
+ * this is not supported; #TDDataPtr will always be a
+ * #SEGOFF16_t.
+ */
+ UINT8_t TDPtrType;
+ UINT8_t TDRsvdByte; /**< Must be zero */
+ UINT16_t TDDataLen; /**< Length of this transmit buffer */
+ SEGOFF16_t TDDataPtr; /**< Address of this transmit buffer */
+ } DataBlock[MAX_DATA_BLKS];
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_TBD PXENV_UNDI_TBD_t;
+
+/** Parameter block for pxenv_undi_transmit() */
+struct s_PXENV_UNDI_TRANSMIT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Protocol
+ *
+ * Valid values are #P_UNKNOWN, #P_IP, #P_ARP or #P_RARP. If
+ * the caller has already filled in the media header, this
+ * field must be set to #P_UNKNOWN.
+ */
+ UINT8_t Protocol;
+ /** Unicast/broadcast flag
+ *
+ * Valid values are #XMT_DESTADDR or #XMT_BROADCAST.
+ */
+ UINT8_t XmitFlag;
+ SEGOFF16_t DestAddr; /**< Destination MAC address */
+ /** Address of the Transmit Buffer Descriptor
+ *
+ * This is a pointer to a struct s_PXENV_UNDI_TBD.
+ */
+ SEGOFF16_t TBD;
+ UINT32_t Reserved[2]; /**< Must be zero */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_TRANSMIT PXENV_UNDI_TRANSMIT_t;
+
+/** @} */ /* pxenv_undi_transmit */
+
+/** @defgroup pxenv_undi_set_mcast_address PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * UNDI SET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_mcast_address() */
+#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
+
+/** Parameter block for pxenv_undi_set_mcast_address() */
+struct s_PXENV_UNDI_SET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** List of multicast addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS PXENV_UNDI_SET_MCAST_ADDRESS_t;
+
+/** @} */ /* pxenv_undi_set_mcast_address */
+
+/** @defgroup pxenv_undi_set_station_address PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * UNDI SET STATION ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_station_address() */
+#define PXENV_UNDI_SET_STATION_ADDRESS 0x000a
+
+/** Parameter block for pxenv_undi_set_station_address() */
+struct s_PXENV_UNDI_SET_STATION_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ MAC_ADDR_t StationAddress; /**< Station MAC address */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS PXENV_UNDI_SET_STATION_ADDRESS_t;
+
+/** @} */ /* pxenv_undi_set_station_address */
+
+/** @defgroup pxenv_undi_set_packet_filter PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * UNDI SET PACKET FILTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_packet_filter() */
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000b
+
+/** Parameter block for pxenv_undi_set_packet_filter() */
+struct s_PXENV_UNDI_SET_PACKET_FILTER {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Receive packet filter
+ *
+ * This field takes the same values as
+ * s_PXENV_UNDI_OPEN::PktFilter.
+ *
+ * @note Yes, this field is a different size to
+ * s_PXENV_UNDI_OPEN::PktFilter. Blame "the managers at Intel
+ * who apparently let a consultant come up with the spec
+ * without any kind of adult supervision" (quote from hpa).
+ */
+ UINT8_t filter;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_SET_PACKET_FILTER PXENV_UNDI_SET_PACKET_FILTER_t;
+
+/** @} */ /* pxenv_undi_set_packet_filter */
+
+/** @defgroup pxenv_undi_get_information PXENV_UNDI_GET_INFORMATION
+ *
+ * UNDI GET INFORMATION
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_information() */
+#define PXENV_UNDI_GET_INFORMATION 0x000c
+
+#define ETHER_TYPE 1 /**< Ethernet (10Mb) */
+#define EXP_ETHER_TYPE 2 /**< Experimental Ethernet (3Mb) */
+#define AX25_TYPE 3 /**< Amateur Radio AX.25 */
+#define TOKEN_RING_TYPE 4 /**< Proteon ProNET Token Ring */
+#define CHAOS_TYPE 5 /**< Chaos */
+#define IEEE_TYPE 6 /**< IEEE 802 Networks */
+#define ARCNET_TYPE 7 /**< ARCNET */
+
+/** Parameter block for pxenv_undi_get_information() */
+struct s_PXENV_UNDI_GET_INFORMATION {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t BaseIo; /**< I/O base address */
+ UINT16_t IntNumber; /**< IRQ number */
+ UINT16_t MaxTranUnit; /**< Adapter MTU */
+ /** Hardware type
+ *
+ * Valid values are defined in RFC1010 ("Assigned numbers"),
+ * and are #ETHER_TYPE, #EXP_ETHER_TYPE, #AX25_TYPE,
+ * #TOKEN_RING_TYPE, #CHAOS_TYPE, #IEEE_TYPE or #ARCNET_TYPE.
+ */
+ UINT16_t HwType;
+ UINT16_t HwAddrLen; /**< MAC address length */
+ MAC_ADDR_t CurrentNodeAddress; /**< Current MAC address */
+ MAC_ADDR_t PermNodeAddress; /**< Permanent (EEPROM) MAC address */
+ SEGSEL_t ROMAddress; /**< Real-mode ROM segment address */
+ UINT16_t RxBufCt; /**< Receive queue length */
+ UINT16_t TxBufCt; /**< Transmit queue length */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_INFORMATION PXENV_UNDI_GET_INFORMATION_t;
+
+/** @} */ /* pxenv_undi_get_information */
+
+/** @defgroup pxenv_undi_get_statistics PXENV_UNDI_GET_STATISTICS
+ *
+ * UNDI GET STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_statistics() */
+#define PXENV_UNDI_GET_STATISTICS 0x000d
+
+/** Parameter block for pxenv_undi_get_statistics() */
+struct s_PXENV_UNDI_GET_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT32_t XmtGoodFrames; /**< Successful transmission count */
+ UINT32_t RcvGoodFrames; /**< Successful reception count */
+ UINT32_t RcvCRCErrors; /**< Receive CRC error count */
+ UINT32_t RcvResourceErrors; /**< Receive queue overflow count */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_STATISTICS PXENV_UNDI_GET_STATISTICS_t;
+
+/** @} */ /* pxenv_undi_get_statistics */
+
+/** @defgroup pxenv_undi_clear_statistics PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * UNDI CLEAR STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_clear_statistics() */
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000e
+
+/** Parameter block for pxenv_undi_clear_statistics() */
+struct s_PXENV_UNDI_CLEAR_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_CLEAR_STATISTICS PXENV_UNDI_CLEAR_STATISTICS_t;
+
+/** @} */ /* pxenv_undi_clear_statistics */
+
+/** @defgroup pxenv_undi_initiate_diags PXENV_UNDI_INITIATE_DIAGS
+ *
+ * UNDI INITIATE DIAGS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initiate_diags() */
+#define PXENV_UNDI_INITIATE_DIAGS 0x000f
+
+/** Parameter block for pxenv_undi_initiate_diags() */
+struct s_PXENV_UNDI_INITIATE_DIAGS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_INITIATE_DIAGS PXENV_UNDI_INITIATE_DIAGS_t;
+
+/** @} */ /* pxenv_undi_initiate_diags */
+
+/** @defgroup pxenv_undi_force_interrupt PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * UNDI FORCE INTERRUPT
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_force_interrupt() */
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+
+/** Parameter block for pxenv_undi_force_interrupt() */
+struct s_PXENV_UNDI_FORCE_INTERRUPT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_FORCE_INTERRUPT PXENV_UNDI_FORCE_INTERRUPT_t;
+
+/** @} */ /* pxenv_undi_force_interrupt */
+
+/** @defgroup pxenv_undi_get_mcast_address PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * UNDI GET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_mcast_address() */
+#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
+
+/** Parameter block for pxenv_undi_get_mcast_address() */
+struct s_PXENV_UNDI_GET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t InetAddr; /**< Multicast IP address */
+ MAC_ADDR_t MediaAddr; /**< Multicast MAC address */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS PXENV_UNDI_GET_MCAST_ADDRESS_t;
+
+/** @} */ /* pxenv_undi_get_mcast_address */
+
+/** @defgroup pxenv_undi_get_nic_type PXENV_UNDI_GET_NIC_TYPE
+ *
+ * UNDI GET NIC TYPE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_nic_type() */
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+
+#define PCI_NIC 2 /**< PCI network card */
+#define PnP_NIC 3 /**< ISAPnP network card */
+#define CardBus_NIC 4 /**< CardBus network card */
+
+/** Information for a PCI or equivalent NIC */
+struct pci_nic_info {
+ UINT16_t Vendor_ID; /**< PCI vendor ID */
+ UINT16_t Dev_ID; /**< PCI device ID */
+ UINT8_t Base_Class; /**< PCI base class */
+ UINT8_t Sub_Class; /**< PCI sub class */
+ UINT8_t Prog_Intf; /**< PCI programming interface */
+ UINT8_t Rev; /**< PCI revision */
+ UINT16_t BusDevFunc; /**< PCI bus:dev:fn address */
+ UINT16_t SubVendor_ID; /**< PCI subvendor ID */
+ UINT16_t SubDevice_ID; /**< PCI subdevice ID */
+} __attribute__ (( packed ));
+
+/** Information for an ISAPnP or equivalent NIC */
+struct pnp_nic_info {
+ UINT32_t EISA_Dev_ID; /**< EISA device ID */
+ UINT8_t Base_Class; /**< Base class */
+ UINT8_t Sub_Class; /**< Sub class */
+ UINT8_t Prog_Intf; /**< Programming interface */
+ /** Card Select Number assigned to card */
+ UINT16_t CardSelNum;
+} __attribute__ (( packed ));
+
+/** Parameter block for pxenv_undi_get_nic_type() */
+struct s_PXENV_UNDI_GET_NIC_TYPE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NIC type
+ *
+ * Valid values are #PCI_NIC, #PnP_NIC or #CardBus_NIC.
+ */
+ UINT8_t NicType;
+ /** NIC information */
+ union nic_type_info {
+ /** NIC information (if #NicType==#PCI_NIC) */
+ struct pci_nic_info pci;
+ /** NIC information (if #NicType==#CardBus_NIC) */
+ struct pci_nic_info cardbus;
+ /** NIC information (if #NicType==#PnP_NIC) */
+ struct pnp_nic_info pnp;
+ } info;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_NIC_TYPE PXENV_UNDI_GET_NIC_TYPE_t;
+
+/** @} */ /* pxenv_undi_get_nic_type */
+
+/** @defgroup pxenv_undi_get_iface_info PXENV_UNDI_GET_IFACE_INFO
+ *
+ * UNDI GET IFACE INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_iface_info() */
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+
+/** Broadcast supported */
+#define SUPPORTED_BROADCAST 0x0001
+/** Multicast supported */
+#define SUPPORTED_MULTICAST 0x0002
+/** Functional/group addressing supported */
+#define SUPPORTED_GROUP 0x0004
+/** Promiscuous mode supported */
+#define SUPPORTED_PROMISCUOUS 0x0008
+/** Software settable station address */
+#define SUPPORTED_SET_STATION_ADDRESS 0x0010
+/** InitiateDiagnostics supported */
+#define SUPPORTED_DIAGNOSTICS 0x0040
+/** Reset MAC supported */
+#define SUPPORTED_RESET 0x0400
+/** Open / Close Adapter supported */
+#define SUPPORTED_OPEN_CLOSE 0x0800
+/** Interrupt Request supported */
+#define SUPPORTED_IRQ 0x1000
+
+/** Parameter block for pxenv_undi_get_iface_info() */
+struct s_PXENV_UNDI_GET_IFACE_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Interface type
+ *
+ * This is defined in the NDIS 2.0 specification to be one of
+ * the strings "802.3", "802.4", "802.5", "802.6", "DIX",
+ * "DIX+802.3", "APPLETALK", "ARCNET", "FDDI", "SDLC", "BSC",
+ * "HDLC", or "ISDN".
+ *
+ * "Normal" Ethernet, for various historical reasons, is
+ * "DIX+802.3".
+ */
+ UINT8_t IfaceType[16];
+ UINT32_t LinkSpeed; /**< Link speed, in bits per second */
+ /** Service flags
+ *
+ * These are the "service flags" defined in the "MAC
+ * Service-Specific Characteristics" table in the NDIS 2.0
+ * specification. Almost all of them are irrelevant to PXE.
+ */
+ UINT32_t ServiceFlags;
+ UINT32_t Reserved[4]; /**< Must be zero */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_IFACE_INFO PXENV_UNDI_GET_IFACE_INFO_t;
+
+/** @} */ /* pxenv_undi_get_iface_info */
+
+/** @defgroup pxenv_undi_get_state PXENV_UNDI_GET_STATE
+ *
+ * UNDI GET STATE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_state() */
+#define PXENV_UNDI_GET_STATE 0x0015
+
+/** pxenv_start_undi() has been called */
+#define PXE_UNDI_GET_STATE_STARTED 1
+/** pxenv_undi_initialize() has been called */
+#define PXE_UNDI_GET_STATE_INITIALIZED 2
+/** pxenv_undi_open() has been called */
+#define PXE_UNDI_GET_STATE_OPENED 3
+
+/** Parameter block for pxenv_undi_get_state() */
+struct s_PXENV_UNDI_GET_STATE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Current state of the UNDI driver
+ *
+ * Valid values are #PXE_UNDI_GET_STATE_STARTED,
+ * #PXE_UNDI_GET_STATE_INITIALIZED or
+ * #PXE_UNDI_GET_STATE_OPENED.
+ */
+ UINT8_t UNDIstate;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_GET_STATE PXENV_UNDI_GET_STATE_t;
+
+/** @} */ /* pxenv_undi_get_state */
+
+/** @defgroup pxenv_undi_isr PXENV_UNDI_ISR
+ *
+ * UNDI ISR
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_isr() */
+#define PXENV_UNDI_ISR 0x0014
+
+/** Determine whether or not this is our interrupt */
+#define PXENV_UNDI_ISR_IN_START 1
+/** Start processing interrupt */
+#define PXENV_UNDI_ISR_IN_PROCESS 2
+/** Continue processing interrupt */
+#define PXENV_UNDI_ISR_IN_GET_NEXT 3
+/** This interrupt was ours */
+#define PXENV_UNDI_ISR_OUT_OURS 0
+/** This interrupt was not ours */
+#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+/** Finished processing interrupt */
+#define PXENV_UNDI_ISR_OUT_DONE 0
+/** A packet transmission has completed */
+#define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+/** A packet has been received */
+#define PXENV_UNDI_ISR_OUT_RECEIVE 3
+/** We are already in the middle of processing an interrupt */
+#define PXENV_UNDI_ISR_OUT_BUSY 4
+
+/** Unicast packet (or packet captured in promiscuous mode) */
+#define P_DIRECTED 0
+/** Broadcast packet */
+#define P_BROADCAST 1
+/** Multicast packet */
+#define P_MULTICAST 2
+
+/** Parameter block for pxenv_undi_isr() */
+struct s_PXENV_UNDI_ISR {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Function flag
+ *
+ * Valid values are #PXENV_UNDI_ISR_IN_START,
+ * #PXENV_UNDI_ISR_IN_PROCESS, #PXENV_UNDI_ISR_IN_GET_NEXT,
+ * #PXENV_UNDI_ISR_OUT_OURS, #PXENV_UNDI_ISR_OUT_NOT_OURS,
+ * #PXENV_UNDI_ISR_OUT_DONE, #PXENV_UNDI_ISR_OUT_TRANSMIT,
+ * #PXENV_UNDI_ISR_OUT_RECEIVE or #PXENV_UNDI_ISR_OUT_BUSY.
+ */
+ UINT16_t FuncFlag;
+ UINT16_t BufferLength; /**< Data buffer length */
+ UINT16_t FrameLength; /**< Total frame length */
+ UINT16_t FrameHeaderLength; /**< Frame header length */
+ SEGOFF16_t Frame; /**< Data buffer address */
+ /** Protocol type
+ *
+ * Valid values are #P_IP, #P_ARP, #P_RARP or #P_OTHER.
+ */
+ UINT8_t ProtType;
+ /** Packet type
+ *
+ * Valid values are #P_DIRECTED, #P_BROADCAST or #P_MULTICAST.
+ */
+ UINT8_t PktType;
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_UNDI_ISR PXENV_UNDI_ISR_t;
+
+/** @} */ /* pxenv_undi_isr */
+
+/** @} */ /* pxe_undi_api */
+
+/** @defgroup pxe_file_api PXE FILE API
+ *
+ * POSIX-like file operations
+ *
+ * @{
+ */
+
+/** Minimum possible opcode used within PXE FILE API */
+#define PXENV_FILE_MIN 0x00e0
+
+/** Minimum possible opcode used within PXE FILE API */
+#define PXENV_FILE_MAX 0x00ef
+
+/** @defgroup pxenv_file_open PXENV_FILE_OPEN
+ *
+ * FILE OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_open() */
+#define PXENV_FILE_OPEN 0x00e0
+
+/** Parameter block for pxenv_file_open() */
+struct s_PXENV_FILE_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ SEGOFF16_t FileName; /**< File URL */
+ UINT32_t Reserved; /**< Reserved */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_OPEN PXENV_FILE_OPEN_t;
+
+/** @} */ /* pxenv_file_open */
+
+/** @defgroup pxenv_file_close PXENV_FILE_CLOSE
+ *
+ * FILE CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_close() */
+#define PXENV_FILE_CLOSE 0x00e1
+
+/** Parameter block for pxenv_file_close() */
+struct s_PXENV_FILE_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_CLOSE PXENV_FILE_CLOSE_t;
+
+/** @} */ /* pxenv_file_close */
+
+/** @defgroup pxenv_file_select PXENV_FILE_SELECT
+ *
+ * FILE SELECT
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_select() */
+#define PXENV_FILE_SELECT 0x00e2
+
+/** File is ready for reading */
+#define RDY_READ 0x0001
+
+/** Parameter block for pxenv_file_select() */
+struct s_PXENV_FILE_SELECT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT16_t Ready; /**< Indication of readiness */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_SELECT PXENV_FILE_SELECT_t;
+
+/** @} */ /* pxenv_file_select */
+
+/** @defgroup pxenv_file_read PXENV_FILE_READ
+ *
+ * FILE READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_read() */
+#define PXENV_FILE_READ 0x00e3
+
+/** Parameter block for pxenv_file_read() */
+struct s_PXENV_FILE_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT16_t BufferSize; /**< Data buffer size */
+ SEGOFF16_t Buffer; /**< Data buffer */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_READ PXENV_FILE_READ_t;
+
+/** @} */ /* pxenv_file_read */
+
+/** @defgroup pxenv_get_file_size PXENV_GET_FILE_SIZE
+ *
+ * GET FILE SIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_get_file_size() */
+#define PXENV_GET_FILE_SIZE 0x00e4
+
+/** Parameter block for pxenv_get_file_size() */
+struct s_PXENV_GET_FILE_SIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT32_t FileSize; /**< File size */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_GET_FILE_SIZE PXENV_GET_FILE_SIZE_t;
+
+/** @} */ /* pxenv_get_file_size */
+
+/** @defgroup pxenv_file_exec PXENV_FILE_EXEC
+ *
+ * FILE EXEC
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_exec() */
+#define PXENV_FILE_EXEC 0x00e5
+
+/** Parameter block for pxenv_file_exec() */
+struct s_PXENV_FILE_EXEC {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ SEGOFF16_t Command; /**< Command to execute */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_EXEC PXENV_FILE_EXEC_t;
+
+/** @} */ /* pxenv_file_exec */
+
+/** @defgroup pxenv_file_api_check PXENV_FILE_API_CHECK
+ *
+ * FILE API CHECK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_api_check() */
+#define PXENV_FILE_API_CHECK 0x00e6
+
+/** Parameter block for pxenv_file_api_check() */
+struct s_PXENV_FILE_API_CHECK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t Size; /**< Size of structure */
+ UINT32_t Magic; /**< Magic number */
+ UINT32_t Provider; /**< Implementation identifier */
+ UINT32_t APIMask; /**< Supported API functions */
+ UINT32_t Flags; /**< Reserved for the future */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_API_CHECK PXENV_FILE_API_CHECK_t;
+
+/** @} */ /* pxenv_file_api_check */
+
+/** @defgroup pxenv_file_exit_hook PXENV_FILE_EXIT_HOOK
+ *
+ * FILE EXIT HOOK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_exit_hook() */
+#define PXENV_FILE_EXIT_HOOK 0x00e7
+
+/** Parameter block for pxenv_file_exit_hook() */
+struct s_PXENV_FILE_EXIT_HOOK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ SEGOFF16_t Hook; /**< SEG16:OFF16 to jump to */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_EXIT_HOOK PXENV_FILE_EXIT_HOOK_t;
+
+/** @} */ /* pxenv_file_exit_hook */
+
+/** @defgroup pxenv_file_cmdline PXENV_FILE_CMDLINE
+ *
+ * FILE CMDLINE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_cmdline() */
+#define PXENV_FILE_CMDLINE 0x00e8
+
+/** Parameter block for pxenv_file_cmdline() */
+struct s_PXENV_FILE_CMDLINE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t BufferSize; /**< Data buffer size */
+ SEGOFF16_t Buffer; /**< Data buffer */
+} __attribute__ (( packed ));
+
+typedef struct s_PXENV_FILE_CMDLINE PXENV_FILE_CMDLINE_t;
+
+/** @} */ /* pxe_file_cmdline */
+
+/** @} */ /* pxe_file_api */
+
+/** @defgroup pxe_loader_api PXE Loader API
+ *
+ * The UNDI ROM loader API
+ *
+ * @{
+ */
+
+/** Parameter block for undi_loader() */
+struct s_UNDI_LOADER {
+ /** PXE status code */
+ PXENV_STATUS_t Status;
+ /** %ax register as for PXENV_START_UNDI */
+ UINT16_t AX;
+ /** %bx register as for PXENV_START_UNDI */
+ UINT16_t BX;
+ /** %dx register as for PXENV_START_UNDI */
+ UINT16_t DX;
+ /** %di register as for PXENV_START_UNDI */
+ OFF16_t DI;
+ /** %es register as for PXENV_START_UNDI */
+ SEGSEL_t ES;
+ /** UNDI data segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t UNDI_DS;
+ /** UNDI code segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t UNDI_CS;
+ /** Address of the !PXE structure (a struct s_PXE) */
+ SEGOFF16_t PXEptr;
+ /** Address of the PXENV+ structure (a struct s_PXENV) */
+ SEGOFF16_t PXENVptr;
+} __attribute__ (( packed ));
+
+typedef struct s_UNDI_LOADER UNDI_LOADER_t;
+
+/** @} */ /* pxe_loader_api */
+
+/** @} */ /* pxe */
+
+/** @page pxe_notes Etherboot PXE implementation notes
+
+@section pxe_routing IP routing
+
+Several PXE API calls (e.g. pxenv_tftp_open() and pxenv_udp_write())
+allow for the caller to specify a "relay agent IP address", often in a
+field called "gateway" or similar. The PXE specification states that
+"The IP layer should provide space for a minimum of four routing
+entries obtained from the default router and static route DHCP option
+tags in the DHCPACK message, plus any non-zero giaddr field from the
+DHCPOFFER message(s) accepted by the client".
+
+The DHCP static route option ("option static-routes" in dhcpd.conf)
+works only for classed IP routing (i.e. it provides no way to specify
+a subnet mask). Since virtually everything now uses classless IP
+routing, the DHCP static route option is almost totally useless, and
+is (according to the dhcp-options man page) not implemented by any of
+the popular DHCP clients.
+
+This leaves the caller-specified "relay agent IP address", the giaddr
+field from the DHCPOFFER message(s) and the default gateway(s)
+provided via the routers option ("option routers" in dhcpd.conf) in
+the DHCPACK message. Each of these is a default gateway address.
+It's a fair bet that the routers option should take priority over the
+giaddr field, since the routers option has to be explicitly specified
+by the DHCP server operator. Similarly, it's fair to assume that the
+caller-specified "relay agent IP address", if present, should take
+priority over any other routing table entries.
+
+@bug Etherboot currently ignores all potential sources of routing
+information other than the first router provided to it by a DHCP
+routers option.
+
+@section pxe_x86_modes x86 processor mode restrictions
+
+On the x86 platform, different PXE API calls have different
+restrictions on the processor modes (real or protected) that can be
+used. See the individual API call descriptions for the restrictions
+that apply to any particular call.
+
+@subsection pxe_x86_pmode16 Real mode, or protected-mode with 16-bit stack
+
+The PXE specification states that the API function can be called in
+protected mode only if the s_PXE::StatusCallout field is set to a
+non-zero value, and that the API function cannot be called with a
+32-bit stack segment.
+
+Etherboot does not enforce either of these restrictions; they seem (as
+with so much of the PXE specification) to be artifacts of the Intel
+implementation.
+
+*/
+
+#endif /* PXE_API_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxe_call.h b/roms/ipxe/src/arch/i386/include/pxe_call.h
new file mode 100644
index 000000000..cbd548318
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxe_call.h
@@ -0,0 +1,43 @@
+#ifndef _PXE_CALL_H
+#define _PXE_CALL_H
+
+/** @file
+ *
+ * PXE API entry point
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <pxe_api.h>
+#include <realmode.h>
+#include <setjmp.h>
+
+struct net_device;
+
+/** PXE load address segment */
+#define PXE_LOAD_SEGMENT 0
+
+/** PXE load address offset */
+#define PXE_LOAD_OFFSET 0x7c00
+
+/** PXE physical load address */
+#define PXE_LOAD_PHYS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET )
+
+/** !PXE structure */
+extern struct s_PXE __text16 ( ppxe );
+#define ppxe __use_text16 ( ppxe )
+
+/** PXENV+ structure */
+extern struct s_PXENV __text16 ( pxenv );
+#define pxenv __use_text16 ( pxenv )
+
+/** PXENV_RESTART_TFTP jump buffer */
+extern rmjmp_buf pxe_restart_nbp;
+
+extern void pxe_activate ( struct net_device *netdev );
+extern int pxe_deactivate ( void );
+extern int pxe_start_nbp ( void );
+extern __asmcall void pxe_api_call ( struct i386_all_regs *ix86 );
+extern int pxe_api_call_weak ( struct i386_all_regs *ix86 );
+
+#endif /* _PXE_CALL_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxe_error.h b/roms/ipxe/src/arch/i386/include/pxe_error.h
new file mode 100644
index 000000000..51298e665
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxe_error.h
@@ -0,0 +1,123 @@
+#ifndef PXE_ERROR_H
+#define PXE_ERROR_H
+
+/** @file
+ *
+ * Preboot eXecution Environment (PXE) error definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @defgroup pxeerrors PXE error codes
+ *
+ * @{
+ */
+
+/* Generic errors */
+#define PXENV_STATUS_SUCCESS 0x0000
+#define PXENV_STATUS_FAILURE 0x0001
+#define PXENV_STATUS_BAD_FUNC 0x0002
+#define PXENV_STATUS_UNSUPPORTED 0x0003
+#define PXENV_STATUS_KEEP_UNDI 0x0004
+#define PXENV_STATUS_KEEP_ALL 0x0005
+#define PXENV_STATUS_OUT_OF_RESOURCES 0x0006
+
+/* ARP errors (0x0010 to 0x001f) */
+#define PXENV_STATUS_ARP_TIMEOUT 0x0011
+
+/* Base-Code state errors */
+#define PXENV_STATUS_UDP_CLOSED 0x0018
+#define PXENV_STATUS_UDP_OPEN 0x0019
+#define PXENV_STATUS_TFTP_CLOSED 0x001a
+#define PXENV_STATUS_TFTP_OPEN 0x001b
+
+/* BIOS/system errors (0x0020 to 0x002f) */
+#define PXENV_STATUS_MCOPY_PROBLEM 0x0020
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x0021
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x0022
+#define PXENV_STATUS_BIS_INIT_FAILURE 0x0023
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x0024
+#define PXENV_STATUS_BIS_GBOA_FAILURE 0x0025
+#define PXENV_STATUS_BIS_FREE_FAILURE 0x0026
+#define PXENV_STATUS_BIS_GSI_FAILURE 0x0027
+#define PXENV_STATUS_BIS_BAD_CKSUM 0x0028
+
+/* TFTP/MTFTP errors (0x0030 to 0x003f) */
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x0030
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x0032
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x0033
+#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x0035
+#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x0036
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x0038
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x0039
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x003a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x003b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x003c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x003d
+#define PXENV_STATUS_TFTP_NO_FILESIZE 0x003e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x003f
+
+/* Reserved errors 0x0040 to 0x004f) */
+
+/* DHCP/BOOTP errors (0x0050 to 0x005f) */
+#define PXENV_STATUS_DHCP_TIMEOUT 0x0051
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x0052
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x0053
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x0054
+
+/* Driver errors (0x0060 to 0x006f) */
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x0060
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x0061
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x0062
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x0063
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x0064
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x0065
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x0066
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x0067
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x0068
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x0069
+#define PXENV_STATUS_UNDI_INVALID_STATE 0x006a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x006b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x006c
+
+/* ROM and NBP bootstrap errors (0x0070 to 0x007f) */
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x0074
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x0076
+#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x0077
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x0078
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x0079
+
+/* Environment NBP errors (0x0080 to 0x008f) */
+
+/* Reserved errors (0x0090 to 0x009f) */
+
+/* Miscellaneous errors (0x00a0 to 0x00af) */
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0x00a0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER 0x00a1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0x00a2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0x00a3
+
+/* BUSD errors (0x00b0 to 0x00bf) */
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0x00b0
+
+/* Loader errors (0x00c0 to 0x00cf) */
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0x00c0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID 0x00c1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0x00c2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0x00c3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0x00c4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0x00c5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0x00c6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0x00c8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0x00c9
+#define PXENV_STATUS_LOADER_UNDI_START 0x00ca
+#define PXENV_STATUS_LOADER_BC_START 0x00cb
+
+/** @} */
+
+/** Derive PXENV_STATUS code from iPXE error number */
+#define PXENV_STATUS( rc ) ( (-(rc)) & 0x00ff )
+
+#endif /* PXE_ERROR_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxe_types.h b/roms/ipxe/src/arch/i386/include/pxe_types.h
new file mode 100644
index 000000000..483666e33
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxe_types.h
@@ -0,0 +1,127 @@
+#ifndef PXE_TYPES_H
+#define PXE_TYPES_H
+
+/** @file
+ *
+ * PXE data types
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <errno.h> /* PXE status codes */
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_types PXE data types
+ *
+ * Basic PXE data types such as #UINT16_t, #ADDR32_t, #SEGSEL_t etc.
+ *
+ * These definitions are based on Table 1-1 ("Data Type Definitions")
+ * in the Intel PXE specification version 2.1. They have been
+ * generalised to non-x86 architectures where possible.
+ *
+ * @{
+ */
+
+/** An 8-bit unsigned integer */
+typedef uint8_t UINT8_t;
+
+/** A 16-bit unsigned integer */
+typedef uint16_t UINT16_t;
+
+/** A 32-bit unsigned integer */
+typedef uint32_t UINT32_t;
+
+/** A PXE exit code.
+ *
+ * Permitted values are #PXENV_EXIT_SUCCESS and #PXENV_EXIT_FAILURE.
+ *
+ */
+typedef UINT16_t PXENV_EXIT_t;
+#define PXENV_EXIT_SUCCESS 0x0000 /**< No error occurred */
+#define PXENV_EXIT_FAILURE 0x0001 /**< An error occurred */
+
+/** A PXE status code.
+ *
+ * Status codes are defined in errno.h.
+ *
+ */
+typedef UINT16_t PXENV_STATUS_t;
+
+/** An IPv4 address.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT32_t IP4_t;
+
+/** A UDP port.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT16_t UDP_PORT_t;
+
+/** Maximum length of a MAC address */
+#define MAC_ADDR_LEN 16
+
+/** A MAC address */
+typedef UINT8_t MAC_ADDR_t[MAC_ADDR_LEN];
+
+#ifndef HAVE_ARCH_ADDR32
+/** A physical address.
+ *
+ * For x86, this is a 32-bit physical address, and is therefore
+ * limited to the low 4GB.
+ *
+ */
+typedef UINT32_t ADDR32_t;
+#endif
+
+#ifndef HAVE_ARCH_SEGSEL
+/** A segment selector.
+ *
+ * For x86, this is a real mode segment (0x0000-0xffff), or a
+ * protected-mode segment selector, such as could be loaded into a
+ * segment register.
+ *
+ */
+typedef UINT16_t SEGSEL_t;
+#endif
+
+#ifndef HAVE_ARCH_OFF16
+/** An offset within a segment identified by #SEGSEL
+ *
+ * For x86, this is a 16-bit offset.
+ *
+ */
+typedef UINT16_t OFF16_t;
+#endif
+
+/** A segment:offset address
+ *
+ * For x86, this is a 16-bit real-mode or protected-mode
+ * segment:offset address.
+ *
+ */
+typedef struct s_SEGOFF16 {
+ OFF16_t offset; /**< Offset within the segment */
+ SEGSEL_t segment; /**< Segment selector */
+} __attribute__ (( packed )) SEGOFF16_t;
+
+/** A segment descriptor */
+typedef struct s_SEGDESC {
+ SEGSEL_t segment_address; /**< Segment selector */
+ ADDR32_t Physical_address; /**< Segment base address */
+ OFF16_t Seg_size; /**< Size of the segment */
+} __attribute__ (( packed )) SEGDESC_t;
+
+/** @} */ /* pxe_types */
+
+/** @} */ /* pxe */
+
+#endif /* PXE_TYPES_H */
diff --git a/roms/ipxe/src/arch/i386/include/pxeparent.h b/roms/ipxe/src/arch/i386/include/pxeparent.h
new file mode 100644
index 000000000..b31e24a76
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/pxeparent.h
@@ -0,0 +1,11 @@
+#ifndef PXEPARENT_H
+#define PXEPARENT_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <pxe_types.h>
+
+extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
+ void *params, size_t params_len );
+
+#endif
diff --git a/roms/ipxe/src/arch/i386/include/realmode.h b/roms/ipxe/src/arch/i386/include/realmode.h
new file mode 100644
index 000000000..4defd3b97
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/realmode.h
@@ -0,0 +1,139 @@
+#ifndef REALMODE_H
+#define REALMODE_H
+
+#include <stdint.h>
+#include <registers.h>
+#include <ipxe/uaccess.h>
+
+/*
+ * Data structures and type definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/*
+ * Declaration of variables in .data16
+ *
+ * To place a variable in the .data16 segment, declare it using the
+ * pattern:
+ *
+ * int __data16 ( foo );
+ * #define foo __use_data16 ( foo );
+ *
+ * extern uint32_t __data16 ( bar );
+ * #define bar __use_data16 ( bar );
+ *
+ * static long __data16 ( baz ) = 0xff000000UL;
+ * #define baz __use_data16 ( baz );
+ *
+ * i.e. take a normal declaration, add __data16() around the variable
+ * name, and add a line saying "#define <name> __use_data16 ( <name> )
+ *
+ * You can then access them just like any other variable, for example
+ *
+ * int x = foo + bar;
+ *
+ * This magic is achieved at a cost of only around 7 extra bytes per
+ * group of accesses to .data16 variables. When using KEEP_IT_REAL,
+ * there is no extra cost.
+ *
+ * You should place variables in .data16 when they need to be accessed
+ * by real-mode code. Real-mode assembly (e.g. as created by
+ * REAL_CODE()) can access these variables via the usual data segment.
+ * You can therefore write something like
+ *
+ * static uint16_t __data16 ( foo );
+ * #define foo __use_data16 ( foo )
+ *
+ * int bar ( void ) {
+ * __asm__ __volatile__ ( REAL_CODE ( "int $0xff\n\t"
+ * "movw %ax, foo" )
+ * : : );
+ * return foo;
+ * }
+ *
+ * Variables may also be placed in .text16 using __text16 and
+ * __use_text16. Some variables (e.g. chained interrupt vectors) fit
+ * most naturally in .text16; most should be in .data16.
+ *
+ * If you have only a pointer to a magic symbol within .data16 or
+ * .text16, rather than the symbol itself, you can attempt to extract
+ * the underlying symbol name using __from_data16() or
+ * __from_text16(). This is not for the faint-hearted; check the
+ * assembler output to make sure that it's doing the right thing.
+ */
+
+/**
+ * Convert segment:offset address to user buffer
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @ret buffer User buffer
+ */
+static inline __always_inline userptr_t
+real_to_user ( unsigned int segment, unsigned int offset ) {
+ return ( phys_to_user ( ( segment << 4 ) + offset ) );
+}
+
+/**
+ * Copy data to base memory
+ *
+ * @v dest_seg Destination segment
+ * @v dest_off Destination offset
+ * @v src Source
+ * @v len Length
+ */
+static inline __always_inline void
+copy_to_real ( unsigned int dest_seg, unsigned int dest_off,
+ void *src, size_t n ) {
+ copy_to_user ( real_to_user ( dest_seg, dest_off ), 0, src, n );
+}
+
+/**
+ * Copy data to base memory
+ *
+ * @v dest Destination
+ * @v src_seg Source segment
+ * @v src_off Source offset
+ * @v len Length
+ */
+static inline __always_inline void
+copy_from_real ( void *dest, unsigned int src_seg,
+ unsigned int src_off, size_t n ) {
+ copy_from_user ( dest, real_to_user ( src_seg, src_off ), 0, n );
+}
+
+/**
+ * Write a single variable to base memory
+ *
+ * @v var Variable to write
+ * @v dest_seg Destination segment
+ * @v dest_off Destination offset
+ */
+#define put_real( var, dest_seg, dest_off ) \
+ copy_to_real ( (dest_seg), (dest_off), &(var), sizeof (var) )
+
+/**
+ * Read a single variable from base memory
+ *
+ * @v var Variable to read
+ * @v src_seg Source segment
+ * @v src_off Source offset
+ */
+#define get_real( var, src_seg, src_off ) \
+ copy_from_real ( &(var), (src_seg), (src_off), sizeof (var) )
+
+/*
+ * REAL_CODE ( asm_code_str )
+ *
+ * This can be used in inline assembly to create a fragment of code
+ * that will execute in real mode. For example: to write a character
+ * to the BIOS console using INT 10, you would do something like:
+ *
+ * __asm__ __volatile__ ( REAL_CODE ( "int $0x16" )
+ * : "=a" ( character ) : "a" ( 0x0000 ) );
+ *
+ */
+
+#endif /* REALMODE_H */
diff --git a/roms/ipxe/src/arch/i386/include/registers.h b/roms/ipxe/src/arch/i386/include/registers.h
new file mode 100644
index 000000000..d9aa3c376
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/registers.h
@@ -0,0 +1,198 @@
+#ifndef REGISTERS_H
+#define REGISTERS_H
+
+/** @file
+ *
+ * i386 registers.
+ *
+ * This file defines data structures that allow easy access to i386
+ * register dumps.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * A 16-bit general register.
+ *
+ * This type encapsulates a 16-bit register such as %ax, %bx, %cx,
+ * %dx, %si, %di, %bp or %sp.
+ *
+ */
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } __attribute__ (( packed ));
+ uint16_t word;
+} __attribute__ (( packed )) reg16_t;
+
+/**
+ * A 32-bit general register.
+ *
+ * This type encapsulates a 32-bit register such as %eax, %ebx, %ecx,
+ * %edx, %esi, %edi, %ebp or %esp.
+ *
+ */
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } __attribute__ (( packed ));
+ uint16_t word;
+ uint32_t dword;
+} __attribute__ (( packed )) reg32_t;
+
+/**
+ * A 32-bit general register dump.
+ *
+ * This is the data structure that is created on the stack by the @c
+ * pushal instruction, and can be read back using the @c popal
+ * instruction.
+ *
+ */
+struct i386_regs {
+ union {
+ uint16_t di;
+ uint32_t edi;
+ };
+ union {
+ uint16_t si;
+ uint32_t esi;
+ };
+ union {
+ uint16_t bp;
+ uint32_t ebp;
+ };
+ union {
+ uint16_t sp;
+ uint32_t esp;
+ };
+ union {
+ struct {
+ uint8_t bl;
+ uint8_t bh;
+ } __attribute__ (( packed ));
+ uint16_t bx;
+ uint32_t ebx;
+ };
+ union {
+ struct {
+ uint8_t dl;
+ uint8_t dh;
+ } __attribute__ (( packed ));
+ uint16_t dx;
+ uint32_t edx;
+ };
+ union {
+ struct {
+ uint8_t cl;
+ uint8_t ch;
+ } __attribute__ (( packed ));
+ uint16_t cx;
+ uint32_t ecx;
+ };
+ union {
+ struct {
+ uint8_t al;
+ uint8_t ah;
+ } __attribute__ (( packed ));
+ uint16_t ax;
+ uint32_t eax;
+ };
+} __attribute__ (( packed ));
+
+/**
+ * A segment register dump.
+ *
+ * The i386 has no equivalent of the @c pushal or @c popal
+ * instructions for the segment registers. We adopt the convention of
+ * always using the sequences
+ *
+ * @code
+ *
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ *
+ * @endcode
+ *
+ * This is the data structure that is created and read back by these
+ * instruction sequences.
+ *
+ */
+struct i386_seg_regs {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} __attribute__ (( packed ));
+
+/**
+ * A full register dump.
+ *
+ * This data structure is created by the instructions
+ *
+ * @code
+ *
+ * pushfl
+ * pushal
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and can be read back using the instructions
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ * popal
+ * popfl
+ *
+ * @endcode
+ *
+ * prot_call() and kir_call() create this data structure on the stack
+ * and pass in a pointer to this structure.
+ *
+ */
+struct i386_all_regs {
+ struct i386_seg_regs segs;
+ struct i386_regs regs;
+ uint32_t flags;
+} __attribute__ (( packed ));
+
+/* Flags */
+#define CF ( 1 << 0 )
+#define PF ( 1 << 2 )
+#define AF ( 1 << 4 )
+#define ZF ( 1 << 6 )
+#define SF ( 1 << 7 )
+#define OF ( 1 << 11 )
+
+/* Segment:offset structure. Note that the order within the structure
+ * is offset:segment.
+ */
+struct segoff {
+ uint16_t offset;
+ uint16_t segment;
+} __attribute__ (( packed ));
+
+typedef struct segoff segoff_t;
+
+#endif /* REGISTERS_H */
diff --git a/roms/ipxe/src/arch/i386/include/rtc.h b/roms/ipxe/src/arch/i386/include/rtc.h
new file mode 100644
index 000000000..6294b63e3
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/rtc.h
@@ -0,0 +1,83 @@
+#ifndef _RTC_H
+#define _RTC_H
+
+/** @file
+ *
+ * CMOS Real-Time Clock (RTC)
+ *
+ * The CMOS/RTC registers are documented (with varying degrees of
+ * accuracy and consistency) at
+ *
+ * http://www.nondot.org/sabre/os/files/MiscHW/RealtimeClockFAQ.txt
+ * http://wiki.osdev.org/RTC
+ * http://wiki.osdev.org/CMOS
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <pic8259.h>
+
+/** RTC IRQ */
+#define RTC_IRQ 8
+
+/** RTC interrupt vector */
+#define RTC_INT IRQ_INT ( RTC_IRQ )
+
+/** CMOS/RTC address (and NMI) register */
+#define CMOS_ADDRESS 0x70
+
+/** NMI disable bit */
+#define CMOS_DISABLE_NMI 0x80
+
+/** CMOS/RTC data register */
+#define CMOS_DATA 0x71
+
+/** RTC seconds */
+#define RTC_SEC 0x00
+
+/** RTC minutes */
+#define RTC_MIN 0x02
+
+/** RTC hours */
+#define RTC_HOUR 0x04
+
+/** RTC weekday */
+#define RTC_WDAY 0x06
+
+/** RTC day of month */
+#define RTC_MDAY 0x07
+
+/** RTC month */
+#define RTC_MON 0x08
+
+/** RTC year */
+#define RTC_YEAR 0x09
+
+/** RTC status register A */
+#define RTC_STATUS_A 0x0a
+
+/** RTC update in progress bit */
+#define RTC_STATUS_A_UPDATE_IN_PROGRESS 0x80
+
+/** RTC status register B */
+#define RTC_STATUS_B 0x0b
+
+/** RTC 24 hour format bit */
+#define RTC_STATUS_B_24_HOUR 0x02
+
+/** RTC binary mode bit */
+#define RTC_STATUS_B_BINARY 0x04
+
+/** RTC Periodic Interrupt Enabled bit */
+#define RTC_STATUS_B_PIE 0x40
+
+/** RTC status register C */
+#define RTC_STATUS_C 0x0c
+
+/** RTC status register D */
+#define RTC_STATUS_D 0x0d
+
+/** CMOS default address */
+#define CMOS_DEFAULT_ADDRESS RTC_STATUS_D
+
+#endif /* _RTC_H */
diff --git a/roms/ipxe/src/arch/i386/include/sdi.h b/roms/ipxe/src/arch/i386/include/sdi.h
new file mode 100644
index 000000000..806c3f194
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/sdi.h
@@ -0,0 +1,39 @@
+#ifndef _SDI_H
+#define _SDI_H
+
+/** @file
+ *
+ * System Deployment Image (SDI)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** SDI image header */
+struct sdi_header {
+ /** Signature */
+ uint32_t magic;
+ /** Version (as an ASCII string) */
+ uint32_t version;
+ /** Reserved */
+ uint8_t reserved[8];
+ /** Boot code offset */
+ uint64_t boot_offset;
+ /** Boot code size */
+ uint64_t boot_size;
+} __attribute__ (( packed ));
+
+/** SDI image signature */
+#define SDI_MAGIC \
+ ( ( '$' << 0 ) | ( 'S' << 8 ) | ( 'D' << 16 ) | ( 'I' << 24 ) )
+
+/** SDI boot segment */
+#define SDI_BOOT_SEG 0x0000
+
+/** SDI boot offset */
+#define SDI_BOOT_OFF 0x7c00
+
+/** Constant to binary-OR with physical address of SDI image */
+#define SDI_WTF 0x41
+
+#endif /* _SDI_H */
diff --git a/roms/ipxe/src/arch/i386/include/setjmp.h b/roms/ipxe/src/arch/i386/include/setjmp.h
index 98566696a..fe1a9ef4d 100644
--- a/roms/ipxe/src/arch/i386/include/setjmp.h
+++ b/roms/ipxe/src/arch/i386/include/setjmp.h
@@ -4,6 +4,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
+#include <realmode.h>
/** A jump buffer */
typedef struct {
@@ -21,10 +22,29 @@ typedef struct {
uint32_t ebp;
} jmp_buf[1];
+/** A real-mode-extended jump buffer */
+typedef struct {
+ /** Jump buffer */
+ jmp_buf env;
+ /** Real-mode stack pointer */
+ segoff_t rm_stack;
+} rmjmp_buf[1];
+
extern int __asmcall __attribute__ (( returns_twice ))
setjmp ( jmp_buf env );
extern void __asmcall __attribute__ (( noreturn ))
longjmp ( jmp_buf env, int val );
+#define rmsetjmp( _env ) ( { \
+ (_env)->rm_stack.segment = rm_ss; \
+ (_env)->rm_stack.offset = rm_sp; \
+ setjmp ( (_env)->env ); } ) \
+
+#define rmlongjmp( _env, _val ) do { \
+ rm_ss = (_env)->rm_stack.segment; \
+ rm_sp = (_env)->rm_stack.offset; \
+ longjmp ( (_env)->env, (_val) ); \
+ } while ( 0 )
+
#endif /* _SETJMP_H */
diff --git a/roms/ipxe/src/arch/i386/include/undi.h b/roms/ipxe/src/arch/i386/include/undi.h
new file mode 100644
index 000000000..7a5624f93
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/undi.h
@@ -0,0 +1,106 @@
+#ifndef _UNDI_H
+#define _UNDI_H
+
+/** @file
+ *
+ * UNDI driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifndef ASSEMBLY
+
+#include <ipxe/device.h>
+#include <pxe_types.h>
+
+/** An UNDI device
+ *
+ * This structure is used by assembly code as well as C; do not alter
+ * this structure without editing pxeprefix.S to match.
+ */
+struct undi_device {
+ /** PXENV+ structure address */
+ SEGOFF16_t pxenv;
+ /** !PXE structure address */
+ SEGOFF16_t ppxe;
+ /** Entry point */
+ SEGOFF16_t entry;
+ /** Free base memory after load */
+ UINT16_t fbms;
+ /** Free base memory prior to load */
+ UINT16_t restore_fbms;
+ /** PCI bus:dev.fn, or @c UNDI_NO_PCI_BUSDEVFN */
+ UINT16_t pci_busdevfn;
+ /** ISAPnP card select number, or @c UNDI_NO_ISAPNP_CSN */
+ UINT16_t isapnp_csn;
+ /** ISAPnP read port, or @c UNDI_NO_ISAPNP_READ_PORT */
+ UINT16_t isapnp_read_port;
+ /** PCI vendor ID
+ *
+ * Filled in only for the preloaded UNDI device by pxeprefix.S
+ */
+ UINT16_t pci_vendor;
+ /** PCI device ID
+ *
+ * Filled in only for the preloaded UNDI device by pxeprefix.S
+ */
+ UINT16_t pci_device;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more UNDI_FL_XXX
+ * constants.
+ */
+ UINT16_t flags;
+
+ /** Generic device */
+ struct device dev;
+ /** Driver-private data
+ *
+ * Use undi_set_drvdata() and undi_get_drvdata() to access this
+ * field.
+ */
+ void *priv;
+} __attribute__ (( packed ));
+
+/**
+ * Set UNDI driver-private data
+ *
+ * @v undi UNDI device
+ * @v priv Private data
+ */
+static inline void undi_set_drvdata ( struct undi_device *undi, void *priv ) {
+ undi->priv = priv;
+}
+
+/**
+ * Get UNDI driver-private data
+ *
+ * @v undi UNDI device
+ * @ret priv Private data
+ */
+static inline void * undi_get_drvdata ( struct undi_device *undi ) {
+ return undi->priv;
+}
+
+#endif /* ASSEMBLY */
+
+/** PCI bus:dev.fn field is invalid */
+#define UNDI_NO_PCI_BUSDEVFN 0xffff
+
+/** ISAPnP card select number field is invalid */
+#define UNDI_NO_ISAPNP_CSN 0xffff
+
+/** ISAPnP read port field is invalid */
+#define UNDI_NO_ISAPNP_READ_PORT 0xffff
+
+/** UNDI flag: START_UNDI has been called */
+#define UNDI_FL_STARTED 0x0001
+
+/** UNDI flag: UNDI_STARTUP and UNDI_INITIALIZE have been called */
+#define UNDI_FL_INITIALIZED 0x0002
+
+/** UNDI flag: keep stack resident */
+#define UNDI_FL_KEEP_ALL 0x0004
+
+#endif /* _UNDI_H */
diff --git a/roms/ipxe/src/arch/i386/include/undiload.h b/roms/ipxe/src/arch/i386/include/undiload.h
new file mode 100644
index 000000000..235e7a79e
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/undiload.h
@@ -0,0 +1,35 @@
+#ifndef _UNDILOAD_H
+#define _UNDILOAD_H
+
+/** @file
+ *
+ * UNDI load/unload
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+struct undi_device;
+struct undi_rom;
+
+extern int undi_load ( struct undi_device *undi, struct undi_rom *undirom );
+extern int undi_unload ( struct undi_device *undi );
+
+/**
+ * Call UNDI loader to create a pixie
+ *
+ * @v undi UNDI device
+ * @v undirom UNDI ROM
+ * @v pci_busdevfn PCI bus:dev.fn
+ * @ret rc Return status code
+ */
+static inline int undi_load_pci ( struct undi_device *undi,
+ struct undi_rom *undirom,
+ unsigned int pci_busdevfn ) {
+ undi->pci_busdevfn = pci_busdevfn;
+ undi->isapnp_csn = UNDI_NO_ISAPNP_CSN;
+ undi->isapnp_read_port = UNDI_NO_ISAPNP_READ_PORT;
+ return undi_load ( undi, undirom );
+}
+
+#endif /* _UNDILOAD_H */
diff --git a/roms/ipxe/src/arch/i386/include/undinet.h b/roms/ipxe/src/arch/i386/include/undinet.h
new file mode 100644
index 000000000..2798c4466
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/undinet.h
@@ -0,0 +1,17 @@
+#ifndef _UNDINET_H
+#define _UNDINET_H
+
+/** @file
+ *
+ * UNDI network device driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+struct undi_device;
+
+extern int undinet_probe ( struct undi_device *undi );
+extern void undinet_remove ( struct undi_device *undi );
+
+#endif /* _UNDINET_H */
diff --git a/roms/ipxe/src/arch/i386/include/undipreload.h b/roms/ipxe/src/arch/i386/include/undipreload.h
new file mode 100644
index 000000000..57f493cec
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/undipreload.h
@@ -0,0 +1,18 @@
+#ifndef _UNDIPRELOAD_H
+#define _UNDIPRELOAD_H
+
+/** @file
+ *
+ * Preloaded UNDI stack
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <realmode.h>
+#include <undi.h>
+
+extern struct undi_device __data16 ( preloaded_undi );
+#define preloaded_undi __use_data16 ( preloaded_undi )
+
+#endif /* _UNDIPRELOAD_H */
diff --git a/roms/ipxe/src/arch/i386/include/undirom.h b/roms/ipxe/src/arch/i386/include/undirom.h
new file mode 100644
index 000000000..1c530118d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/undirom.h
@@ -0,0 +1,53 @@
+#ifndef _UNDIROM_H
+#define _UNDIROM_H
+
+/** @file
+ *
+ * UNDI expansion ROMs
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <pxe_types.h>
+
+/** An UNDI PCI device ID */
+struct undi_pci_device_id {
+ /** PCI vendor ID */
+ unsigned int vendor_id;
+ /** PCI device ID */
+ unsigned int device_id;
+};
+
+/** An UNDI device ID */
+union undi_device_id {
+ /** PCI device ID */
+ struct undi_pci_device_id pci;
+};
+
+/** An UNDI ROM */
+struct undi_rom {
+ /** List of UNDI ROMs */
+ struct list_head list;
+ /** ROM segment address */
+ unsigned int rom_segment;
+ /** UNDI loader entry point */
+ SEGOFF16_t loader_entry;
+ /** Code segment size */
+ size_t code_size;
+ /** Data segment size */
+ size_t data_size;
+ /** Bus type
+ *
+ * Values are as used by @c PXENV_UNDI_GET_NIC_TYPE
+ */
+ unsigned int bus_type;
+ /** Device ID */
+ union undi_device_id bus_id;
+};
+
+extern struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
+ unsigned int device_id,
+ unsigned int rombase );
+
+#endif /* _UNDIROM_H */
diff --git a/roms/ipxe/src/arch/i386/include/vga.h b/roms/ipxe/src/arch/i386/include/vga.h
new file mode 100644
index 000000000..01fc39d86
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/include/vga.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * modified
+ * by Steve M. Gehlbach <steve@kesa.com>
+ *
+ * Originally from linux/drivers/video/vga16.c by
+ * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef VGA_H_INCL
+#define VGA_H_INCL 1
+
+//#include <cpu/p5/io.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define __u32 u32
+
+#define VERROR -1
+#define CHAR_HEIGHT 16
+#define LINES 25
+#define COLS 80
+
+// macros for writing to vga regs
+#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
+#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
+#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
+#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
+u8 read_seq_b(u16 addr);
+u8 read_gra_b(u16 addr);
+u8 read_crtc_b(u16 addr);
+u8 read_att_b(u16 addr);
+
+
+#ifdef VGA_HARDWARE_FIXUP
+void vga_hardware_fixup(void);
+#else
+#define vga_hardware_fixup() do{} while(0)
+#endif
+
+#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define SYNC_EXT 4 /* external sync */
+#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+
+#define SYNC_ON_GREEN 32 /* sync on green */
+
+#define VMODE_NONINTERLACED 0 /* non interlaced */
+#define VMODE_INTERLACED 1 /* interlaced */
+#define VMODE_DOUBLE 2 /* double scan */
+#define VMODE_MASK 255
+
+#define VMODE_YWRAP 256 /* ywrap instead of panning */
+#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+
+#define MIS_R 0x3CC // Misc Output Read Register
+#define MIS_W 0x3C2 // Misc Output Write Register
+
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
+ // the remainder are not in the par array
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+// text attributes
+#define VGA_ATTR_CLR_RED 0x4
+#define VGA_ATTR_CLR_GRN 0x2
+#define VGA_ATTR_CLR_BLU 0x1
+#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
+#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
+#define VGA_ATTR_CLR_BLK 0
+#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_BNK 0x80
+#define VGA_ATTR_ITN 0x08
+
+/*
+ * vga register parameters
+ * these are copied to the
+ * registers.
+ *
+ */
+struct vga_par {
+ u8 crtc[CRTC_C];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc; // the misc register, MIS_W
+ u8 vss;
+};
+
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* sync polarity */
+ __u32 vmode; /* interlaced etc */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+
+#endif
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/apm.c b/roms/ipxe/src/arch/i386/interface/pcbios/apm.c
new file mode 100644
index 000000000..50b19cb81
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/apm.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * Advanced Power Management
+ *
+ */
+
+#include <errno.h>
+#include <realmode.h>
+#include <ipxe/reboot.h>
+
+/**
+ * Power off the computer using APM
+ *
+ * @ret rc Return status code
+ */
+static int apm_poweroff ( void ) {
+ uint16_t apm_version;
+ uint16_t apm_signature;
+ uint16_t apm_flags;
+ uint16_t carry;
+
+ /* APM check */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
+ "adc %%edx,0\n\t" )
+ : "=a" ( apm_version ), "=b" ( apm_signature ),
+ "=c" ( apm_flags ), "=d" ( carry )
+ : "a" ( 0x5300 ), "b" ( 0x0000 ),
+ "d" ( 0x0000 ) );
+ if ( carry ) {
+ DBG ( "APM not present\n" );
+ return -ENOTSUP;
+ }
+ if ( apm_signature != 0x504d ) { /* signature 'PM' */
+ DBG ( "APM not present\n" );
+ return -ENOTSUP;
+ }
+ if ( apm_version < 0x0101 ) { /* Need version 1.1+ */
+ DBG ( "APM 1.1+ not supported\n" );
+ return -ENOTSUP;
+ }
+ if ( ( apm_flags & 0x8 ) == 0x8 ) {
+ DBG ( "APM power management disabled\n" );
+ return -EPERM;
+ }
+ DBG2 ( "APM check completed\n" );
+
+ /* APM initialisation */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
+ "adc %%edx,0\n\t" )
+ : "=d" ( carry )
+ : "a" ( 0x5301 ), "b" ( 0x0000 ),
+ "d" ( 0x0000 ) );
+ if ( carry ) {
+ DBG ( "APM initialisation failed\n" );
+ return -EIO;
+ }
+ DBG2 ( "APM initialisation completed\n" );
+
+ /* Set APM driver version */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
+ "adc %%edx,0\n\t" )
+ : "=d" ( carry )
+ : "a" ( 0x530e ), "b" ( 0x0000 ),
+ "c" ( 0x0101 ), "d" ( 0x0000 ) );
+ if ( carry ) {
+ DBG ( "APM setting driver version failed\n" );
+ return -EIO;
+ }
+ DBG2 ( "APM driver version set\n" );
+
+ /* Setting power state to off */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
+ "adc %%edx,0\n\t" )
+ : "=d" ( carry )
+ : "a" ( 0x5307 ), "b" ( 0x0001 ),
+ "c" ( 0x0003 ), "d" ( 0x0000) );
+ if ( carry ) {
+ DBG ( "APM setting power state failed\n" );
+ return -ENOTTY;
+ }
+
+ /* Should never happen */
+ return -ECANCELED;
+}
+
+PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c
new file mode 100644
index 000000000..f1ba8297b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c
@@ -0,0 +1,16 @@
+#include <ipxe/nap.h>
+#include <realmode.h>
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Save power by halting the CPU until the next interrupt
+ *
+ */
+static void bios_cpu_nap ( void ) {
+ __asm__ __volatile__ ( "sti\n\t"
+ "hlt\n\t"
+ "cli\n\t" );
+}
+
+PROVIDE_NAP ( pcbios, cpu_nap, bios_cpu_nap );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c
new file mode 100644
index 000000000..10a1ecb89
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Standard PC-BIOS reboot mechanism
+ *
+ */
+
+#include <ipxe/reboot.h>
+#include <realmode.h>
+#include <bios.h>
+
+/**
+ * Reboot system
+ *
+ * @v warm Perform a warm reboot
+ */
+static void bios_reboot ( int warm ) {
+ uint16_t flag;
+
+ /* Configure BIOS for cold/warm reboot */
+ flag = ( warm ? BDA_REBOOT_WARM : 0 );
+ put_real ( flag, BDA_SEG, BDA_REBOOT );
+
+ /* Jump to system reset vector */
+ __asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : : );
+}
+
+PROVIDE_REBOOT ( pcbios, reboot, bios_reboot );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c
new file mode 100644
index 000000000..a8c0fc325
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/smbios.h>
+#include <realmode.h>
+#include <pnpbios.h>
+
+/** @file
+ *
+ * System Management BIOS
+ *
+ */
+
+/**
+ * Find SMBIOS
+ *
+ * @v smbios SMBIOS entry point descriptor structure to fill in
+ * @ret rc Return status code
+ */
+static int bios_find_smbios ( struct smbios *smbios ) {
+ struct smbios_entry entry;
+ int rc;
+
+ /* Scan through BIOS segment to find SMBIOS entry point */
+ if ( ( rc = find_smbios_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
+ &entry ) ) != 0 )
+ return rc;
+
+ /* Fill in entry point descriptor structure */
+ smbios->address = phys_to_user ( entry.smbios_address );
+ smbios->len = entry.smbios_len;
+ smbios->count = entry.smbios_count;
+ smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
+
+ return 0;
+}
+
+PROVIDE_SMBIOS ( pcbios, find_smbios, bios_find_smbios );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c
new file mode 100644
index 000000000..3299c9aae
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * BIOS timer
+ *
+ */
+
+#include <ipxe/timer.h>
+#include <realmode.h>
+#include <bios.h>
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ *
+ * Use direct memory access to BIOS variables, longword 0040:006C
+ * (ticks today) and byte 0040:0070 (midnight crossover flag) instead
+ * of calling timeofday BIOS interrupt.
+ */
+static unsigned long bios_currticks ( void ) {
+ static int days = 0;
+ uint32_t ticks;
+ uint8_t midnight;
+
+ /* Re-enable interrupts so that the timer interrupt can occur */
+ __asm__ __volatile__ ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" );
+
+ get_real ( ticks, BDA_SEG, 0x006c );
+ get_real ( midnight, BDA_SEG, 0x0070 );
+
+ if ( midnight ) {
+ midnight = 0;
+ put_real ( midnight, BDA_SEG, 0x0070 );
+ days += 0x1800b0;
+ }
+
+ return ( days + ticks );
+}
+
+PROVIDE_TIMER_INLINE ( pcbios, udelay );
+PROVIDE_TIMER ( pcbios, currticks, bios_currticks );
+PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c b/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c
new file mode 100644
index 000000000..3b8e80438
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c
@@ -0,0 +1,92 @@
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+
+/**
+ * @file BIOS interrupts
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Hook INT vector
+ *
+ * @v interrupt INT number
+ * @v handler Offset within .text16 to interrupt handler
+ * @v chain_vector Vector for chaining to previous handler
+ *
+ * Hooks in an i386 INT handler. The handler itself must reside
+ * within the .text16 segment. @c chain_vector will be filled in with
+ * the address of the previously-installed handler for this interrupt;
+ * the handler should probably exit by ljmping via this vector.
+ */
+void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector ) {
+ struct segoff vector = {
+ .segment = rm_cs,
+ .offset = handler,
+ };
+
+ DBG ( "Hooking INT %#02x to %04x:%04x\n",
+ interrupt, rm_cs, handler );
+
+ if ( ( chain_vector->segment != 0 ) ||
+ ( chain_vector->offset != 0 ) ) {
+ /* Already hooked; do nothing */
+ DBG ( "...already hooked\n" );
+ return;
+ }
+
+ copy_from_real ( chain_vector, 0, ( interrupt * 4 ),
+ sizeof ( *chain_vector ) );
+ DBG ( "...chaining to %04x:%04x\n",
+ chain_vector->segment, chain_vector->offset );
+ if ( DBG_LOG ) {
+ char code[64];
+ copy_from_real ( code, chain_vector->segment,
+ chain_vector->offset, sizeof ( code ) );
+ DBG_HDA ( *chain_vector, code, sizeof ( code ) );
+ }
+
+ copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) );
+ hooked_bios_interrupts++;
+}
+
+/**
+ * Unhook INT vector
+ *
+ * @v interrupt INT number
+ * @v handler Offset within .text16 to interrupt handler
+ * @v chain_vector Vector containing address of previous handler
+ *
+ * Unhooks an i386 interrupt handler hooked by hook_i386_vector().
+ * Note that this operation may fail, if some external code has hooked
+ * the vector since we hooked in our handler. If it fails, it means
+ * that it is not possible to unhook our handler, and we must leave it
+ * (and its chaining vector) resident in memory.
+ */
+int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector ) {
+ struct segoff vector;
+
+ DBG ( "Unhooking INT %#02x from %04x:%04x\n",
+ interrupt, rm_cs, handler );
+
+ copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) );
+ if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) {
+ DBG ( "...cannot unhook; vector points to %04x:%04x\n",
+ vector.segment, vector.offset );
+ return -EBUSY;
+ }
+
+ DBG ( "...restoring to %04x:%04x\n",
+ chain_vector->segment, chain_vector->offset );
+ copy_to_real ( 0, ( interrupt * 4 ), chain_vector,
+ sizeof ( *chain_vector ) );
+
+ chain_vector->segment = 0;
+ chain_vector->offset = 0;
+ hooked_bios_interrupts--;
+ return 0;
+}
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/int13.c b/roms/ipxe/src/arch/i386/interface/pcbios/int13.c
new file mode 100644
index 000000000..f0450da90
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/int13.c
@@ -0,0 +1,1993 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/list.h>
+#include <ipxe/blockdev.h>
+#include <ipxe/io.h>
+#include <ipxe/open.h>
+#include <ipxe/uri.h>
+#include <ipxe/process.h>
+#include <ipxe/xfer.h>
+#include <ipxe/retry.h>
+#include <ipxe/timer.h>
+#include <ipxe/acpi.h>
+#include <ipxe/sanboot.h>
+#include <ipxe/device.h>
+#include <ipxe/pci.h>
+#include <ipxe/iso9660.h>
+#include <ipxe/eltorito.h>
+#include <realmode.h>
+#include <bios.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <int13.h>
+
+/** @file
+ *
+ * INT 13 emulation
+ *
+ * This module provides a mechanism for exporting block devices via
+ * the BIOS INT 13 disk interrupt interface.
+ *
+ */
+
+/**
+ * Overall timeout for INT 13 commands (independent of underlying device
+ *
+ * Underlying devices should ideally never become totally stuck.
+ * However, if they do, then the INT 13 mechanism provides no means
+ * for the caller to cancel the operation, and the machine appears to
+ * hang. Use an overall timeout for all commands to avoid this
+ * problem and bounce timeout failures to the caller.
+ */
+#define INT13_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC )
+
+/** An INT 13 emulated drive */
+struct int13_drive {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** List of all registered drives */
+ struct list_head list;
+
+ /** Block device URI */
+ struct uri *uri;
+ /** Underlying block device interface */
+ struct interface block;
+
+ /** BIOS in-use drive number (0x00-0xff) */
+ unsigned int drive;
+ /** BIOS natural drive number (0x00-0xff)
+ *
+ * This is the drive number that would have been assigned by
+ * 'naturally' appending the drive to the end of the BIOS
+ * drive list.
+ *
+ * If the emulated drive replaces a preexisting drive, this is
+ * the drive number that the preexisting drive gets remapped
+ * to.
+ */
+ unsigned int natural_drive;
+
+ /** Block device capacity */
+ struct block_device_capacity capacity;
+ /** INT 13 emulated blocksize shift
+ *
+ * To allow for emulation of CD-ROM access, this represents
+ * the left-shift required to translate from INT 13 blocks to
+ * underlying blocks.
+ */
+ unsigned int blksize_shift;
+
+ /** Number of cylinders
+ *
+ * The cylinder number field in an INT 13 call is ten bits
+ * wide, giving a maximum of 1024 cylinders. Conventionally,
+ * when the 7.8GB limit of a CHS address is exceeded, it is
+ * the number of cylinders that is increased beyond the
+ * addressable limit.
+ */
+ unsigned int cylinders;
+ /** Number of heads
+ *
+ * The head number field in an INT 13 call is eight bits wide,
+ * giving a maximum of 256 heads. However, apparently all
+ * versions of MS-DOS up to and including Win95 fail with 256
+ * heads, so the maximum encountered in practice is 255.
+ */
+ unsigned int heads;
+ /** Number of sectors per track
+ *
+ * The sector number field in an INT 13 call is six bits wide,
+ * giving a maximum of 63 sectors, since sector numbering
+ * (unlike head and cylinder numbering) starts at 1, not 0.
+ */
+ unsigned int sectors_per_track;
+
+ /** Drive is a CD-ROM */
+ int is_cdrom;
+ /** Address of El Torito boot catalog (if any) */
+ unsigned int boot_catalog;
+
+ /** Underlying device status, if in error */
+ int block_rc;
+ /** Status of last operation */
+ int last_status;
+};
+
+/** Vector for chaining to other INT 13 handlers */
+static struct segoff __text16 ( int13_vector );
+#define int13_vector __use_text16 ( int13_vector )
+
+/** Assembly wrapper */
+extern void int13_wrapper ( void );
+
+/** Dummy floppy disk parameter table */
+static struct int13_fdd_parameters __data16 ( int13_fdd_params ) = {
+ /* 512 bytes per sector */
+ .bytes_per_sector = 0x02,
+ /* Highest sectors per track that we ever return */
+ .sectors_per_track = 48,
+};
+#define int13_fdd_params __use_data16 ( int13_fdd_params )
+
+/** List of registered emulated drives */
+static LIST_HEAD ( int13s );
+
+/**
+ * Equipment word
+ *
+ * This is a cached copy of the BIOS Data Area equipment word at
+ * 40:10.
+ */
+static uint16_t equipment_word;
+
+/**
+ * Number of BIOS floppy disk drives
+ *
+ * This is derived from the equipment word. It is held in .text16 to
+ * allow for easy access by the INT 13,08 wrapper.
+ */
+static uint8_t __text16 ( num_fdds );
+#define num_fdds __use_text16 ( num_fdds )
+
+/**
+ * Number of BIOS hard disk drives
+ *
+ * This is a cached copy of the BIOS Data Area number of hard disk
+ * drives at 40:75. It is held in .text16 to allow for easy access by
+ * the INT 13,08 wrapper.
+ */
+static uint8_t __text16 ( num_drives );
+#define num_drives __use_text16 ( num_drives )
+
+/**
+ * Calculate INT 13 drive sector size
+ *
+ * @v int13 Emulated drive
+ * @ret blksize Sector size
+ */
+static inline size_t int13_blksize ( struct int13_drive *int13 ) {
+ return ( int13->capacity.blksize << int13->blksize_shift );
+}
+
+/**
+ * Calculate INT 13 drive capacity
+ *
+ * @v int13 Emulated drive
+ * @ret blocks Number of blocks
+ */
+static inline uint64_t int13_capacity ( struct int13_drive *int13 ) {
+ return ( int13->capacity.blocks >> int13->blksize_shift );
+}
+
+/**
+ * Calculate INT 13 drive capacity (limited to 32 bits)
+ *
+ * @v int13 Emulated drive
+ * @ret blocks Number of blocks
+ */
+static inline uint32_t int13_capacity32 ( struct int13_drive *int13 ) {
+ uint64_t capacity = int13_capacity ( int13 );
+ return ( ( capacity <= 0xffffffffUL ) ? capacity : 0xffffffff );
+}
+
+/**
+ * Test if INT 13 drive is a floppy disk drive
+ *
+ * @v int13 Emulated drive
+ * @ret is_fdd Emulated drive is a floppy disk
+ */
+static inline int int13_is_fdd ( struct int13_drive *int13 ) {
+ return ( ! ( int13->drive & 0x80 ) );
+}
+
+/** An INT 13 command */
+struct int13_command {
+ /** Status */
+ int rc;
+ /** INT 13 drive */
+ struct int13_drive *int13;
+ /** Underlying block device interface */
+ struct interface block;
+ /** Command timeout timer */
+ struct retry_timer timer;
+};
+
+/**
+ * Record INT 13 drive capacity
+ *
+ * @v command INT 13 command
+ * @v capacity Block device capacity
+ */
+static void int13_command_capacity ( struct int13_command *command,
+ struct block_device_capacity *capacity ) {
+ memcpy ( &command->int13->capacity, capacity,
+ sizeof ( command->int13->capacity ) );
+}
+
+/**
+ * Close INT 13 command
+ *
+ * @v command INT 13 command
+ * @v rc Reason for close
+ */
+static void int13_command_close ( struct int13_command *command, int rc ) {
+ intf_restart ( &command->block, rc );
+ stop_timer ( &command->timer );
+ command->rc = rc;
+}
+
+/**
+ * Handle INT 13 command timer expiry
+ *
+ * @v timer Timer
+ */
+static void int13_command_expired ( struct retry_timer *timer,
+ int over __unused ) {
+ struct int13_command *command =
+ container_of ( timer, struct int13_command, timer );
+
+ int13_command_close ( command, -ETIMEDOUT );
+}
+
+/** INT 13 command interface operations */
+static struct interface_operation int13_command_op[] = {
+ INTF_OP ( intf_close, struct int13_command *, int13_command_close ),
+ INTF_OP ( block_capacity, struct int13_command *,
+ int13_command_capacity ),
+};
+
+/** INT 13 command interface descriptor */
+static struct interface_descriptor int13_command_desc =
+ INTF_DESC ( struct int13_command, block, int13_command_op );
+
+/**
+ * Open (or reopen) INT 13 emulated drive underlying block device
+ *
+ * @v int13 Emulated drive
+ * @ret rc Return status code
+ */
+static int int13_reopen_block ( struct int13_drive *int13 ) {
+ int rc;
+
+ /* Close any existing block device */
+ intf_restart ( &int13->block, -ECONNRESET );
+
+ /* Open block device */
+ if ( ( rc = xfer_open_uri ( &int13->block, int13->uri ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not reopen block "
+ "device: %s\n", int13->drive, strerror ( rc ) );
+ int13->block_rc = rc;
+ return rc;
+ }
+
+ /* Clear block device error status */
+ int13->block_rc = 0;
+
+ return 0;
+}
+
+/**
+ * Prepare to issue INT 13 command
+ *
+ * @v command INT 13 command
+ * @v int13 Emulated drive
+ * @ret rc Return status code
+ */
+static int int13_command_start ( struct int13_command *command,
+ struct int13_drive *int13 ) {
+ int rc;
+
+ /* Sanity check */
+ assert ( command->int13 == NULL );
+ assert ( ! timer_running ( &command->timer ) );
+
+ /* Reopen block device if necessary */
+ if ( ( int13->block_rc != 0 ) &&
+ ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) )
+ return rc;
+
+ /* Initialise command */
+ command->rc = -EINPROGRESS;
+ command->int13 = int13;
+ start_timer_fixed ( &command->timer, INT13_COMMAND_TIMEOUT );
+
+ /* Wait for block control interface to become ready */
+ while ( ( command->rc == -EINPROGRESS ) &&
+ ( xfer_window ( &int13->block ) == 0 ) ) {
+ step();
+ }
+
+ return ( ( command->rc == -EINPROGRESS ) ?
+ int13->block_rc : command->rc );
+}
+
+/**
+ * Wait for INT 13 command to complete
+ *
+ * @v command INT 13 command
+ * @ret rc Return status code
+ */
+static int int13_command_wait ( struct int13_command *command ) {
+
+ /* Sanity check */
+ assert ( timer_running ( &command->timer ) );
+
+ /* Wait for command to complete */
+ while ( command->rc == -EINPROGRESS )
+ step();
+
+ assert ( ! timer_running ( &command->timer ) );
+ return command->rc;
+}
+
+/**
+ * Terminate INT 13 command
+ *
+ * @v command INT 13 command
+ */
+static void int13_command_stop ( struct int13_command *command ) {
+ stop_timer ( &command->timer );
+ command->int13 = NULL;
+}
+
+/** The single active INT 13 command */
+static struct int13_command int13_command = {
+ .block = INTF_INIT ( int13_command_desc ),
+ .timer = TIMER_INIT ( int13_command_expired ),
+};
+
+/**
+ * Read from or write to INT 13 drive
+ *
+ * @v int13 Emulated drive
+ * @v lba Starting logical block address
+ * @v count Number of logical blocks
+ * @v buffer Data buffer
+ * @v block_rw Block read/write method
+ * @ret rc Return status code
+ */
+static int int13_rw ( struct int13_drive *int13, uint64_t lba,
+ unsigned int count, userptr_t buffer,
+ int ( * block_rw ) ( struct interface *control,
+ struct interface *data,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) ) {
+ struct int13_command *command = &int13_command;
+ unsigned int frag_count;
+ size_t frag_len;
+ int rc;
+
+ /* Translate to underlying blocksize */
+ lba <<= int13->blksize_shift;
+ count <<= int13->blksize_shift;
+
+ while ( count ) {
+
+ /* Determine fragment length */
+ frag_count = count;
+ if ( frag_count > int13->capacity.max_count )
+ frag_count = int13->capacity.max_count;
+ frag_len = ( int13->capacity.blksize * frag_count );
+
+ /* Issue command */
+ if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) ||
+ ( ( rc = block_rw ( &int13->block, &command->block, lba,
+ frag_count, buffer,
+ frag_len ) ) != 0 ) ||
+ ( ( rc = int13_command_wait ( command ) ) != 0 ) ) {
+ int13_command_stop ( command );
+ return rc;
+ }
+ int13_command_stop ( command );
+
+ /* Move to next fragment */
+ lba += frag_count;
+ count -= frag_count;
+ buffer = userptr_add ( buffer, frag_len );
+ }
+
+ return 0;
+}
+
+/**
+ * Read INT 13 drive capacity
+ *
+ * @v int13 Emulated drive
+ * @ret rc Return status code
+ */
+static int int13_read_capacity ( struct int13_drive *int13 ) {
+ struct int13_command *command = &int13_command;
+ int rc;
+
+ /* Issue command */
+ if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) ||
+ ( ( rc = block_read_capacity ( &int13->block,
+ &command->block ) ) != 0 ) ||
+ ( ( rc = int13_command_wait ( command ) ) != 0 ) ) {
+ int13_command_stop ( command );
+ return rc;
+ }
+
+ int13_command_stop ( command );
+ return 0;
+}
+
+/**
+ * Parse ISO9660 parameters
+ *
+ * @v int13 Emulated drive
+ * @v scratch Scratch area for single-sector reads
+ * @ret rc Return status code
+ *
+ * Reads and parses ISO9660 parameters, if present.
+ */
+static int int13_parse_iso9660 ( struct int13_drive *int13, void *scratch ) {
+ static const struct iso9660_primary_descriptor_fixed primary_check = {
+ .type = ISO9660_TYPE_PRIMARY,
+ .id = ISO9660_ID,
+ };
+ struct iso9660_primary_descriptor *primary = scratch;
+ static const struct eltorito_descriptor_fixed boot_check = {
+ .type = ISO9660_TYPE_BOOT,
+ .id = ISO9660_ID,
+ .version = 1,
+ .system_id = "EL TORITO SPECIFICATION",
+ };
+ struct eltorito_descriptor *boot = scratch;
+ unsigned int blksize;
+ unsigned int blksize_shift;
+ int rc;
+
+ /* Calculate required blocksize shift */
+ blksize = int13_blksize ( int13 );
+ blksize_shift = 0;
+ while ( blksize < ISO9660_BLKSIZE ) {
+ blksize <<= 1;
+ blksize_shift++;
+ }
+ if ( blksize > ISO9660_BLKSIZE ) {
+ /* Do nothing if the blksize is invalid for CD-ROM access */
+ return 0;
+ }
+
+ /* Read primary volume descriptor */
+ if ( ( rc = int13_rw ( int13,
+ ( ISO9660_PRIMARY_LBA << blksize_shift ), 1,
+ virt_to_user ( primary ), block_read ) ) != 0 ){
+ DBGC ( int13, "INT13 drive %02x could not read ISO9660 "
+ "primary volume descriptor: %s\n",
+ int13->drive, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Do nothing unless this is an ISO image */
+ if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 )
+ return 0;
+ DBGC ( int13, "INT13 drive %02x contains an ISO9660 filesystem; "
+ "treating as CD-ROM\n", int13->drive );
+ int13->is_cdrom = 1;
+
+ /* Read boot record volume descriptor */
+ if ( ( rc = int13_rw ( int13,
+ ( ELTORITO_LBA << blksize_shift ), 1,
+ virt_to_user ( boot ), block_read ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not read El Torito boot "
+ "record volume descriptor: %s\n",
+ int13->drive, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check for an El Torito boot catalog */
+ if ( memcmp ( boot, &boot_check, sizeof ( boot_check ) ) == 0 ) {
+ int13->boot_catalog = boot->sector;
+ DBGC ( int13, "INT13 drive %02x has an El Torito boot catalog "
+ "at LBA %08x\n", int13->drive, int13->boot_catalog );
+ } else {
+ DBGC ( int13, "INT13 drive %02x has no El Torito boot "
+ "catalog\n", int13->drive );
+ }
+
+ /* Configure drive for no-emulation CD-ROM access */
+ int13->blksize_shift += blksize_shift;
+
+ return 0;
+}
+
+/**
+ * Guess INT 13 hard disk drive geometry
+ *
+ * @v int13 Emulated drive
+ * @v scratch Scratch area for single-sector reads
+ * @ret heads Guessed number of heads
+ * @ret sectors Guessed number of sectors per track
+ * @ret rc Return status code
+ *
+ * Guesses the drive geometry by inspecting the partition table.
+ */
+static int int13_guess_geometry_hdd ( struct int13_drive *int13, void *scratch,
+ unsigned int *heads,
+ unsigned int *sectors ) {
+ struct master_boot_record *mbr = scratch;
+ struct partition_table_entry *partition;
+ unsigned int i;
+ int rc;
+
+ /* Default guess is xx/255/63 */
+ *heads = 255;
+ *sectors = 63;
+
+ /* Read partition table */
+ if ( ( rc = int13_rw ( int13, 0, 1, virt_to_user ( mbr ),
+ block_read ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not read "
+ "partition table to guess geometry: %s\n",
+ int13->drive, strerror ( rc ) );
+ return rc;
+ }
+ DBGC2 ( int13, "INT13 drive %02x has MBR:\n", int13->drive );
+ DBGC2_HDA ( int13, 0, mbr, sizeof ( *mbr ) );
+ DBGC ( int13, "INT13 drive %02x has signature %08x\n",
+ int13->drive, mbr->signature );
+
+ /* Scan through partition table and modify guesses for
+ * heads and sectors_per_track if we find any used
+ * partitions.
+ */
+ for ( i = 0 ; i < 4 ; i++ ) {
+ partition = &mbr->partitions[i];
+ if ( ! partition->type )
+ continue;
+ *heads = ( PART_HEAD ( partition->chs_end ) + 1 );
+ *sectors = PART_SECTOR ( partition->chs_end );
+ DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based "
+ "on partition %d\n",
+ int13->drive, *heads, *sectors, ( i + 1 ) );
+ }
+
+ return 0;
+}
+
+/** Recognised floppy disk geometries */
+static const struct int13_fdd_geometry int13_fdd_geometries[] = {
+ INT13_FDD_GEOMETRY ( 40, 1, 8 ),
+ INT13_FDD_GEOMETRY ( 40, 1, 9 ),
+ INT13_FDD_GEOMETRY ( 40, 2, 8 ),
+ INT13_FDD_GEOMETRY ( 40, 1, 9 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 8 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 9 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 15 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 18 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 20 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 21 ),
+ INT13_FDD_GEOMETRY ( 82, 2, 21 ),
+ INT13_FDD_GEOMETRY ( 83, 2, 21 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 22 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 23 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 24 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 36 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 39 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 40 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 44 ),
+ INT13_FDD_GEOMETRY ( 80, 2, 48 ),
+};
+
+/**
+ * Guess INT 13 floppy disk drive geometry
+ *
+ * @v int13 Emulated drive
+ * @ret heads Guessed number of heads
+ * @ret sectors Guessed number of sectors per track
+ * @ret rc Return status code
+ *
+ * Guesses the drive geometry by inspecting the disk size.
+ */
+static int int13_guess_geometry_fdd ( struct int13_drive *int13,
+ unsigned int *heads,
+ unsigned int *sectors ) {
+ unsigned int blocks = int13_capacity ( int13 );
+ const struct int13_fdd_geometry *geometry;
+ unsigned int cylinders;
+ unsigned int i;
+
+ /* Look for a match against a known geometry */
+ for ( i = 0 ; i < ( sizeof ( int13_fdd_geometries ) /
+ sizeof ( int13_fdd_geometries[0] ) ) ; i++ ) {
+ geometry = &int13_fdd_geometries[i];
+ cylinders = INT13_FDD_CYLINDERS ( geometry );
+ *heads = INT13_FDD_HEADS ( geometry );
+ *sectors = INT13_FDD_SECTORS ( geometry );
+ if ( ( cylinders * (*heads) * (*sectors) ) == blocks ) {
+ DBGC ( int13, "INT13 drive %02x guessing C/H/S "
+ "%d/%d/%d based on size %dK\n", int13->drive,
+ cylinders, *heads, *sectors, ( blocks / 2 ) );
+ return 0;
+ }
+ }
+
+ /* Otherwise, assume a partial disk image in the most common
+ * format (1440K, 80/2/18).
+ */
+ *heads = 2;
+ *sectors = 18;
+ DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size "
+ "%dK\n", int13->drive, *heads, *sectors, ( blocks / 2 ) );
+ return 0;
+}
+
+/**
+ * Guess INT 13 drive geometry
+ *
+ * @v int13 Emulated drive
+ * @v scratch Scratch area for single-sector reads
+ * @ret rc Return status code
+ */
+static int int13_guess_geometry ( struct int13_drive *int13, void *scratch ) {
+ unsigned int guessed_heads;
+ unsigned int guessed_sectors;
+ unsigned int blocks;
+ unsigned int blocks_per_cyl;
+ int rc;
+
+ /* Don't even try when the blksize is invalid for C/H/S access */
+ if ( int13_blksize ( int13 ) != INT13_BLKSIZE )
+ return 0;
+
+ /* Guess geometry according to drive type */
+ if ( int13_is_fdd ( int13 ) ) {
+ if ( ( rc = int13_guess_geometry_fdd ( int13, &guessed_heads,
+ &guessed_sectors )) != 0)
+ return rc;
+ } else {
+ if ( ( rc = int13_guess_geometry_hdd ( int13, scratch,
+ &guessed_heads,
+ &guessed_sectors )) != 0)
+ return rc;
+ }
+
+ /* Apply guesses if no geometry already specified */
+ if ( ! int13->heads )
+ int13->heads = guessed_heads;
+ if ( ! int13->sectors_per_track )
+ int13->sectors_per_track = guessed_sectors;
+ if ( ! int13->cylinders ) {
+ /* Avoid attempting a 64-bit divide on a 32-bit system */
+ blocks = int13_capacity32 ( int13 );
+ blocks_per_cyl = ( int13->heads * int13->sectors_per_track );
+ assert ( blocks_per_cyl != 0 );
+ int13->cylinders = ( blocks / blocks_per_cyl );
+ if ( int13->cylinders > 1024 )
+ int13->cylinders = 1024;
+ }
+
+ return 0;
+}
+
+/**
+ * Update BIOS drive count
+ */
+static void int13_sync_num_drives ( void ) {
+ struct int13_drive *int13;
+ uint8_t *counter;
+ uint8_t max_drive;
+ uint8_t required;
+
+ /* Get current drive counts */
+ get_real ( equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+ num_fdds = ( ( equipment_word & 0x0001 ) ?
+ ( ( ( equipment_word >> 6 ) & 0x3 ) + 1 ) : 0 );
+
+ /* Ensure count is large enough to cover all of our emulated drives */
+ list_for_each_entry ( int13, &int13s, list ) {
+ counter = ( int13_is_fdd ( int13 ) ? &num_fdds : &num_drives );
+ max_drive = int13->drive;
+ if ( max_drive < int13->natural_drive )
+ max_drive = int13->natural_drive;
+ required = ( ( max_drive & 0x7f ) + 1 );
+ if ( *counter < required ) {
+ *counter = required;
+ DBGC ( int13, "INT13 drive %02x added to drive count: "
+ "%d HDDs, %d FDDs\n",
+ int13->drive, num_drives, num_fdds );
+ }
+ }
+
+ /* Update current drive count */
+ equipment_word &= ~( ( 0x3 << 6 ) | 0x0001 );
+ if ( num_fdds ) {
+ equipment_word |= ( 0x0001 |
+ ( ( ( num_fdds - 1 ) & 0x3 ) << 6 ) );
+ }
+ put_real ( equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+ put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+}
+
+/**
+ * Check number of drives
+ */
+static void int13_check_num_drives ( void ) {
+ uint16_t check_equipment_word;
+ uint8_t check_num_drives;
+
+ get_real ( check_equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+ get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
+ if ( ( check_equipment_word != equipment_word ) ||
+ ( check_num_drives != num_drives ) ) {
+ int13_sync_num_drives();
+ }
+}
+
+/**
+ * INT 13, 00 - Reset disk system
+ *
+ * @v int13 Emulated drive
+ * @ret status Status code
+ */
+static int int13_reset ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 __unused ) {
+ int rc;
+
+ DBGC2 ( int13, "Reset drive\n" );
+
+ /* Reopen underlying block device */
+ if ( ( rc = int13_reopen_block ( int13 ) ) != 0 )
+ return -INT13_STATUS_RESET_FAILED;
+
+ /* Check that block device is functional */
+ if ( ( rc = int13_read_capacity ( int13 ) ) != 0 )
+ return -INT13_STATUS_RESET_FAILED;
+
+ return 0;
+}
+
+/**
+ * INT 13, 01 - Get status of last operation
+ *
+ * @v int13 Emulated drive
+ * @ret status Status code
+ */
+static int int13_get_last_status ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 __unused ) {
+ DBGC2 ( int13, "Get status of last operation\n" );
+ return int13->last_status;
+}
+
+/**
+ * Read / write sectors
+ *
+ * @v int13 Emulated drive
+ * @v al Number of sectors to read or write (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @v block_rw Block read/write method
+ * @ret status Status code
+ * @ret al Number of sectors read or written
+ */
+static int int13_rw_sectors ( struct int13_drive *int13,
+ struct i386_all_regs *ix86,
+ int ( * block_rw ) ( struct interface *control,
+ struct interface *data,
+ uint64_t lba,
+ unsigned int count,
+ userptr_t buffer,
+ size_t len ) ) {
+ unsigned int cylinder, head, sector;
+ unsigned long lba;
+ unsigned int count;
+ userptr_t buffer;
+ int rc;
+
+ /* Validate blocksize */
+ if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) {
+ DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) "
+ "for non-extended read/write\n",
+ int13->drive, int13_blksize ( int13 ) );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Calculate parameters */
+ cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
+ head = ix86->regs.dh;
+ sector = ( ix86->regs.cl & 0x3f );
+ if ( ( cylinder >= int13->cylinders ) ||
+ ( head >= int13->heads ) ||
+ ( sector < 1 ) || ( sector > int13->sectors_per_track ) ) {
+ DBGC ( int13, "C/H/S %d/%d/%d out of range for geometry "
+ "%d/%d/%d\n", cylinder, head, sector, int13->cylinders,
+ int13->heads, int13->sectors_per_track );
+ return -INT13_STATUS_INVALID;
+ }
+ lba = ( ( ( ( cylinder * int13->heads ) + head )
+ * int13->sectors_per_track ) + sector - 1 );
+ count = ix86->regs.al;
+ buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
+
+ DBGC2 ( int13, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n",
+ cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx,
+ count );
+
+ /* Read from / write to block device */
+ if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x I/O failed: %s\n",
+ int13->drive, strerror ( rc ) );
+ return -INT13_STATUS_READ_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * INT 13, 02 - Read sectors
+ *
+ * @v int13 Emulated drive
+ * @v al Number of sectors to read (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @ret status Status code
+ * @ret al Number of sectors read
+ */
+static int int13_read_sectors ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ DBGC2 ( int13, "Read: " );
+ return int13_rw_sectors ( int13, ix86, block_read );
+}
+
+/**
+ * INT 13, 03 - Write sectors
+ *
+ * @v int13 Emulated drive
+ * @v al Number of sectors to write (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @ret status Status code
+ * @ret al Number of sectors written
+ */
+static int int13_write_sectors ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ DBGC2 ( int13, "Write: " );
+ return int13_rw_sectors ( int13, ix86, block_write );
+}
+
+/**
+ * INT 13, 08 - Get drive parameters
+ *
+ * @v int13 Emulated drive
+ * @ret status Status code
+ * @ret ch Low bits of maximum cylinder number
+ * @ret cl (bits 7:6) High bits of maximum cylinder number
+ * @ret cl (bits 5:0) Maximum sector number
+ * @ret dh Maximum head number
+ * @ret dl Number of drives
+ */
+static int int13_get_parameters ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ unsigned int max_cylinder = int13->cylinders - 1;
+ unsigned int max_head = int13->heads - 1;
+ unsigned int max_sector = int13->sectors_per_track; /* sic */
+
+ DBGC2 ( int13, "Get drive parameters\n" );
+
+ /* Validate blocksize */
+ if ( int13_blksize ( int13 ) != INT13_BLKSIZE ) {
+ DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) "
+ "for non-extended parameters\n",
+ int13->drive, int13_blksize ( int13 ) );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Common parameters */
+ ix86->regs.ch = ( max_cylinder & 0xff );
+ ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
+ ix86->regs.dh = max_head;
+ ix86->regs.dl = ( int13_is_fdd ( int13 ) ? num_fdds : num_drives );
+
+ /* Floppy-specific parameters */
+ if ( int13_is_fdd ( int13 ) ) {
+ ix86->regs.bl = INT13_FDD_TYPE_1M44;
+ ix86->segs.es = rm_ds;
+ ix86->regs.di = __from_data16 ( &int13_fdd_params );
+ }
+
+ return 0;
+}
+
+/**
+ * INT 13, 15 - Get disk type
+ *
+ * @v int13 Emulated drive
+ * @ret ah Type code
+ * @ret cx:dx Sector count
+ * @ret status Status code / disk type
+ */
+static int int13_get_disk_type ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ uint32_t blocks;
+
+ DBGC2 ( int13, "Get disk type\n" );
+
+ if ( int13_is_fdd ( int13 ) ) {
+ return INT13_DISK_TYPE_FDD;
+ } else {
+ blocks = int13_capacity32 ( int13 );
+ ix86->regs.cx = ( blocks >> 16 );
+ ix86->regs.dx = ( blocks & 0xffff );
+ return INT13_DISK_TYPE_HDD;
+ }
+}
+
+/**
+ * INT 13, 41 - Extensions installation check
+ *
+ * @v int13 Emulated drive
+ * @v bx 0x55aa
+ * @ret bx 0xaa55
+ * @ret cx Extensions API support bitmap
+ * @ret status Status code / API version
+ */
+static int int13_extension_check ( struct int13_drive *int13 __unused,
+ struct i386_all_regs *ix86 ) {
+ if ( ix86->regs.bx == 0x55aa ) {
+ DBGC2 ( int13, "INT13 extensions installation check\n" );
+ ix86->regs.bx = 0xaa55;
+ ix86->regs.cx = ( INT13_EXTENSION_LINEAR |
+ INT13_EXTENSION_EDD |
+ INT13_EXTENSION_64BIT );
+ return INT13_EXTENSION_VER_3_0;
+ } else {
+ return -INT13_STATUS_INVALID;
+ }
+}
+
+/**
+ * Extended read / write
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @v block_rw Block read/write method
+ * @ret status Status code
+ */
+static int int13_extended_rw ( struct int13_drive *int13,
+ struct i386_all_regs *ix86,
+ int ( * block_rw ) ( struct interface *control,
+ struct interface *data,
+ uint64_t lba,
+ unsigned int count,
+ userptr_t buffer,
+ size_t len ) ) {
+ struct int13_disk_address addr;
+ uint8_t bufsize;
+ uint64_t lba;
+ unsigned long count;
+ userptr_t buffer;
+ int rc;
+
+ /* Extended reads are not allowed on floppy drives.
+ * ELTORITO.SYS seems to assume that we are really a CD-ROM if
+ * we support extended reads for a floppy drive.
+ */
+ if ( int13_is_fdd ( int13 ) )
+ return -INT13_STATUS_INVALID;
+
+ /* Get buffer size */
+ get_real ( bufsize, ix86->segs.ds,
+ ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) );
+ if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) {
+ DBGC2 ( int13, "<invalid buffer size %#02x\n>\n", bufsize );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Read parameters from disk address structure */
+ memset ( &addr, 0, sizeof ( addr ) );
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
+ lba = addr.lba;
+ DBGC2 ( int13, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) );
+ if ( ( addr.count == 0xff ) ||
+ ( ( addr.buffer.segment == 0xffff ) &&
+ ( addr.buffer.offset == 0xffff ) ) ) {
+ buffer = phys_to_user ( addr.buffer_phys );
+ DBGC2 ( int13, "%08llx",
+ ( ( unsigned long long ) addr.buffer_phys ) );
+ } else {
+ buffer = real_to_user ( addr.buffer.segment,
+ addr.buffer.offset );
+ DBGC2 ( int13, "%04x:%04x", addr.buffer.segment,
+ addr.buffer.offset );
+ }
+ if ( addr.count <= 0x7f ) {
+ count = addr.count;
+ } else if ( addr.count == 0xff ) {
+ count = addr.long_count;
+ } else {
+ DBGC2 ( int13, " <invalid count %#02x>\n", addr.count );
+ return -INT13_STATUS_INVALID;
+ }
+ DBGC2 ( int13, " (count %ld)\n", count );
+
+ /* Read from / write to block device */
+ if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n",
+ int13->drive, strerror ( rc ) );
+ /* Record that no blocks were transferred successfully */
+ addr.count = 0;
+ put_real ( addr.count, ix86->segs.ds,
+ ( ix86->regs.si +
+ offsetof ( typeof ( addr ), count ) ) );
+ return -INT13_STATUS_READ_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * INT 13, 42 - Extended read
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_read ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ DBGC2 ( int13, "Extended read: " );
+ return int13_extended_rw ( int13, ix86, block_read );
+}
+
+/**
+ * INT 13, 43 - Extended write
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_write ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ DBGC2 ( int13, "Extended write: " );
+ return int13_extended_rw ( int13, ix86, block_write );
+}
+
+/**
+ * INT 13, 44 - Verify sectors
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_verify ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_address addr;
+ uint64_t lba;
+ unsigned long count;
+
+ /* Read parameters from disk address structure */
+ if ( DBG_EXTRA ) {
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+ sizeof ( addr ));
+ lba = addr.lba;
+ count = addr.count;
+ DBGC2 ( int13, "Verify: LBA %08llx (count %ld)\n",
+ ( ( unsigned long long ) lba ), count );
+ }
+
+ /* We have no mechanism for verifying sectors */
+ return -INT13_STATUS_INVALID;
+}
+
+/**
+ * INT 13, 44 - Extended seek
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_seek ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_address addr;
+ uint64_t lba;
+ unsigned long count;
+
+ /* Read parameters from disk address structure */
+ if ( DBG_EXTRA ) {
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+ sizeof ( addr ));
+ lba = addr.lba;
+ count = addr.count;
+ DBGC2 ( int13, "Seek: LBA %08llx (count %ld)\n",
+ ( ( unsigned long long ) lba ), count );
+ }
+
+ /* Ignore and return success */
+ return 0;
+}
+
+/**
+ * Build device path information
+ *
+ * @v int13 Emulated drive
+ * @v dpi Device path information
+ * @ret rc Return status code
+ */
+static int int13_device_path_info ( struct int13_drive *int13,
+ struct edd_device_path_information *dpi ) {
+ struct device *device;
+ struct device_description *desc;
+ unsigned int i;
+ uint8_t sum = 0;
+ int rc;
+
+ /* Reopen block device if necessary */
+ if ( ( int13->block_rc != 0 ) &&
+ ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) )
+ return rc;
+
+ /* Get underlying hardware device */
+ device = identify_device ( &int13->block );
+ if ( ! device ) {
+ DBGC ( int13, "INT13 drive %02x cannot identify hardware "
+ "device\n", int13->drive );
+ return -ENODEV;
+ }
+
+ /* Fill in bus type and interface path */
+ desc = &device->desc;
+ switch ( desc->bus_type ) {
+ case BUS_TYPE_PCI:
+ dpi->host_bus_type.type = EDD_BUS_TYPE_PCI;
+ dpi->interface_path.pci.bus = PCI_BUS ( desc->location );
+ dpi->interface_path.pci.slot = PCI_SLOT ( desc->location );
+ dpi->interface_path.pci.function = PCI_FUNC ( desc->location );
+ dpi->interface_path.pci.channel = 0xff; /* unused */
+ break;
+ default:
+ DBGC ( int13, "INT13 drive %02x unrecognised bus type %d\n",
+ int13->drive, desc->bus_type );
+ return -ENOTSUP;
+ }
+
+ /* Get EDD block device description */
+ if ( ( rc = edd_describe ( &int13->block, &dpi->interface_type,
+ &dpi->device_path ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x cannot identify block device: "
+ "%s\n", int13->drive, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Fill in common fields and fix checksum */
+ dpi->key = EDD_DEVICE_PATH_INFO_KEY;
+ dpi->len = sizeof ( *dpi );
+ for ( i = 0 ; i < sizeof ( *dpi ) ; i++ )
+ sum += *( ( ( uint8_t * ) dpi ) + i );
+ dpi->checksum -= sum;
+
+ return 0;
+}
+
+/**
+ * INT 13, 48 - Get extended parameters
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Drive parameter table
+ * @ret status Status code
+ */
+static int int13_get_extended_parameters ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_parameters params;
+ struct segoff address;
+ size_t len = sizeof ( params );
+ uint16_t bufsize;
+ int rc;
+
+ /* Get buffer size */
+ get_real ( bufsize, ix86->segs.ds,
+ ( ix86->regs.si + offsetof ( typeof ( params ), bufsize )));
+
+ DBGC2 ( int13, "Get extended drive parameters to %04x:%04x+%02x\n",
+ ix86->segs.ds, ix86->regs.si, bufsize );
+
+ /* Build drive parameters */
+ memset ( &params, 0, sizeof ( params ) );
+ params.flags = INT13_FL_DMA_TRANSPARENT;
+ if ( ( int13->cylinders < 1024 ) &&
+ ( int13_capacity ( int13 ) <= INT13_MAX_CHS_SECTORS ) ) {
+ params.flags |= INT13_FL_CHS_VALID;
+ }
+ params.cylinders = int13->cylinders;
+ params.heads = int13->heads;
+ params.sectors_per_track = int13->sectors_per_track;
+ params.sectors = int13_capacity ( int13 );
+ params.sector_size = int13_blksize ( int13 );
+ memset ( &params.dpte, 0xff, sizeof ( params.dpte ) );
+ if ( ( rc = int13_device_path_info ( int13, &params.dpi ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not provide device "
+ "path information: %s\n",
+ int13->drive, strerror ( rc ) );
+ len = offsetof ( typeof ( params ), dpi );
+ }
+
+ /* Calculate returned "buffer size" (which will be less than
+ * the length actually copied if device path information is
+ * present).
+ */
+ if ( bufsize < offsetof ( typeof ( params ), dpte ) )
+ return -INT13_STATUS_INVALID;
+ if ( bufsize < offsetof ( typeof ( params ), dpi ) ) {
+ params.bufsize = offsetof ( typeof ( params ), dpte );
+ } else {
+ params.bufsize = offsetof ( typeof ( params ), dpi );
+ }
+
+ DBGC ( int13, "INT 13 drive %02x described using extended "
+ "parameters:\n", int13->drive );
+ address.segment = ix86->segs.ds;
+ address.offset = ix86->regs.si;
+ DBGC_HDA ( int13, address, &params, len );
+
+ /* Return drive parameters */
+ if ( len > bufsize )
+ len = bufsize;
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, &params, len );
+
+ return 0;
+}
+
+/**
+ * INT 13, 4b - Get status or terminate CD-ROM emulation
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Specification packet
+ * @ret status Status code
+ */
+static int int13_cdrom_status_terminate ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_cdrom_specification specification;
+
+ DBGC2 ( int13, "Get CD-ROM emulation status to %04x:%04x%s\n",
+ ix86->segs.ds, ix86->regs.si,
+ ( ix86->regs.al ? "" : " and terminate" ) );
+
+ /* Fail if we are not a CD-ROM */
+ if ( ! int13->is_cdrom ) {
+ DBGC ( int13, "INT13 drive %02x is not a CD-ROM\n",
+ int13->drive );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Build specification packet */
+ memset ( &specification, 0, sizeof ( specification ) );
+ specification.size = sizeof ( specification );
+ specification.drive = int13->drive;
+
+ /* Return specification packet */
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, &specification,
+ sizeof ( specification ) );
+
+ return 0;
+}
+
+
+/**
+ * INT 13, 4d - Read CD-ROM boot catalog
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Command packet
+ * @ret status Status code
+ */
+static int int13_cdrom_read_boot_catalog ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_cdrom_boot_catalog_command command;
+ int rc;
+
+ /* Read parameters from command packet */
+ copy_from_real ( &command, ix86->segs.ds, ix86->regs.si,
+ sizeof ( command ) );
+ DBGC2 ( int13, "Read CD-ROM boot catalog to %08x\n", command.buffer );
+
+ /* Fail if we have no boot catalog */
+ if ( ! int13->boot_catalog ) {
+ DBGC ( int13, "INT13 drive %02x has no boot catalog\n",
+ int13->drive );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Read from boot catalog */
+ if ( ( rc = int13_rw ( int13, ( int13->boot_catalog + command.start ),
+ command.count, phys_to_user ( command.buffer ),
+ block_read ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not read boot catalog: "
+ "%s\n", int13->drive, strerror ( rc ) );
+ return -INT13_STATUS_READ_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * INT 13 handler
+ *
+ */
+static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
+ int command = ix86->regs.ah;
+ unsigned int bios_drive = ix86->regs.dl;
+ struct int13_drive *int13;
+ int status;
+
+ /* Check BIOS hasn't killed off our drive */
+ int13_check_num_drives();
+
+ list_for_each_entry ( int13, &int13s, list ) {
+
+ if ( bios_drive != int13->drive ) {
+ /* Remap any accesses to this drive's natural number */
+ if ( bios_drive == int13->natural_drive ) {
+ DBGC2 ( int13, "INT13,%02x (%02x) remapped to "
+ "(%02x)\n", ix86->regs.ah,
+ bios_drive, int13->drive );
+ ix86->regs.dl = int13->drive;
+ return;
+ } else if ( ( ( bios_drive & 0x7f ) == 0x7f ) &&
+ ( command == INT13_CDROM_STATUS_TERMINATE )
+ && int13->is_cdrom ) {
+ /* Catch non-drive-specific CD-ROM calls */
+ } else {
+ continue;
+ }
+ }
+
+ DBGC2 ( int13, "INT13,%02x (%02x): ",
+ ix86->regs.ah, bios_drive );
+
+ switch ( command ) {
+ case INT13_RESET:
+ status = int13_reset ( int13, ix86 );
+ break;
+ case INT13_GET_LAST_STATUS:
+ status = int13_get_last_status ( int13, ix86 );
+ break;
+ case INT13_READ_SECTORS:
+ status = int13_read_sectors ( int13, ix86 );
+ break;
+ case INT13_WRITE_SECTORS:
+ status = int13_write_sectors ( int13, ix86 );
+ break;
+ case INT13_GET_PARAMETERS:
+ status = int13_get_parameters ( int13, ix86 );
+ break;
+ case INT13_GET_DISK_TYPE:
+ status = int13_get_disk_type ( int13, ix86 );
+ break;
+ case INT13_EXTENSION_CHECK:
+ status = int13_extension_check ( int13, ix86 );
+ break;
+ case INT13_EXTENDED_READ:
+ status = int13_extended_read ( int13, ix86 );
+ break;
+ case INT13_EXTENDED_WRITE:
+ status = int13_extended_write ( int13, ix86 );
+ break;
+ case INT13_EXTENDED_VERIFY:
+ status = int13_extended_verify ( int13, ix86 );
+ break;
+ case INT13_EXTENDED_SEEK:
+ status = int13_extended_seek ( int13, ix86 );
+ break;
+ case INT13_GET_EXTENDED_PARAMETERS:
+ status = int13_get_extended_parameters ( int13, ix86 );
+ break;
+ case INT13_CDROM_STATUS_TERMINATE:
+ status = int13_cdrom_status_terminate ( int13, ix86 );
+ break;
+ case INT13_CDROM_READ_BOOT_CATALOG:
+ status = int13_cdrom_read_boot_catalog ( int13, ix86 );
+ break;
+ default:
+ DBGC2 ( int13, "*** Unrecognised INT13 ***\n" );
+ status = -INT13_STATUS_INVALID;
+ break;
+ }
+
+ /* Store status for INT 13,01 */
+ int13->last_status = status;
+
+ /* Negative status indicates an error */
+ if ( status < 0 ) {
+ status = -status;
+ DBGC ( int13, "INT13,%02x (%02x) failed with status "
+ "%02x\n", ix86->regs.ah, int13->drive, status );
+ } else {
+ ix86->flags &= ~CF;
+ }
+ ix86->regs.ah = status;
+
+ /* Set OF to indicate to wrapper not to chain this call */
+ ix86->flags |= OF;
+
+ return;
+ }
+}
+
+/**
+ * Hook INT 13 handler
+ *
+ */
+static void int13_hook_vector ( void ) {
+ /* Assembly wrapper to call int13(). int13() sets OF if we
+ * should not chain to the previous handler. (The wrapper
+ * clears CF and OF before calling int13()).
+ */
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint13_wrapper:\n\t"
+ /* Preserve %ax and %dx for future reference */
+ "pushw %%bp\n\t"
+ "movw %%sp, %%bp\n\t"
+ "pushw %%ax\n\t"
+ "pushw %%dx\n\t"
+ /* Clear OF, set CF, call int13() */
+ "orb $0, %%al\n\t"
+ "stc\n\t"
+ "pushl %0\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ /* Chain if OF not set */
+ "jo 1f\n\t"
+ "pushfw\n\t"
+ "lcall *%%cs:int13_vector\n\t"
+ "\n1:\n\t"
+ /* Overwrite flags for iret */
+ "pushfw\n\t"
+ "popw 6(%%bp)\n\t"
+ /* Fix up %dl:
+ *
+ * INT 13,15 : do nothing if hard disk
+ * INT 13,08 : load with number of drives
+ * all others: restore original value
+ */
+ "cmpb $0x15, -1(%%bp)\n\t"
+ "jne 2f\n\t"
+ "testb $0x80, -4(%%bp)\n\t"
+ "jnz 3f\n\t"
+ "\n2:\n\t"
+ "movb -4(%%bp), %%dl\n\t"
+ "cmpb $0x08, -1(%%bp)\n\t"
+ "jne 3f\n\t"
+ "testb $0x80, %%dl\n\t"
+ "movb %%cs:num_drives, %%dl\n\t"
+ "jnz 3f\n\t"
+ "movb %%cs:num_fdds, %%dl\n\t"
+ /* Return */
+ "\n3:\n\t"
+ "movw %%bp, %%sp\n\t"
+ "popw %%bp\n\t"
+ "iret\n\t" )
+ : : "i" ( int13 ) );
+
+ hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
+ &int13_vector );
+}
+
+/**
+ * Unhook INT 13 handler
+ */
+static void int13_unhook_vector ( void ) {
+ unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
+ &int13_vector );
+}
+
+/**
+ * Check INT13 emulated drive flow control window
+ *
+ * @v int13 Emulated drive
+ */
+static size_t int13_block_window ( struct int13_drive *int13 __unused ) {
+
+ /* We are never ready to receive data via this interface.
+ * This prevents objects that support both block and stream
+ * interfaces from attempting to send us stream data.
+ */
+ return 0;
+}
+
+/**
+ * Handle INT 13 emulated drive underlying block device closing
+ *
+ * @v int13 Emulated drive
+ * @v rc Reason for close
+ */
+static void int13_block_close ( struct int13_drive *int13, int rc ) {
+
+ /* Any closing is an error from our point of view */
+ if ( rc == 0 )
+ rc = -ENOTCONN;
+
+ DBGC ( int13, "INT13 drive %02x went away: %s\n",
+ int13->drive, strerror ( rc ) );
+
+ /* Record block device error code */
+ int13->block_rc = rc;
+
+ /* Shut down interfaces */
+ intf_restart ( &int13->block, rc );
+}
+
+/** INT 13 drive interface operations */
+static struct interface_operation int13_block_op[] = {
+ INTF_OP ( xfer_window, struct int13_drive *, int13_block_window ),
+ INTF_OP ( intf_close, struct int13_drive *, int13_block_close ),
+};
+
+/** INT 13 drive interface descriptor */
+static struct interface_descriptor int13_block_desc =
+ INTF_DESC ( struct int13_drive, block, int13_block_op );
+
+/**
+ * Free INT 13 emulated drive
+ *
+ * @v refcnt Reference count
+ */
+static void int13_free ( struct refcnt *refcnt ) {
+ struct int13_drive *int13 =
+ container_of ( refcnt, struct int13_drive, refcnt );
+
+ uri_put ( int13->uri );
+ free ( int13 );
+}
+
+/**
+ * Hook INT 13 emulated drive
+ *
+ * @v uri URI
+ * @v drive Drive number
+ * @ret rc Return status code
+ *
+ * Registers the drive with the INT 13 emulation subsystem, and hooks
+ * the INT 13 interrupt vector (if not already hooked).
+ */
+static int int13_hook ( struct uri *uri, unsigned int drive ) {
+ struct int13_drive *int13;
+ unsigned int natural_drive;
+ void *scratch;
+ int rc;
+
+ /* Calculate natural drive number */
+ int13_sync_num_drives();
+ natural_drive = ( ( drive & 0x80 ) ? ( num_drives | 0x80 ) : num_fdds );
+
+ /* Check that drive number is not in use */
+ list_for_each_entry ( int13, &int13s, list ) {
+ if ( int13->drive == drive ) {
+ rc = -EADDRINUSE;
+ goto err_in_use;
+ }
+ }
+
+ /* Allocate and initialise structure */
+ int13 = zalloc ( sizeof ( *int13 ) );
+ if ( ! int13 ) {
+ rc = -ENOMEM;
+ goto err_zalloc;
+ }
+ ref_init ( &int13->refcnt, int13_free );
+ intf_init ( &int13->block, &int13_block_desc, &int13->refcnt );
+ int13->uri = uri_get ( uri );
+ int13->drive = drive;
+ int13->natural_drive = natural_drive;
+
+ /* Open block device interface */
+ if ( ( rc = int13_reopen_block ( int13 ) ) != 0 )
+ goto err_reopen_block;
+
+ /* Read device capacity */
+ if ( ( rc = int13_read_capacity ( int13 ) ) != 0 )
+ goto err_read_capacity;
+
+ /* Allocate scratch area */
+ scratch = malloc ( int13_blksize ( int13 ) );
+ if ( ! scratch )
+ goto err_alloc_scratch;
+
+ /* Parse parameters, if present */
+ if ( ( rc = int13_parse_iso9660 ( int13, scratch ) ) != 0 )
+ goto err_parse_iso9660;
+
+ /* Give drive a default geometry */
+ if ( ( rc = int13_guess_geometry ( int13, scratch ) ) != 0 )
+ goto err_guess_geometry;
+
+ DBGC ( int13, "INT13 drive %02x (naturally %02x) registered with C/H/S "
+ "geometry %d/%d/%d\n", int13->drive, int13->natural_drive,
+ int13->cylinders, int13->heads, int13->sectors_per_track );
+
+ /* Hook INT 13 vector if not already hooked */
+ if ( list_empty ( &int13s ) ) {
+ int13_hook_vector();
+ devices_get();
+ }
+
+ /* Add to list of emulated drives */
+ list_add ( &int13->list, &int13s );
+
+ /* Update BIOS drive count */
+ int13_sync_num_drives();
+
+ free ( scratch );
+ return 0;
+
+ err_guess_geometry:
+ err_parse_iso9660:
+ free ( scratch );
+ err_alloc_scratch:
+ err_read_capacity:
+ err_reopen_block:
+ intf_shutdown ( &int13->block, rc );
+ ref_put ( &int13->refcnt );
+ err_zalloc:
+ err_in_use:
+ return rc;
+}
+
+/**
+ * Find INT 13 emulated drive by drive number
+ *
+ * @v drive Drive number
+ * @ret int13 Emulated drive, or NULL
+ */
+static struct int13_drive * int13_find ( unsigned int drive ) {
+ struct int13_drive *int13;
+
+ list_for_each_entry ( int13, &int13s, list ) {
+ if ( int13->drive == drive )
+ return int13;
+ }
+ return NULL;
+}
+
+/**
+ * Unhook INT 13 emulated drive
+ *
+ * @v drive Drive number
+ *
+ * Unregisters the drive from the INT 13 emulation subsystem. If this
+ * is the last emulated drive, the INT 13 vector is unhooked (if
+ * possible).
+ */
+static void int13_unhook ( unsigned int drive ) {
+ struct int13_drive *int13;
+
+ /* Find drive */
+ int13 = int13_find ( drive );
+ if ( ! int13 ) {
+ DBG ( "INT13 cannot find emulated drive %02x\n", drive );
+ return;
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &int13->block, 0 );
+
+ /* Remove from list of emulated drives */
+ list_del ( &int13->list );
+
+ /* Should adjust BIOS drive count, but it's difficult
+ * to do so reliably.
+ */
+
+ DBGC ( int13, "INT13 drive %02x unregistered\n", int13->drive );
+
+ /* Unhook INT 13 vector if no more drives */
+ if ( list_empty ( &int13s ) ) {
+ devices_put();
+ int13_unhook_vector();
+ }
+
+ /* Drop list's reference to drive */
+ ref_put ( &int13->refcnt );
+}
+
+/**
+ * Load and verify master boot record from INT 13 drive
+ *
+ * @v drive Drive number
+ * @v address Boot code address to fill in
+ * @ret rc Return status code
+ */
+static int int13_load_mbr ( unsigned int drive, struct segoff *address ) {
+ uint8_t status;
+ int discard_b, discard_c, discard_d;
+ uint16_t magic;
+
+ /* Use INT 13, 02 to read the MBR */
+ address->segment = 0;
+ address->offset = 0x7c00;
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
+ "pushl %%ebx\n\t"
+ "popw %%bx\n\t"
+ "popw %%es\n\t"
+ "stc\n\t"
+ "sti\n\t"
+ "int $0x13\n\t"
+ "sti\n\t" /* BIOS bugs */
+ "jc 1f\n\t"
+ "xorw %%ax, %%ax\n\t"
+ "\n1:\n\t"
+ "popw %%es\n\t" )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( discard_c ), "=d" ( discard_d )
+ : "a" ( 0x0201 ), "b" ( *address ),
+ "c" ( 1 ), "d" ( drive ) );
+ if ( status ) {
+ DBG ( "INT13 drive %02x could not read MBR (status %02x)\n",
+ drive, status );
+ return -EIO;
+ }
+
+ /* Check magic signature */
+ get_real ( magic, address->segment,
+ ( address->offset +
+ offsetof ( struct master_boot_record, magic ) ) );
+ if ( magic != INT13_MBR_MAGIC ) {
+ DBG ( "INT13 drive %02x does not contain a valid MBR\n",
+ drive );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/** El Torito boot catalog command packet */
+static struct int13_cdrom_boot_catalog_command __data16 ( eltorito_cmd ) = {
+ .size = sizeof ( struct int13_cdrom_boot_catalog_command ),
+ .count = 1,
+ .buffer = 0x7c00,
+ .start = 0,
+};
+#define eltorito_cmd __use_data16 ( eltorito_cmd )
+
+/** El Torito disk address packet */
+static struct int13_disk_address __bss16 ( eltorito_address );
+#define eltorito_address __use_data16 ( eltorito_address )
+
+/**
+ * Load and verify El Torito boot record from INT 13 drive
+ *
+ * @v drive Drive number
+ * @v address Boot code address to fill in
+ * @ret rc Return status code
+ */
+static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
+ struct {
+ struct eltorito_validation_entry valid;
+ struct eltorito_boot_entry boot;
+ } __attribute__ (( packed )) catalog;
+ uint8_t status;
+
+ /* Use INT 13, 4d to read the boot catalog */
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "sti\n\t"
+ "int $0x13\n\t"
+ "sti\n\t" /* BIOS bugs */
+ "jc 1f\n\t"
+ "xorw %%ax, %%ax\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status )
+ : "a" ( 0x4d00 ), "d" ( drive ),
+ "S" ( __from_data16 ( &eltorito_cmd ) ) );
+ if ( status ) {
+ DBG ( "INT13 drive %02x could not read El Torito boot catalog "
+ "(status %02x)\n", drive, status );
+ return -EIO;
+ }
+ copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0,
+ sizeof ( catalog ) );
+
+ /* Sanity checks */
+ if ( catalog.valid.platform_id != ELTORITO_PLATFORM_X86 ) {
+ DBG ( "INT13 drive %02x El Torito specifies unknown platform "
+ "%02x\n", drive, catalog.valid.platform_id );
+ return -ENOEXEC;
+ }
+ if ( catalog.boot.indicator != ELTORITO_BOOTABLE ) {
+ DBG ( "INT13 drive %02x El Torito is not bootable\n", drive );
+ return -ENOEXEC;
+ }
+ if ( catalog.boot.media_type != ELTORITO_NO_EMULATION ) {
+ DBG ( "INT13 drive %02x El Torito requires emulation "
+ "type %02x\n", drive, catalog.boot.media_type );
+ return -ENOTSUP;
+ }
+ DBG ( "INT13 drive %02x El Torito boot image at LBA %08x (count %d)\n",
+ drive, catalog.boot.start, catalog.boot.length );
+ address->segment = ( catalog.boot.load_segment ?
+ catalog.boot.load_segment : 0x7c0 );
+ address->offset = 0;
+ DBG ( "INT13 drive %02x El Torito boot image loads at %04x:%04x\n",
+ drive, address->segment, address->offset );
+
+ /* Use INT 13, 42 to read the boot image */
+ eltorito_address.bufsize =
+ offsetof ( typeof ( eltorito_address ), buffer_phys );
+ eltorito_address.count = catalog.boot.length;
+ eltorito_address.buffer = *address;
+ eltorito_address.lba = catalog.boot.start;
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "sti\n\t"
+ "int $0x13\n\t"
+ "sti\n\t" /* BIOS bugs */
+ "jc 1f\n\t"
+ "xorw %%ax, %%ax\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status )
+ : "a" ( 0x4200 ), "d" ( drive ),
+ "S" ( __from_data16 ( &eltorito_address ) ) );
+ if ( status ) {
+ DBG ( "INT13 drive %02x could not read El Torito boot image "
+ "(status %02x)\n", drive, status );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Attempt to boot from an INT 13 drive
+ *
+ * @v drive Drive number
+ * @ret rc Return status code
+ *
+ * This boots from the specified INT 13 drive by loading the Master
+ * Boot Record to 0000:7c00 and jumping to it. INT 18 is hooked to
+ * capture an attempt by the MBR to boot the next device. (This is
+ * the closest thing to a return path from an MBR).
+ *
+ * Note that this function can never return success, by definition.
+ */
+static int int13_boot ( unsigned int drive ) {
+ struct memory_map memmap;
+ struct segoff address;
+ int rc;
+
+ /* Look for a usable boot sector */
+ if ( ( ( rc = int13_load_mbr ( drive, &address ) ) != 0 ) &&
+ ( ( rc = int13_load_eltorito ( drive, &address ) ) != 0 ) )
+ return rc;
+
+ /* Dump out memory map prior to boot, if memmap debugging is
+ * enabled. Not required for program flow, but we have so
+ * many problems that turn out to be memory-map related that
+ * it's worth doing.
+ */
+ get_memmap ( &memmap );
+
+ /* Jump to boot sector */
+ if ( ( rc = call_bootsector ( address.segment, address.offset,
+ drive ) ) != 0 ) {
+ DBG ( "INT13 drive %02x boot returned: %s\n",
+ drive, strerror ( rc ) );
+ return rc;
+ }
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+}
+
+/** A boot firmware table generated by iPXE */
+union xbft_table {
+ /** ACPI header */
+ struct acpi_description_header acpi;
+ /** Padding */
+ char pad[768];
+};
+
+/** The boot firmware table generated by iPXE */
+static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) ));
+#define xbftab __use_data16 ( xbftab )
+
+/**
+ * Describe INT 13 emulated drive for SAN-booted operating system
+ *
+ * @v drive Drive number
+ * @ret rc Return status code
+ */
+static int int13_describe ( unsigned int drive ) {
+ struct int13_drive *int13;
+ struct segoff xbft_address;
+ int rc;
+
+ /* Find drive */
+ int13 = int13_find ( drive );
+ if ( ! int13 ) {
+ DBG ( "INT13 cannot find emulated drive %02x\n", drive );
+ return -ENODEV;
+ }
+
+ /* Reopen block device if necessary */
+ if ( ( int13->block_rc != 0 ) &&
+ ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) )
+ return rc;
+
+ /* Clear table */
+ memset ( &xbftab, 0, sizeof ( xbftab ) );
+
+ /* Fill in common parameters */
+ strncpy ( xbftab.acpi.oem_id, "FENSYS",
+ sizeof ( xbftab.acpi.oem_id ) );
+ strncpy ( xbftab.acpi.oem_table_id, "iPXE",
+ sizeof ( xbftab.acpi.oem_table_id ) );
+
+ /* Fill in remaining parameters */
+ if ( ( rc = acpi_describe ( &int13->block, &xbftab.acpi,
+ sizeof ( xbftab ) ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not create ACPI "
+ "description: %s\n", int13->drive, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Fix up ACPI checksum */
+ acpi_fix_checksum ( &xbftab.acpi );
+ xbft_address.segment = rm_ds;
+ xbft_address.offset = __from_data16 ( &xbftab );
+ DBGC ( int13, "INT13 drive %02x described using boot firmware "
+ "table:\n", int13->drive );
+ DBGC_HDA ( int13, xbft_address, &xbftab,
+ le32_to_cpu ( xbftab.acpi.length ) );
+
+ return 0;
+}
+
+PROVIDE_SANBOOT_INLINE ( pcbios, san_default_drive );
+PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook );
+PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook );
+PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot );
+PROVIDE_SANBOOT ( pcbios, san_describe, int13_describe );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c b/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c
new file mode 100644
index 000000000..2414c6909
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2015 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 (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/console.h>
+#include <ipxe/init.h>
+#include <realmode.h>
+#include <int13.h>
+#include <config/console.h>
+
+/** @file
+ *
+ * INT13 disk log console
+ *
+ */
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) )
+#undef CONSOLE_INT13
+#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Disk drive number */
+#define INT13CON_DRIVE 0x80
+
+/** Log partition type */
+#define INT13CON_PARTITION_TYPE 0xe0
+
+/** Maximum number of outstanding unwritten characters */
+#define INT13CON_MAX_UNWRITTEN 64
+
+/** Log partition header */
+struct int13con_header {
+ /** Magic signature */
+ char magic[10];
+} __attribute__ (( packed ));
+
+/** Log partition magic signature */
+#define INT13CON_MAGIC "iPXE LOG\n\n"
+
+/** Sector buffer */
+static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
+#define int13con_buffer __use_data16 ( int13con_buffer )
+
+/** Disk address packet */
+static struct int13_disk_address __bss16 ( int13con_address );
+#define int13con_address __use_data16 ( int13con_address )
+
+/** Current LBA */
+static uint64_t int13con_lba;
+
+/** Maximum LBA */
+static uint64_t int13con_max_lba;
+
+/** Current offset within sector */
+static size_t int13con_offset;
+
+/** Number of unwritten characters */
+static size_t int13con_unwritten;
+
+struct console_driver int13con __console_driver;
+
+/**
+ * Read/write disk sector
+ *
+ * @v op Operation
+ * @v lba Logical block address
+ * @ret rc Return status code
+ */
+static int int13con_rw ( unsigned int op, uint64_t lba ) {
+ uint8_t error;
+
+ /* Construct disk address packet */
+ int13con_address.bufsize = sizeof ( int13con_address );
+ int13con_address.count = 1;
+ int13con_address.buffer.segment = rm_ds;
+ int13con_address.buffer.offset = __from_data16 ( int13con_buffer );
+ int13con_address.lba = lba;
+
+ /* Issue INT13 */
+ __asm__ ( REAL_CODE ( "int $0x13\n\t" )
+ : "=a" ( error )
+ : "0" ( op << 8 ), "d" ( INT13CON_DRIVE ),
+ "S" ( __from_data16 ( &int13con_address ) ) );
+ if ( error ) {
+ DBG ( "INT13CON operation %04x failed: %02x\n",
+ op, error );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Write character to console
+ *
+ * @v character Character
+ */
+static void int13con_putchar ( int character ) {
+ static int busy;
+ int rc;
+
+ /* Ignore if we are already mid-logging */
+ if ( busy )
+ return;
+ busy = 1;
+
+ /* Write character to buffer */
+ int13con_buffer[int13con_offset++] = character;
+ int13con_unwritten++;
+
+ /* Write sector to disk, if applicable */
+ if ( ( int13con_offset == INT13_BLKSIZE ) ||
+ ( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) ||
+ ( character == '\n' ) ) {
+
+ /* Write sector to disk */
+ if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE,
+ int13con_lba ) ) != 0 ) {
+ DBG ( "INT13CON could not write log\n" );
+ /* Ignore and continue; there's nothing we can do */
+ }
+
+ /* Reset count of unwritten characters */
+ int13con_unwritten = 0;
+ }
+
+ /* Move to next sector, if applicable */
+ if ( int13con_offset == INT13_BLKSIZE ) {
+
+ /* Disable console if we have run out of space */
+ if ( int13con_lba >= int13con_max_lba )
+ int13con.disabled = 1;
+
+ /* Clear log buffer */
+ memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) );
+ int13con_offset = 0;
+
+ /* Move to next sector */
+ int13con_lba++;
+ }
+
+ /* Clear busy flag */
+ busy = 0;
+}
+
+/**
+ * Find log partition
+ *
+ * @ret rc Return status code
+ */
+static int int13con_find ( void ) {
+ struct master_boot_record *mbr =
+ ( ( struct master_boot_record * ) int13con_buffer );
+ struct int13con_header *hdr =
+ ( ( struct int13con_header * ) int13con_buffer );
+ struct partition_table_entry part[4];
+ unsigned int i;
+ int rc;
+
+ /* Read MBR */
+ if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) {
+ DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check MBR magic */
+ if ( mbr->magic != INT13_MBR_MAGIC ) {
+ DBG ( "INT13CON incorrect MBR magic\n" );
+ DBG2_HDA ( 0, mbr, sizeof ( *mbr ) );
+ return -EINVAL;
+ }
+
+ /* Look for magic partition */
+ memcpy ( part, mbr->partitions, sizeof ( part ) );
+ for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) {
+
+ /* Skip partitions of the wrong type */
+ if ( part[i].type != INT13CON_PARTITION_TYPE )
+ continue;
+
+ /* Read partition header */
+ if ( ( rc = int13con_rw ( INT13_EXTENDED_READ,
+ part[i].start ) ) != 0 ) {
+ DBG ( "INT13CON partition %d could not read header: "
+ "%s\n", ( i + 1 ), strerror ( rc ) );
+ continue;
+ }
+
+ /* Check partition header */
+ if ( memcmp ( hdr->magic, INT13CON_MAGIC,
+ sizeof ( hdr->magic ) ) != 0 ) {
+ DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) );
+ DBG2_HDA ( 0, hdr, sizeof ( *hdr ) );
+ continue;
+ }
+
+ /* Found log partition */
+ DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ),
+ part[i].start, ( part[i].start + part[i].length ) );
+ int13con_lba = part[i].start;
+ int13con_max_lba = ( part[i].start + part[i].length - 1 );
+
+ /* Initialise log buffer */
+ memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0,
+ ( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) );
+ int13con_offset = sizeof ( hdr->magic );
+
+ return 0;
+ }
+
+ DBG ( "INT13CON found no log partition\n" );
+ return -ENOENT;
+}
+
+/**
+ * Initialise INT13 console
+ *
+ */
+static void int13con_init ( void ) {
+ uint8_t error;
+ uint16_t check;
+ unsigned int discard_c;
+ unsigned int discard_d;
+ int rc;
+
+ /* Check for INT13 extensions */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t"
+ "setc %%al\n\t" )
+ : "=a" ( error ), "=b" ( check ),
+ "=c" ( discard_c ), "=d" ( discard_d )
+ : "0" ( INT13_EXTENSION_CHECK << 8 ),
+ "1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) );
+ if ( error || ( check != 0xaa55 ) ) {
+ DBG ( "INT13CON missing extensions (%02x,%04x)\n",
+ error, check );
+ return;
+ }
+
+ /* Locate log partition */
+ if ( ( rc = int13con_find() ) != 0)
+ return;
+
+ /* Enable console */
+ int13con.disabled = 0;
+}
+
+/**
+ * INT13 console initialisation function
+ */
+struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .initialise = int13con_init,
+};
+
+/** INT13 console driver */
+struct console_driver int13con __console_driver = {
+ .putchar = int13con_putchar,
+ .disabled = CONSOLE_DISABLED,
+ .usage = CONSOLE_INT13,
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c
new file mode 100644
index 000000000..957f8e324
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * External memory allocation
+ *
+ */
+
+#include <limits.h>
+#include <errno.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/hidemem.h>
+#include <ipxe/io.h>
+#include <ipxe/memblock.h>
+#include <ipxe/umalloc.h>
+
+/** Alignment of external allocated memory */
+#define EM_ALIGN ( 4 * 1024 )
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/** An external memory block */
+struct external_memory {
+ /** Size of this memory block (excluding this header) */
+ size_t size;
+ /** Block is currently in use */
+ int used;
+};
+
+/** Top of heap */
+static userptr_t top = UNULL;
+
+/** Bottom of heap (current lowest allocated block) */
+static userptr_t bottom = UNULL;
+
+/** Remaining space on heap */
+static size_t heap_size;
+
+/**
+ * Initialise external heap
+ *
+ */
+static void init_eheap ( void ) {
+ userptr_t base;
+
+ heap_size = largest_memblock ( &base );
+ bottom = top = userptr_add ( base, heap_size );
+ DBG ( "External heap grows downwards from %lx (size %zx)\n",
+ user_to_phys ( top, 0 ), heap_size );
+}
+
+/**
+ * Collect free blocks
+ *
+ */
+static void ecollect_free ( void ) {
+ struct external_memory extmem;
+ size_t len;
+
+ /* Walk the free list and collect empty blocks */
+ while ( bottom != top ) {
+ copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
+ sizeof ( extmem ) );
+ if ( extmem.used )
+ break;
+ DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
+ user_to_phys ( bottom, extmem.size ) );
+ len = ( extmem.size + sizeof ( extmem ) );
+ bottom = userptr_add ( bottom, len );
+ heap_size += len;
+ }
+}
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr Memory previously allocated by umalloc(), or UNULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or UNULL
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
+ struct external_memory extmem;
+ userptr_t new = ptr;
+ size_t align;
+
+ /* (Re)initialise external memory allocator if necessary */
+ if ( bottom == top )
+ init_eheap();
+
+ /* Get block properties into extmem */
+ if ( ptr && ( ptr != UNOWHERE ) ) {
+ /* Determine old size */
+ copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
+ sizeof ( extmem ) );
+ } else {
+ /* Create a zero-length block */
+ if ( heap_size < sizeof ( extmem ) ) {
+ DBG ( "EXTMEM out of space\n" );
+ return UNULL;
+ }
+ ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
+ heap_size -= sizeof ( extmem );
+ DBG ( "EXTMEM allocating [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
+ extmem.size = 0;
+ }
+ extmem.used = ( new_size > 0 );
+
+ /* Expand/shrink block if possible */
+ if ( ptr == bottom ) {
+ /* Update block */
+ if ( new_size > ( heap_size - extmem.size ) ) {
+ DBG ( "EXTMEM out of space\n" );
+ return UNULL;
+ }
+ new = userptr_add ( ptr, - ( new_size - extmem.size ) );
+ align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
+ new_size += align;
+ new = userptr_add ( new, -align );
+ DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ),
+ user_to_phys ( ptr, extmem.size ),
+ user_to_phys ( new, 0 ),
+ user_to_phys ( new, new_size ));
+ memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
+ extmem.size : new_size ) );
+ bottom = new;
+ heap_size -= ( new_size - extmem.size );
+ extmem.size = new_size;
+ } else {
+ /* Cannot expand; can only pretend to shrink */
+ if ( new_size > extmem.size ) {
+ /* Refuse to expand */
+ DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ),
+ user_to_phys ( ptr, extmem.size ) );
+ return UNULL;
+ }
+ }
+
+ /* Write back block properties */
+ copy_to_user ( new, -sizeof ( extmem ), &extmem,
+ sizeof ( extmem ) );
+
+ /* Collect any free blocks and update hidden memory region */
+ ecollect_free();
+ hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
+ 0 : -sizeof ( extmem ) ) ),
+ user_to_phys ( top, 0 ) );
+
+ return ( new_size ? new : UNOWHERE );
+}
+
+PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c b/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c
new file mode 100644
index 000000000..34efa0b39
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c
@@ -0,0 +1,123 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/pci.h>
+#include <realmode.h>
+
+/** @file
+ *
+ * PCI configuration space access via PCI BIOS
+ *
+ */
+
+/**
+ * Determine number of PCI buses within system
+ *
+ * @ret num_bus Number of buses
+ */
+static int pcibios_num_bus ( void ) {
+ int discard_a, discard_D;
+ uint8_t max_bus;
+
+ /* We issue this call using flat real mode, to work around a
+ * bug in some HP BIOSes.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( "call flatten_real_mode\n\t"
+ "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "xorw %%cx, %%cx\n\t"
+ "\n1:\n\t" )
+ : "=c" ( max_bus ), "=a" ( discard_a ),
+ "=D" ( discard_D )
+ : "a" ( PCIBIOS_INSTALLATION_CHECK >> 16 ),
+ "D" ( 0 )
+ : "ebx", "edx" );
+
+ return ( max_bus + 1 );
+}
+
+/**
+ * Read configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v command PCI BIOS command
+ * @v value Value read
+ * @ret rc Return status code
+ */
+int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){
+ int discard_b, discard_D;
+ int status;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "xorl %%eax, %%eax\n\t"
+ "decl %%eax\n\t"
+ "movl %%eax, %%ecx\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( *value ), "=D" ( discard_D )
+ : "a" ( command >> 16 ), "D" ( command ),
+ "b" ( pci->busdevfn )
+ : "edx" );
+
+ return ( ( status >> 8 ) & 0xff );
+}
+
+/**
+ * Write configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v command PCI BIOS command
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
+ int discard_b, discard_c, discard_D;
+ int status;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "movb $0xff, %%ah\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( discard_c ), "=D" ( discard_D )
+ : "a" ( command >> 16 ), "D" ( command ),
+ "b" ( pci->busdevfn ), "c" ( value )
+ : "edx" );
+
+ return ( ( status >> 8 ) & 0xff );
+}
+
+PROVIDE_PCIAPI ( pcbios, pci_num_bus, pcibios_num_bus );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_dword );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c
new file mode 100644
index 000000000..9aab03c03
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * RTC-based entropy source
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <biosint.h>
+#include <pic8259.h>
+#include <rtc.h>
+#include <ipxe/entropy.h>
+
+/** RTC "interrupt triggered" flag */
+static uint8_t __text16 ( rtc_flag );
+#define rtc_flag __use_text16 ( rtc_flag )
+
+/** RTC interrupt handler */
+extern void rtc_isr ( void );
+
+/** Previous RTC interrupt handler */
+static struct segoff rtc_old_handler;
+
+/**
+ * Hook RTC interrupt handler
+ *
+ */
+static void rtc_hook_isr ( void ) {
+
+ /* RTC interrupt handler */
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nrtc_isr:\n\t"
+ /* Preserve registers */
+ "pushw %%ax\n\t"
+ /* Set "interrupt triggered" flag */
+ "cs movb $0x01, %c0\n\t"
+ /* Read RTC status register C to
+ * acknowledge interrupt
+ */
+ "movb %3, %%al\n\t"
+ "outb %%al, %1\n\t"
+ "inb %2\n\t"
+ /* Send EOI */
+ "movb $0x20, %%al\n\t"
+ "outb %%al, $0xa0\n\t"
+ "outb %%al, $0x20\n\t"
+ /* Restore registers and return */
+ "popw %%ax\n\t"
+ "iret\n\t" )
+ :
+ : "p" ( __from_text16 ( &rtc_flag ) ),
+ "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
+ "i" ( RTC_STATUS_C ) );
+
+ hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
+ &rtc_old_handler );
+}
+
+/**
+ * Unhook RTC interrupt handler
+ *
+ */
+static void rtc_unhook_isr ( void ) {
+ int rc;
+
+ rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
+ &rtc_old_handler );
+ assert ( rc == 0 ); /* Should always be able to unhook */
+}
+
+/**
+ * Enable RTC interrupts
+ *
+ */
+static void rtc_enable_int ( void ) {
+ uint8_t status_b;
+
+ /* Set Periodic Interrupt Enable bit in status register B */
+ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
+ status_b = inb ( CMOS_DATA );
+ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
+ outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
+
+ /* Re-enable NMI and reset to default address */
+ outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
+ inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
+}
+
+/**
+ * Disable RTC interrupts
+ *
+ */
+static void rtc_disable_int ( void ) {
+ uint8_t status_b;
+
+ /* Clear Periodic Interrupt Enable bit in status register B */
+ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
+ status_b = inb ( CMOS_DATA );
+ outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
+ outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
+
+ /* Re-enable NMI and reset to default address */
+ outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
+ inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
+}
+
+/**
+ * Enable entropy gathering
+ *
+ * @ret rc Return status code
+ */
+static int rtc_entropy_enable ( void ) {
+
+ rtc_hook_isr();
+ enable_irq ( RTC_IRQ );
+ rtc_enable_int();
+
+ return 0;
+}
+
+/**
+ * Disable entropy gathering
+ *
+ */
+static void rtc_entropy_disable ( void ) {
+
+ rtc_disable_int();
+ disable_irq ( RTC_IRQ );
+ rtc_unhook_isr();
+}
+
+/**
+ * Measure a single RTC tick
+ *
+ * @ret delta Length of RTC tick (in TSC units)
+ */
+uint8_t rtc_sample ( void ) {
+ uint32_t before;
+ uint32_t after;
+ uint32_t temp;
+
+ __asm__ __volatile__ (
+ REAL_CODE ( /* Enable interrupts */
+ "sti\n\t"
+ /* Wait for RTC interrupt */
+ "cs movb %b2, %c4\n\t"
+ "\n1:\n\t"
+ "cs xchgb %b2, %c4\n\t" /* Serialize */
+ "testb %b2, %b2\n\t"
+ "jz 1b\n\t"
+ /* Read "before" TSC */
+ "rdtsc\n\t"
+ /* Store "before" TSC on stack */
+ "pushl %0\n\t"
+ /* Wait for another RTC interrupt */
+ "xorb %b2, %b2\n\t"
+ "cs movb %b2, %c4\n\t"
+ "\n1:\n\t"
+ "cs xchgb %b2, %c4\n\t" /* Serialize */
+ "testb %b2, %b2\n\t"
+ "jz 1b\n\t"
+ /* Read "after" TSC */
+ "rdtsc\n\t"
+ /* Retrieve "before" TSC on stack */
+ "popl %1\n\t"
+ /* Disable interrupts */
+ "cli\n\t"
+ )
+ : "=a" ( after ), "=d" ( before ), "=q" ( temp )
+ : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) );
+
+ return ( after - before );
+}
+
+PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
+PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
+PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
+PROVIDE_ENTROPY_INLINE ( rtc, get_noise );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c
new file mode 100644
index 000000000..cdbeac8d5
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * RTC-based time source
+ *
+ */
+
+#include <stdint.h>
+#include <time.h>
+#include <rtc.h>
+#include <ipxe/time.h>
+
+/**
+ * Read RTC register
+ *
+ * @v address Register address
+ * @ret data Data
+ */
+static unsigned int rtc_readb ( int address ) {
+ outb ( address, CMOS_ADDRESS );
+ return inb ( CMOS_DATA );
+}
+
+/**
+ * Check if RTC update is in progress
+ *
+ * @ret is_busy RTC update is in progress
+ */
+static int rtc_is_busy ( void ) {
+ return ( rtc_readb ( RTC_STATUS_A ) & RTC_STATUS_A_UPDATE_IN_PROGRESS );
+}
+
+/**
+ * Read RTC BCD register
+ *
+ * @v address Register address
+ * @ret value Value
+ */
+static unsigned int rtc_readb_bcd ( int address ) {
+ unsigned int bcd;
+
+ bcd = rtc_readb ( address );
+ return ( bcd - ( 6 * ( bcd >> 4 ) ) );
+}
+
+/**
+ * Read RTC time
+ *
+ * @ret time Time, in seconds
+ */
+static time_t rtc_read_time ( void ) {
+ unsigned int status_b;
+ int is_binary;
+ int is_24hour;
+ unsigned int ( * read_component ) ( int address );
+ struct tm tm;
+ int is_pm;
+ unsigned int hour;
+ time_t time;
+
+ /* Wait for any in-progress update to complete */
+ while ( rtc_is_busy() ) {}
+
+ /* Determine RTC mode */
+ status_b = rtc_readb ( RTC_STATUS_B );
+ is_binary = ( status_b & RTC_STATUS_B_BINARY );
+ is_24hour = ( status_b & RTC_STATUS_B_24_HOUR );
+ read_component = ( is_binary ? rtc_readb : rtc_readb_bcd );
+
+ /* Read time values */
+ tm.tm_sec = read_component ( RTC_SEC );
+ tm.tm_min = read_component ( RTC_MIN );
+ hour = read_component ( RTC_HOUR );
+ if ( ! is_24hour ) {
+ is_pm = ( hour >= 80 );
+ hour = ( ( ( ( hour & 0x7f ) % 80 ) % 12 ) +
+ ( is_pm ? 12 : 0 ) );
+ }
+ tm.tm_hour = hour;
+ tm.tm_mday = read_component ( RTC_MDAY );
+ tm.tm_mon = ( read_component ( RTC_MON ) - 1 );
+ tm.tm_year = ( read_component ( RTC_YEAR ) +
+ 100 /* Assume we are in the 21st century, since
+ * this code was written in 2012 */ );
+
+ DBGC ( RTC_STATUS_A, "RTCTIME is %04d-%02d-%02d %02d:%02d:%02d "
+ "(%s,%d-hour)\n", ( tm.tm_year + 1900 ), ( tm.tm_mon + 1 ),
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ ( is_binary ? "binary" : "BCD" ), ( is_24hour ? 24 : 12 ) );
+
+ /* Convert to seconds since the Epoch */
+ time = mktime ( &tm );
+
+ return time;
+}
+
+/**
+ * Get current time in seconds
+ *
+ * @ret time Time, in seconds
+ */
+static time_t rtc_now ( void ) {
+ time_t time = 0;
+ time_t last_time;
+
+ /* Read time until we get two matching values in a row, in
+ * case we end up reading a corrupted value in the middle of
+ * an update.
+ */
+ do {
+ last_time = time;
+ time = rtc_read_time();
+ } while ( time != last_time );
+
+ return time;
+}
+
+PROVIDE_TIME ( rtc, time_now, rtc_now );
diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c b/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c
new file mode 100644
index 000000000..9cf2bf29e
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * VESA frame buffer console
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <realmode.h>
+#include <ipxe/console.h>
+#include <ipxe/io.h>
+#include <ipxe/ansicol.h>
+#include <ipxe/fbcon.h>
+#include <ipxe/vesafb.h>
+#include <config/console.h>
+
+/* Avoid dragging in BIOS console if not otherwise used */
+extern struct console_driver bios_console;
+struct console_driver bios_console __attribute__ (( weak ));
+
+/* Disambiguate the various error causes */
+#define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED )
+#define EINFO_EIO_FAILED \
+ __einfo_uniqify ( EINFO_EIO, 0x01, \
+ "Function call failed" )
+#define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE )
+#define EINFO_EIO_HARDWARE \
+ __einfo_uniqify ( EINFO_EIO, 0x02, \
+ "Not supported in current configuration" )
+#define EIO_MODE __einfo_error ( EINFO_EIO_MODE )
+#define EINFO_EIO_MODE \
+ __einfo_uniqify ( EINFO_EIO, 0x03, \
+ "Invalid in current video mode" )
+#define EIO_VBE( code ) \
+ EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE )
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) )
+#undef CONSOLE_VESAFB
+#define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Font corresponding to selected character width and height */
+#define VESAFB_FONT VBE_FONT_8x16
+
+/* Forward declaration */
+struct console_driver vesafb_console __console_driver;
+
+/** A VESA frame buffer */
+struct vesafb {
+ /** Frame buffer console */
+ struct fbcon fbcon;
+ /** Physical start address */
+ physaddr_t start;
+ /** Pixel geometry */
+ struct fbcon_geometry pixel;
+ /** Margin */
+ struct fbcon_margin margin;
+ /** Colour mapping */
+ struct fbcon_colour_map map;
+ /** Font definition */
+ struct fbcon_font font;
+ /** Saved VGA mode */
+ uint8_t saved_mode;
+};
+
+/** The VESA frame buffer */
+static struct vesafb vesafb;
+
+/** Base memory buffer used for VBE calls */
+union vbe_buffer {
+ /** VBE controller information block */
+ struct vbe_controller_info controller;
+ /** VBE mode information block */
+ struct vbe_mode_info mode;
+};
+static union vbe_buffer __bss16 ( vbe_buf );
+#define vbe_buf __use_data16 ( vbe_buf )
+
+/**
+ * Convert VBE status code to iPXE status code
+ *
+ * @v status VBE status code
+ * @ret rc Return status code
+ */
+static int vesafb_rc ( unsigned int status ) {
+ unsigned int code;
+
+ if ( ( status & 0xff ) != 0x4f )
+ return -ENOTSUP;
+ code = ( ( status >> 8 ) & 0xff );
+ return ( code ? -EIO_VBE ( code ) : 0 );
+}
+
+/**
+ * Get font definition
+ *
+ */
+static void vesafb_font ( void ) {
+ struct segoff font;
+
+ /* Get font information
+ *
+ * Working around gcc bugs is icky here. The value we want is
+ * returned in %ebp, but there's no way to specify %ebp in an
+ * output constraint. We can't put %ebp in the clobber list,
+ * because this tends to cause random build failures on some
+ * gcc versions. We can't manually push/pop %ebp and return
+ * the value via a generic register output constraint, because
+ * gcc might choose to use %ebp to satisfy that constraint
+ * (and we have no way to prevent it from so doing).
+ *
+ * Work around this hideous mess by using %ecx and %edx as the
+ * output registers, since they get clobbered anyway.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */
+ "int $0x10\n\t"
+ "movw %%es, %%cx\n\t"
+ "movw %%bp, %%dx\n\t"
+ "popw %%bp\n\t" /* gcc bug */ )
+ : "=c" ( font.segment ),
+ "=d" ( font.offset )
+ : "a" ( VBE_GET_FONT ),
+ "b" ( VESAFB_FONT ) );
+ DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
+ VESAFB_FONT, font.segment, font.offset );
+ vesafb.font.start = real_to_user ( font.segment, font.offset );
+}
+
+/**
+ * Get VBE mode list
+ *
+ * @ret mode_numbers Mode number list (terminated with VBE_MODE_END)
+ * @ret rc Return status code
+ *
+ * The caller is responsible for eventually freeing the mode list.
+ */
+static int vesafb_mode_list ( uint16_t **mode_numbers ) {
+ struct vbe_controller_info *controller = &vbe_buf.controller;
+ userptr_t video_mode_ptr;
+ uint16_t mode_number;
+ uint16_t status;
+ size_t len;
+ int rc;
+
+ /* Avoid returning uninitialised data on error */
+ *mode_numbers = NULL;
+
+ /* Get controller information block */
+ controller->vbe_signature = 0;
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_CONTROLLER_INFO ),
+ "D" ( __from_data16 ( controller ) )
+ : "memory", "ebx", "edx" );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not get controller information: "
+ "[%04x] %s\n", status, strerror ( rc ) );
+ return rc;
+ }
+ if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) {
+ DBGC ( &vbe_buf, "VESAFB invalid controller signature "
+ "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ),
+ ( controller->vbe_signature >> 8 ),
+ ( controller->vbe_signature >> 16 ),
+ ( controller->vbe_signature >> 24 ) );
+ DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) );
+ return -EINVAL;
+ }
+ DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at "
+ "%04x:%04x\n", controller->vbe_major_version,
+ controller->vbe_minor_version,
+ controller->video_mode_ptr.segment,
+ controller->video_mode_ptr.offset );
+
+ /* Calculate length of mode list */
+ video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
+ controller->video_mode_ptr.offset );
+ len = 0;
+ do {
+ copy_from_user ( &mode_number, video_mode_ptr, len,
+ sizeof ( mode_number ) );
+ len += sizeof ( mode_number );
+ } while ( mode_number != VBE_MODE_END );
+
+ /* Allocate and fill mode list */
+ *mode_numbers = malloc ( len );
+ if ( ! *mode_numbers )
+ return -ENOMEM;
+ copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
+
+ return 0;
+}
+
+/**
+ * Get video mode information
+ *
+ * @v mode_number Mode number
+ * @ret rc Return status code
+ */
+static int vesafb_mode_info ( unsigned int mode_number ) {
+ struct vbe_mode_info *mode = &vbe_buf.mode;
+ uint16_t status;
+ int rc;
+
+ /* Get mode information */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_MODE_INFO ),
+ "c" ( mode_number ),
+ "D" ( __from_data16 ( mode ) )
+ : "memory" );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not get mode %04x information: "
+ "[%04x] %s\n", mode_number, status, strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) model "
+ "%02x [x%d]%s%s%s%s%s\n", mode_number, mode->x_resolution,
+ mode->y_resolution, mode->bits_per_pixel, mode->rsvd_mask_size,
+ mode->red_mask_size, mode->green_mask_size, mode->blue_mask_size,
+ mode->memory_model, ( mode->number_of_image_pages + 1 ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
+ "" : " [unsupported]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
+ " [tty]" : "" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
+ "" : " [text]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
+ "" : " [nonlinear]" ),
+ ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
+ " [buf]" : "" ) );
+
+ return 0;
+}
+
+/**
+ * Set video mode
+ *
+ * @v mode_number Mode number
+ * @ret rc Return status code
+ */
+static int vesafb_set_mode ( unsigned int mode_number ) {
+ struct vbe_mode_info *mode = &vbe_buf.mode;
+ uint16_t status;
+ int rc;
+
+ /* Get mode information */
+ if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
+ return rc;
+
+ /* Record mode parameters */
+ vesafb.start = mode->phys_base_ptr;
+ vesafb.pixel.width = mode->x_resolution;
+ vesafb.pixel.height = mode->y_resolution;
+ vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 );
+ vesafb.pixel.stride = mode->bytes_per_scan_line;
+ DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n",
+ mode_number, mode->phys_base_ptr );
+
+ /* Initialise font colours */
+ vesafb.map.red_scale = ( 8 - mode->red_mask_size );
+ vesafb.map.green_scale = ( 8 - mode->green_mask_size );
+ vesafb.map.blue_scale = ( 8 - mode->blue_mask_size );
+ vesafb.map.red_lsb = mode->red_field_position;
+ vesafb.map.green_lsb = mode->green_field_position;
+ vesafb.map.blue_lsb = mode->blue_field_position;
+
+ /* Select this mode */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( status )
+ : "a" ( VBE_SET_MODE ),
+ "b" ( mode_number ) );
+ if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
+ DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n",
+ mode_number, status, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Select video mode
+ *
+ * @v mode_numbers Mode number list (terminated with VBE_MODE_END)
+ * @v min_width Minimum required width (in pixels)
+ * @v min_height Minimum required height (in pixels)
+ * @v min_bpp Minimum required colour depth (in bits per pixel)
+ * @ret mode_number Mode number, or negative error
+ */
+static int vesafb_select_mode ( const uint16_t *mode_numbers,
+ unsigned int min_width, unsigned int min_height,
+ unsigned int min_bpp ) {
+ struct vbe_mode_info *mode = &vbe_buf.mode;
+ int best_mode_number = -ENOENT;
+ unsigned int best_score = INT_MAX;
+ unsigned int score;
+ uint16_t mode_number;
+ int rc;
+
+ /* Find the first suitable mode */
+ while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) {
+
+ /* Force linear mode variant */
+ mode_number |= VBE_MODE_LINEAR;
+
+ /* Get mode information */
+ if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
+ continue;
+
+ /* Skip unusable modes */
+ if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
+ VBE_MODE_ATTR_GRAPHICS |
+ VBE_MODE_ATTR_LINEAR ) ) !=
+ ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS |
+ VBE_MODE_ATTR_LINEAR ) ) {
+ continue;
+ }
+ if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR )
+ continue;
+
+ /* Skip modes not meeting the requirements */
+ if ( ( mode->x_resolution < min_width ) ||
+ ( mode->y_resolution < min_height ) ||
+ ( mode->bits_per_pixel < min_bpp ) ) {
+ continue;
+ }
+
+ /* Select this mode if it has the best (i.e. lowest)
+ * score. We choose the scoring system to favour
+ * modes close to the specified width and height;
+ * within modes of the same width and height we prefer
+ * a higher colour depth.
+ */
+ score = ( ( mode->x_resolution * mode->y_resolution ) -
+ mode->bits_per_pixel );
+ if ( score < best_score ) {
+ best_mode_number = mode_number;
+ best_score = score;
+ }
+ }
+
+ if ( best_mode_number >= 0 ) {
+ DBGC ( &vbe_buf, "VESAFB selected mode %04x\n",
+ best_mode_number );
+ } else {
+ DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" );
+ }
+
+ return best_mode_number;
+}
+
+/**
+ * Restore video mode
+ *
+ */
+static void vesafb_restore ( void ) {
+ uint32_t discard_a;
+
+ /* Restore saved VGA mode */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( discard_a )
+ : "a" ( VBE_SET_VGA_MODE | vesafb.saved_mode ) );
+ DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n",
+ vesafb.saved_mode );
+}
+
+/**
+ * Initialise VESA frame buffer
+ *
+ * @v config Console configuration, or NULL to reset
+ * @ret rc Return status code
+ */
+static int vesafb_init ( struct console_configuration *config ) {
+ uint32_t discard_b;
+ uint16_t *mode_numbers;
+ unsigned int xgap;
+ unsigned int ygap;
+ unsigned int left;
+ unsigned int right;
+ unsigned int top;
+ unsigned int bottom;
+ int mode_number;
+ int rc;
+
+ /* Record current VGA mode */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
+ : "=a" ( vesafb.saved_mode ), "=b" ( discard_b )
+ : "a" ( VBE_GET_VGA_MODE ) );
+ DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode );
+
+ /* Get VESA mode list */
+ if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
+ goto err_mode_list;
+
+ /* Select mode */
+ if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width,
+ config->height,
+ config->depth ) ) < 0 ) {
+ rc = mode_number;
+ goto err_select_mode;
+ }
+
+ /* Set mode */
+ if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 )
+ goto err_set_mode;
+
+ /* Calculate margin. If the actual screen size is larger than
+ * the requested screen size, then update the margins so that
+ * the margin remains relative to the requested screen size.
+ * (As an exception, if a zero margin was specified then treat
+ * this as meaning "expand to edge of actual screen".)
+ */
+ xgap = ( vesafb.pixel.width - config->width );
+ ygap = ( vesafb.pixel.height - config->height );
+ left = ( xgap / 2 );
+ right = ( xgap - left );
+ top = ( ygap / 2 );
+ bottom = ( ygap - top );
+ vesafb.margin.left = ( config->left + ( config->left ? left : 0 ) );
+ vesafb.margin.right = ( config->right + ( config->right ? right : 0 ) );
+ vesafb.margin.top = ( config->top + ( config->top ? top : 0 ) );
+ vesafb.margin.bottom =
+ ( config->bottom + ( config->bottom ? bottom : 0 ) );
+
+ /* Get font data */
+ vesafb_font();
+
+ /* Initialise frame buffer console */
+ if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
+ &vesafb.pixel, &vesafb.margin, &vesafb.map,
+ &vesafb.font, config->pixbuf ) ) != 0 )
+ goto err_fbcon_init;
+
+ free ( mode_numbers );
+ return 0;
+
+ fbcon_fini ( &vesafb.fbcon );
+ err_fbcon_init:
+ err_set_mode:
+ vesafb_restore();
+ err_select_mode:
+ free ( mode_numbers );
+ err_mode_list:
+ return rc;
+}
+
+/**
+ * Finalise VESA frame buffer
+ *
+ */
+static void vesafb_fini ( void ) {
+
+ /* Finalise frame buffer console */
+ fbcon_fini ( &vesafb.fbcon );
+
+ /* Restore saved VGA mode */
+ vesafb_restore();
+}
+
+/**
+ * Print a character to current cursor position
+ *
+ * @v character Character
+ */
+static void vesafb_putchar ( int character ) {
+
+ fbcon_putchar ( &vesafb.fbcon, character );
+}
+
+/**
+ * Configure console
+ *
+ * @v config Console configuration, or NULL to reset
+ * @ret rc Return status code
+ */
+static int vesafb_configure ( struct console_configuration *config ) {
+ int rc;
+
+ /* Reset console, if applicable */
+ if ( ! vesafb_console.disabled ) {
+ vesafb_fini();
+ bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
+ ansicol_reset_magic();
+ }
+ vesafb_console.disabled = CONSOLE_DISABLED;
+
+ /* Do nothing more unless we have a usable configuration */
+ if ( ( config == NULL ) ||
+ ( config->width == 0 ) || ( config->height == 0 ) ) {
+ return 0;
+ }
+
+ /* Initialise VESA frame buffer */
+ if ( ( rc = vesafb_init ( config ) ) != 0 )
+ return rc;
+
+ /* Mark console as enabled */
+ vesafb_console.disabled = 0;
+ bios_console.disabled |= CONSOLE_DISABLED_OUTPUT;
+
+ /* Set magic colour to transparent if we have a background picture */
+ if ( config->pixbuf )
+ ansicol_set_magic_transparent();
+
+ return 0;
+}
+
+/** VESA frame buffer console driver */
+struct console_driver vesafb_console __console_driver = {
+ .usage = CONSOLE_VESAFB,
+ .putchar = vesafb_putchar,
+ .configure = vesafb_configure,
+ .disabled = CONSOLE_DISABLED,
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c
new file mode 100644
index 000000000..104313666
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c
@@ -0,0 +1,354 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/uaccess.h>
+#include <ipxe/init.h>
+#include <ipxe/profile.h>
+#include <setjmp.h>
+#include <registers.h>
+#include <biosint.h>
+#include <pxe.h>
+#include <pxe_call.h>
+
+/** @file
+ *
+ * PXE API entry point
+ */
+
+/* Disambiguate the various error causes */
+#define EINFO_EPXENBP \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "External PXE NBP error" )
+#define EPXENBP( status ) EPLATFORM ( EINFO_EPXENBP, status )
+
+/** Vector for chaining INT 1A */
+extern struct segoff __text16 ( pxe_int_1a_vector );
+#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
+
+/** INT 1A handler */
+extern void pxe_int_1a ( void );
+
+/** INT 1A hooked flag */
+static int int_1a_hooked = 0;
+
+/** PXENV_UNDI_TRANSMIT API call profiler */
+static struct profiler pxe_api_tx_profiler __profiler =
+ { .name = "pxeapi.tx" };
+
+/** PXENV_UNDI_ISR API call profiler */
+static struct profiler pxe_api_isr_profiler __profiler =
+ { .name = "pxeapi.isr" };
+
+/** PXE unknown API call profiler
+ *
+ * This profiler can be used to measure the overhead of a dummy PXE
+ * API call.
+ */
+static struct profiler pxe_api_unknown_profiler __profiler =
+ { .name = "pxeapi.unknown" };
+
+/** Miscellaneous PXE API call profiler */
+static struct profiler pxe_api_misc_profiler __profiler =
+ { .name = "pxeapi.misc" };
+
+/**
+ * Handle an unknown PXE API call
+ *
+ * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
+ * @ret #PXENV_EXIT_FAILURE Always
+ * @err #PXENV_STATUS_UNSUPPORTED Always
+ */
+static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
+ pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/** Unknown PXE API call list */
+struct pxe_api_call pxenv_unknown_api __pxe_api_call =
+ PXE_API_CALL ( PXENV_UNKNOWN, pxenv_unknown, struct s_PXENV_UNKNOWN );
+
+/**
+ * Locate PXE API call
+ *
+ * @v opcode Opcode
+ * @ret call PXE API call, or NULL
+ */
+static struct pxe_api_call * find_pxe_api_call ( uint16_t opcode ) {
+ struct pxe_api_call *call;
+
+ for_each_table_entry ( call, PXE_API_CALLS ) {
+ if ( call->opcode == opcode )
+ return call;
+ }
+ return NULL;
+}
+
+/**
+ * Determine applicable profiler (for debugging)
+ *
+ * @v opcode PXE opcode
+ * @ret profiler Profiler
+ */
+static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
+
+ /* Determine applicable profiler */
+ switch ( opcode ) {
+ case PXENV_UNDI_TRANSMIT:
+ return &pxe_api_tx_profiler;
+ case PXENV_UNDI_ISR:
+ return &pxe_api_isr_profiler;
+ case PXENV_UNKNOWN:
+ return &pxe_api_unknown_profiler;
+ default:
+ return &pxe_api_misc_profiler;
+ }
+}
+
+/**
+ * Dispatch PXE API call
+ *
+ * @v bx PXE opcode
+ * @v es:di Address of PXE parameter block
+ * @ret ax PXE exit code
+ */
+__asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
+ uint16_t opcode = ix86->regs.bx;
+ userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
+ struct profiler *profiler = pxe_api_profiler ( opcode );
+ struct pxe_api_call *call;
+ union u_PXENV_ANY params;
+ PXENV_EXIT_t ret;
+
+ /* Start profiling */
+ profile_start ( profiler );
+
+ /* Locate API call */
+ call = find_pxe_api_call ( opcode );
+ if ( ! call ) {
+ DBGC ( &pxe_netdev, "PXENV_UNKNOWN_%04x\n", opcode );
+ call = &pxenv_unknown_api;
+ }
+
+ /* Copy parameter block from caller */
+ copy_from_user ( &params, uparams, 0, call->params_len );
+
+ /* Set default status in case child routine fails to do so */
+ params.Status = PXENV_STATUS_FAILURE;
+
+ /* Hand off to relevant API routine */
+ ret = call->entry ( &params );
+
+ /* Copy modified parameter block back to caller and return */
+ copy_to_user ( uparams, 0, &params, call->params_len );
+ ix86->regs.ax = ret;
+
+ /* Stop profiling, if applicable */
+ profile_stop ( profiler );
+}
+
+/**
+ * Dispatch weak PXE API call with PXE stack available
+ *
+ * @v ix86 Registers for PXE call
+ * @ret present Zero (PXE stack present)
+ */
+int pxe_api_call_weak ( struct i386_all_regs *ix86 ) {
+ pxe_api_call ( ix86 );
+ return 0;
+}
+
+/**
+ * Dispatch PXE loader call
+ *
+ * @v es:di Address of PXE parameter block
+ * @ret ax PXE exit code
+ */
+__asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
+ userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
+ struct s_UNDI_LOADER params;
+ PXENV_EXIT_t ret;
+
+ /* Copy parameter block from caller */
+ copy_from_user ( &params, uparams, 0, sizeof ( params ) );
+
+ /* Fill in ROM segment address */
+ ppxe.UNDIROMID.segment = ix86->segs.ds;
+
+ /* Set default status in case child routine fails to do so */
+ params.Status = PXENV_STATUS_FAILURE;
+
+ /* Call UNDI loader */
+ ret = undi_loader ( &params );
+
+ /* Copy modified parameter block back to caller and return */
+ copy_to_user ( uparams, 0, &params, sizeof ( params ) );
+ ix86->regs.ax = ret;
+}
+
+/**
+ * Calculate byte checksum as used by PXE
+ *
+ * @v data Data
+ * @v size Length of data
+ * @ret sum Checksum
+ */
+static uint8_t pxe_checksum ( void *data, size_t size ) {
+ uint8_t *bytes = data;
+ uint8_t sum = 0;
+
+ while ( size-- ) {
+ sum += *bytes++;
+ }
+ return sum;
+}
+
+/**
+ * Initialise !PXE and PXENV+ structures
+ *
+ */
+static void pxe_init_structures ( void ) {
+ uint32_t rm_cs_phys = ( rm_cs << 4 );
+ uint32_t rm_ds_phys = ( rm_ds << 4 );
+
+ /* Fill in missing segment fields */
+ ppxe.EntryPointSP.segment = rm_cs;
+ ppxe.EntryPointESP.segment = rm_cs;
+ ppxe.Stack.segment_address = rm_ds;
+ ppxe.Stack.Physical_address = rm_ds_phys;
+ ppxe.UNDIData.segment_address = rm_ds;
+ ppxe.UNDIData.Physical_address = rm_ds_phys;
+ ppxe.UNDICode.segment_address = rm_cs;
+ ppxe.UNDICode.Physical_address = rm_cs_phys;
+ ppxe.UNDICodeWrite.segment_address = rm_cs;
+ ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
+ pxenv.RMEntry.segment = rm_cs;
+ pxenv.StackSeg = rm_ds;
+ pxenv.UNDIDataSeg = rm_ds;
+ pxenv.UNDICodeSeg = rm_cs;
+ pxenv.PXEPtr.segment = rm_cs;
+
+ /* Update checksums */
+ ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
+ pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
+}
+
+/** PXE structure initialiser */
+struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = pxe_init_structures,
+};
+
+/**
+ * Activate PXE stack
+ *
+ * @v netdev Net device to use as PXE net device
+ */
+void pxe_activate ( struct net_device *netdev ) {
+
+ /* Ensure INT 1A is hooked */
+ if ( ! int_1a_hooked ) {
+ hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
+ &pxe_int_1a_vector );
+ devices_get();
+ int_1a_hooked = 1;
+ }
+
+ /* Set PXE network device */
+ pxe_set_netdev ( netdev );
+}
+
+/**
+ * Deactivate PXE stack
+ *
+ * @ret rc Return status code
+ */
+int pxe_deactivate ( void ) {
+ int rc;
+
+ /* Clear PXE network device */
+ pxe_set_netdev ( NULL );
+
+ /* Ensure INT 1A is unhooked, if possible */
+ if ( int_1a_hooked ) {
+ if ( ( rc = unhook_bios_interrupt ( 0x1a,
+ (unsigned int) pxe_int_1a,
+ &pxe_int_1a_vector ))!= 0){
+ DBG ( "Could not unhook INT 1A: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ devices_put();
+ int_1a_hooked = 0;
+ }
+
+ return 0;
+}
+
+/** Jump buffer for PXENV_RESTART_TFTP */
+rmjmp_buf pxe_restart_nbp;
+
+/**
+ * Start PXE NBP at 0000:7c00
+ *
+ * @ret rc Return status code
+ */
+int pxe_start_nbp ( void ) {
+ int jmp;
+ int discard_b, discard_c, discard_d, discard_D;
+ uint16_t status;
+
+ /* Allow restarting NBP via PXENV_RESTART_TFTP */
+ jmp = rmsetjmp ( pxe_restart_nbp );
+ if ( jmp )
+ DBG ( "Restarting NBP (%x)\n", jmp );
+
+ /* Far call to PXE NBP */
+ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "movw %%cx, %%es\n\t"
+ "pushw %%es\n\t"
+ "pushw %%di\n\t"
+ "sti\n\t"
+ "lcall $0, $0x7c00\n\t"
+ "popl %%ebp\n\t" /* discard */
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( discard_c ), "=d" ( discard_d ),
+ "=D" ( discard_D )
+ : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
+ "c" ( rm_cs ),
+ "d" ( virt_to_phys ( &pxenv ) ),
+ "D" ( __from_text16 ( &ppxe ) )
+ : "esi", "memory" );
+ if ( status )
+ return -EPXENBP ( status );
+
+ return 0;
+}
+
+REQUIRING_SYMBOL ( pxe_api_call );
+REQUIRE_OBJECT ( pxe_preboot );
+REQUIRE_OBJECT ( pxe_undi );
+REQUIRE_OBJECT ( pxe_udp );
+REQUIRE_OBJECT ( pxe_tftp );
+REQUIRE_OBJECT ( pxe_file );
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S b/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S
new file mode 100644
index 000000000..07852cd50
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S
@@ -0,0 +1,221 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .arch i386
+
+/****************************************************************************
+ * !PXE structure
+ ****************************************************************************
+ */
+ .section ".text16.data", "aw", @progbits
+ .globl ppxe
+ .align 16
+ppxe:
+ .ascii "!PXE" /* Signature */
+ .byte pxe_length /* StructLength */
+ .byte 0 /* StructCksum */
+ .byte 0 /* StructRev */
+ .byte 0 /* reserved_1 */
+ .word undiheader, 0 /* UNDIROMID */
+ .word 0, 0 /* BaseROMID */
+ .word pxe_entry_sp, 0 /* EntryPointSP */
+ .word pxe_entry_esp, 0 /* EntryPointESP */
+ .word -1, -1 /* StatusCallout */
+ .byte 0 /* reserved_2 */
+ .byte SegDescCnt /* SegDescCnt */
+ .word 0 /* FirstSelector */
+pxe_segments:
+ .word 0, 0, 0, _data16_memsz /* Stack */
+ .word 0, 0, 0, _data16_memsz /* UNDIData */
+ .word 0, 0, 0, _text16_memsz /* UNDICode */
+ .word 0, 0, 0, _text16_memsz /* UNDICodeWrite */
+ .word 0, 0, 0, 0 /* BC_Data */
+ .word 0, 0, 0, 0 /* BC_Code */
+ .word 0, 0, 0, 0 /* BC_CodeWrite */
+ .equ SegDescCnt, ( ( . - pxe_segments ) / 8 )
+ .equ pxe_length, . - ppxe
+ .size ppxe, . - ppxe
+
+ /* Define undiheader=0 as a weak symbol for non-ROM builds */
+ .section ".weak", "a", @nobits
+ .weak undiheader
+undiheader:
+
+/****************************************************************************
+ * PXENV+ structure
+ ****************************************************************************
+ */
+ .section ".text16.data", "aw", @progbits
+ .globl pxenv
+ .align 16
+pxenv:
+ .ascii "PXENV+" /* Signature */
+ .word 0x0201 /* Version */
+ .byte pxenv_length /* Length */
+ .byte 0 /* Checksum */
+ .word pxenv_entry, 0 /* RMEntry */
+ .long 0 /* PMEntry */
+ .word 0 /* PMSelector */
+ .word 0 /* StackSeg */
+ .word _data16_memsz /* StackSize */
+ .word 0 /* BC_CodeSeg */
+ .word 0 /* BC_CodeSize */
+ .word 0 /* BC_DataSeg */
+ .word 0 /* BC_DataSize */
+ .word 0 /* UNDIDataSeg */
+ .word _data16_memsz /* UNDIDataSize */
+ .word 0 /* UNDICodeSeg */
+ .word _text16_memsz /* UNDICodeSize */
+ .word ppxe, 0 /* PXEPtr */
+ .equ pxenv_length, . - pxenv
+ .size pxenv, . - pxenv
+
+/****************************************************************************
+ * pxenv_entry (16-bit far call)
+ *
+ * PXE API call PXENV+ entry point
+ *
+ * Parameters:
+ * %es:di : Far pointer to PXE parameter structure
+ * %bx : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ /* Wyse Streaming Manager server (WLDRM13.BIN) assumes that
+ * the PXENV+ entry point is at UNDI_CS:0000; apparently,
+ * somebody at Wyse has difficulty distinguishing between the
+ * words "may" and "must"...
+ */
+ .section ".text16.null", "ax", @progbits
+ .code16
+pxenv_null_entry:
+ jmp pxenv_entry
+
+ .section ".text16", "ax", @progbits
+ .code16
+pxenv_entry:
+ pushl $pxe_api_call
+ pushw %cs
+ call prot_call
+ addl $4, %esp
+ lret
+ .size pxenv_entry, . - pxenv_entry
+
+/****************************************************************************
+ * pxe_entry
+ *
+ * PXE API call !PXE entry point
+ *
+ * Parameters:
+ * stack : Far pointer to PXE parameter structure
+ * stack : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+pxe_entry:
+pxe_entry_sp:
+ /* Preserve original %esp */
+ pushl %esp
+ /* Zero high word of %esp to allow use of common code */
+ movzwl %sp, %esp
+ jmp pxe_entry_common
+pxe_entry_esp:
+ /* Preserve %esp to match behaviour of pxe_entry_sp */
+ pushl %esp
+pxe_entry_common:
+ /* Save PXENV+ API call registers */
+ pushw %es
+ pushw %di
+ pushw %bx
+ /* Load !PXE parameters from stack into PXENV+ registers */
+ addr32 movw 18(%esp), %bx
+ movw %bx, %es
+ addr32 movw 16(%esp), %di
+ addr32 movw 14(%esp), %bx
+ /* Make call as for PXENV+ */
+ pushw %cs
+ call pxenv_entry
+ /* Restore PXENV+ registers */
+ popw %bx
+ popw %di
+ popw %es
+ /* Restore original %esp and return */
+ popl %esp
+ lret
+ .size pxe_entry, . - pxe_entry
+
+/****************************************************************************
+ * pxe_int_1a
+ *
+ * PXE INT 1A handler
+ *
+ * Parameters:
+ * %ax : 0x5650
+ * Returns:
+ * %ax : 0x564e
+ * %es:bx : Far pointer to the PXENV+ structure
+ * %edx : Physical address of the PXENV+ structure
+ * CF cleared
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl pxe_int_1a
+pxe_int_1a:
+ pushfw
+ cmpw $0x5650, %ax
+ jne 1f
+ /* INT 1A,5650 - PXE installation check */
+ xorl %edx, %edx
+ movw %cs, %dx
+ movw %dx, %es
+ movw $pxenv, %bx
+ shll $4, %edx
+ addl $pxenv, %edx
+ movw $0x564e, %ax
+ pushw %bp
+ movw %sp, %bp
+ andb $~0x01, 8(%bp) /* Clear CF on return */
+ popw %bp
+ popfw
+ iret
+1: /* INT 1A,other - pass through */
+ popfw
+ ljmp *%cs:pxe_int_1a_vector
+
+ .section ".text16.data", "aw", @progbits
+ .globl pxe_int_1a_vector
+pxe_int_1a_vector: .long 0
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c
new file mode 100644
index 000000000..f92dae0d1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c
@@ -0,0 +1,65 @@
+/** @file
+ *
+ * PXE exit hook
+ *
+ */
+
+/*
+ * Copyright (C) 2010 Shao Miller <shao.miller@yrdsb.edu.on.ca>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <realmode.h>
+#include <pxe.h>
+
+/** PXE exit hook */
+extern segoff_t __data16 ( pxe_exit_hook );
+#define pxe_exit_hook __use_data16 ( pxe_exit_hook )
+
+/**
+ * FILE EXIT HOOK
+ *
+ * @v file_exit_hook Pointer to a struct
+ * s_PXENV_FILE_EXIT_HOOK
+ * @v s_PXENV_FILE_EXIT_HOOK::Hook SEG16:OFF16 to jump to
+ * @ret #PXENV_EXIT_SUCCESS Successfully set hook
+ * @ret #PXENV_EXIT_FAILURE We're not an NBP build
+ * @ret s_PXENV_FILE_EXIT_HOOK::Status PXE status code
+ *
+ */
+static PXENV_EXIT_t
+pxenv_file_exit_hook ( struct s_PXENV_FILE_EXIT_HOOK *file_exit_hook ) {
+ DBG ( "PXENV_FILE_EXIT_HOOK" );
+
+ /* We'll jump to the specified SEG16:OFF16 during exit */
+ pxe_exit_hook.segment = file_exit_hook->Hook.segment;
+ pxe_exit_hook.offset = file_exit_hook->Hook.offset;
+ file_exit_hook->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** PXE file API */
+struct pxe_api_call pxe_file_api_exit_hook __pxe_api_call =
+ PXE_API_CALL ( PXENV_FILE_EXIT_HOOK, pxenv_file_exit_hook,
+ struct s_PXENV_FILE_EXIT_HOOK );
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c
new file mode 100644
index 000000000..456ffb5fd
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c
@@ -0,0 +1,346 @@
+/** @file
+ *
+ * PXE FILE API
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/posix_io.h>
+#include <ipxe/features.h>
+#include <pxe.h>
+#include <realmode.h>
+
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
+
+/**
+ * FILE OPEN
+ *
+ * @v file_open Pointer to a struct s_PXENV_FILE_OPEN
+ * @v s_PXENV_FILE_OPEN::FileName URL of file to open
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_FILE_OPEN::Status PXE status code
+ * @ret s_PXENV_FILE_OPEN::FileHandle Handle of opened file
+ *
+ */
+static PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
+ userptr_t filename;
+ size_t filename_len;
+ int fd;
+
+ DBG ( "PXENV_FILE_OPEN" );
+
+ /* Copy name from external program, and open it */
+ filename = real_to_user ( file_open->FileName.segment,
+ file_open->FileName.offset );
+ filename_len = strlen_user ( filename, 0 );
+ {
+ char uri_string[ filename_len + 1 ];
+
+ copy_from_user ( uri_string, filename, 0,
+ sizeof ( uri_string ) );
+ DBG ( " %s", uri_string );
+ fd = open ( uri_string );
+ }
+
+ if ( fd < 0 ) {
+ file_open->Status = PXENV_STATUS ( fd );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " as file %d", fd );
+
+ file_open->FileHandle = fd;
+ file_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE CLOSE
+ *
+ * @v file_close Pointer to a struct s_PXENV_FILE_CLOSE
+ * @v s_PXENV_FILE_CLOSE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File was closed
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_FILE_CLOSE::Status PXE status code
+ *
+ */
+static PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
+
+ DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
+
+ close ( file_close->FileHandle );
+ file_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE SELECT
+ *
+ * @v file_select Pointer to a struct s_PXENV_FILE_SELECT
+ * @v s_PXENV_FILE_SELECT::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File has been checked for readiness
+ * @ret #PXENV_EXIT_FAILURE File has not been checked for readiness
+ * @ret s_PXENV_FILE_SELECT::Status PXE status code
+ * @ret s_PXENV_FILE_SELECT::Ready Indication of readiness
+ *
+ */
+static PXENV_EXIT_t
+pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
+ fd_set fdset;
+ int ready;
+
+ DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle );
+
+ FD_ZERO ( &fdset );
+ FD_SET ( file_select->FileHandle, &fdset );
+ if ( ( ready = select ( &fdset, 0 ) ) < 0 ) {
+ file_select->Status = PXENV_STATUS ( ready );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ file_select->Ready = ( ready ? RDY_READ : 0 );
+ file_select->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE READ
+ *
+ * @v file_read Pointer to a struct s_PXENV_FILE_READ
+ * @v s_PXENV_FILE_READ::FileHandle File handle
+ * @v s_PXENV_FILE_READ::BufferSize Size of data buffer
+ * @v s_PXENV_FILE_READ::Buffer Data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data has been read from file
+ * @ret #PXENV_EXIT_FAILURE Data has not been read from file
+ * @ret s_PXENV_FILE_READ::Status PXE status code
+ * @ret s_PXENV_FILE_READ::Ready Indication of readiness
+ * @ret s_PXENV_FILE_READ::BufferSize Length of data read
+ *
+ */
+static PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
+ userptr_t buffer;
+ ssize_t len;
+
+ DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
+ file_read->Buffer.segment, file_read->Buffer.offset,
+ file_read->BufferSize );
+
+ buffer = real_to_user ( file_read->Buffer.segment,
+ file_read->Buffer.offset );
+ if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
+ file_read->BufferSize ) ) < 0 ) {
+ file_read->Status = PXENV_STATUS ( len );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " read %04zx", ( ( size_t ) len ) );
+
+ file_read->BufferSize = len;
+ file_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * GET FILE SIZE
+ *
+ * @v get_file_size Pointer to a struct s_PXENV_GET_FILE_SIZE
+ * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File size has been determined
+ * @ret #PXENV_EXIT_FAILURE File size has not been determined
+ * @ret s_PXENV_GET_FILE_SIZE::Status PXE status code
+ * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file
+ */
+static PXENV_EXIT_t
+pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE *get_file_size ) {
+ ssize_t filesize;
+
+ DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle );
+
+ filesize = fsize ( get_file_size->FileHandle );
+ if ( filesize < 0 ) {
+ get_file_size->Status = PXENV_STATUS ( filesize );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " is %zd", ( ( size_t ) filesize ) );
+
+ get_file_size->FileSize = filesize;
+ get_file_size->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE EXEC
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_EXEC
+ * @v s_PXENV_FILE_EXEC::Command Command to execute
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_EXEC::Status PXE status code
+ *
+ */
+static PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
+ userptr_t command;
+ size_t command_len;
+ int rc;
+
+ DBG ( "PXENV_FILE_EXEC" );
+
+ /* Copy name from external program, and exec it */
+ command = real_to_user ( file_exec->Command.segment,
+ file_exec->Command.offset );
+ command_len = strlen_user ( command, 0 );
+ {
+ char command_string[ command_len + 1 ];
+
+ copy_from_user ( command_string, command, 0,
+ sizeof ( command_string ) );
+ DBG ( " %s", command_string );
+
+ if ( ( rc = system ( command_string ) ) != 0 ) {
+ file_exec->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ file_exec->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE CMDLINE
+ *
+ * @v file_cmdline Pointer to a struct s_PXENV_FILE_CMDLINE
+ * @v s_PXENV_FILE_CMDLINE::Buffer Buffer to contain command line
+ * @v s_PXENV_FILE_CMDLINE::BufferSize Size of buffer
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_EXEC::Status PXE status code
+ * @ret s_PXENV_FILE_EXEC::BufferSize Length of command line (including NUL)
+ *
+ */
+static PXENV_EXIT_t
+pxenv_file_cmdline ( struct s_PXENV_FILE_CMDLINE *file_cmdline ) {
+ userptr_t buffer;
+ size_t max_len;
+ size_t len;
+
+ DBG ( "PXENV_FILE_CMDLINE to %04x:%04x+%04x \"%s\"\n",
+ file_cmdline->Buffer.segment, file_cmdline->Buffer.offset,
+ file_cmdline->BufferSize, pxe_cmdline );
+
+ buffer = real_to_user ( file_cmdline->Buffer.segment,
+ file_cmdline->Buffer.offset );
+ len = file_cmdline->BufferSize;
+ max_len = ( pxe_cmdline ?
+ ( strlen ( pxe_cmdline ) + 1 /* NUL */ ) : 0 );
+ if ( len > max_len )
+ len = max_len;
+ copy_to_user ( buffer, 0, pxe_cmdline, len );
+ file_cmdline->BufferSize = max_len;
+
+ file_cmdline->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE API CHECK
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_API_CHECK
+ * @v s_PXENV_FILE_API_CHECK::Magic Inbound magic number (0x91d447b2)
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_API_CHECK::Status PXE status code
+ * @ret s_PXENV_FILE_API_CHECK::Magic Outbound magic number (0xe9c17b20)
+ * @ret s_PXENV_FILE_API_CHECK::Provider "iPXE" (0x45585067)
+ * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
+ * @ret s_PXENV_FILE_API_CHECK::Flags Reserved
+ *
+ */
+static PXENV_EXIT_t
+pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
+ struct pxe_api_call *call;
+ unsigned int mask = 0;
+ unsigned int offset;
+
+ DBG ( "PXENV_FILE_API_CHECK" );
+
+ /* Check for magic value */
+ if ( file_api_check->Magic != 0x91d447b2 ) {
+ file_api_check->Status = PXENV_STATUS_BAD_FUNC;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Check for required parameter size */
+ if ( file_api_check->Size < sizeof ( *file_api_check ) ) {
+ file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Determine supported calls */
+ for_each_table_entry ( call, PXE_API_CALLS ) {
+ offset = ( call->opcode - PXENV_FILE_MIN );
+ if ( offset <= ( PXENV_FILE_MAX - PXENV_FILE_MIN ) )
+ mask |= ( 1 << offset );
+ }
+
+ /* Fill in parameters */
+ file_api_check->Size = sizeof ( *file_api_check );
+ file_api_check->Magic = 0xe9c17b20;
+ file_api_check->Provider = 0x45585067; /* "iPXE" */
+ file_api_check->APIMask = mask;
+ file_api_check->Flags = 0; /* None defined */
+
+ file_api_check->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** PXE file API */
+struct pxe_api_call pxe_file_api[] __pxe_api_call = {
+ PXE_API_CALL ( PXENV_FILE_OPEN, pxenv_file_open,
+ struct s_PXENV_FILE_OPEN ),
+ PXE_API_CALL ( PXENV_FILE_CLOSE, pxenv_file_close,
+ struct s_PXENV_FILE_CLOSE ),
+ PXE_API_CALL ( PXENV_FILE_SELECT, pxenv_file_select,
+ struct s_PXENV_FILE_SELECT ),
+ PXE_API_CALL ( PXENV_FILE_READ, pxenv_file_read,
+ struct s_PXENV_FILE_READ ),
+ PXE_API_CALL ( PXENV_GET_FILE_SIZE, pxenv_get_file_size,
+ struct s_PXENV_GET_FILE_SIZE ),
+ PXE_API_CALL ( PXENV_FILE_EXEC, pxenv_file_exec,
+ struct s_PXENV_FILE_EXEC ),
+ PXE_API_CALL ( PXENV_FILE_CMDLINE, pxenv_file_cmdline,
+ struct s_PXENV_FILE_CMDLINE ),
+ PXE_API_CALL ( PXENV_FILE_API_CHECK, pxenv_file_api_check,
+ struct s_PXENV_FILE_API_CHECK ),
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c
new file mode 100644
index 000000000..e6a2e072a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/init.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/** @file
+ *
+ * PXE UNDI loader
+ *
+ */
+
+/* PXENV_UNDI_LOADER
+ *
+ */
+PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
+
+ /* Perform one-time initialisation (e.g. heap) */
+ initialise();
+
+ DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
+ undi_loader->UNDI_CS, undi_loader->UNDI_DS );
+
+ /* Fill in UNDI loader structure */
+ undi_loader->PXEptr.segment = rm_cs;
+ undi_loader->PXEptr.offset = __from_text16 ( &ppxe );
+ undi_loader->PXENVptr.segment = rm_cs;
+ undi_loader->PXENVptr.offset = __from_text16 ( &pxenv );
+
+ undi_loader->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c
new file mode 100644
index 000000000..6e09080bc
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c
@@ -0,0 +1,387 @@
+/** @file
+ *
+ * PXE Preboot API
+ *
+ */
+
+/* PXE API interface for Etherboot.
+ *
+ * Copyright (C) 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/fakedhcp.h>
+#include <ipxe/device.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/isapnp.h>
+#include <ipxe/init.h>
+#include <ipxe/if_ether.h>
+#include <basemem_packet.h>
+#include <biosint.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/* Avoid dragging in isapnp.o unnecessarily */
+uint16_t isapnp_read_port;
+
+/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
+enum pxe_cached_info_indices {
+ CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ),
+ CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ),
+ CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ),
+ NUM_CACHED_INFOS
+};
+
+/** A cached DHCP packet */
+union pxe_cached_info {
+ struct dhcphdr dhcphdr;
+ /* This buffer must be *exactly* the size of a BOOTPLAYER_t
+ * structure, otherwise WinPE will die horribly. It takes the
+ * size of *our* buffer and feeds it in to us as the size of
+ * one of *its* buffers. If our buffer is larger than it
+ * expects, we therefore end up overwriting part of its data
+ * segment, since it tells us to do so. (D'oh!)
+ *
+ * Note that a BOOTPLAYER_t is not necessarily large enough to
+ * hold a DHCP packet; this is a flaw in the PXE spec.
+ */
+ BOOTPLAYER_t packet;
+} __attribute__ (( packed ));
+
+/** A PXE DHCP packet creator */
+struct pxe_dhcp_packet_creator {
+ /** Create DHCP packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ */
+ int ( * create ) ( struct net_device *netdev, void *data,
+ size_t max_len );
+};
+
+/** PXE DHCP packet creators */
+static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = {
+ [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover },
+ [CACHED_INFO_DHCPACK] = { create_fakedhcpack },
+ [CACHED_INFO_BINL] = { create_fakepxebsack },
+};
+
+/**
+ * Name PXENV_GET_CACHED_INFO packet type
+ *
+ * @v packet_type Packet type
+ * @ret name Name of packet type
+ */
+static inline __attribute__ (( always_inline )) const char *
+pxenv_get_cached_info_name ( int packet_type ) {
+ switch ( packet_type ) {
+ case PXENV_PACKET_TYPE_DHCP_DISCOVER:
+ return "DHCPDISCOVER";
+ case PXENV_PACKET_TYPE_DHCP_ACK:
+ return "DHCPACK";
+ case PXENV_PACKET_TYPE_CACHED_REPLY:
+ return "BINL";
+ default:
+ return "<INVALID>";
+ }
+}
+
+/* The case in which the caller doesn't supply a buffer is really
+ * awkward to support given that we have multiple sources of options,
+ * and that we don't actually store the DHCP packets. (We may not
+ * even have performed DHCP; we may have obtained all configuration
+ * from non-volatile stored options or from the command line.)
+ *
+ * Some NBPs rely on the buffers we provide being persistent, so we
+ * can't just use the temporary packet buffer. 4.5kB of base memory
+ * always wasted just because some clients are too lazy to provide
+ * their own buffers...
+ */
+static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] );
+#define cached_info __use_data16 ( cached_info )
+
+/**
+ * UNLOAD BASE CODE STACK
+ *
+ * @v None -
+ * @ret ...
+ *
+ */
+static PXENV_EXIT_t
+pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
+ DBGC ( &pxe_netdev, "PXENV_UNLOAD_STACK\n" );
+
+ unload_stack->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_GET_CACHED_INFO
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) {
+ struct pxe_dhcp_packet_creator *creator;
+ union pxe_cached_info *info;
+ unsigned int idx;
+ size_t len;
+ userptr_t buffer;
+ int rc;
+
+ DBGC ( &pxe_netdev, "PXENV_GET_CACHED_INFO %s to %04x:%04x+%x",
+ pxenv_get_cached_info_name ( get_cached_info->PacketType ),
+ get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset, get_cached_info->BufferSize );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_GET_CACHED_INFO called with no "
+ "network device\n" );
+ get_cached_info->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Sanity check */
+ idx = ( get_cached_info->PacketType - 1 );
+ if ( idx >= NUM_CACHED_INFOS ) {
+ DBGC ( &pxe_netdev, " bad PacketType %d\n",
+ get_cached_info->PacketType );
+ goto err;
+ }
+ info = &cached_info[idx];
+
+ /* Construct DHCP packet */
+ creator = &pxe_dhcp_packet_creators[idx];
+ if ( ( rc = creator->create ( pxe_netdev, info,
+ sizeof ( *info ) ) ) != 0 ) {
+ DBGC ( &pxe_netdev, " failed to build packet: %s\n",
+ strerror ( rc ) );
+ goto err;
+ }
+
+ /* Copy packet (if applicable) */
+ len = get_cached_info->BufferSize;
+ if ( len == 0 ) {
+ /* Point client at our cached buffer.
+ *
+ * To add to the fun, Intel decided at some point in
+ * the evolution of the PXE specification to add the
+ * BufferLimit field, which we are meant to fill in
+ * with the length of our packet buffer, so that the
+ * caller can safely modify the boot server reply
+ * packet stored therein. However, this field was not
+ * present in earlier versions of the PXE spec, and
+ * there is at least one PXE NBP (Altiris) which
+ * allocates only exactly enough space for this
+ * earlier, shorter version of the structure. If we
+ * actually fill in the BufferLimit field, we
+ * therefore risk trashing random areas of the
+ * caller's memory. If we *don't* fill it in, then
+ * the caller is at liberty to assume that whatever
+ * random value happened to be in that location
+ * represents the length of the buffer we've just
+ * passed back to it.
+ *
+ * Since older PXE stacks won't fill this field in
+ * anyway, it's probably safe to assume that no
+ * callers actually rely on it, so we choose to not
+ * fill it in.
+ */
+ get_cached_info->Buffer.segment = rm_ds;
+ get_cached_info->Buffer.offset = __from_data16 ( info );
+ get_cached_info->BufferSize = sizeof ( *info );
+ DBGC ( &pxe_netdev, " using %04x:%04x+%04x['%x']",
+ get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset,
+ get_cached_info->BufferSize,
+ get_cached_info->BufferLimit );
+ } else {
+ /* Copy packet to client buffer */
+ if ( len > sizeof ( *info ) )
+ len = sizeof ( *info );
+ if ( len < sizeof ( *info ) )
+ DBGC ( &pxe_netdev, " buffer may be too short" );
+ buffer = real_to_user ( get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset );
+ copy_to_user ( buffer, 0, info, len );
+ get_cached_info->BufferSize = len;
+ }
+
+ DBGC ( &pxe_netdev, "\n" );
+ get_cached_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ err:
+ get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_RESTART_TFTP
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE *restart_tftp ) {
+ PXENV_EXIT_t tftp_exit;
+
+ DBGC ( &pxe_netdev, "PXENV_RESTART_TFTP\n" );
+
+ /* Words cannot describe the complete mismatch between the PXE
+ * specification and any possible version of reality...
+ */
+ restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
+ restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
+ tftp_exit = pxenv_tftp_read_file ( restart_tftp );
+ if ( tftp_exit != PXENV_EXIT_SUCCESS )
+ return tftp_exit;
+
+ /* Restart NBP */
+ rmlongjmp ( pxe_restart_nbp, PXENV_RESTART_TFTP );
+}
+
+/* PXENV_START_UNDI
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
+ unsigned int bus_type;
+ unsigned int location;
+ struct net_device *netdev;
+
+ DBGC ( &pxe_netdev, "PXENV_START_UNDI %04x:%04x:%04x\n",
+ start_undi->AX, start_undi->BX, start_undi->DX );
+
+ /* Determine bus type and location. Use a heuristic to decide
+ * whether we are PCI or ISAPnP
+ */
+ if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
+ ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
+ ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
+ ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
+ bus_type = BUS_TYPE_ISAPNP;
+ location = start_undi->BX;
+ /* Record ISAPnP read port for use by isapnp.c */
+ isapnp_read_port = start_undi->DX;
+ } else {
+ bus_type = BUS_TYPE_PCI;
+ location = start_undi->AX;
+ }
+
+ /* Probe for devices, etc. */
+ startup();
+
+ /* Look for a matching net device */
+ netdev = find_netdev_by_location ( bus_type, location );
+ if ( ! netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_START_UNDI could not find matching "
+ "net device\n" );
+ start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBGC ( &pxe_netdev, "PXENV_START_UNDI found net device %s\n",
+ netdev->name );
+
+ /* Activate PXE */
+ pxe_activate ( netdev );
+
+ start_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_STOP_UNDI
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
+ DBGC ( &pxe_netdev, "PXENV_STOP_UNDI\n" );
+
+ /* Deactivate PXE */
+ pxe_deactivate();
+
+ /* Prepare for unload */
+ shutdown_boot();
+
+ /* Check to see if we still have any hooked interrupts */
+ if ( hooked_bios_interrupts != 0 ) {
+ DBGC ( &pxe_netdev, "PXENV_STOP_UNDI failed: %d interrupts "
+ "still hooked\n", hooked_bios_interrupts );
+ stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ stop_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_BASE
+ *
+ * Status: won't implement (requires major structural changes)
+ */
+static PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
+ DBGC ( &pxe_netdev, "PXENV_START_BASE\n" );
+
+ start_base->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_STOP_BASE
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
+ DBGC ( &pxe_netdev, "PXENV_STOP_BASE\n" );
+
+ /* The only time we will be called is when the NBP is trying
+ * to shut down the PXE stack. There's nothing we need to do
+ * in this call.
+ */
+
+ stop_base->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** PXE preboot API */
+struct pxe_api_call pxe_preboot_api[] __pxe_api_call = {
+ PXE_API_CALL ( PXENV_UNLOAD_STACK, pxenv_unload_stack,
+ struct s_PXENV_UNLOAD_STACK ),
+ PXE_API_CALL ( PXENV_GET_CACHED_INFO, pxenv_get_cached_info,
+ struct s_PXENV_GET_CACHED_INFO ),
+ PXE_API_CALL ( PXENV_RESTART_TFTP, pxenv_restart_tftp,
+ struct s_PXENV_TFTP_READ_FILE ),
+ PXE_API_CALL ( PXENV_START_UNDI, pxenv_start_undi,
+ struct s_PXENV_START_UNDI ),
+ PXE_API_CALL ( PXENV_STOP_UNDI, pxenv_stop_undi,
+ struct s_PXENV_STOP_UNDI ),
+ PXE_API_CALL ( PXENV_START_BASE, pxenv_start_base,
+ struct s_PXENV_START_BASE ),
+ PXE_API_CALL ( PXENV_STOP_BASE, pxenv_stop_base,
+ struct s_PXENV_STOP_BASE ),
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c
new file mode 100644
index 000000000..068d8a7b2
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c
@@ -0,0 +1,597 @@
+/** @file
+ *
+ * PXE TFTP API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/in.h>
+#include <ipxe/tftp.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/xfer.h>
+#include <ipxe/open.h>
+#include <ipxe/process.h>
+#include <ipxe/uri.h>
+#include <realmode.h>
+#include <pxe.h>
+
+/** A PXE TFTP connection */
+struct pxe_tftp_connection {
+ /** Data transfer interface */
+ struct interface xfer;
+ /** Data buffer */
+ userptr_t buffer;
+ /** Size of data buffer */
+ size_t size;
+ /** Starting offset of data buffer */
+ size_t start;
+ /** File position */
+ size_t offset;
+ /** Maximum file position */
+ size_t max_offset;
+ /** Block size */
+ size_t blksize;
+ /** Block index */
+ unsigned int blkidx;
+ /** Overall return status code */
+ int rc;
+};
+
+/**
+ * Close PXE TFTP connection
+ *
+ * @v pxe_tftp PXE TFTP connection
+ * @v rc Final status code
+ */
+static void pxe_tftp_close ( struct pxe_tftp_connection *pxe_tftp, int rc ) {
+ intf_shutdown ( &pxe_tftp->xfer, rc );
+ pxe_tftp->rc = rc;
+}
+
+/**
+ * Check flow control window
+ *
+ * @v pxe_tftp PXE TFTP connection
+ * @ret len Length of window
+ */
+static size_t pxe_tftp_xfer_window ( struct pxe_tftp_connection *pxe_tftp ) {
+
+ return pxe_tftp->blksize;
+}
+
+/**
+ * Receive new data
+ *
+ * @v pxe_tftp PXE TFTP connection
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata
+ * @ret rc Return status code
+ */
+static int pxe_tftp_xfer_deliver ( struct pxe_tftp_connection *pxe_tftp,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ size_t len = iob_len ( iobuf );
+ int rc = 0;
+
+ /* Calculate new buffer position */
+ if ( meta->flags & XFER_FL_ABS_OFFSET )
+ pxe_tftp->offset = 0;
+ pxe_tftp->offset += meta->offset;
+
+ /* Copy data block to buffer */
+ if ( len == 0 ) {
+ /* No data (pure seek); treat as success */
+ } else if ( pxe_tftp->offset < pxe_tftp->start ) {
+ DBG ( " buffer underrun at %zx (min %zx)",
+ pxe_tftp->offset, pxe_tftp->start );
+ rc = -ENOBUFS;
+ } else if ( ( pxe_tftp->offset + len ) >
+ ( pxe_tftp->start + pxe_tftp->size ) ) {
+ DBG ( " buffer overrun at %zx (max %zx)",
+ ( pxe_tftp->offset + len ),
+ ( pxe_tftp->start + pxe_tftp->size ) );
+ rc = -ENOBUFS;
+ } else {
+ copy_to_user ( pxe_tftp->buffer,
+ ( pxe_tftp->offset - pxe_tftp->start ),
+ iobuf->data, len );
+ }
+
+ /* Calculate new buffer position */
+ pxe_tftp->offset += len;
+
+ /* Record maximum offset as the file size */
+ if ( pxe_tftp->max_offset < pxe_tftp->offset )
+ pxe_tftp->max_offset = pxe_tftp->offset;
+
+ /* Terminate transfer on error */
+ if ( rc != 0 )
+ pxe_tftp_close ( pxe_tftp, rc );
+
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** PXE TFTP connection interface operations */
+static struct interface_operation pxe_tftp_xfer_ops[] = {
+ INTF_OP ( xfer_deliver, struct pxe_tftp_connection *,
+ pxe_tftp_xfer_deliver ),
+ INTF_OP ( xfer_window, struct pxe_tftp_connection *,
+ pxe_tftp_xfer_window ),
+ INTF_OP ( intf_close, struct pxe_tftp_connection *, pxe_tftp_close ),
+};
+
+/** PXE TFTP connection interface descriptor */
+static struct interface_descriptor pxe_tftp_xfer_desc =
+ INTF_DESC ( struct pxe_tftp_connection, xfer, pxe_tftp_xfer_ops );
+
+/** The PXE TFTP connection */
+static struct pxe_tftp_connection pxe_tftp = {
+ .xfer = INTF_INIT ( pxe_tftp_xfer_desc ),
+};
+
+/**
+ * Maximum length of a PXE TFTP URI
+ *
+ * The PXE TFTP API provides 128 characters for the filename; the
+ * extra 128 bytes allow for the remainder of the URI.
+ */
+#define PXE_TFTP_URI_LEN 256
+
+/**
+ * Open PXE TFTP connection
+ *
+ * @v ipaddress IP address
+ * @v port TFTP server port
+ * @v filename File name
+ * @v blksize Requested block size
+ * @ret rc Return status code
+ */
+static int pxe_tftp_open ( IP4_t ipaddress, UDP_PORT_t port,
+ UINT8_t *filename, UINT16_t blksize ) {
+ struct in_addr address;
+ struct uri *uri;
+ int rc;
+
+ /* Reset PXE TFTP connection structure */
+ memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
+ intf_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_desc, NULL );
+ if ( blksize < TFTP_DEFAULT_BLKSIZE )
+ blksize = TFTP_DEFAULT_BLKSIZE;
+ pxe_tftp.blksize = blksize;
+ pxe_tftp.rc = -EINPROGRESS;
+
+ /* Construct URI */
+ address.s_addr = ipaddress;
+ DBG ( " %s", inet_ntoa ( address ) );
+ if ( port )
+ DBG ( ":%d", ntohs ( port ) );
+ DBG ( ":%s", filename );
+ uri = tftp_uri ( address, ntohs ( port ), ( ( char * ) filename ) );
+ if ( ! uri ) {
+ DBG ( " could not create URI\n" );
+ return -ENOMEM;
+ }
+
+ /* Open PXE TFTP connection */
+ if ( ( rc = xfer_open_uri ( &pxe_tftp.xfer, uri ) ) != 0 ) {
+ DBG ( " could not open (%s)\n", strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TFTP OPEN
+ *
+ * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN
+ * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
+ * @v s_PXENV_TFTP_OPEN::FileName Name of file to open
+ * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port
+ * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_TFTP_OPEN::Status PXE status code
+ * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated blksize
+ * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small
+ *
+ * Opens a TFTP connection for downloading a file a block at a time
+ * using pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note According to the PXE specification version 2.1, this call
+ * "opens a file for reading/writing", though how writing is to be
+ * achieved without the existence of an API call %pxenv_tftp_write()
+ * is not made clear.
+ *
+ * @note Despite the existence of the numerous statements within the
+ * PXE specification of the form "...if a TFTP/MTFTP or UDP connection
+ * is active...", you cannot use pxenv_tftp_open() and
+ * pxenv_tftp_read() to read a file via MTFTP; only via plain old
+ * TFTP. If you want to use MTFTP, use pxenv_tftp_read_file()
+ * instead. Astute readers will note that, since
+ * pxenv_tftp_read_file() is an atomic operation from the point of
+ * view of the PXE API, it is conceptually impossible to issue any
+ * other PXE API call "if an MTFTP connection is active".
+ */
+static PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_OPEN" );
+
+ /* Guard against callers that fail to close before re-opening */
+ pxe_tftp_close ( &pxe_tftp, 0 );
+
+ /* Open connection */
+ if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
+ tftp_open->TFTPPort,
+ tftp_open->FileName,
+ tftp_open->PacketSize ) ) != 0 ) {
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for OACK to arrive so that we have the block size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.max_offset == 0 ) ) {
+ step();
+ }
+ pxe_tftp.blksize = xfer_window ( &pxe_tftp.xfer );
+ tftp_open->PacketSize = pxe_tftp.blksize;
+ DBG ( " blksize=%d", tftp_open->PacketSize );
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP CLOSE
+ *
+ * @v tftp_close Pointer to a struct s_PXENV_TFTP_CLOSE
+ * @ret #PXENV_EXIT_SUCCESS File was closed successfully
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_TFTP_CLOSE::Status PXE status code
+ * @err None -
+ *
+ * Close a connection previously opened with pxenv_tftp_open(). You
+ * must have previously opened a connection with pxenv_tftp_open().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ */
+static PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
+ DBG ( "PXENV_TFTP_CLOSE" );
+
+ pxe_tftp_close ( &pxe_tftp, 0 );
+ tftp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * TFTP READ
+ *
+ * @v tftp_read Pointer to a struct s_PXENV_TFTP_READ
+ * @v s_PXENV_TFTP_READ::Buffer Address of data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data was read successfully
+ * @ret #PXENV_EXIT_FAILURE Data was not read
+ * @ret s_PXENV_TFTP_READ::Status PXE status code
+ * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number
+ * @ret s_PXENV_TFTP_READ::BufferSize Length of data written into buffer
+ *
+ * Reads a single packet from a connection previously opened with
+ * pxenv_tftp_open() into the data buffer pointed to by
+ * s_PXENV_TFTP_READ::Buffer. You must have previously opened a
+ * connection with pxenv_tftp_open(). The data written into
+ * s_PXENV_TFTP_READ::Buffer is just the file data; the various
+ * network headers have already been removed.
+ *
+ * The buffer must be large enough to contain a packet of the size
+ * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the
+ * pxenv_tftp_open() call. It is worth noting that the PXE
+ * specification does @b not require the caller to fill in
+ * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so
+ * the PXE stack is free to ignore whatever value the caller might
+ * place there and just assume that the buffer is large enough. That
+ * said, it may be worth the caller always filling in
+ * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that
+ * mistake it for an input parameter.
+ *
+ * The length of the TFTP data packet will be returned via
+ * s_PXENV_TFTP_READ::BufferSize. If this length is less than the
+ * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to
+ * pxenv_tftp_open(), this indicates that the block is the last block
+ * in the file. Note that zero is a valid length for
+ * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of
+ * the file is a multiple of the blksize.
+ *
+ * The PXE specification doesn't actually state that calls to
+ * pxenv_tftp_read() will return the data packets in strict sequential
+ * order, though most PXE stacks will probably do so. The sequence
+ * number of the packet will be returned in
+ * s_PXENV_TFTP_READ::PacketNumber. The first packet in the file has
+ * a sequence number of one, not zero.
+ *
+ * To guard against flawed PXE stacks, the caller should probably set
+ * s_PXENV_TFTP_READ::PacketNumber to one less than the expected
+ * returned value (i.e. set it to zero for the first call to
+ * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ
+ * parameter block for subsequent calls without modifying
+ * s_PXENV_TFTP_READ::PacketNumber between calls). The caller should
+ * also guard against potential problems caused by flawed
+ * implementations returning the occasional duplicate packet, by
+ * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber
+ * is as expected (i.e. one greater than that returned from the
+ * previous call to pxenv_tftp_read()).
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ */
+static PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ to %04x:%04x",
+ tftp_read->Buffer.segment, tftp_read->Buffer.offset );
+
+ /* Read single block into buffer */
+ pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
+ tftp_read->Buffer.offset );
+ pxe_tftp.size = pxe_tftp.blksize;
+ pxe_tftp.start = pxe_tftp.offset;
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.offset == pxe_tftp.start ) )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
+ tftp_read->PacketNumber = ++pxe_tftp.blkidx;
+
+ /* EINPROGRESS is normal if we haven't reached EOF yet */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_read->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP/MTFTP read file
+ *
+ * @v tftp_read_file Pointer to a struct s_PXENV_TFTP_READ_FILE
+ * @v s_PXENV_TFTP_READ_FILE::FileName File name
+ * @v s_PXENV_TFTP_READ_FILE::BufferSize Size of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::Buffer Address of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_READ_FILE::McastIPAddress File's multicast IP address
+ * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort Client multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort Server multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut Time to wait for first packet
+ * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay MTFTP inactivity timeout
+ * @ret #PXENV_EXIT_SUCCESS File downloaded successfully
+ * @ret #PXENV_EXIT_FAILURE File not downloaded
+ * @ret s_PXENV_TFTP_READ_FILE::Status PXE status code
+ * @ret s_PXENV_TFTP_READ_FILE::BufferSize Length of downloaded file
+ *
+ * Downloads an entire file via either TFTP or MTFTP into the buffer
+ * pointed to by s_PXENV_TFTP_READ_FILE::Buffer.
+ *
+ * The PXE specification does not make it clear how the caller
+ * requests that MTFTP be used rather than TFTP (or vice versa). One
+ * reasonable guess is that setting
+ * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP
+ * to be used instead of MTFTP, though it is conceivable that some PXE
+ * stacks would interpret that as "use the DHCP-provided multicast IP
+ * address" instead. Some PXE stacks will not implement MTFTP at all,
+ * and will always use TFTP.
+ *
+ * It is not specified whether or not
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server
+ * port for TFTP (rather than MTFTP) downloads. Callers should assume
+ * that the only way to access a TFTP server on a non-standard port is
+ * to use pxenv_tftp_open() and pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an
+ * #ADDR32_t type, i.e. nominally a flat physical address. Some PXE
+ * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real
+ * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above
+ * 1MB. This means that PXE stacks must be prepared to write to areas
+ * outside base memory. Exactly how this is to be achieved is not
+ * specified, though using INT 15,87 is as close to a standard method
+ * as any, and should probably be used. Switching to protected-mode
+ * in order to access high memory will fail if pxenv_tftp_read_file()
+ * is called in V86 mode; it is reasonably to expect that a V86
+ * monitor would intercept the relatively well-defined INT 15,87 if it
+ * wants the PXE stack to be able to write to high memory.
+ *
+ * Things get even more interesting if pxenv_tftp_read_file() is
+ * called in protected mode, because there is then absolutely no way
+ * for the PXE stack to write to an absolute physical address. You
+ * can't even get around the problem by creating a special "access
+ * everything" segment in the s_PXE data structure, because the
+ * #SEGDESC_t descriptors are limited to 64kB in size.
+ *
+ * Previous versions of the PXE specification (e.g. WfM 1.1a) provide
+ * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to
+ * work around this problem. The s_PXENV_TFTP_READ_FILE_PMODE
+ * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a
+ * protected-mode segment:offset address for the data buffer. This
+ * API call is no longer present in version 2.1 of the PXE
+ * specification.
+ *
+ * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer
+ * is an offset relative to the caller's data segment, when
+ * pxenv_tftp_read_file() is called in protected mode.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ */
+PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ_FILE to %08x+%x", tftp_read_file->Buffer,
+ tftp_read_file->BufferSize );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
+ tftp_read_file->FileName, 0 ) ) != 0 ) {
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Read entire file */
+ pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
+ pxe_tftp.size = tftp_read_file->BufferSize;
+ while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read_file->BufferSize = pxe_tftp.max_offset;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( &pxe_tftp, rc );
+
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP GET FILE SIZE
+ *
+ * @v tftp_get_fsize Pointer to a struct s_PXENV_TFTP_GET_FSIZE
+ * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::FileName File name
+ * @ret #PXENV_EXIT_SUCCESS File size was determined successfully
+ * @ret #PXENV_EXIT_FAILURE File size was not determined
+ * @ret s_PXENV_TFTP_GET_FSIZE::Status PXE status code
+ * @ret s_PXENV_TFTP_GET_FSIZE::FileSize File size
+ *
+ * Determine the size of a file on a TFTP server. This uses the
+ * "tsize" TFTP option, and so will not work with a TFTP server that
+ * does not support TFTP options, or that does not support the "tsize"
+ * option.
+ *
+ * The PXE specification states that this API call will @b not open a
+ * TFTP connection for subsequent use with pxenv_tftp_read(). (This
+ * is somewhat daft, since the only way to obtain the file size via
+ * the "tsize" option involves issuing a TFTP open request, but that's
+ * life.)
+ *
+ * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP
+ * connection is open.
+ *
+ * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note There is no way to specify the TFTP server port with this API
+ * call. Though you can open a file using a non-standard TFTP server
+ * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially,
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of
+ * a file from a TFTP server listening on the standard TFTP port.
+ * "Consistency" is not a word in Intel's vocabulary.
+ */
+static PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+ *tftp_get_fsize ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_GET_FSIZE" );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
+ tftp_get_fsize->FileName, 0 ) ) != 0 ) {
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for initial seek to arrive, and record size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.max_offset == 0 ) ) {
+ step();
+ }
+ tftp_get_fsize->FileSize = pxe_tftp.max_offset;
+ DBG ( " fsize=%d", tftp_get_fsize->FileSize );
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( &pxe_tftp, rc );
+
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/** PXE TFTP API */
+struct pxe_api_call pxe_tftp_api[] __pxe_api_call = {
+ PXE_API_CALL ( PXENV_TFTP_OPEN, pxenv_tftp_open,
+ struct s_PXENV_TFTP_OPEN ),
+ PXE_API_CALL ( PXENV_TFTP_CLOSE, pxenv_tftp_close,
+ struct s_PXENV_TFTP_CLOSE ),
+ PXE_API_CALL ( PXENV_TFTP_READ, pxenv_tftp_read,
+ struct s_PXENV_TFTP_READ ),
+ PXE_API_CALL ( PXENV_TFTP_READ_FILE, pxenv_tftp_read_file,
+ struct s_PXENV_TFTP_READ_FILE ),
+ PXE_API_CALL ( PXENV_TFTP_GET_FSIZE, pxenv_tftp_get_fsize,
+ struct s_PXENV_TFTP_GET_FSIZE ),
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c
new file mode 100644
index 000000000..071cb59db
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c
@@ -0,0 +1,474 @@
+/** @file
+ *
+ * PXE UDP API
+ *
+ */
+
+#include <string.h>
+#include <byteswap.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/xfer.h>
+#include <ipxe/udp.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/process.h>
+#include <realmode.h>
+#include <pxe.h>
+
+/*
+ * Copyright (C) 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** A PXE UDP pseudo-header */
+struct pxe_udp_pseudo_header {
+ /** Source IP address */
+ IP4_t src_ip;
+ /** Source port */
+ UDP_PORT_t s_port;
+ /** Destination IP address */
+ IP4_t dest_ip;
+ /** Destination port */
+ UDP_PORT_t d_port;
+} __attribute__ (( packed ));
+
+/** A PXE UDP connection */
+struct pxe_udp_connection {
+ /** Data transfer interface to UDP stack */
+ struct interface xfer;
+ /** Local address */
+ struct sockaddr_in local;
+ /** List of received packets */
+ struct list_head list;
+};
+
+/**
+ * Receive PXE UDP data
+ *
+ * @v pxe_udp PXE UDP connection
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ *
+ * Receives a packet as part of the current pxenv_udp_read()
+ * operation.
+ */
+static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct pxe_udp_pseudo_header *pshdr;
+ struct sockaddr_in *sin_src;
+ struct sockaddr_in *sin_dest;
+ int rc;
+
+ /* Extract metadata */
+ assert ( meta );
+ sin_src = ( struct sockaddr_in * ) meta->src;
+ assert ( sin_src );
+ assert ( sin_src->sin_family == AF_INET );
+ sin_dest = ( struct sockaddr_in * ) meta->dest;
+ assert ( sin_dest );
+ assert ( sin_dest->sin_family == AF_INET );
+
+ /* Construct pseudo-header */
+ if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *pshdr ) ) ) != 0 ) {
+ DBG ( "PXE could not prepend pseudo-header\n" );
+ rc = -ENOMEM;
+ goto drop;
+ }
+ pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
+ pshdr->src_ip = sin_src->sin_addr.s_addr;
+ pshdr->s_port = sin_src->sin_port;
+ pshdr->dest_ip = sin_dest->sin_addr.s_addr;
+ pshdr->d_port = sin_dest->sin_port;
+
+ /* Add to queue */
+ list_add_tail ( &iobuf->list, &pxe_udp->list );
+
+ return 0;
+
+ drop:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** PXE UDP data transfer interface operations */
+static struct interface_operation pxe_udp_xfer_operations[] = {
+ INTF_OP ( xfer_deliver, struct pxe_udp_connection *, pxe_udp_deliver ),
+};
+
+/** PXE UDP data transfer interface descriptor */
+static struct interface_descriptor pxe_udp_xfer_desc =
+ INTF_DESC ( struct pxe_udp_connection, xfer, pxe_udp_xfer_operations );
+
+/** The PXE UDP connection */
+static struct pxe_udp_connection pxe_udp = {
+ .xfer = INTF_INIT ( pxe_udp_xfer_desc ),
+ .local = {
+ .sin_family = AF_INET,
+ },
+ .list = LIST_HEAD_INIT ( pxe_udp.list ),
+};
+
+/**
+ * UDP OPEN
+ *
+ * @v pxenv_udp_open Pointer to a struct s_PXENV_UDP_OPEN
+ * @v s_PXENV_UDP_OPEN::src_ip IP address of this station, or 0.0.0.0
+ * @ret #PXENV_EXIT_SUCCESS Always
+ * @ret s_PXENV_UDP_OPEN::Status PXE status code
+ * @err #PXENV_STATUS_UDP_OPEN UDP connection already open
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Could not open connection
+ *
+ * Prepares the PXE stack for communication using pxenv_udp_write()
+ * and pxenv_udp_read().
+ *
+ * The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be
+ * recorded and used as the local station's IP address for all further
+ * communication, including communication by means other than
+ * pxenv_udp_write() and pxenv_udp_read(). (If
+ * s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address
+ * will remain unchanged.)
+ *
+ * You can only have one open UDP connection at a time. This is not a
+ * meaningful restriction, since pxenv_udp_write() and
+ * pxenv_udp_read() allow you to specify arbitrary local and remote
+ * ports and an arbitrary remote address for each packet. According
+ * to the PXE specifiation, you cannot have a UDP connection open at
+ * the same time as a TFTP connection; this restriction does not apply
+ * to Etherboot.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note The PXE specification does not make it clear whether the IP
+ * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only
+ * for this UDP connection, or retained for all future communication.
+ * The latter seems more consistent with typical PXE stack behaviour.
+ *
+ * @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip
+ * parameter.
+ *
+ */
+static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
+ int rc;
+
+ DBG ( "PXENV_UDP_OPEN" );
+
+ /* Record source IP address */
+ pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
+ DBG ( " %s\n", inet_ntoa ( pxe_udp.local.sin_addr ) );
+
+ /* Open promiscuous UDP connection */
+ intf_restart ( &pxe_udp.xfer, 0 );
+ if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
+ DBG ( "PXENV_UDP_OPEN could not open promiscuous socket: %s\n",
+ strerror ( rc ) );
+ pxenv_udp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP CLOSE
+ *
+ * @v pxenv_udp_close Pointer to a struct s_PXENV_UDP_CLOSE
+ * @ret #PXENV_EXIT_SUCCESS Always
+ * @ret s_PXENV_UDP_CLOSE::Status PXE status code
+ * @err None -
+ *
+ * Closes a UDP connection opened with pxenv_udp_open().
+ *
+ * You can only have one open UDP connection at a time. You cannot
+ * have a UDP connection open at the same time as a TFTP connection.
+ * You cannot use pxenv_udp_close() to close a TFTP connection; use
+ * pxenv_tftp_close() instead.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ */
+static PXENV_EXIT_t
+pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
+
+ DBG ( "PXENV_UDP_CLOSE\n" );
+
+ /* Close UDP connection */
+ intf_restart ( &pxe_udp.xfer, 0 );
+
+ /* Discard any received packets */
+ list_for_each_entry_safe ( iobuf, tmp, &pxe_udp.list, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+
+ pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP WRITE
+ *
+ * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE
+ * @v s_PXENV_UDP_WRITE::ip Destination IP address
+ * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0
+ * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0
+ * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port
+ * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload
+ * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload
+ * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully
+ * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted
+ * @ret s_PXENV_UDP_WRITE::Status PXE status code
+ * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
+ * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
+ *
+ * Transmits a single UDP packet. A valid IP and UDP header will be
+ * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
+ * should not contain precomputed IP and UDP headers, nor should it
+ * contain space allocated for these headers. The first byte of the
+ * buffer will be transmitted as the first byte following the UDP
+ * header.
+ *
+ * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
+ * place. See the relevant @ref pxe_routing "implementation note" for
+ * more details.
+ *
+ * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
+ *
+ * You must have opened a UDP connection with pxenv_udp_open() before
+ * calling pxenv_udp_write().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
+ * parameter.
+ *
+ */
+static PXENV_EXIT_t
+pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
+ struct sockaddr_in dest;
+ struct xfer_metadata meta = {
+ .src = ( struct sockaddr * ) &pxe_udp.local,
+ .dest = ( struct sockaddr * ) &dest,
+ .netdev = pxe_netdev,
+ };
+ size_t len;
+ struct io_buffer *iobuf;
+ userptr_t buffer;
+ int rc;
+
+ DBG ( "PXENV_UDP_WRITE" );
+
+ /* Construct destination socket address */
+ memset ( &dest, 0, sizeof ( dest ) );
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = pxenv_udp_write->ip;
+ dest.sin_port = pxenv_udp_write->dst_port;
+
+ /* Set local (source) port. PXE spec says source port is 2069
+ * if not specified. Really, this ought to be set at UDP open
+ * time but hey, we didn't design this API.
+ */
+ pxe_udp.local.sin_port = pxenv_udp_write->src_port;
+ if ( ! pxe_udp.local.sin_port )
+ pxe_udp.local.sin_port = htons ( 2069 );
+
+ /* FIXME: we ignore the gateway specified, since we're
+ * confident of being able to do our own routing. We should
+ * probably allow for multiple gateways.
+ */
+
+ /* Allocate and fill data buffer */
+ len = pxenv_udp_write->buffer_size;
+ iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
+ if ( ! iobuf ) {
+ DBG ( " out of memory\n" );
+ pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ buffer = real_to_user ( pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset );
+ copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );
+
+ DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
+ ntohs ( pxenv_udp_write->src_port ),
+ inet_ntoa ( dest.sin_addr ),
+ ntohs ( pxenv_udp_write->dst_port ) );
+
+ /* Transmit packet */
+ if ( ( rc = xfer_deliver ( &pxe_udp.xfer, iobuf, &meta ) ) != 0 ) {
+ DBG ( "PXENV_UDP_WRITE could not transmit: %s\n",
+ strerror ( rc ) );
+ pxenv_udp_write->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP READ
+ *
+ * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ
+ * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0
+ * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0
+ * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer
+ * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer
+ * @ret #PXENV_EXIT_SUCCESS A packet has been received
+ * @ret #PXENV_EXIT_FAILURE No packet has been received
+ * @ret s_PXENV_UDP_READ::Status PXE status code
+ * @ret s_PXENV_UDP_READ::src_ip Source IP address
+ * @ret s_PXENV_UDP_READ::dest_ip Destination IP address
+ * @ret s_PXENV_UDP_READ::s_port Source UDP port
+ * @ret s_PXENV_UDP_READ::d_port Destination UDP port
+ * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload
+ * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
+ * @err #PXENV_STATUS_FAILURE No packet was ready to read
+ *
+ * Receive a single UDP packet. This is a non-blocking call; if no
+ * packet is ready to read, the call will return instantly with
+ * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
+ *
+ * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
+ * any IP address will be accepted and may be returned to the caller.
+ *
+ * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
+ * port will be accepted and may be returned to the caller.
+ *
+ * You must have opened a UDP connection with pxenv_udp_open() before
+ * calling pxenv_udp_read().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note The PXE specification (version 2.1) does not state that we
+ * should fill in s_PXENV_UDP_READ::dest_ip and
+ * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
+ * expects us to do so, and will fail if we don't.
+ *
+ */
+static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
+ struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
+ struct in_addr dest_ip;
+ struct io_buffer *iobuf;
+ struct pxe_udp_pseudo_header *pshdr;
+ uint16_t d_port_wanted = pxenv_udp_read->d_port;
+ uint16_t d_port;
+ userptr_t buffer;
+ size_t len;
+
+ /* Try receiving a packet, if the queue is empty */
+ if ( list_empty ( &pxe_udp.list ) )
+ step();
+
+ /* Remove first packet from the queue */
+ iobuf = list_first_entry ( &pxe_udp.list, struct io_buffer, list );
+ if ( ! iobuf ) {
+ /* No packet received */
+ DBG2 ( "PXENV_UDP_READ\n" );
+ goto no_packet;
+ }
+ list_del ( &iobuf->list );
+
+ /* Strip pseudo-header */
+ assert ( iob_len ( iobuf ) >= sizeof ( *pshdr ) );
+ pshdr = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *pshdr ) );
+ dest_ip.s_addr = pshdr->dest_ip;
+ d_port = pshdr->d_port;
+ DBG ( "PXENV_UDP_READ" );
+
+ /* Filter on destination address and/or port */
+ if ( dest_ip_wanted.s_addr &&
+ ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
+ DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
+ DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
+ goto drop;
+ }
+ if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
+ DBG ( " wrong port %d", htons ( d_port ) );
+ DBG ( " (wanted %d)\n", htons ( d_port_wanted ) );
+ goto drop;
+ }
+
+ /* Copy packet to buffer and record length */
+ buffer = real_to_user ( pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset );
+ len = iob_len ( iobuf );
+ if ( len > pxenv_udp_read->buffer_size )
+ len = pxenv_udp_read->buffer_size;
+ copy_to_user ( buffer, 0, iobuf->data, len );
+ pxenv_udp_read->buffer_size = len;
+
+ /* Fill in source/dest information */
+ pxenv_udp_read->src_ip = pshdr->src_ip;
+ pxenv_udp_read->s_port = pshdr->s_port;
+ pxenv_udp_read->dest_ip = pshdr->dest_ip;
+ pxenv_udp_read->d_port = pshdr->d_port;
+
+ DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
+ DBG ( "%d<-%s:%d\n", ntohs ( pxenv_udp_read->s_port ),
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
+ ntohs ( pxenv_udp_read->d_port ) );
+
+ /* Free I/O buffer */
+ free_iob ( iobuf );
+
+ pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ drop:
+ free_iob ( iobuf );
+ no_packet:
+ pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+}
+
+/** PXE UDP API */
+struct pxe_api_call pxe_udp_api[] __pxe_api_call = {
+ PXE_API_CALL ( PXENV_UDP_OPEN, pxenv_udp_open,
+ struct s_PXENV_UDP_OPEN ),
+ PXE_API_CALL ( PXENV_UDP_CLOSE, pxenv_udp_close,
+ struct s_PXENV_UDP_CLOSE ),
+ PXE_API_CALL ( PXENV_UDP_WRITE, pxenv_udp_write,
+ struct s_PXENV_UDP_WRITE ),
+ PXE_API_CALL ( PXENV_UDP_READ, pxenv_udp_read,
+ struct s_PXENV_UDP_READ ),
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c
new file mode 100644
index 000000000..2eb68178a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c
@@ -0,0 +1,1084 @@
+/** @file
+ *
+ * PXE UNDI API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+#include <basemem_packet.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/device.h>
+#include <ipxe/pci.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/ip.h>
+#include <ipxe/arp.h>
+#include <ipxe/rarp.h>
+#include <ipxe/profile.h>
+#include "pxe.h"
+
+/**
+ * Count of outstanding transmitted packets
+ *
+ * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
+ * decremented each time that PXENV_UNDI_ISR is called with the TX
+ * queue empty, stopping when the count reaches zero. This allows us
+ * to provide a pessimistic approximation of TX completion events to
+ * the PXE NBP simply by monitoring the netdev's TX queue.
+ */
+static int undi_tx_count = 0;
+
+struct net_device *pxe_netdev = NULL;
+
+/** Transmit profiler */
+static struct profiler undi_tx_profiler __profiler = { .name = "undi.tx" };
+
+/**
+ * Set network device as current PXE network device
+ *
+ * @v netdev Network device, or NULL
+ */
+void pxe_set_netdev ( struct net_device *netdev ) {
+
+ if ( pxe_netdev ) {
+ netdev_rx_unfreeze ( pxe_netdev );
+ netdev_put ( pxe_netdev );
+ }
+
+ pxe_netdev = NULL;
+
+ if ( netdev )
+ pxe_netdev = netdev_get ( netdev );
+}
+
+/**
+ * Open PXE network device
+ *
+ * @ret rc Return status code
+ */
+static int pxe_netdev_open ( void ) {
+ int rc;
+
+ assert ( pxe_netdev != NULL );
+
+ if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
+ return rc;
+
+ netdev_rx_freeze ( pxe_netdev );
+ netdev_irq ( pxe_netdev, 1 );
+
+ return 0;
+}
+
+/**
+ * Close PXE network device
+ *
+ */
+static void pxe_netdev_close ( void ) {
+
+ assert ( pxe_netdev != NULL );
+ netdev_rx_unfreeze ( pxe_netdev );
+ netdev_irq ( pxe_netdev, 0 );
+ netdev_close ( pxe_netdev );
+ undi_tx_count = 0;
+}
+
+/**
+ * Dump multicast address list
+ *
+ * @v mcast PXE multicast address list
+ */
+static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+ unsigned int i;
+
+ for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
+ DBGC ( &pxe_netdev, " %s",
+ ll_protocol->ntoa ( mcast->McastAddr[i] ) );
+ }
+}
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP called with no "
+ "network device\n" );
+ undi_startup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_startup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEANUP
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP called with no "
+ "network device\n" );
+ undi_cleanup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Close network device */
+ pxe_netdev_close();
+
+ undi_cleanup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIALIZE
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE *undi_initialize ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE protocolini %08x\n",
+ undi_initialize->ProtocolIni );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE called with no "
+ "network device\n" );
+ undi_initialize->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_initialize->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_RESET_ADAPTER
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET *undi_reset_adapter ) {
+ int rc;
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER" );
+ pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
+ DBGC ( &pxe_netdev, "\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER called with no "
+ "network device\n" );
+ undi_reset_adapter->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Close and reopen network device */
+ pxe_netdev_close();
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER could not "
+ "reopen %s: %s\n", pxe_netdev->name, strerror ( rc ) );
+ undi_reset_adapter->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SHUTDOWN
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN called with no "
+ "network device\n" );
+ undi_shutdown->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Close network device */
+ pxe_netdev_close();
+
+ undi_shutdown->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_OPEN
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
+ int rc;
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN flag %04x filter %04x",
+ undi_open->OpenFlag, undi_open->PktFilter );
+ pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
+ DBGC ( &pxe_netdev, "\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN called with no "
+ "network device\n" );
+ undi_open->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Open network device */
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN could not open %s: %s\n",
+ pxe_netdev->name, strerror ( rc ) );
+ undi_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLOSE
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE called with no "
+ "network device\n" );
+ undi_close->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Close network device */
+ pxe_netdev_close();
+
+ undi_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_TRANSMIT
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT *undi_transmit ) {
+ struct s_PXENV_UNDI_TBD tbd;
+ struct DataBlk *datablk;
+ struct io_buffer *iobuf;
+ struct net_protocol *net_protocol;
+ struct ll_protocol *ll_protocol;
+ char destaddr[MAX_LL_ADDR_LEN];
+ const void *ll_dest;
+ size_t len;
+ unsigned int i;
+ int rc;
+
+ /* Start profiling */
+ profile_start ( &undi_tx_profiler );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_TRANSMIT called with no "
+ "network device\n" );
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT" );
+
+ /* Forcibly enable interrupts and freeze receive queue
+ * processing at this point, to work around callers that never
+ * call PXENV_UNDI_OPEN before attempting to use the UNDI API.
+ */
+ if ( ! netdev_rx_frozen ( pxe_netdev ) ) {
+ netdev_rx_freeze ( pxe_netdev );
+ netdev_irq ( pxe_netdev, 1 );
+ }
+
+ /* Identify network-layer protocol */
+ switch ( undi_transmit->Protocol ) {
+ case P_IP: net_protocol = &ipv4_protocol; break;
+ case P_ARP: net_protocol = &arp_protocol; break;
+ case P_RARP: net_protocol = &rarp_protocol; break;
+ case P_UNKNOWN:
+ net_protocol = NULL;
+ break;
+ default:
+ DBGC2 ( &pxe_netdev, " %02x invalid protocol\n",
+ undi_transmit->Protocol );
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBGC2 ( &pxe_netdev, " %s",
+ ( net_protocol ? net_protocol->name : "RAW" ) );
+
+ /* Calculate total packet length */
+ copy_from_real ( &tbd, undi_transmit->TBD.segment,
+ undi_transmit->TBD.offset, sizeof ( tbd ) );
+ len = tbd.ImmedLength;
+ DBGC2 ( &pxe_netdev, " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
+ tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ len += datablk->TDDataLen;
+ DBGC2 ( &pxe_netdev, " %04x:%04x+%x",
+ datablk->TDDataPtr.segment, datablk->TDDataPtr.offset,
+ datablk->TDDataLen );
+ }
+
+ /* Allocate and fill I/O buffer */
+ iobuf = alloc_iob ( MAX_LL_HEADER_LEN +
+ ( ( len > IOB_ZLEN ) ? len : IOB_ZLEN ) );
+ if ( ! iobuf ) {
+ DBGC2 ( &pxe_netdev, " could not allocate iobuf\n" );
+ undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
+ copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
+ tbd.Xmit.offset, tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
+ datablk->TDDataPtr.segment,
+ datablk->TDDataPtr.offset,
+ datablk->TDDataLen );
+ }
+
+ /* Add link-layer header, if required to do so */
+ if ( net_protocol != NULL ) {
+
+ /* Calculate destination address */
+ ll_protocol = pxe_netdev->ll_protocol;
+ if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
+ copy_from_real ( destaddr,
+ undi_transmit->DestAddr.segment,
+ undi_transmit->DestAddr.offset,
+ ll_protocol->ll_addr_len );
+ ll_dest = destaddr;
+ DBGC2 ( &pxe_netdev, " DEST %s",
+ ll_protocol->ntoa ( ll_dest ) );
+ } else {
+ ll_dest = pxe_netdev->ll_broadcast;
+ DBGC2 ( &pxe_netdev, " BCAST" );
+ }
+
+ /* Add link-layer header */
+ if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
+ pxe_netdev->ll_addr,
+ net_protocol->net_proto ))!=0){
+ DBGC2 ( &pxe_netdev, " could not add link-layer "
+ "header: %s\n", strerror ( rc ) );
+ free_iob ( iobuf );
+ undi_transmit->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ /* Flag transmission as in-progress. Do this before starting
+ * to transmit the packet, because the ISR may trigger before
+ * we return from netdev_tx().
+ */
+ undi_tx_count++;
+
+ /* Transmit packet */
+ DBGC2 ( &pxe_netdev, "\n" );
+ if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
+ DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT could not transmit: "
+ "%s\n", strerror ( rc ) );
+ undi_tx_count--;
+ undi_transmit->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ profile_stop ( &undi_tx_profiler );
+ undi_transmit->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * Status: working (for NICs that support receive-all-multicast)
+ */
+static PXENV_EXIT_t
+pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
+ *undi_set_mcast_address ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS" );
+ pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
+ DBGC ( &pxe_netdev, "\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS called with "
+ "no network device\n" );
+ undi_set_mcast_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
+ *undi_set_station_address ) {
+ struct ll_protocol *ll_protocol;
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS called "
+ "with no network device\n" );
+ undi_set_station_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ ll_protocol = pxe_netdev->ll_protocol;
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS %s",
+ ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
+
+ /* If adapter is open, the change will have no effect; return
+ * an error
+ */
+ if ( netdev_is_open ( pxe_netdev ) ) {
+ DBGC ( &pxe_netdev, " failed: netdev is open\n" );
+ undi_set_station_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Update MAC address */
+ memcpy ( pxe_netdev->ll_addr,
+ &undi_set_station_address->StationAddress,
+ ll_protocol->ll_addr_len );
+
+ DBGC ( &pxe_netdev, "\n" );
+ undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+static PXENV_EXIT_t
+pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
+ *undi_set_packet_filter ) {
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
+ undi_set_packet_filter->filter );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER called with "
+ "no network device\n" );
+ undi_set_packet_filter->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
+ * driver refuses to load. (We ignore the filter value in the
+ * PXENV_UNDI_OPEN call anyway.)
+ */
+ undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
+
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_INFORMATION
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
+ *undi_get_information ) {
+ struct device *dev;
+ struct ll_protocol *ll_protocol;
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION called with no "
+ "network device\n" );
+ undi_get_information->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION" );
+
+ /* Fill in information */
+ dev = pxe_netdev->dev;
+ ll_protocol = pxe_netdev->ll_protocol;
+ undi_get_information->BaseIo = dev->desc.ioaddr;
+ undi_get_information->IntNumber =
+ ( netdev_irq_supported ( pxe_netdev ) ? dev->desc.irq : 0 );
+ /* Cheat: assume all cards can cope with this */
+ undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+ undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
+ undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
+ assert ( ll_protocol->ll_addr_len <=
+ sizeof ( undi_get_information->CurrentNodeAddress ) );
+ memcpy ( &undi_get_information->CurrentNodeAddress,
+ pxe_netdev->ll_addr,
+ sizeof ( undi_get_information->CurrentNodeAddress ) );
+ ll_protocol->init_addr ( pxe_netdev->hw_addr,
+ &undi_get_information->PermNodeAddress );
+ undi_get_information->ROMAddress = 0;
+ /* nic.rom_info->rom_segment; */
+ /* We only provide the ability to receive or transmit a single
+ * packet at a time. This is a bootloader, not an OS.
+ */
+ undi_get_information->RxBufCt = 1;
+ undi_get_information->TxBufCt = 1;
+
+ DBGC ( &pxe_netdev, " io %04x irq %d mtu %d %s %s\n",
+ undi_get_information->BaseIo, undi_get_information->IntNumber,
+ undi_get_information->MaxTranUnit, ll_protocol->name,
+ ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
+ undi_get_information->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATISTICS
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
+ *undi_get_statistics ) {
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS called with no "
+ "network device\n" );
+ undi_get_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS" );
+
+ /* Report statistics */
+ undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
+ undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
+ undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
+ undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
+ DBGC ( &pxe_netdev, " txok %d rxok %d rxcrc %d rxrsrc %d\n",
+ undi_get_statistics->XmtGoodFrames,
+ undi_get_statistics->RcvGoodFrames,
+ undi_get_statistics->RcvCRCErrors,
+ undi_get_statistics->RcvResourceErrors );
+
+ undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
+ *undi_clear_statistics ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS called with "
+ "no network device\n" );
+ undi_clear_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Clear statistics */
+ memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
+ memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
+
+ undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIATE_DIAGS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+static PXENV_EXIT_t
+pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
+ *undi_initiate_diags ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS called with no "
+ "network device\n" );
+ undi_initiate_diags->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: won't implement (would require driver API changes for no
+ * perceptible benefit)
+ */
+static PXENV_EXIT_t
+pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
+ *undi_force_interrupt ) {
+ DBGC ( &pxe_netdev,
+ "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_FORCE_INTERRUPT called with no "
+ "network device\n" );
+ undi_force_interrupt->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
+ *undi_get_mcast_address ) {
+ struct ll_protocol *ll_protocol;
+ struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
+ int rc;
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS called with "
+ "no network device\n" );
+ undi_get_mcast_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS %s",
+ inet_ntoa ( ip ) );
+
+ /* Hash address using the network device's link-layer protocol */
+ ll_protocol = pxe_netdev->ll_protocol;
+ if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
+ undi_get_mcast_address->MediaAddr ))!=0){
+ DBGC ( &pxe_netdev, " failed: %s\n", strerror ( rc ) );
+ undi_get_mcast_address->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ DBGC ( &pxe_netdev, "=>%s\n",
+ ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
+
+ undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_NIC_TYPE
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
+ *undi_get_nic_type ) {
+ struct device *dev;
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE called with "
+ "no network device\n" );
+ undi_get_nic_type->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE" );
+
+ /* Fill in information */
+ memset ( &undi_get_nic_type->info, 0,
+ sizeof ( undi_get_nic_type->info ) );
+ dev = pxe_netdev->dev;
+ switch ( dev->desc.bus_type ) {
+ case BUS_TYPE_PCI: {
+ struct pci_nic_info *info = &undi_get_nic_type->info.pci;
+
+ undi_get_nic_type->NicType = PCI_NIC;
+ info->Vendor_ID = dev->desc.vendor;
+ info->Dev_ID = dev->desc.device;
+ info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
+ info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
+ info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
+ info->BusDevFunc = dev->desc.location;
+ /* Earlier versions of the PXE specification do not
+ * have the SubVendor_ID and SubDevice_ID fields. It
+ * is possible that some NBPs will not provide space
+ * for them, and so we must not fill them in.
+ */
+ DBGC ( &pxe_netdev, " PCI %02x:%02x.%x %04x:%04x "
+ "('%04x:%04x') %02x%02x%02x rev %02x\n",
+ PCI_BUS ( info->BusDevFunc ),
+ PCI_SLOT ( info->BusDevFunc ),
+ PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
+ info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
+ info->Base_Class, info->Sub_Class, info->Prog_Intf,
+ info->Rev );
+ break; }
+ case BUS_TYPE_ISAPNP: {
+ struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
+
+ undi_get_nic_type->NicType = PnP_NIC;
+ info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
+ dev->desc.device );
+ info->CardSelNum = dev->desc.location;
+ /* Cheat: remaining fields are probably unnecessary,
+ * and would require adding extra code to isapnp.c.
+ */
+ DBGC ( &pxe_netdev, " ISAPnP CSN %04x %08x %02x%02x%02x\n",
+ info->CardSelNum, info->EISA_Dev_ID,
+ info->Base_Class, info->Sub_Class, info->Prog_Intf );
+ break; }
+ default:
+ DBGC ( &pxe_netdev, " failed: unknown bus type\n" );
+ undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_IFACE_INFO
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t
+pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
+ *undi_get_iface_info ) {
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO called with "
+ "no network device\n" );
+ undi_get_iface_info->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO" );
+
+ /* Just hand back some info, doesn't really matter what it is.
+ * Most PXE stacks seem to take this approach.
+ */
+ snprintf ( ( char * ) undi_get_iface_info->IfaceType,
+ sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
+ undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
+ undi_get_iface_info->ServiceFlags =
+ ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
+ SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
+ SUPPORTED_OPEN_CLOSE );
+ if ( netdev_irq_supported ( pxe_netdev ) )
+ undi_get_iface_info->ServiceFlags |= SUPPORTED_IRQ;
+ memset ( undi_get_iface_info->Reserved, 0,
+ sizeof(undi_get_iface_info->Reserved) );
+
+ DBGC ( &pxe_netdev, " %s %dbps flags %08x\n",
+ undi_get_iface_info->IfaceType, undi_get_iface_info->LinkSpeed,
+ undi_get_iface_info->ServiceFlags );
+ undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATE
+ *
+ * Status: impossible due to opcode collision
+ */
+
+/* PXENV_UNDI_ISR
+ *
+ * Status: working
+ */
+static PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
+ struct io_buffer *iobuf;
+ size_t len;
+ struct ll_protocol *ll_protocol;
+ const void *ll_dest;
+ const void *ll_source;
+ uint16_t net_proto;
+ unsigned int flags;
+ size_t ll_hlen;
+ struct net_protocol *net_protocol;
+ unsigned int prottype;
+ int rc;
+
+ /* Use a different debug colour, since UNDI ISR messages are
+ * likely to be interspersed amongst other UNDI messages.
+ */
+
+ /* Sanity check */
+ if ( ! pxe_netdev ) {
+ DBGC ( &pxenv_undi_isr, "PXENV_UNDI_ISR called with "
+ "no network device\n" );
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
+
+ /* Just in case some idiot actually looks at these fields when
+ * we weren't meant to fill them in...
+ */
+ undi_isr->BufferLength = 0;
+ undi_isr->FrameLength = 0;
+ undi_isr->FrameHeaderLength = 0;
+ undi_isr->ProtType = 0;
+ undi_isr->PktType = 0;
+
+ switch ( undi_isr->FuncFlag ) {
+ case PXENV_UNDI_ISR_IN_START :
+ DBGC2 ( &pxenv_undi_isr, " START" );
+
+ /* Call poll(). This should acknowledge the device
+ * interrupt and queue up any received packet.
+ */
+ net_poll();
+
+ /* A 100% accurate determination of "OURS" vs "NOT
+ * OURS" is difficult to achieve without invasive and
+ * unpleasant changes to the driver model. We settle
+ * for always returning "OURS" if interrupts are
+ * currently enabled.
+ *
+ * Returning "NOT OURS" when interrupts are disabled
+ * allows us to avoid a potential interrupt storm when
+ * we are on a shared interrupt line; if we were to
+ * always return "OURS" then the other device's ISR
+ * may never be called.
+ */
+ if ( netdev_irq_enabled ( pxe_netdev ) ) {
+ DBGC2 ( &pxenv_undi_isr, " OURS" );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+ } else {
+ DBGC2 ( &pxenv_undi_isr, " NOT OURS" );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
+ }
+
+ /* Disable interrupts */
+ netdev_irq ( pxe_netdev, 0 );
+
+ break;
+ case PXENV_UNDI_ISR_IN_PROCESS :
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
+ DBGC2 ( &pxenv_undi_isr, " %s",
+ ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
+ "PROCESS" : "GET_NEXT" ) );
+
+ /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
+ * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
+ * they just sit in a tight polling loop merrily
+ * violating the PXE spec with repeated calls to
+ * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to
+ * cope with these out-of-spec clients.
+ */
+ net_poll();
+
+ /* If we have not yet marked a TX as complete, and the
+ * netdev TX queue is empty, report the TX completion.
+ */
+ if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
+ DBGC2 ( &pxenv_undi_isr, " TXC" );
+ undi_tx_count--;
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
+ break;
+ }
+
+ /* Remove first packet from netdev RX queue */
+ iobuf = netdev_rx_dequeue ( pxe_netdev );
+ if ( ! iobuf ) {
+ DBGC2 ( &pxenv_undi_isr, " DONE" );
+ /* No more packets remaining */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ /* Re-enable interrupts */
+ netdev_irq ( pxe_netdev, 1 );
+ break;
+ }
+
+ /* Copy packet to base memory buffer */
+ len = iob_len ( iobuf );
+ DBGC2 ( &pxenv_undi_isr, " RX" );
+ if ( len > sizeof ( basemem_packet ) ) {
+ /* Should never happen */
+ DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
+ len = sizeof ( basemem_packet );
+ }
+ memcpy ( basemem_packet, iobuf->data, len );
+
+ /* Strip link-layer header */
+ ll_protocol = pxe_netdev->ll_protocol;
+ if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
+ &ll_source, &net_proto,
+ &flags ) ) != 0 ) {
+ /* Assume unknown net_proto and no ll_source */
+ net_proto = 0;
+ ll_source = NULL;
+ }
+ ll_hlen = ( len - iob_len ( iobuf ) );
+
+ /* Determine network-layer protocol */
+ switch ( net_proto ) {
+ case htons ( ETH_P_IP ):
+ net_protocol = &ipv4_protocol;
+ prottype = P_IP;
+ break;
+ case htons ( ETH_P_ARP ):
+ net_protocol = &arp_protocol;
+ prottype = P_ARP;
+ break;
+ case htons ( ETH_P_RARP ):
+ net_protocol = &rarp_protocol;
+ prottype = P_RARP;
+ break;
+ default:
+ net_protocol = NULL;
+ prottype = P_UNKNOWN;
+ break;
+ }
+
+ /* Fill in UNDI_ISR structure */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+ undi_isr->BufferLength = len;
+ undi_isr->FrameLength = len;
+ undi_isr->FrameHeaderLength = ll_hlen;
+ undi_isr->Frame.segment = rm_ds;
+ undi_isr->Frame.offset = __from_data16 ( basemem_packet );
+ undi_isr->ProtType = prottype;
+ if ( flags & LL_BROADCAST ) {
+ undi_isr->PktType = P_BROADCAST;
+ } else if ( flags & LL_MULTICAST ) {
+ undi_isr->PktType = P_MULTICAST;
+ } else {
+ undi_isr->PktType = P_DIRECTED;
+ }
+ DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
+ undi_isr->Frame.segment, undi_isr->Frame.offset,
+ undi_isr->BufferLength, undi_isr->FrameLength,
+ ( net_protocol ? net_protocol->name : "RAW" ),
+ undi_isr->FrameHeaderLength );
+
+ /* Free packet */
+ free_iob ( iobuf );
+ break;
+ default :
+ DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
+ undi_isr->FuncFlag );
+
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBGC2 ( &pxenv_undi_isr, "\n" );
+ undi_isr->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** PXE UNDI API */
+struct pxe_api_call pxe_undi_api[] __pxe_api_call = {
+ PXE_API_CALL ( PXENV_UNDI_STARTUP, pxenv_undi_startup,
+ struct s_PXENV_UNDI_STARTUP ),
+ PXE_API_CALL ( PXENV_UNDI_CLEANUP, pxenv_undi_cleanup,
+ struct s_PXENV_UNDI_CLEANUP ),
+ PXE_API_CALL ( PXENV_UNDI_INITIALIZE, pxenv_undi_initialize,
+ struct s_PXENV_UNDI_INITIALIZE ),
+ PXE_API_CALL ( PXENV_UNDI_RESET_ADAPTER, pxenv_undi_reset_adapter,
+ struct s_PXENV_UNDI_RESET ),
+ PXE_API_CALL ( PXENV_UNDI_SHUTDOWN, pxenv_undi_shutdown,
+ struct s_PXENV_UNDI_SHUTDOWN ),
+ PXE_API_CALL ( PXENV_UNDI_OPEN, pxenv_undi_open,
+ struct s_PXENV_UNDI_OPEN ),
+ PXE_API_CALL ( PXENV_UNDI_CLOSE, pxenv_undi_close,
+ struct s_PXENV_UNDI_CLOSE ),
+ PXE_API_CALL ( PXENV_UNDI_TRANSMIT, pxenv_undi_transmit,
+ struct s_PXENV_UNDI_TRANSMIT ),
+ PXE_API_CALL ( PXENV_UNDI_SET_MCAST_ADDRESS,
+ pxenv_undi_set_mcast_address,
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS ),
+ PXE_API_CALL ( PXENV_UNDI_SET_STATION_ADDRESS,
+ pxenv_undi_set_station_address,
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS ),
+ PXE_API_CALL ( PXENV_UNDI_SET_PACKET_FILTER,
+ pxenv_undi_set_packet_filter,
+ struct s_PXENV_UNDI_SET_PACKET_FILTER ),
+ PXE_API_CALL ( PXENV_UNDI_GET_INFORMATION, pxenv_undi_get_information,
+ struct s_PXENV_UNDI_GET_INFORMATION ),
+ PXE_API_CALL ( PXENV_UNDI_GET_STATISTICS, pxenv_undi_get_statistics,
+ struct s_PXENV_UNDI_GET_STATISTICS ),
+ PXE_API_CALL ( PXENV_UNDI_CLEAR_STATISTICS, pxenv_undi_clear_statistics,
+ struct s_PXENV_UNDI_CLEAR_STATISTICS ),
+ PXE_API_CALL ( PXENV_UNDI_INITIATE_DIAGS, pxenv_undi_initiate_diags,
+ struct s_PXENV_UNDI_INITIATE_DIAGS ),
+ PXE_API_CALL ( PXENV_UNDI_FORCE_INTERRUPT, pxenv_undi_force_interrupt,
+ struct s_PXENV_UNDI_FORCE_INTERRUPT ),
+ PXE_API_CALL ( PXENV_UNDI_GET_MCAST_ADDRESS,
+ pxenv_undi_get_mcast_address,
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS ),
+ PXE_API_CALL ( PXENV_UNDI_GET_NIC_TYPE, pxenv_undi_get_nic_type,
+ struct s_PXENV_UNDI_GET_NIC_TYPE ),
+ PXE_API_CALL ( PXENV_UNDI_GET_IFACE_INFO, pxenv_undi_get_iface_info,
+ struct s_PXENV_UNDI_GET_IFACE_INFO ),
+ PXE_API_CALL ( PXENV_UNDI_ISR, pxenv_undi_isr,
+ struct s_PXENV_UNDI_ISR ),
+};
diff --git a/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c b/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c
new file mode 100644
index 000000000..0b6be9a03
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/dhcp.h>
+#include <ipxe/profile.h>
+#include <pxeparent.h>
+#include <pxe_api.h>
+#include <pxe_types.h>
+#include <pxe.h>
+
+/** @file
+ *
+ * Call interface to parent PXE stack
+ *
+ */
+
+/* Disambiguate the various error causes */
+#define EINFO_EPXECALL \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "External PXE API error" )
+#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
+
+/** A parent PXE API call profiler */
+struct pxeparent_profiler {
+ /** Total time spent performing REAL_CALL() */
+ struct profiler total;
+ /** Time spent transitioning to real mode */
+ struct profiler p2r;
+ /** Time spent in external code */
+ struct profiler ext;
+ /** Time spent transitioning back to protected mode */
+ struct profiler r2p;
+};
+
+/** PXENV_UNDI_TRANSMIT profiler */
+static struct pxeparent_profiler pxeparent_tx_profiler __profiler = {
+ { .name = "pxeparent.tx" },
+ { .name = "pxeparent.tx_p2r" },
+ { .name = "pxeparent.tx_ext" },
+ { .name = "pxeparent.tx_r2p" },
+};
+
+/** PXENV_UNDI_ISR profiler
+ *
+ * Note that this profiler will not see calls to
+ * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
+ * not go via pxeparent_call().
+ */
+static struct pxeparent_profiler pxeparent_isr_profiler __profiler = {
+ { .name = "pxeparent.isr" },
+ { .name = "pxeparent.isr_p2r" },
+ { .name = "pxeparent.isr_ext" },
+ { .name = "pxeparent.isr_r2p" },
+};
+
+/** PXE unknown API call profiler
+ *
+ * This profiler can be used to measure the overhead of a dummy PXE
+ * API call.
+ */
+static struct pxeparent_profiler pxeparent_unknown_profiler __profiler = {
+ { .name = "pxeparent.unknown" },
+ { .name = "pxeparent.unknown_p2r" },
+ { .name = "pxeparent.unknown_ext" },
+ { .name = "pxeparent.unknown_r2p" },
+};
+
+/** Miscellaneous PXE API call profiler */
+static struct pxeparent_profiler pxeparent_misc_profiler __profiler = {
+ { .name = "pxeparent.misc" },
+ { .name = "pxeparent.misc_p2r" },
+ { .name = "pxeparent.misc_ext" },
+ { .name = "pxeparent.misc_r2p" },
+};
+
+/**
+ * Name PXE API call
+ *
+ * @v function API call number
+ * @ret name API call name
+ */
+static inline __attribute__ (( always_inline )) const char *
+pxeparent_function_name ( unsigned int function ) {
+ switch ( function ) {
+ case PXENV_START_UNDI:
+ return "PXENV_START_UNDI";
+ case PXENV_STOP_UNDI:
+ return "PXENV_STOP_UNDI";
+ case PXENV_UNDI_STARTUP:
+ return "PXENV_UNDI_STARTUP";
+ case PXENV_UNDI_CLEANUP:
+ return "PXENV_UNDI_CLEANUP";
+ case PXENV_UNDI_INITIALIZE:
+ return "PXENV_UNDI_INITIALIZE";
+ case PXENV_UNDI_RESET_ADAPTER:
+ return "PXENV_UNDI_RESET_ADAPTER";
+ case PXENV_UNDI_SHUTDOWN:
+ return "PXENV_UNDI_SHUTDOWN";
+ case PXENV_UNDI_OPEN:
+ return "PXENV_UNDI_OPEN";
+ case PXENV_UNDI_CLOSE:
+ return "PXENV_UNDI_CLOSE";
+ case PXENV_UNDI_TRANSMIT:
+ return "PXENV_UNDI_TRANSMIT";
+ case PXENV_UNDI_SET_MCAST_ADDRESS:
+ return "PXENV_UNDI_SET_MCAST_ADDRESS";
+ case PXENV_UNDI_SET_STATION_ADDRESS:
+ return "PXENV_UNDI_SET_STATION_ADDRESS";
+ case PXENV_UNDI_SET_PACKET_FILTER:
+ return "PXENV_UNDI_SET_PACKET_FILTER";
+ case PXENV_UNDI_GET_INFORMATION:
+ return "PXENV_UNDI_GET_INFORMATION";
+ case PXENV_UNDI_GET_STATISTICS:
+ return "PXENV_UNDI_GET_STATISTICS";
+ case PXENV_UNDI_CLEAR_STATISTICS:
+ return "PXENV_UNDI_CLEAR_STATISTICS";
+ case PXENV_UNDI_INITIATE_DIAGS:
+ return "PXENV_UNDI_INITIATE_DIAGS";
+ case PXENV_UNDI_FORCE_INTERRUPT:
+ return "PXENV_UNDI_FORCE_INTERRUPT";
+ case PXENV_UNDI_GET_MCAST_ADDRESS:
+ return "PXENV_UNDI_GET_MCAST_ADDRESS";
+ case PXENV_UNDI_GET_NIC_TYPE:
+ return "PXENV_UNDI_GET_NIC_TYPE";
+ case PXENV_UNDI_GET_IFACE_INFO:
+ return "PXENV_UNDI_GET_IFACE_INFO";
+ /*
+ * Duplicate case value; this is a bug in the PXE specification.
+ *
+ * case PXENV_UNDI_GET_STATE:
+ * return "PXENV_UNDI_GET_STATE";
+ */
+ case PXENV_UNDI_ISR:
+ return "PXENV_UNDI_ISR";
+ case PXENV_GET_CACHED_INFO:
+ return "PXENV_GET_CACHED_INFO";
+ default:
+ return "UNKNOWN API CALL";
+ }
+}
+
+/**
+ * Determine applicable profiler pair (for debugging)
+ *
+ * @v function API call number
+ * @ret profiler Profiler
+ */
+static struct pxeparent_profiler * pxeparent_profiler ( unsigned int function ){
+
+ /* Determine applicable profiler */
+ switch ( function ) {
+ case PXENV_UNDI_TRANSMIT:
+ return &pxeparent_tx_profiler;
+ case PXENV_UNDI_ISR:
+ return &pxeparent_isr_profiler;
+ case PXENV_UNKNOWN:
+ return &pxeparent_unknown_profiler;
+ default:
+ return &pxeparent_misc_profiler;
+ }
+}
+
+/**
+ * PXE parent parameter block
+ *
+ * Used as the parameter block for all parent PXE API calls. Resides
+ * in base memory.
+ */
+static union u_PXENV_ANY __bss16 ( pxeparent_params );
+#define pxeparent_params __use_data16 ( pxeparent_params )
+
+/** PXE parent entry point
+ *
+ * Used as the indirection vector for all parent PXE API calls. Resides in
+ * base memory.
+ */
+SEGOFF16_t __bss16 ( pxeparent_entry_point );
+#define pxeparent_entry_point __use_data16 ( pxeparent_entry_point )
+
+/**
+ * Issue parent PXE API call
+ *
+ * @v entry Parent PXE stack entry point
+ * @v function API call number
+ * @v params PXE parameter block
+ * @v params_len Length of PXE parameter block
+ * @ret rc Return status code
+ */
+int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
+ void *params, size_t params_len ) {
+ struct pxeparent_profiler *profiler = pxeparent_profiler ( function );
+ PXENV_EXIT_t exit;
+ unsigned long started;
+ unsigned long stopped;
+ int discard_D;
+ int rc;
+
+ /* Copy parameter block and entry point */
+ assert ( params_len <= sizeof ( pxeparent_params ) );
+ memcpy ( &pxeparent_params, params, params_len );
+ memcpy ( &pxeparent_entry_point, &entry, sizeof ( entry ) );
+
+ /* Call real-mode entry point. This calling convention will
+ * work with both the !PXE and the PXENV+ entry points.
+ */
+ profile_start ( &profiler->total );
+ __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
+ "rdtsc\n\t"
+ "pushl %%eax\n\t"
+ "pushw %%es\n\t"
+ "pushw %%di\n\t"
+ "pushw %%bx\n\t"
+ "lcall *pxeparent_entry_point\n\t"
+ "movw %%ax, %%bx\n\t"
+ "rdtsc\n\t"
+ "addw $6, %%sp\n\t"
+ "popl %%edx\n\t"
+ "popl %%ebp\n\t" /* gcc bug */ )
+ : "=a" ( stopped ), "=d" ( started ),
+ "=b" ( exit ), "=D" ( discard_D )
+ : "b" ( function ),
+ "D" ( __from_data16 ( &pxeparent_params ) )
+ : "ecx", "esi" );
+ profile_stop ( &profiler->total );
+ profile_start_at ( &profiler->p2r, profile_started ( &profiler->total));
+ profile_stop_at ( &profiler->p2r, started );
+ profile_start_at ( &profiler->ext, started );
+ profile_stop_at ( &profiler->ext, stopped );
+ profile_start_at ( &profiler->r2p, stopped );
+ profile_stop_at ( &profiler->r2p, profile_stopped ( &profiler->total ));
+
+ /* Determine return status code based on PXENV_EXIT and
+ * PXENV_STATUS
+ */
+ rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
+ 0 : -EPXECALL ( pxeparent_params.Status ) );
+
+ /* If anything goes wrong, print as much debug information as
+ * it's possible to give.
+ */
+ if ( rc != 0 ) {
+ SEGOFF16_t rm_params = {
+ .segment = rm_ds,
+ .offset = __from_data16 ( &pxeparent_params ),
+ };
+
+ DBG ( "PXEPARENT %s failed: %s\n",
+ pxeparent_function_name ( function ), strerror ( rc ) );
+ DBG ( "PXEPARENT parameters at %04x:%04x length "
+ "%#02zx, entry point at %04x:%04x\n",
+ rm_params.segment, rm_params.offset, params_len,
+ pxeparent_entry_point.segment,
+ pxeparent_entry_point.offset );
+ DBG ( "PXEPARENT parameters provided:\n" );
+ DBG_HDA ( rm_params, params, params_len );
+ DBG ( "PXEPARENT parameters returned:\n" );
+ DBG_HDA ( rm_params, &pxeparent_params, params_len );
+ }
+
+ /* Copy parameter block back */
+ memcpy ( params, &pxeparent_params, params_len );
+
+ return rc;
+}
+
diff --git a/roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c b/roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c
new file mode 100644
index 000000000..75dcc238f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file SYSLINUX COM32 helpers
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <realmode.h>
+#include <comboot.h>
+#include <assert.h>
+#include <ipxe/uaccess.h>
+
+static com32sys_t __bss16 ( com32_regs );
+#define com32_regs __use_data16 ( com32_regs )
+
+static uint8_t __bss16 ( com32_int_vector );
+#define com32_int_vector __use_data16 ( com32_int_vector )
+
+static uint32_t __bss16 ( com32_farcall_proc );
+#define com32_farcall_proc __use_data16 ( com32_farcall_proc )
+
+uint16_t __bss16 ( com32_saved_sp );
+
+/**
+ * Interrupt call helper
+ */
+void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) {
+
+ memcpy_user ( virt_to_user( &com32_regs ), 0,
+ phys_to_user ( inregs_phys ), 0,
+ sizeof(com32sys_t) );
+
+ com32_int_vector = interrupt;
+
+ __asm__ __volatile__ (
+ REAL_CODE ( /* Save all registers */
+ "pushal\n\t"
+ "pushw %%ds\n\t"
+ "pushw %%es\n\t"
+ "pushw %%fs\n\t"
+ "pushw %%gs\n\t"
+ /* Mask off unsafe flags */
+ "movl (com32_regs + 40), %%eax\n\t"
+ "andl $0x200cd7, %%eax\n\t"
+ "movl %%eax, (com32_regs + 40)\n\t"
+ /* Load com32_regs into the actual registers */
+ "movw %%sp, %%ss:(com32_saved_sp)\n\t"
+ "movw $com32_regs, %%sp\n\t"
+ "popw %%gs\n\t"
+ "popw %%fs\n\t"
+ "popw %%es\n\t"
+ "popw %%ds\n\t"
+ "popal\n\t"
+ "popfl\n\t"
+ "movw %%ss:(com32_saved_sp), %%sp\n\t"
+ /* patch INT instruction */
+ "pushw %%ax\n\t"
+ "movb %%ss:(com32_int_vector), %%al\n\t"
+ "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t"
+ /* perform a jump to avoid problems with cache
+ * consistency in self-modifying code on some CPUs (486)
+ */
+ "jmp 1f\n"
+ "1:\n\t"
+ "popw %%ax\n\t"
+ "com32_intcall_instr:\n\t"
+ /* INT instruction to be patched */
+ "int $0xFF\n\t"
+ /* Copy regs back to com32_regs */
+ "movw %%sp, %%ss:(com32_saved_sp)\n\t"
+ "movw $(com32_regs + 44), %%sp\n\t"
+ "pushfl\n\t"
+ "pushal\n\t"
+ "pushw %%ds\n\t"
+ "pushw %%es\n\t"
+ "pushw %%fs\n\t"
+ "pushw %%gs\n\t"
+ "movw %%ss:(com32_saved_sp), %%sp\n\t"
+ /* Restore registers */
+ "popw %%gs\n\t"
+ "popw %%fs\n\t"
+ "popw %%es\n\t"
+ "popw %%ds\n\t"
+ "popal\n\t")
+ : : );
+
+ if ( outregs_phys ) {
+ memcpy_user ( phys_to_user ( outregs_phys ), 0,
+ virt_to_user( &com32_regs ), 0,
+ sizeof(com32sys_t) );
+ }
+}
+
+/**
+ * Farcall helper
+ */
+void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) {
+
+ memcpy_user ( virt_to_user( &com32_regs ), 0,
+ phys_to_user ( inregs_phys ), 0,
+ sizeof(com32sys_t) );
+
+ com32_farcall_proc = proc;
+
+ __asm__ __volatile__ (
+ REAL_CODE ( /* Save all registers */
+ "pushal\n\t"
+ "pushw %%ds\n\t"
+ "pushw %%es\n\t"
+ "pushw %%fs\n\t"
+ "pushw %%gs\n\t"
+ /* Mask off unsafe flags */
+ "movl (com32_regs + 40), %%eax\n\t"
+ "andl $0x200cd7, %%eax\n\t"
+ "movl %%eax, (com32_regs + 40)\n\t"
+ /* Load com32_regs into the actual registers */
+ "movw %%sp, %%ss:(com32_saved_sp)\n\t"
+ "movw $com32_regs, %%sp\n\t"
+ "popw %%gs\n\t"
+ "popw %%fs\n\t"
+ "popw %%es\n\t"
+ "popw %%ds\n\t"
+ "popal\n\t"
+ "popfl\n\t"
+ "movw %%ss:(com32_saved_sp), %%sp\n\t"
+ /* Call procedure */
+ "lcall *%%ss:(com32_farcall_proc)\n\t"
+ /* Copy regs back to com32_regs */
+ "movw %%sp, %%ss:(com32_saved_sp)\n\t"
+ "movw $(com32_regs + 44), %%sp\n\t"
+ "pushfl\n\t"
+ "pushal\n\t"
+ "pushw %%ds\n\t"
+ "pushw %%es\n\t"
+ "pushw %%fs\n\t"
+ "pushw %%gs\n\t"
+ "movw %%ss:(com32_saved_sp), %%sp\n\t"
+ /* Restore registers */
+ "popw %%gs\n\t"
+ "popw %%fs\n\t"
+ "popw %%es\n\t"
+ "popw %%ds\n\t"
+ "popal\n\t")
+ : : );
+
+ if ( outregs_phys ) {
+ memcpy_user ( phys_to_user ( outregs_phys ), 0,
+ virt_to_user( &com32_regs ), 0,
+ sizeof(com32sys_t) );
+ }
+}
+
+/**
+ * CDECL farcall helper
+ */
+int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) {
+ int32_t eax;
+
+ copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz );
+ com32_farcall_proc = proc;
+
+ __asm__ __volatile__ (
+ REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" )
+ : "=a" (eax)
+ :
+ : "ecx", "edx" );
+
+ remove_user_from_rm_stack ( 0, stacksz );
+
+ return eax;
+}
diff --git a/roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S b/roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S
new file mode 100644
index 000000000..c9d1452b4
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+ .text
+ .arch i386
+ .code32
+
+ .globl com32_farcall_wrapper
+com32_farcall_wrapper:
+
+ movl $com32_farcall, %eax
+ jmp com32_wrapper
+
+
+ .globl com32_cfarcall_wrapper
+com32_cfarcall_wrapper:
+
+ movl $com32_cfarcall, %eax
+ jmp com32_wrapper
+
+
+ .globl com32_intcall_wrapper
+com32_intcall_wrapper:
+
+ movl $com32_intcall, %eax
+ /*jmp com32_wrapper*/ /* fall through */
+
+com32_wrapper:
+ cli
+
+ /* Switch to internal virtual address space */
+ call _phys_to_virt
+
+ mov %eax, (com32_helper_function)
+
+ /* Save external COM32 stack pointer */
+ movl %esp, (com32_external_esp)
+
+ /* Copy arguments to caller-save registers */
+ movl 12(%esp), %eax
+ movl 8(%esp), %ecx
+ movl 4(%esp), %edx
+
+ /* Switch to internal stack */
+ movl (com32_internal_esp), %esp
+
+ /* Copy arguments to internal stack */
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+
+ call *(com32_helper_function)
+
+ /* Clean up stack */
+ addl $12, %esp
+
+ /* Save internal stack pointer and restore external stack pointer */
+ movl %esp, (com32_internal_esp)
+ movl (com32_external_esp), %esp
+
+ /* Switch to external flat physical address space */
+ call _virt_to_phys
+
+ sti
+ ret
+
+
+ .data
+
+/* Internal iPXE virtual address space %esp */
+.globl com32_internal_esp
+.lcomm com32_internal_esp, 4
+
+/* External flat physical address space %esp */
+.globl com32_external_esp
+.lcomm com32_external_esp, 4
+
+/* Function pointer of helper to call */
+.lcomm com32_helper_function, 4
diff --git a/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c
new file mode 100644
index 000000000..69d94c407
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c
@@ -0,0 +1,717 @@
+/*
+ * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file SYSLINUX COMBOOT API
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <ipxe/console.h>
+#include <stdlib.h>
+#include <comboot.h>
+#include <bzimage.h>
+#include <pxe_call.h>
+#include <setjmp.h>
+#include <string.h>
+#include <ipxe/posix_io.h>
+#include <ipxe/process.h>
+#include <ipxe/serial.h>
+#include <ipxe/init.h>
+#include <ipxe/image.h>
+#include <ipxe/version.h>
+#include <usr/imgmgmt.h>
+
+/** The "SYSLINUX" version string */
+static char __bss16_array ( syslinux_version, [32] );
+#define syslinux_version __use_data16 ( syslinux_version )
+
+/** The "SYSLINUX" copyright string */
+static char __data16_array ( syslinux_copyright, [] ) = " http://ipxe.org";
+#define syslinux_copyright __use_data16 ( syslinux_copyright )
+
+static char __data16_array ( syslinux_configuration_file, [] ) = "";
+#define syslinux_configuration_file __use_data16 ( syslinux_configuration_file )
+
+/** Feature flags */
+static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP;
+#define comboot_feature_flags __use_data16 ( comboot_feature_flags )
+
+typedef union {
+ syslinux_pm_regs pm; syslinux_rm_regs rm;
+} syslinux_regs;
+
+/** Initial register values for INT 22h AX=1Ah and 1Bh */
+static syslinux_regs __text16 ( comboot_initial_regs );
+#define comboot_initial_regs __use_text16 ( comboot_initial_regs )
+
+static struct segoff __text16 ( int20_vector );
+#define int20_vector __use_text16 ( int20_vector )
+
+static struct segoff __text16 ( int21_vector );
+#define int21_vector __use_text16 ( int21_vector )
+
+static struct segoff __text16 ( int22_vector );
+#define int22_vector __use_text16 ( int22_vector )
+
+extern void int20_wrapper ( void );
+extern void int21_wrapper ( void );
+extern void int22_wrapper ( void );
+
+/* setjmp/longjmp context buffer used to return after loading an image */
+rmjmp_buf comboot_return;
+
+/* Mode flags set by INT 22h AX=0017h */
+static uint16_t comboot_graphics_mode = 0;
+
+/**
+ * Print a string with a particular terminator
+ */
+static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) {
+ int i = 0;
+ char c;
+ userptr_t str = real_to_user ( segment, offset );
+ for ( ; ; ) {
+ copy_from_user ( &c, str, i, 1 );
+ if ( c == terminator ) break;
+ putchar ( c );
+ i++;
+ }
+}
+
+
+/**
+ * Perform a series of memory copies from a list in low memory
+ */
+static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count )
+{
+ comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS];
+ unsigned int i;
+
+ /* Copy shuffle descriptor list so it doesn't get overwritten */
+ copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0,
+ count * sizeof( comboot_shuffle_descriptor ) );
+
+ /* Do the copies */
+ for ( i = 0; i < count; i++ ) {
+ userptr_t src_u = phys_to_user ( shuf[ i ].src );
+ userptr_t dest_u = phys_to_user ( shuf[ i ].dest );
+
+ if ( shuf[ i ].src == 0xFFFFFFFF ) {
+ /* Fill with 0 instead of copying */
+ memset_user ( dest_u, 0, 0, shuf[ i ].len );
+ } else if ( shuf[ i ].dest == 0xFFFFFFFF ) {
+ /* Copy new list of descriptors */
+ count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor );
+ assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS );
+ copy_from_user ( shuf, src_u, 0, shuf[ i ].len );
+ i = -1;
+ } else {
+ /* Regular copy */
+ memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len );
+ }
+ }
+}
+
+
+/**
+ * Set default text mode
+ */
+void comboot_force_text_mode ( void ) {
+ if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) {
+ /* Set VGA mode 3 via VESA VBE mode set */
+ __asm__ __volatile__ (
+ REAL_CODE (
+ "mov $0x4F02, %%ax\n\t"
+ "mov $0x03, %%bx\n\t"
+ "int $0x10\n\t"
+ )
+ : : );
+ } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) {
+ /* Set VGA mode 3 via standard VGA mode set */
+ __asm__ __volatile__ (
+ REAL_CODE (
+ "mov $0x03, %%ax\n\t"
+ "int $0x10\n\t"
+ )
+ : : );
+ }
+
+ comboot_graphics_mode = 0;
+}
+
+
+/**
+ * Fetch kernel and optional initrd
+ */
+static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) {
+ struct image *kernel;
+ struct image *initrd;
+ char *initrd_file;
+ int rc;
+
+ /* Find initrd= parameter, if any */
+ if ( ( initrd_file = strstr ( cmdline, "initrd=" ) ) != NULL ) {
+ char *initrd_end;
+
+ /* skip "initrd=" */
+ initrd_file += 7;
+
+ /* Find terminating space, if any, and replace with NUL */
+ initrd_end = strchr ( initrd_file, ' ' );
+ if ( initrd_end )
+ *initrd_end = '\0';
+
+ DBG ( "COMBOOT: fetching initrd '%s'\n", initrd_file );
+
+ /* Fetch initrd */
+ if ( ( rc = imgdownload_string ( initrd_file, 0,
+ &initrd ) ) != 0 ) {
+ DBG ( "COMBOOT: could not fetch initrd: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Restore space after initrd name, if applicable */
+ if ( initrd_end )
+ *initrd_end = ' ';
+ }
+
+ DBG ( "COMBOOT: fetching kernel '%s'\n", kernel_file );
+
+ /* Fetch kernel */
+ if ( ( rc = imgdownload_string ( kernel_file, 0, &kernel ) ) != 0 ) {
+ DBG ( "COMBOOT: could not fetch kernel: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Replace comboot image with kernel */
+ if ( ( rc = image_replace ( kernel ) ) != 0 ) {
+ DBG ( "COMBOOT: could not replace with kernel: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Terminate program interrupt handler
+ */
+static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) {
+ rmlongjmp ( comboot_return, COMBOOT_EXIT );
+}
+
+
+/**
+ * DOS-compatible API
+ */
+static __asmcall void int21 ( struct i386_all_regs *ix86 ) {
+ ix86->flags |= CF;
+
+ switch ( ix86->regs.ah ) {
+ case 0x00:
+ case 0x4C: /* Terminate program */
+ rmlongjmp ( comboot_return, COMBOOT_EXIT );
+ break;
+
+ case 0x01: /* Get Key with Echo */
+ case 0x08: /* Get Key without Echo */
+ /* TODO: handle extended characters? */
+ ix86->regs.al = getchar( );
+
+ /* Enter */
+ if ( ix86->regs.al == 0x0A )
+ ix86->regs.al = 0x0D;
+
+ if ( ix86->regs.ah == 0x01 )
+ putchar ( ix86->regs.al );
+
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x02: /* Write Character */
+ putchar ( ix86->regs.dl );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x04: /* Write Character to Serial Port */
+ if ( serial_console.base ) {
+ uart_transmit ( &serial_console, ix86->regs.dl );
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x09: /* Write DOS String to Console */
+ print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0B: /* Check Keyboard */
+ if ( iskey() )
+ ix86->regs.al = 0xFF;
+ else
+ ix86->regs.al = 0x00;
+
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x30: /* Check DOS Version */
+ /* Bottom halves all 0; top halves spell "SYSLINUX" */
+ ix86->regs.eax = 0x59530000;
+ ix86->regs.ebx = 0x4C530000;
+ ix86->regs.ecx = 0x4E490000;
+ ix86->regs.edx = 0x58550000;
+ ix86->flags &= ~CF;
+ break;
+
+ default:
+ DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah );
+ break;
+ }
+}
+
+
+/**
+ * Dispatch PXE API call weakly
+ *
+ * @v ix86 Registers for PXE call
+ * @ret present Zero if the PXE stack is present, nonzero if not
+ *
+ * A successful return only indicates that the PXE stack was available
+ * for dispatching the call; it says nothing about the success of
+ * whatever the call asked for.
+ */
+__weak int pxe_api_call_weak ( struct i386_all_regs *ix86 __unused ) {
+ return -1;
+}
+
+/**
+ * SYSLINUX API
+ */
+static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
+ ix86->flags |= CF;
+
+ switch ( ix86->regs.ax ) {
+ case 0x0001: /* Get Version */
+
+ /* Number of INT 22h API functions available */
+ ix86->regs.ax = 0x001D;
+
+ /* SYSLINUX version number */
+ ix86->regs.ch = 0; /* major */
+ ix86->regs.cl = 0; /* minor */
+
+ /* SYSLINUX derivative ID */
+ ix86->regs.dl = BZI_LOADER_TYPE_IPXE;
+
+ /* SYSLINUX version */
+ snprintf ( syslinux_version, sizeof ( syslinux_version ),
+ "\r\niPXE %s", product_version );
+
+ /* SYSLINUX version and copyright strings */
+ ix86->segs.es = rm_ds;
+ ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) );
+ ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) );
+
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0002: /* Write String */
+ print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0003: /* Run command */
+ {
+ userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
+ int len = strlen_user ( cmd_u, 0 );
+ char cmd[len + 1];
+ copy_from_user ( cmd, cmd_u, 0, len + 1 );
+ DBG ( "COMBOOT: executing command '%s'\n", cmd );
+ system ( cmd );
+ DBG ( "COMBOOT: exiting after executing command...\n" );
+ rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
+ }
+ break;
+
+ case 0x0004: /* Run default command */
+ /* FIXME: just exit for now */
+ rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
+ break;
+
+ case 0x0005: /* Force text mode */
+ comboot_force_text_mode ( );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0006: /* Open file */
+ {
+ int fd;
+ userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si );
+ int len = strlen_user ( file_u, 0 );
+ char file[len + 1];
+
+ copy_from_user ( file, file_u, 0, len + 1 );
+
+ if ( file[0] == '\0' ) {
+ DBG ( "COMBOOT: attempted open with empty file name\n" );
+ break;
+ }
+
+ DBG ( "COMBOOT: opening file '%s'\n", file );
+
+ fd = open ( file );
+
+ if ( fd < 0 ) {
+ DBG ( "COMBOOT: error opening file %s\n", file );
+ break;
+ }
+
+ /* This relies on the fact that a iPXE POSIX fd will
+ * always fit in 16 bits.
+ */
+#if (POSIX_FD_MAX > 65535)
+#error POSIX_FD_MAX too large
+#endif
+ ix86->regs.si = (uint16_t) fd;
+
+ ix86->regs.cx = COMBOOT_FILE_BLOCKSZ;
+ ix86->regs.eax = fsize ( fd );
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x0007: /* Read file */
+ {
+ int fd = ix86->regs.si;
+ int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ;
+ int rc;
+ fd_set fds;
+ userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx );
+
+ /* Wait for data ready to read */
+ FD_ZERO ( &fds );
+ FD_SET ( fd, &fds );
+
+ select ( &fds, 1 );
+
+ rc = read_user ( fd, buf, 0, len );
+ if ( rc < 0 ) {
+ DBG ( "COMBOOT: read failed\n" );
+ ix86->regs.si = 0;
+ break;
+ }
+
+ ix86->regs.ecx = rc;
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x0008: /* Close file */
+ {
+ int fd = ix86->regs.si;
+ close ( fd );
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x0009: /* Call PXE Stack */
+ if ( pxe_api_call_weak ( ix86 ) != 0 )
+ ix86->flags |= CF;
+ else
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x000A: /* Get Derivative-Specific Information */
+
+ /* iPXE has its own derivative ID, so there is no defined
+ * output here; just return AL for now */
+ ix86->regs.al = BZI_LOADER_TYPE_IPXE;
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x000B: /* Get Serial Console Configuration */
+ if ( serial_console.base ) {
+ ix86->regs.dx = ( ( intptr_t ) serial_console.base );
+ ix86->regs.cx = serial_console.divisor;
+ ix86->regs.bx = 0;
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x000C: /* Perform final cleanup */
+ shutdown_boot();
+ break;
+
+ case 0x000E: /* Get configuration file name */
+ /* FIXME: stub */
+ ix86->segs.es = rm_ds;
+ ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x000F: /* Get IPAPPEND strings */
+ /* FIXME: stub */
+ ix86->regs.cx = 0;
+ ix86->segs.es = 0;
+ ix86->regs.bx = 0;
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0010: /* Resolve hostname */
+ {
+ userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
+ int len = strlen_user ( hostname_u, 0 );
+ char hostname[len];
+ struct in_addr addr;
+
+ copy_from_user ( hostname, hostname_u, 0, len + 1 );
+
+ /* TODO:
+ * "If the hostname does not contain a dot (.), the
+ * local domain name is automatically appended."
+ */
+
+ comboot_resolv ( hostname, &addr );
+
+ ix86->regs.eax = addr.s_addr;
+ ix86->flags &= ~CF;
+ }
+ break;
+
+ case 0x0011: /* Maximum number of shuffle descriptors */
+ ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS;
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0012: /* Cleanup, shuffle and boot */
+ if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS )
+ break;
+
+ /* Perform final cleanup */
+ shutdown_boot();
+
+ /* Perform sequence of copies */
+ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx );
+
+ /* Jump to real-mode entry point */
+ __asm__ __volatile__ (
+ REAL_CODE (
+ "pushw %0\n\t"
+ "popw %%ds\n\t"
+ "pushl %1\n\t"
+ "lret\n\t"
+ )
+ :
+ : "r" ( ix86->segs.ds ),
+ "r" ( ix86->regs.ebp ),
+ "d" ( ix86->regs.ebx ),
+ "S" ( ix86->regs.esi ) );
+
+ assert ( 0 ); /* Execution should never reach this point */
+
+ break;
+
+ case 0x0013: /* Idle loop call */
+ step ( );
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0015: /* Get feature flags */
+ ix86->segs.es = rm_ds;
+ ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) );
+ ix86->regs.cx = 1; /* Number of feature flag bytes */
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0016: /* Run kernel image */
+ {
+ userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si );
+ userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
+ int file_len = strlen_user ( file_u, 0 );
+ int cmd_len = strlen_user ( cmd_u, 0 );
+ char file[file_len + 1];
+ char cmd[cmd_len + 1];
+
+ copy_from_user ( file, file_u, 0, file_len + 1 );
+ copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 );
+
+ DBG ( "COMBOOT: run kernel %s %s\n", file, cmd );
+ comboot_fetch_kernel ( file, cmd );
+ /* Technically, we should return if we
+ * couldn't load the kernel, but it's not safe
+ * to do that since we have just overwritten
+ * part of the COMBOOT program's memory space.
+ */
+ DBG ( "COMBOOT: exiting to run kernel...\n" );
+ rmlongjmp ( comboot_return, COMBOOT_EXIT_RUN_KERNEL );
+ }
+ break;
+
+ case 0x0017: /* Report video mode change */
+ comboot_graphics_mode = ix86->regs.bx;
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x0018: /* Query custom font */
+ /* FIXME: stub */
+ ix86->regs.al = 0;
+ ix86->segs.es = 0;
+ ix86->regs.bx = 0;
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x001B: /* Cleanup, shuffle and boot to real mode */
+ if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS )
+ break;
+
+ /* Perform final cleanup */
+ shutdown_boot();
+
+ /* Perform sequence of copies */
+ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx );
+
+ /* Copy initial register values to .text16 */
+ memcpy_user ( real_to_user ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ), 0,
+ real_to_user ( ix86->segs.ds, ix86->regs.si ), 0,
+ sizeof(syslinux_rm_regs) );
+
+ /* Load initial register values */
+ __asm__ __volatile__ (
+ REAL_CODE (
+ /* Point SS:SP at the register value structure */
+ "pushw %%cs\n\t"
+ "popw %%ss\n\t"
+ "movw $comboot_initial_regs, %%sp\n\t"
+
+ /* Segment registers */
+ "popw %%es\n\t"
+ "popw %%ax\n\t" /* Skip CS */
+ "popw %%ds\n\t"
+ "popw %%ax\n\t" /* Skip SS for now */
+ "popw %%fs\n\t"
+ "popw %%gs\n\t"
+
+ /* GP registers */
+ "popl %%eax\n\t"
+ "popl %%ecx\n\t"
+ "popl %%edx\n\t"
+ "popl %%ebx\n\t"
+ "popl %%ebp\n\t" /* Skip ESP for now */
+ "popl %%ebp\n\t"
+ "popl %%esi\n\t"
+ "popl %%edi\n\t"
+
+ /* Load correct SS:ESP */
+ "movw $(comboot_initial_regs + 6), %%sp\n\t"
+ "popw %%ss\n\t"
+ "movl %%cs:(comboot_initial_regs + 28), %%esp\n\t"
+
+ "ljmp *%%cs:(comboot_initial_regs + 44)\n\t"
+ )
+ : : );
+
+ break;
+
+ case 0x001C: /* Get pointer to auxilliary data vector */
+ /* FIXME: stub */
+ ix86->regs.cx = 0; /* Size of the ADV */
+ ix86->flags &= ~CF;
+ break;
+
+ case 0x001D: /* Write auxilliary data vector */
+ /* FIXME: stub */
+ ix86->flags &= ~CF;
+ break;
+
+ default:
+ DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax );
+ break;
+ }
+}
+
+/**
+ * Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h)
+ */
+void hook_comboot_interrupts ( ) {
+
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint20_wrapper:\n\t"
+ "pushl %0\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ "addw $4, %%sp\n\t"
+ "call patch_cf\n\t"
+ "iret\n\t" )
+ : : "i" ( int20 ) );
+
+ hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper,
+ &int20_vector );
+
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint21_wrapper:\n\t"
+ "pushl %0\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ "addw $4, %%sp\n\t"
+ "call patch_cf\n\t"
+ "iret\n\t" )
+ : : "i" ( int21 ) );
+
+ hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper,
+ &int21_vector );
+
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint22_wrapper:\n\t"
+ "pushl %0\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ "addw $4, %%sp\n\t"
+ "call patch_cf\n\t"
+ "iret\n\t" )
+ : : "i" ( int22) );
+
+ hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper,
+ &int22_vector );
+}
+
+/**
+ * Unhook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h)
+ */
+void unhook_comboot_interrupts ( ) {
+
+ unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper,
+ &int20_vector );
+
+ unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper,
+ &int21_vector );
+
+ unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper,
+ &int22_vector );
+}
+
+/* Avoid dragging in serial console support unconditionally */
+struct uart serial_console __attribute__ (( weak ));
diff --git a/roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c
new file mode 100644
index 000000000..03bbfd04a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c
@@ -0,0 +1,61 @@
+#include <errno.h>
+#include <comboot.h>
+#include <ipxe/in.h>
+#include <ipxe/list.h>
+#include <ipxe/process.h>
+#include <ipxe/resolv.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct comboot_resolver {
+ struct interface intf;
+ int rc;
+ struct in_addr addr;
+};
+
+static void comboot_resolv_close ( struct comboot_resolver *comboot_resolver,
+ int rc ) {
+ comboot_resolver->rc = rc;
+ intf_shutdown ( &comboot_resolver->intf, rc );
+}
+
+static void comboot_resolv_done ( struct comboot_resolver *comboot_resolver,
+ struct sockaddr *sa ) {
+ struct sockaddr_in *sin;
+
+ if ( sa->sa_family == AF_INET ) {
+ sin = ( ( struct sockaddr_in * ) sa );
+ comboot_resolver->addr = sin->sin_addr;
+ }
+}
+
+static struct interface_operation comboot_resolv_op[] = {
+ INTF_OP ( intf_close, struct comboot_resolver *, comboot_resolv_close ),
+ INTF_OP ( resolv_done, struct comboot_resolver *, comboot_resolv_done ),
+};
+
+static struct interface_descriptor comboot_resolv_desc =
+ INTF_DESC ( struct comboot_resolver, intf, comboot_resolv_op );
+
+static struct comboot_resolver comboot_resolver = {
+ .intf = INTF_INIT ( comboot_resolv_desc ),
+};
+
+int comboot_resolv ( const char *name, struct in_addr *address ) {
+ int rc;
+
+ comboot_resolver.rc = -EINPROGRESS;
+ comboot_resolver.addr.s_addr = 0;
+
+ if ( ( rc = resolv ( &comboot_resolver.intf, name, NULL ) ) != 0 )
+ return rc;
+
+ while ( comboot_resolver.rc == -EINPROGRESS )
+ step();
+
+ if ( ! comboot_resolver.addr.s_addr )
+ return -EAFNOSUPPORT;
+
+ *address = comboot_resolver.addr;
+ return comboot_resolver.rc;
+}
diff --git a/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c b/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c
new file mode 100644
index 000000000..a0530c8d1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * VMware GuestInfo settings
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/init.h>
+#include <ipxe/settings.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/guestrpc.h>
+
+/** GuestInfo GuestRPC channel */
+static int guestinfo_channel;
+
+/**
+ * Fetch value of typed GuestInfo setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v type Setting type to attempt (or NULL for default)
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret found Setting found in GuestInfo
+ * @ret len Length of setting data, or negative error
+ */
+static int guestinfo_fetch_type ( struct settings *settings,
+ struct setting *setting,
+ const struct setting_type *type,
+ void *data, size_t len, int *found ) {
+ const char *parent_name = settings->parent->name;
+ char command[ 24 /* "info-get guestinfo.ipxe." */ +
+ strlen ( parent_name ) + 1 /* "." */ +
+ strlen ( setting->name ) + 1 /* "." */ +
+ ( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ];
+ struct setting *predefined;
+ char *info;
+ int info_len;
+ int check_len;
+ int ret;
+
+ /* Construct info-get command */
+ snprintf ( command, sizeof ( command ),
+ "info-get guestinfo.ipxe.%s%s%s%s%s",
+ parent_name, ( parent_name[0] ? "." : "" ), setting->name,
+ ( type ? "." : "" ), ( type ? type->name : "" ) );
+
+ /* Check for existence and obtain length of GuestInfo value */
+ info_len = guestrpc_command ( guestinfo_channel, command, NULL, 0 );
+ if ( info_len < 0 ) {
+ ret = info_len;
+ goto err_get_info_len;
+ }
+
+ /* Mark as found */
+ *found = 1;
+
+ /* Determine default type if necessary */
+ if ( ! type ) {
+ predefined = find_setting ( setting->name );
+ type = ( predefined ? predefined->type : &setting_type_string );
+ }
+ assert ( type != NULL );
+
+ /* Allocate temporary block to hold GuestInfo value */
+ info = zalloc ( info_len + 1 /* NUL */ );
+ if ( ! info ) {
+ DBGC ( settings, "GuestInfo %p could not allocate %d bytes\n",
+ settings, info_len );
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ info[info_len] = '\0';
+
+ /* Fetch GuestInfo value */
+ check_len = guestrpc_command ( guestinfo_channel, command,
+ info, info_len );
+ if ( check_len < 0 ) {
+ ret = check_len;
+ goto err_get_info;
+ }
+ if ( check_len != info_len ) {
+ DBGC ( settings, "GuestInfo %p length mismatch (expected %d, "
+ "got %d)\n", settings, info_len, check_len );
+ ret = -EIO;
+ goto err_get_info;
+ }
+ DBGC2 ( settings, "GuestInfo %p found %s = \"%s\"\n",
+ settings, &command[9] /* Skip "info-get " */, info );
+
+ /* Parse GuestInfo value according to type */
+ ret = setting_parse ( type, info, data, len );
+ if ( ret < 0 ) {
+ DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: "
+ "%s\n", settings, info, type->name, strerror ( ret ) );
+ goto err_parse;
+ }
+
+ err_parse:
+ err_get_info:
+ free ( info );
+ err_alloc:
+ err_get_info_len:
+ return ret;
+}
+
+/**
+ * Fetch value of GuestInfo setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int guestinfo_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ struct setting_type *type;
+ int found = 0;
+ int ret;
+
+ /* Try default type first */
+ ret = guestinfo_fetch_type ( settings, setting, NULL,
+ data, len, &found );
+ if ( found )
+ return ret;
+
+ /* Otherwise, try all possible types */
+ for_each_table_entry ( type, SETTING_TYPES ) {
+ ret = guestinfo_fetch_type ( settings, setting, type,
+ data, len, &found );
+ if ( found )
+ return ret;
+ }
+
+ /* Not found */
+ return -ENOENT;
+}
+
+/** GuestInfo settings operations */
+static struct settings_operations guestinfo_settings_operations = {
+ .fetch = guestinfo_fetch,
+};
+
+/** GuestInfo settings */
+static struct settings guestinfo_settings = {
+ .refcnt = NULL,
+ .siblings = LIST_HEAD_INIT ( guestinfo_settings.siblings ),
+ .children = LIST_HEAD_INIT ( guestinfo_settings.children ),
+ .op = &guestinfo_settings_operations,
+};
+
+/** Initialise GuestInfo settings */
+static void guestinfo_init ( void ) {
+ int rc;
+
+ /* Open GuestRPC channel */
+ guestinfo_channel = guestrpc_open();
+ if ( guestinfo_channel < 0 ) {
+ rc = guestinfo_channel;
+ DBG ( "GuestInfo could not open channel: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+
+ /* Register root GuestInfo settings */
+ if ( ( rc = register_settings ( &guestinfo_settings, NULL,
+ "vmware" ) ) != 0 ) {
+ DBG ( "GuestInfo could not register settings: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** GuestInfo settings initialiser */
+struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = guestinfo_init,
+};
+
+/**
+ * Create per-netdevice GuestInfo settings
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int guestinfo_net_probe ( struct net_device *netdev ) {
+ struct settings *settings;
+ int rc;
+
+ /* Do nothing unless we have a GuestInfo channel available */
+ if ( guestinfo_channel < 0 )
+ return 0;
+
+ /* Allocate and initialise settings block */
+ settings = zalloc ( sizeof ( *settings ) );
+ if ( ! settings ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ settings_init ( settings, &guestinfo_settings_operations, NULL, NULL );
+
+ /* Register settings */
+ if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
+ "vmware" ) ) != 0 ) {
+ DBGC ( settings, "GuestInfo %p could not register for %s: %s\n",
+ settings, netdev->name, strerror ( rc ) );
+ goto err_register;
+ }
+ DBGC ( settings, "GuestInfo %p registered for %s\n",
+ settings, netdev->name );
+
+ return 0;
+
+ err_register:
+ free ( settings );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove per-netdevice GuestInfo settings
+ *
+ * @v netdev Network device
+ */
+static void guestinfo_net_remove ( struct net_device *netdev ) {
+ struct settings *parent = netdev_settings ( netdev );
+ struct settings *settings;
+
+ list_for_each_entry ( settings, &parent->children, siblings ) {
+ if ( settings->op == &guestinfo_settings_operations ) {
+ DBGC ( settings, "GuestInfo %p unregistered for %s\n",
+ settings, netdev->name );
+ unregister_settings ( settings );
+ free ( settings );
+ return;
+ }
+ }
+}
+
+/** GuestInfo per-netdevice driver */
+struct net_driver guestinfo_net_driver __net_driver = {
+ .name = "GuestInfo",
+ .probe = guestinfo_net_probe,
+ .remove = guestinfo_net_remove,
+};
diff --git a/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c b/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c
new file mode 100644
index 000000000..ef7ee8151
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * VMware GuestRPC mechanism
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/vmware.h>
+#include <ipxe/guestrpc.h>
+
+/* Disambiguate the various error causes */
+#define EPROTO_OPEN __einfo_error ( EINFO_EPROTO_OPEN )
+#define EINFO_EPROTO_OPEN \
+ __einfo_uniqify ( EINFO_EPROTO, 0x00, "GuestRPC open failed" )
+#define EPROTO_COMMAND_LEN __einfo_error ( EINFO_EPROTO_COMMAND_LEN )
+#define EINFO_EPROTO_COMMAND_LEN \
+ __einfo_uniqify ( EINFO_EPROTO, 0x01, "GuestRPC command length failed" )
+#define EPROTO_COMMAND_DATA __einfo_error ( EINFO_EPROTO_COMMAND_DATA )
+#define EINFO_EPROTO_COMMAND_DATA \
+ __einfo_uniqify ( EINFO_EPROTO, 0x02, "GuestRPC command data failed" )
+#define EPROTO_REPLY_LEN __einfo_error ( EINFO_EPROTO_REPLY_LEN )
+#define EINFO_EPROTO_REPLY_LEN \
+ __einfo_uniqify ( EINFO_EPROTO, 0x03, "GuestRPC reply length failed" )
+#define EPROTO_REPLY_DATA __einfo_error ( EINFO_EPROTO_REPLY_DATA )
+#define EINFO_EPROTO_REPLY_DATA \
+ __einfo_uniqify ( EINFO_EPROTO, 0x04, "GuestRPC reply data failed" )
+#define EPROTO_REPLY_FINISH __einfo_error ( EINFO_EPROTO_REPLY_FINISH )
+#define EINFO_EPROTO_REPLY_FINISH \
+ __einfo_uniqify ( EINFO_EPROTO, 0x05, "GuestRPC reply finish failed" )
+#define EPROTO_CLOSE __einfo_error ( EINFO_EPROTO_CLOSE )
+#define EINFO_EPROTO_CLOSE \
+ __einfo_uniqify ( EINFO_EPROTO, 0x06, "GuestRPC close failed" )
+
+/**
+ * Open GuestRPC channel
+ *
+ * @ret channel Channel number, or negative error
+ */
+int guestrpc_open ( void ) {
+ uint16_t channel;
+ uint32_t discard_b;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( 0, GUESTRPC_OPEN, GUESTRPC_MAGIC,
+ &channel, &discard_b );
+ if ( status != GUESTRPC_OPEN_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC open failed: status %08x\n",
+ status );
+ return -EPROTO_OPEN;
+ }
+
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d opened\n", channel );
+ return channel;
+}
+
+/**
+ * Send GuestRPC command length
+ *
+ * @v channel Channel number
+ * @v len Command length
+ * @ret rc Return status code
+ */
+static int guestrpc_command_len ( int channel, size_t len ) {
+ uint16_t discard_d;
+ uint32_t discard_b;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_LEN, len,
+ &discard_d, &discard_b );
+ if ( status != GUESTRPC_COMMAND_LEN_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
+ "length %zd failed: status %08x\n",
+ channel, len, status );
+ return -EPROTO_COMMAND_LEN;
+ }
+
+ return 0;
+}
+
+/**
+ * Send GuestRPC command data
+ *
+ * @v channel Channel number
+ * @v data Command data
+ * @ret rc Return status code
+ */
+static int guestrpc_command_data ( int channel, uint32_t data ) {
+ uint16_t discard_d;
+ uint32_t discard_b;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_DATA, data,
+ &discard_d, &discard_b );
+ if ( status != GUESTRPC_COMMAND_DATA_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
+ "data %08x failed: status %08x\n",
+ channel, data, status );
+ return -EPROTO_COMMAND_DATA;
+ }
+
+ return 0;
+}
+
+/**
+ * Receive GuestRPC reply length
+ *
+ * @v channel Channel number
+ * @ret reply_id Reply ID
+ * @ret len Reply length, or negative error
+ */
+static int guestrpc_reply_len ( int channel, uint16_t *reply_id ) {
+ uint32_t len;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_LEN, 0,
+ reply_id, &len );
+ if ( status != GUESTRPC_REPLY_LEN_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
+ "length failed: status %08x\n", channel, status );
+ return -EPROTO_REPLY_LEN;
+ }
+
+ return len;
+}
+
+/**
+ * Receive GuestRPC reply data
+ *
+ * @v channel Channel number
+ * @v reply_id Reply ID
+ * @ret data Reply data
+ * @ret rc Return status code
+ */
+static int guestrpc_reply_data ( int channel, uint16_t reply_id,
+ uint32_t *data ) {
+ uint16_t discard_d;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_DATA, reply_id,
+ &discard_d, data );
+ if ( status != GUESTRPC_REPLY_DATA_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
+ "%d data failed: status %08x\n",
+ channel, reply_id, status );
+ return -EPROTO_REPLY_DATA;
+ }
+
+ return 0;
+}
+
+/**
+ * Finish receiving GuestRPC reply
+ *
+ * @v channel Channel number
+ * @v reply_id Reply ID
+ * @ret rc Return status code
+ */
+static int guestrpc_reply_finish ( int channel, uint16_t reply_id ) {
+ uint16_t discard_d;
+ uint32_t discard_b;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_FINISH, reply_id,
+ &discard_d, &discard_b );
+ if ( status != GUESTRPC_REPLY_FINISH_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d finish reply %d "
+ "failed: status %08x\n", channel, reply_id, status );
+ return -EPROTO_REPLY_FINISH;
+ }
+
+ return 0;
+}
+
+/**
+ * Close GuestRPC channel
+ *
+ * @v channel Channel number
+ */
+void guestrpc_close ( int channel ) {
+ uint16_t discard_d;
+ uint32_t discard_b;
+ uint32_t status;
+
+ /* Issue GuestRPC command */
+ status = vmware_cmd_guestrpc ( channel, GUESTRPC_CLOSE, 0,
+ &discard_d, &discard_b );
+ if ( status != GUESTRPC_CLOSE_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d close failed: "
+ "status %08x\n", channel, status );
+ return;
+ }
+
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d closed\n", channel );
+}
+
+/**
+ * Issue GuestRPC command
+ *
+ * @v channel Channel number
+ * @v command Command
+ * @v reply Reply buffer
+ * @v reply_len Length of reply buffer
+ * @ret len Length of reply, or negative error
+ *
+ * The actual length of the reply will be returned even if the buffer
+ * was too small.
+ */
+int guestrpc_command ( int channel, const char *command, char *reply,
+ size_t reply_len ) {
+ const uint8_t *command_bytes = ( ( const void * ) command );
+ uint8_t *reply_bytes = ( ( void * ) reply );
+ size_t command_len = strlen ( command );
+ int orig_reply_len = reply_len;
+ uint16_t status;
+ uint8_t *status_bytes = ( ( void * ) &status );
+ size_t status_len = sizeof ( status );
+ uint32_t data;
+ uint16_t reply_id;
+ int len;
+ int remaining;
+ unsigned int i;
+ int rc;
+
+ DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d issuing command:\n",
+ channel );
+ DBGC2_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
+
+ /* Sanity check */
+ assert ( ( reply != NULL ) || ( reply_len == 0 ) );
+
+ /* Send command length */
+ if ( ( rc = guestrpc_command_len ( channel, command_len ) ) < 0 )
+ return rc;
+
+ /* Send command data */
+ while ( command_len ) {
+ data = 0;
+ for ( i = sizeof ( data ) ; i ; i-- ) {
+ if ( command_len ) {
+ data = ( ( data & ~0xff ) |
+ *(command_bytes++) );
+ command_len--;
+ }
+ data = ( ( data << 24 ) | ( data >> 8 ) );
+ }
+ if ( ( rc = guestrpc_command_data ( channel, data ) ) < 0 )
+ return rc;
+ }
+
+ /* Receive reply length */
+ if ( ( len = guestrpc_reply_len ( channel, &reply_id ) ) < 0 ) {
+ rc = len;
+ return rc;
+ }
+
+ /* Receive reply */
+ for ( remaining = len ; remaining > 0 ; remaining -= sizeof ( data ) ) {
+ if ( ( rc = guestrpc_reply_data ( channel, reply_id,
+ &data ) ) < 0 ) {
+ return rc;
+ }
+ for ( i = sizeof ( data ) ; i ; i-- ) {
+ if ( status_len ) {
+ *(status_bytes++) = ( data & 0xff );
+ status_len--;
+ len--;
+ } else if ( reply_len ) {
+ *(reply_bytes++) = ( data & 0xff );
+ reply_len--;
+ }
+ data = ( ( data << 24 ) | ( data >> 8 ) );
+ }
+ }
+
+ /* Finish receiving RPC reply */
+ if ( ( rc = guestrpc_reply_finish ( channel, reply_id ) ) < 0 )
+ return rc;
+
+ DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d received reply (id %d, "
+ "length %d):\n", channel, reply_id, len );
+ DBGC2_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
+ DBGC2_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
+ ( ( len < orig_reply_len ) ? len : orig_reply_len ) );
+
+ /* Check reply status */
+ if ( status != GUESTRPC_SUCCESS ) {
+ DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d command failed "
+ "(status %04x, reply id %d, reply length %d):\n",
+ channel, status, reply_id, len );
+ DBGC_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
+ DBGC_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
+ DBGC_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
+ ( ( len < orig_reply_len ) ? len : orig_reply_len ));
+ return -EIO;
+ }
+
+ return len;
+}
diff --git a/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c b/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c
new file mode 100644
index 000000000..f7df4f75b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * VMware logfile console
+ *
+ */
+
+#include <string.h>
+#include <ipxe/console.h>
+#include <ipxe/lineconsole.h>
+#include <ipxe/init.h>
+#include <ipxe/guestrpc.h>
+#include <config/console.h>
+
+/** VMware logfile console buffer size */
+#define VMCONSOLE_BUFSIZE 128
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_VMWARE ) && CONSOLE_EXPLICIT ( CONSOLE_VMWARE ) )
+#undef CONSOLE_VMWARE
+#define CONSOLE_VMWARE ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_TUI )
+#endif
+
+/** VMware logfile console GuestRPC channel */
+static int vmconsole_channel;
+
+/** VMware logfile console line buffer */
+static struct {
+ char prefix[4];
+ char message[VMCONSOLE_BUFSIZE];
+} vmconsole_buffer = {
+ .prefix = "log ",
+};
+
+/** VMware logfile console ANSI escape sequence handlers */
+static struct ansiesc_handler vmconsole_handlers[] = {
+ { 0, NULL }
+};
+
+/** VMware logfile line console */
+static struct line_console vmconsole_line = {
+ .buffer = vmconsole_buffer.message,
+ .len = sizeof ( vmconsole_buffer.message ),
+ .ctx = {
+ .handlers = vmconsole_handlers,
+ },
+};
+
+/** VMware logfile console recursion marker */
+static int vmconsole_entered;
+
+/**
+ * Print a character to VMware logfile console
+ *
+ * @v character Character to be printed
+ */
+static void vmconsole_putchar ( int character ) {
+ int rc;
+
+ /* Ignore if we are already mid-logging */
+ if ( vmconsole_entered )
+ return;
+
+ /* Fill line buffer */
+ if ( line_putchar ( &vmconsole_line, character ) == 0 )
+ return;
+
+ /* Guard against re-entry */
+ vmconsole_entered = 1;
+
+ /* Send log message */
+ if ( ( rc = guestrpc_command ( vmconsole_channel,
+ vmconsole_buffer.prefix, NULL, 0 ) ) <0){
+ DBG ( "VMware console could not send log message: %s\n",
+ strerror ( rc ) );
+ }
+
+ /* Clear re-entry flag */
+ vmconsole_entered = 0;
+}
+
+/** VMware logfile console driver */
+struct console_driver vmconsole __console_driver = {
+ .putchar = vmconsole_putchar,
+ .disabled = CONSOLE_DISABLED,
+ .usage = CONSOLE_VMWARE,
+};
+
+/**
+ * Initialise VMware logfile console
+ *
+ */
+static void vmconsole_init ( void ) {
+ int rc;
+
+ /* Attempt to open console */
+ vmconsole_channel = guestrpc_open();
+ if ( vmconsole_channel < 0 ) {
+ rc = vmconsole_channel;
+ DBG ( "VMware console could not be initialised: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+
+ /* Mark console as available */
+ vmconsole.disabled = 0;
+}
+
+/**
+ * VMware logfile console initialisation function
+ */
+struct init_fn vmconsole_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .initialise = vmconsole_init,
+};
diff --git a/roms/ipxe/src/arch/i386/interface/vmware/vmware.c b/roms/ipxe/src/arch/i386/interface/vmware/vmware.c
new file mode 100644
index 000000000..a415465fb
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/interface/vmware/vmware.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * VMware backdoor mechanism
+ *
+ * Based on the unofficial documentation at
+ *
+ * http://sites.google.com/site/chitchatvmback/backdoor
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <ipxe/vmware.h>
+
+/**
+ * Detect VMware presence
+ *
+ * @ret rc Return status code
+ */
+int vmware_present ( void ) {
+ uint32_t version;
+ uint32_t magic;
+ uint32_t product_type;
+
+ /* Perform backdoor call */
+ vmware_cmd_get_version ( &version, &magic, &product_type );
+
+ /* Check for VMware presence */
+ if ( magic != VMW_MAGIC ) {
+ DBGC ( VMW_MAGIC, "VMware not present\n" );
+ return -ENOENT;
+ }
+
+ DBGC ( VMW_MAGIC, "VMware product type %04x version %08x detected\n",
+ product_type, version );
+ return 0;
+}
diff --git a/roms/ipxe/src/arch/i386/prefix/bootpart.S b/roms/ipxe/src/arch/i386/prefix/bootpart.S
new file mode 100644
index 000000000..6d0c6034a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/bootpart.S
@@ -0,0 +1,218 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#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/roms/ipxe/src/arch/i386/prefix/dskprefix.S b/roms/ipxe/src/arch/i386/prefix/dskprefix.S
new file mode 100644
index 000000000..7aa017ccd
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/dskprefix.S
@@ -0,0 +1,383 @@
+/* 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.
+ */
+
+FILE_LICENCE ( GPL2_ONLY )
+
+.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
+ .globl _dsk_start
+_dsk_start:
+
+ 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
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDW"
+ .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:
+ /* Install iPXE */
+ 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 %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Boot next device */
+ int $0x18
+
diff --git a/roms/ipxe/src/arch/i386/prefix/exeprefix.S b/roms/ipxe/src/arch/i386/prefix/exeprefix.S
new file mode 100644
index 000000000..5c648d51d
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/exeprefix.S
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/* Initial temporary stack size */
+#define EXE_STACK_SIZE 0x400
+
+/* Temporary decompression area (avoid DOS high memory area) */
+#define EXE_DECOMPRESS_ADDRESS 0x110000
+
+/* Fields within the Program Segment Prefix */
+#define PSP_CMDLINE_LEN 0x80
+#define PSP_CMDLINE_START 0x81
+
+ .text
+ .arch i386
+ .org 0
+ .code16
+ .section ".prefix", "awx", @progbits
+
+signature:
+ /* "MZ" signature */
+ .ascii "MZ"
+
+last_block:
+ /* Number of bytes in last block that are really used */
+ .word 0
+
+blocks:
+ /* Number of 512-byte blocks */
+ .word 0
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDW"
+ .long blocks
+ .long 512
+ .long 0
+ .previous
+
+num_reloc:
+ /* Number of relocation entries stored after the header */
+ .word 0
+
+header_pgh:
+ /* Number of paragraphs in the header */
+ .word ( ( _exe_start - signature ) / 16 )
+
+min_bss_pgh:
+ /* Minimum number of paragraphs of additional (BSS) memory */
+ .word ( EXE_STACK_SIZE / 16 )
+
+max_bss_pgh:
+ /* Maximum number of paragraphs of additional (BSS) memory */
+ .word ( EXE_STACK_SIZE / 16 )
+
+init_ss:
+ /* Initial stack segment (relative to start of executable) */
+ .word -( ( _exe_start - signature ) / 16 )
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDW"
+ .long init_ss
+ .long 16
+ .long 0
+ .previous
+
+init_sp:
+ /* Initial stack pointer */
+ .word EXE_STACK_SIZE
+
+checksum:
+ /* Checksum (ignored) */
+ .word 0
+
+init_ip:
+ /* Initial instruction pointer */
+ .word _exe_start
+
+init_cs:
+ /* Initial code segment (relative to start of executable) */
+ .word -( ( _exe_start - signature ) / 16 )
+
+reloc_table:
+ /* Relocation table offset */
+ .word 0
+
+overlay:
+ /* Overlay number */
+ .word 0
+
+ .align 16, 0
+
+ .globl _exe_start
+_exe_start:
+ /* Install iPXE. Use a fixed temporary decompression area to
+ * avoid trashing the DOS high memory area.
+ */
+ call alloc_basemem
+ xorl %esi, %esi
+ movl $EXE_DECOMPRESS_ADDRESS, %edi
+ orl $0xffffffff, %ebp /* Allow arbitrary relocation */
+ 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:
+ /* Terminate command line with a NUL */
+ movzbw PSP_CMDLINE_LEN, %si
+ movb $0, PSP_CMDLINE_START(%si)
+
+ /* Calculate command line physical address */
+ xorl %esi, %esi
+ movw %ds, %si
+ shll $4, %esi
+ addl $PSP_CMDLINE_START, %esi
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+ /* Record command line address */
+ movl %esi, cmdline_phys
+
+ /* Run iPXE */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Exit back to DOS. This is very unlikely to work */
+ movw $0x4c00, %ax
+ int $0x21
diff --git a/roms/ipxe/src/arch/i386/prefix/hdprefix.S b/roms/ipxe/src/arch/i386/prefix/hdprefix.S
new file mode 100644
index 000000000..1d012d80b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/hdprefix.S
@@ -0,0 +1,111 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+ .globl _hd_start
+_hd_start:
+
+ 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 0
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDL"
+ .long load_length
+ .long 512
+ .long 0
+ .previous
+
+
+load_failed:
+ movw $10f, %si
+ jmp boot_error
+10: .asciz "Could not load iPXE\r\n"
+
+ .org 510
+ .byte 0x55, 0xaa
+
+start_image:
+ /* Install iPXE */
+ 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 %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Boot next device */
+ int $0x18
diff --git a/roms/ipxe/src/arch/i386/prefix/isaromprefix.S b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S
new file mode 100644
index 000000000..fb49819ee
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define BUSTYPE "ISAR"
+#define _rom_start _isarom_start
+#include "romprefix.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S
new file mode 100644
index 000000000..6e43cd26a
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S
@@ -0,0 +1,17 @@
+/*****************************************************************************
+ * PXE prefix that keeps the whole PXE stack present and provides an exit hook
+ *
+ * This prefix is essentially intended solely for the case of ipxelinux.0
+ *****************************************************************************
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/* Provide the PXENV_FILE_EXIT_HOOK API call */
+REQUIRING_SYMBOL ( _kkkpxe_start )
+REQUIRE_OBJECT ( pxe_exit_hook )
+
+#define PXELOADER_KEEP_UNDI
+#define PXELOADER_KEEP_PXE
+#define _pxe_start _kkkpxe_start
+#include "pxeprefix.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S
new file mode 100644
index 000000000..3c17dbdb1
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S
@@ -0,0 +1,11 @@
+/*****************************************************************************
+ * PXE prefix that keeps the whole PXE stack present
+ *****************************************************************************
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define PXELOADER_KEEP_UNDI
+#define PXELOADER_KEEP_PXE
+#define _pxe_start _kkpxe_start
+#include "pxeprefix.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S
new file mode 100644
index 000000000..200006d83
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S
@@ -0,0 +1,10 @@
+/*****************************************************************************
+ * PXE prefix that keep the UNDI portion of the PXE stack present
+ *****************************************************************************
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define PXELOADER_KEEP_UNDI
+#define _pxe_start _kpxe_start
+#include "pxeprefix.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/libprefix.S b/roms/ipxe/src/arch/i386/prefix/libprefix.S
new file mode 100644
index 000000000..7d5c1ed53
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/libprefix.S
@@ -0,0 +1,1016 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .arch i386
+
+/* Image compression enabled */
+#define COMPRESS 1
+
+/* Protected mode flag */
+#define CR0_PE 1
+
+/* Allow for DBG()-style messages within libprefix */
+#ifdef NDEBUG
+ .macro progress message
+ .endm
+#else
+ .macro progress message
+ pushfl
+ pushw %ds
+ pushw %si
+ pushw %di
+ pushw %cs
+ popw %ds
+ xorw %di, %di
+ movw $progress_\@, %si
+ call print_message
+ popw %di
+ popw %si
+ popw %ds
+ popfl
+ .section ".prefix.data", "aw", @progbits
+progress_\@:
+ .asciz "\message"
+ .size progress_\@, . - progress_\@
+ .previous
+ .endm
+#endif
+
+/*****************************************************************************
+ * Utility function: print character (with LF -> LF,CR translation)
+ *
+ * Parameters:
+ * %al : character to print
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl print_character
+print_character:
+ /* Preserve registers */
+ pushw %ax
+ pushw %bx
+ pushw %bp
+ /* If %di is non-zero, write character to buffer and exit */
+ testw %di, %di
+ jz 1f
+ movb %al, %ds:(%di)
+ incw %di
+ jmp 3f
+1: /* Print character */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ cmpb $0x0a, %al /* '\n'? */
+ jne 2f
+ int $0x10
+ movb $0x0d, %al
+2: int $0x10
+ /* Restore registers and return */
+3: popw %bp
+ popw %bx
+ popw %ax
+ ret
+ .size print_character, . - print_character
+
+/*****************************************************************************
+ * Utility function: print space
+ *
+ * Parameters:
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl print_space
+print_space:
+ /* Preserve registers */
+ pushw %ax
+ /* Print space */
+ movb $( ' ' ), %al
+ call print_character
+ /* Restore registers and return */
+ popw %ax
+ ret
+ .size print_space, . - print_space
+
+/*****************************************************************************
+ * Utility function: print a NUL-terminated string
+ *
+ * Parameters:
+ * %ds:si : string to print
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:si : character after terminating NUL
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .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
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .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
+
+/*****************************************************************************
+ * Utility function: print PCI bus:dev.fn
+ *
+ * Parameters:
+ * %ax : PCI bus:dev.fn to print
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl print_pci_busdevfn
+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
+ .size print_pci_busdevfn, . - print_pci_busdevfn
+
+/*****************************************************************************
+ * Utility function: clear current line
+ *
+ * Parameters:
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl print_kill_line
+print_kill_line:
+ /* Preserve registers */
+ pushw %ax
+ pushw %cx
+ /* Print CR */
+ movb $( '\r' ), %al
+ call print_character
+ /* Print 79 spaces */
+ movw $79, %cx
+1: call print_space
+ loop 1b
+ /* Print CR */
+ call print_character
+ /* Restore registers and return */
+ popw %cx
+ popw %ax
+ ret
+ .size print_kill_line, . - print_kill_line
+
+/****************************************************************************
+ * copy_bytes
+ *
+ * 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", "awx", @progbits
+ .code16
+copy_bytes:
+ pushl %ecx
+ rep addr32 movsb
+ popl %ecx
+ ret
+ .size copy_bytes, . - copy_bytes
+
+/****************************************************************************
+ * zero_bytes
+ *
+ * Zero bytes
+ *
+ * Parameters:
+ * %es:edi : destination address
+ * %ecx : length
+ * Returns:
+ * %es:edi : next destination address
+ * Corrupts:
+ * None
+ ****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+zero_bytes:
+ pushl %ecx
+ pushw %ax
+ xorw %ax, %ax
+ rep addr32 stosb
+ popw %ax
+ popl %ecx
+ ret
+ .size zero_bytes, . - zero_bytes
+
+/****************************************************************************
+ * process_bytes
+ *
+ * Call memcpy()-like function
+ *
+ * Parameters:
+ * %esi : source physical address
+ * %edi : destination physical address
+ * %ecx : length
+ * %bx : memcpy()-like function to call, passing parameters:
+ * %ds:esi : source address
+ * %es:edi : destination address
+ * %ecx : length
+ * and returning:
+ * %ds:esi : next source address
+ * %es:edi : next destination address
+ * Returns:
+ * %esi : next source physical address
+ * %edi : next destination physical address
+ * Corrupts:
+ * None
+ ****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+process_bytes:
+
+#ifndef KEEP_IT_REAL
+
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebp
+
+ /* Construct GDT on stack (since .prefix may not be writable) */
+ .equ PM_DS, 0x18 /* Flat data segment */
+ pushl $0x00cf9300
+ pushl $0x0000ffff
+ .equ PM_SS, 0x10 /* Stack segment based at %ss:0000 */
+ pushl $0x008f0930
+ pushw %ss
+ pushw $0xffff
+ .equ PM_CS, 0x08 /* Code segment based at %cs:0000 */
+ pushl $0x008f09b0
+ pushw %cs
+ pushw $0xffff
+ pushl $0 /* Base and length */
+ pushw %ss
+ pushw $0x1f
+ movzwl %sp, %ebp
+ shll $4, 0x02(%bp)
+ addl %ebp, 0x02(%bp)
+ shll $4, 0x0a(%bp)
+ shll $4, 0x12(%bp)
+ subw $8, %sp
+ sgdt -8(%bp)
+
+ /* Switch to protected mode */
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ pushw $2f
+ cli
+ data32 lgdt (%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
+
+#ifdef NDEBUG
+ /* Call memcpy()-like function */
+ call *%bx
+#endif
+
+ /* Return to (flat) real mode */
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+ lret
+2: /* lret will ljmp to here */
+ popw %ss
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+
+#ifndef NDEBUG
+ /* Call memcpy()-like function in flat real mode (to allow for
+ * debug output via INT 10).
+ */
+ pushw %ds
+ pushw %es
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ call *%bx
+ popw %es
+ popw %ds
+#endif
+
+ /* Restore GDT */
+ data32 lgdt -8(%bp)
+ addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp
+
+ /* Restore registers and return */
+ popl %ebp
+ popl %eax
+ ret
+
+#else /* KEEP_IT_REAL */
+
+ /* Preserve registers */
+ pushl %eax
+ pushw %ds
+ pushw %es
+
+ /* Convert %esi and %edi to %ds:esi and %es:edi */
+ shrl $4, %esi
+ movw %si, %ds
+ xorw %si, %si
+ shll $4, %esi
+ shrl $4, %edi
+ movw %di, %es
+ xorw %di, %di
+ shll $4, %edi
+
+ /* Call memcpy()-like function */
+ call *%bx
+
+ /* Convert %ds:esi and %es:edi back to physical addresses */
+ xorl %eax, %eax
+ movw %ds, %ax
+ shll $4, %eax
+ addl %eax, %esi
+ xorl %eax, %eax
+ movw %es, %ax
+ shll $4, %eax
+ addl %eax, %edi
+
+ /* Restore registers and return */
+ popw %es
+ popw %ds
+ popl %eax
+ ret
+
+#endif /* KEEP_IT_REAL */
+
+ .size process_bytes, . - process_bytes
+
+/****************************************************************************
+ * install_block
+ *
+ * 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)
+ * %edi : next destination physical address (will be a multiple of 16)
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+install_block:
+ /* Preserve registers */
+ pushl %ecx
+ pushw %bx
+
+ /* Decompress (or copy) source to destination */
+#if COMPRESS
+ movw $decompress16, %bx
+#else
+ movw $copy_bytes, %bx
+#endif
+ call process_bytes
+
+ /* Zero .bss portion */
+ negl %ecx
+ addl %edx, %ecx
+ movw $zero_bytes, %bx
+ call process_bytes
+
+ /* Round up %esi and %edi to start of next blocks */
+ addl $0xf, %esi
+ andl $~0xf, %esi
+ addl $0xf, %edi
+ andl $~0xf, %edi
+
+ /* Restore registers and return */
+ popw %bx
+ popl %ecx
+ ret
+ .size install_block, . - install_block
+
+/****************************************************************************
+ * alloc_basemem
+ *
+ * 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", "awx", @progbits
+ .code16
+ .globl alloc_basemem
+alloc_basemem:
+ /* Preserve registers */
+ pushw %fs
+
+ /* FBMS => %ax as segment address */
+ pushw $0x40
+ popw %fs
+ movw %fs:0x13, %ax
+ shlw $6, %ax
+
+ /* Calculate .data16 segment address */
+ subw $_data16_memsz_pgh, %ax
+ pushw %ax
+
+ /* Calculate .text16 segment address. Round down to ensure
+ * low bits are zero, to speed up mode transitions under KVM.
+ */
+ subw $_text16_memsz_pgh, %ax
+ andb $~0x03, %al
+ pushw %ax
+
+ /* Update FBMS */
+ shrw $6, %ax
+ movw %ax, %fs:0x13
+
+ /* Retrieve .text16 and .data16 segment addresses */
+ popw %ax
+ popw %bx
+
+ /* Restore registers and return */
+ popw %fs
+ ret
+ .size alloc_basemem, . - alloc_basemem
+
+/****************************************************************************
+ * free_basemem
+ *
+ * Free space allocated with alloc_basemem.
+ *
+ * Parameters:
+ * none (.text16 segment address is implicit in %cs)
+ * Returns:
+ * %ax : 0 if successfully freed
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl free_basemem
+free_basemem:
+ /* Preserve registers */
+ pushw %fs
+ pushw %ax
+
+ /* Check FBMS counter */
+ movw %cs, %ax
+ shrw $6, %ax
+ pushw $0x40
+ popw %fs
+ cmpw %ax, %fs:0x13
+ jne 1f
+
+ /* Check hooked interrupt count */
+ cmpw $0, %cs:hooked_bios_interrupts
+ jne 1f
+
+ /* OK to free memory */
+ movw %cs, %ax
+ addw $_text16_memsz_pgh, %ax
+ addw $_data16_memsz_pgh, %ax
+ shrw $6, %ax
+ movw %ax, %fs:0x13
+ xorw %ax, %ax
+
+1: /* Restore registers and return */
+ popw %ax
+ popw %fs
+ ret
+ .size free_basemem, . - free_basemem
+
+ .section ".text16.data", "aw", @progbits
+ .globl hooked_bios_interrupts
+hooked_bios_interrupts:
+ .word 0
+ .size hooked_bios_interrupts, . - hooked_bios_interrupts
+
+/****************************************************************************
+ * install
+ *
+ * Install all text and data segments.
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl install
+install:
+ progress "install:\n"
+ /* Preserve registers */
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ /* Allocate space for .text16 and .data16 */
+ call alloc_basemem
+ /* Image source = %cs:0000 */
+ xorl %esi, %esi
+ /* Image destination = default */
+ xorl %edi, %edi
+ /* Allow arbitrary relocation */
+ orl $0xffffffff, %ebp
+ /* Install text and data segments */
+ call install_prealloc
+ /* Restore registers and return */
+ popl %ebp
+ popl %edi
+ popl %esi
+ ret
+ .size install, . - install
+
+/****************************************************************************
+ * install_prealloc
+ *
+ * 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 (or zero for default)
+ * %ebp : Maximum end address for relocation
+ * - 0xffffffff for no maximum
+ * - 0x00000000 to inhibit use of INT 15,e820 and INT 15,e801
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib", "awx", @progbits
+ .code16
+ .globl install_prealloc
+install_prealloc:
+ progress "install_prealloc:\n"
+ /* Save registers on external stack */
+ pushal
+ pushw %ds
+ pushw %es
+ cld /* Sanity: clear the direction flag asap */
+
+ /* Switch to temporary stack in .bss16 */
+ pushw %ss
+ popw %ds
+ movl %esp, %ecx
+ movw %bx, %ss
+ movl $_data16_memsz, %esp
+ pushw %ds
+ pushl %ecx
+
+ /* Set up %ds for (read-only) access to .prefix */
+ pushw %cs
+ popw %ds
+
+ /* Save decompression temporary area physical address */
+ pushl %edi
+
+ /* Install .text16.early and calculate %ecx as offset to next block */
+ progress " .text16.early\n"
+ pushl %esi
+ xorl %esi, %esi
+ movw %cs, %si
+ shll $4, %esi
+ pushl %esi /* Save original %cs:0000 */
+ addl $_text16_early_lma, %esi
+ movzwl %ax, %edi
+ shll $4, %edi
+ movl $_text16_early_filesz, %ecx
+ movl $_text16_early_memsz, %edx
+ call install_block /* .text16.early */
+ popl %ecx /* Calculate offset to next block */
+ subl %esi, %ecx
+ negl %ecx
+ popl %esi
+
+#ifndef KEEP_IT_REAL
+
+ /* Access high memory by enabling the A20 gate. (We will
+ * already have 4GB segment limits as a result of calling
+ * install_block.)
+ */
+ progress " access_highmem\n"
+ pushw %cs
+ pushw $1f
+ pushw %ax
+ pushw $access_highmem
+ lret
+1: /* Die if we could not access high memory */
+ jnc 3f
+ movw $a20_death_message, %si
+ xorw %di, %di
+ call print_message
+2: jmp 2b
+ .section ".prefix.data", "aw", @progbits
+a20_death_message:
+ .asciz "\nHigh memory inaccessible - cannot continue\n"
+ .size a20_death_message, . - a20_death_message
+ .previous
+3:
+#endif
+
+ /* Open payload (which may not yet be in memory) */
+ progress " open_payload\n"
+ pushw %cs
+ pushw $1f
+ pushw %ax
+ pushw $open_payload
+ lret
+1: /* Die if we could not access the payload */
+ jnc 3f
+ xorw %di, %di
+ movl %esi, %eax
+ call print_hex_dword
+ call print_space
+ movl %ecx, %eax
+ call print_hex_dword
+ movw $payload_death_message, %si
+ call print_message
+2: /* Halt system */
+ cli
+ hlt
+ jmp 2b
+ .section ".prefix.data", "aw", @progbits
+payload_death_message:
+ .asciz "\nPayload inaccessible - cannot continue\n"
+ .size payload_death_message, . - payload_death_message
+ .previous
+3:
+
+ /* Calculate physical address of payload (i.e. first source) */
+ testl %esi, %esi
+ jnz 1f
+ movw %cs, %si
+ shll $4, %esi
+1: addl %ecx, %esi
+
+ /* Install .text16.late and .data16 */
+ progress " .text16.late\n"
+ movl $_text16_late_filesz, %ecx
+ movl $_text16_late_memsz, %edx
+ call install_block /* .text16.late */
+ progress " .data16\n"
+ movzwl %bx, %edi
+ shll $4, %edi
+ movl $_data16_filesz, %ecx
+ movl $_data16_filesz, %edx /* do not zero our temporary stack */
+ call install_block /* .data16 */
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+ /* Restore decompression temporary area physical address */
+ popl %edi
+
+#ifndef KEEP_IT_REAL
+
+ /* Find a suitable decompression temporary area, if none specified */
+ pushl %eax
+ testl %edi, %edi
+ jnz 1f
+ /* Use INT 15,88 to find the highest available address via INT
+ * 15,88. This limits us to around 64MB, which should avoid
+ * all of the POST-time memory map failure modes.
+ */
+ movb $0x88, %ah
+ int $0x15
+ movw %ax, %di
+ addl $0x400, %edi
+ subl $_textdata_memsz_kb, %edi
+ shll $10, %edi
+ /* Sanity check: if we have ended up below 1MB, use 1MB */
+ cmpl $0x100000, %edi
+ jae 1f
+ movl $0x100000, %edi
+1: popl %eax
+
+ /* Install .text and .data to temporary area in high memory,
+ * prior to reading the E820 memory map and relocating
+ * properly.
+ */
+ progress " .textdata\n"
+ pushl %edi
+ movl $_textdata_filesz, %ecx
+ movl $_textdata_memsz, %edx
+ call install_block
+ popl %edi
+
+#endif /* KEEP_IT_REAL */
+
+ /* Switch back to original stack and zero .bss16 */
+ addr32 lss %ss:(%esp), %esp
+ pushl %edi
+ pushw %es
+ movw %bx, %es
+ movl $_data16_filesz, %edi
+ movl $_data16_memsz, %ecx
+ subl %edi, %ecx
+ call zero_bytes
+ popw %es
+ popl %edi
+
+#ifndef KEEP_IT_REAL
+
+ /* Initialise librm at current location */
+ progress " init_librm\n"
+ movw %ax, (init_librm_vector+2)
+ lcall *init_librm_vector
+
+ /* Inhibit INT 15,e820 and INT 15,e801 if applicable */
+ testl %ebp, %ebp
+ jnz 1f
+ incb memmap_post
+ decl %ebp
+1:
+ /* 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.
+ */
+ progress " relocate\n"
+ movw %ax, (prot_call_vector+2)
+ pushl $relocate
+ lcall *prot_call_vector
+ popl %edx /* discard */
+
+ /* Copy code to new location */
+ progress " copy\n"
+ pushl %edi
+ pushw %bx
+ movw $copy_bytes, %bx
+ call process_bytes
+ popw %bx
+ popl %edi
+
+ /* Initialise librm at new location */
+ progress " init_librm\n"
+ lcall *init_librm_vector
+
+#else /* KEEP_IT_REAL */
+
+ /* Initialise libkir */
+ movw %ax, (init_libkir_vector+2)
+ lcall *init_libkir_vector
+
+#endif /* KEEP_IT_REAL */
+
+ /* Close access to payload */
+ progress " close_payload\n"
+ movw %ax, (close_payload_vector+2)
+ lcall *close_payload_vector
+
+ /* Restore registers */
+ popw %es
+ popw %ds
+ popal
+ ret
+ .size install_prealloc, . - install_prealloc
+
+ /* Vectors for far calls to .text16 functions. Must be in
+ * .data16, since .prefix may not be writable.
+ */
+ .section ".data16", "aw", @progbits
+#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
+close_payload_vector:
+ .word close_payload
+ .word 0
+ .size close_payload_vector, . - close_payload_vector
+
+ /* Dummy routines to open and close payload */
+ .section ".text16.early.data", "aw", @progbits
+ .weak open_payload
+ .weak close_payload
+open_payload:
+close_payload:
+ clc
+ lret
+ .size open_payload, . - open_payload
+ .size close_payload, . - close_payload
+
+/****************************************************************************
+ * uninstall
+ *
+ * Uninstall all text and data segments.
+ *
+ * Parameters:
+ * none (.text16 segment address is implicit in %cs)
+ * Returns:
+ * none
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl uninstall
+uninstall:
+ call free_basemem
+ ret
+ .size uninstall, . - uninstall
+
+
+
+ /* File split information for the compressor */
+#if COMPRESS
+#define PACK_OR_COPY "PACK"
+#else
+#define PACK_OR_COPY "COPY"
+#endif
+ .section ".zinfo", "a", @progbits
+ .ascii "COPY"
+ .long _prefix_lma
+ .long _prefix_filesz
+ .long _max_align
+ .ascii PACK_OR_COPY
+ .long _text16_early_lma
+ .long _text16_early_filesz
+ .long _max_align
+ .ascii "PAYL"
+ .long 0
+ .long 0
+ .long _payload_align
+ .ascii "COPY"
+ .long _pprefix_lma
+ .long _pprefix_filesz
+ .long _max_align
+ .ascii PACK_OR_COPY
+ .long _text16_late_lma
+ .long _text16_late_filesz
+ .long _max_align
+ .ascii PACK_OR_COPY
+ .long _data16_lma
+ .long _data16_filesz
+ .long _max_align
+ .ascii PACK_OR_COPY
+ .long _textdata_lma
+ .long _textdata_filesz
+ .long _max_align
+
+ .weak _payload_align
+ .equ _payload_align, 1
diff --git a/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S
new file mode 100644
index 000000000..64135e14b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S
@@ -0,0 +1,236 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define BZI_LOAD_HIGH_ADDR 0x100000
+
+ .text
+ .arch i386
+ .code16
+ .section ".prefix", "ax", @progbits
+ .globl _lkrn_start
+_lkrn_start:
+
+/*****************************************************************************
+ *
+ * Kernel header
+ *
+ * We place our prefix (i.e. our .prefix and .text16.early sections)
+ * within the bzImage real-mode portion which gets loaded at
+ * 1000:0000, and our payload (i.e. everything else) within the
+ * bzImage protected-mode portion which gets loaded at 0x100000
+ * upwards.
+ *
+ */
+
+ .org 0x1f1
+setup_sects:
+ .byte -1 /* Allow for initial "boot sector" */
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADHL"
+ .long setup_sects
+ .long 512
+ .long 0
+ .previous
+root_flags:
+ .word 0
+syssize:
+ .long 0
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADPL"
+ .long syssize
+ .long 16
+ .long 0
+ .previous
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xaa55
+jump:
+ /* Manually specify a two-byte jmp instruction here rather
+ * than leaving it up to the assembler.
+ */
+ .byte 0xeb, ( setup - header )
+header:
+ .byte 'H', 'd', 'r', 'S'
+version:
+ .word 0x0207 /* 2.07 */
+realmode_swtch:
+ .long 0
+start_sys:
+ .word 0
+kernel_version:
+ .word version_string - 0x200
+type_of_loader:
+ .byte 0
+loadflags:
+ .byte 0x01 /* LOADED_HIGH */
+setup_move_size:
+ .word 0
+code32_start:
+ .long 0
+ramdisk_image:
+ .long 0
+ramdisk_size:
+ .long 0
+bootsect_kludge:
+ .long 0
+heap_end_ptr:
+ .word 0
+ext_loader_ver:
+ .byte 0
+ext_loader_type:
+ .byte 0
+cmd_line_ptr:
+ .long 0
+initrd_addr_max:
+ .long 0xffffffff
+kernel_alignment:
+ .long 0
+relocatable_kernel:
+ .byte 0
+min_alignment:
+ .byte 0
+xloadflags:
+ .word 0
+cmdline_size:
+ .long 0x7ff
+hardware_subarch:
+ .long 0
+hardware_subarch_data:
+ .byte 0, 0, 0, 0, 0, 0, 0, 0
+
+version_string:
+ .asciz VERSION
+
+/*****************************************************************************
+ *
+ * Setup code
+ *
+ */
+
+setup:
+ /* Fix up code segment */
+ pushw %ds
+ pushw $1f
+ lret
+1:
+ /* Set up stack just below 0x7c00 and clear direction flag */
+ xorw %ax, %ax
+ movw %ax, %ss
+ movw $0x7c00, %sp
+ cld
+
+ /* Retrieve command-line pointer */
+ movl cmd_line_ptr, %edx
+ testl %edx, %edx
+ jz no_cmd_line
+
+ /* Set up %es:%di to point to command line */
+ movl %edx, %edi
+ andl $0xf, %edi
+ rorl $4, %edx
+ movw %dx, %es
+
+ /* Find length of command line */
+ pushw %di
+ movw $0xffff, %cx
+ repnz scasb
+ notw %cx
+ popw %si
+
+ /* Make space for command line on stack */
+ movw %sp, %di
+ subw %cx, %di
+ andw $~0xf, %di
+ movw %di, %sp
+
+ /* Copy command line to stack */
+ pushw %ds
+ pushw %es
+ popw %ds
+ pushw %ss
+ popw %es
+ rep movsb
+ popw %ds
+
+ /* Store new command-line pointer */
+ movzwl %sp, %edx
+no_cmd_line:
+
+ /* Calculate maximum relocation address */
+ movl ramdisk_image, %ebp
+ testl %ebp, %ebp
+ jnz 1f
+ orl $0xffffffff, %ebp /* Allow arbitrary relocation if no initrd */
+1:
+ /* Install iPXE */
+ call alloc_basemem
+ xorl %esi, %esi
+ xorl %edi, %edi
+ 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:
+ /* Retrieve initrd pointer and size */
+ movl ramdisk_image, %ebp
+ movl ramdisk_size, %ecx
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+ /* Store command-line pointer */
+ movl %edx, cmdline_phys
+
+ /* Store initrd pointer and size */
+ movl %ebp, initrd_phys
+ movl %ecx, initrd_len
+
+ /* Run iPXE */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Boot next device */
+ int $0x18
+
+/*****************************************************************************
+ *
+ * Open payload (called by libprefix)
+ *
+ * Parameters:
+ * %ds:0000 : Prefix
+ * %esi : Buffer for copy of image source (or zero if no buffer available)
+ * %ecx : Expected offset within buffer of first payload block
+ * Returns:
+ * %esi : Valid image source address (buffered or unbuffered)
+ * %ecx : Actual offset within buffer of first payload block
+ * CF set on error
+ */
+
+ .section ".text16.early", "awx", @progbits
+ .globl open_payload
+open_payload:
+
+ /* Our payload will always end up at BZI_LOAD_HIGH_ADDR */
+ movl $BZI_LOAD_HIGH_ADDR, %esi
+ xorl %ecx, %ecx
+ lret
+
+ /* Payload must be aligned to a whole number of setup sectors */
+ .globl _payload_align
+ .equ _payload_align, 512
diff --git a/roms/ipxe/src/arch/i386/prefix/mbr.S b/roms/ipxe/src/arch/i386/prefix/mbr.S
new file mode 100644
index 000000000..a1e237de8
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/mbr.S
@@ -0,0 +1,15 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .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/roms/ipxe/src/arch/i386/prefix/mromprefix.S b/roms/ipxe/src/arch/i386/prefix/mromprefix.S
new file mode 100644
index 000000000..b636b92af
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/mromprefix.S
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define PCIBIOS_READ_CONFIG_WORD 0xb109
+#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
+#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
+#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
+#define PCI_COMMAND 0x04
+#define PCI_COMMAND_MEM 0x02
+#define PCI_BAR_0 0x10
+#define PCI_BAR_5 0x24
+#define PCI_BAR_EXPROM 0x30
+
+#define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
+
+#define ROMPREFIX_EXCLUDE_PAYLOAD 1
+#define ROMPREFIX_MORE_IMAGES 1
+#define _pcirom_start _mrom_start
+#include "pciromprefix.S"
+
+ .text
+ .arch i386
+ .code16
+
+/* Obtain access to payload by exposing the expansion ROM BAR at the
+ * address currently used by a suitably large memory BAR on the same
+ * device. The memory BAR is temporarily disabled. Using a memory
+ * BAR on the same device means that we don't have to worry about the
+ * configuration of any intermediate PCI bridges.
+ *
+ * Parameters:
+ * %ds:0000 : Prefix
+ * %esi : Buffer for copy of image source (or zero if no buffer available)
+ * %ecx : Expected offset within buffer of first payload block
+ * Returns:
+ * %esi : Valid image source address (buffered or unbuffered)
+ * %ecx : Actual offset within buffer of first payload block
+ * CF set on error
+ */
+ .section ".text16.early", "awx", @progbits
+ .globl open_payload
+open_payload:
+ /* Preserve registers */
+ pushl %eax
+ pushw %bx
+ pushl %edx
+ pushl %edi
+ pushw %bp
+ pushw %es
+ pushw %ds
+
+ /* Retrieve bus:dev.fn from .prefix */
+ movw init_pci_busdevfn, %bx
+
+ /* Set up %ds for access to .text16.early */
+ pushw %cs
+ popw %ds
+
+ /* Set up %es for access to flat address space */
+ xorw %ax, %ax
+ movw %ax, %es
+
+ /* Store bus:dev.fn to .text16.early */
+ movw %bx, payload_pci_busdevfn
+
+ /* Get expansion ROM BAR current value */
+ movw $PCI_BAR_EXPROM, %di
+ call pci_read_bar
+ movl %eax, rom_bar_orig_value
+
+ /* Get expansion ROM BAR size */
+ call pci_size_mem_bar_low
+ movl %ecx, rom_bar_size
+
+ /* Find a suitable memory BAR to use */
+ movw $PCI_BAR_0, %di /* %di is PCI BAR register */
+ xorw %bp, %bp /* %bp is increment */
+find_mem_bar:
+ /* Move to next BAR */
+ addw %bp, %di
+ cmpw $PCI_BAR_5, %di
+ jle 1f
+ stc
+ movl $0xbabababa, %esi /* Report "No suitable BAR" */
+ movl rom_bar_size, %ecx
+ jmp 99f
+1: movw $4, %bp
+
+ /* Get BAR current value */
+ call pci_read_bar
+
+ /* Skip non-existent BARs */
+ notl %eax
+ testl %eax, %eax
+ notl %eax
+ jz find_mem_bar
+
+ /* Skip I/O BARs */
+ testb $0x01, %al
+ jnz find_mem_bar
+
+ /* Set increment to 8 for 64-bit BARs */
+ testb $0x04, %al
+ jz 1f
+ movw $8, %bp
+1:
+ /* Skip 64-bit BARs with high dword set; we couldn't use this
+ * address for the (32-bit) expansion ROM BAR anyway
+ */
+ testl %edx, %edx
+ jnz find_mem_bar
+
+ /* Get low dword of BAR size */
+ call pci_size_mem_bar_low
+
+ /* Skip BARs smaller than the expansion ROM BAR */
+ cmpl %ecx, rom_bar_size
+ ja find_mem_bar
+
+ /* We have a memory BAR with a 32-bit address that is large
+ * enough to use. Store BAR number and original value.
+ */
+ movw %di, stolen_bar_register
+ movl %eax, stolen_bar_orig_value
+
+ /* Remove flags from BAR address */
+ xorb %al, %al
+
+ /* Write zero to our stolen BAR. This doesn't technically
+ * disable it, but it's a pretty safe bet that the PCI bridge
+ * won't pass through accesses to this region anyway. Note
+ * that the high dword (if any) must already be zero.
+ */
+ xorl %ecx, %ecx
+ call pci_write_config_dword
+
+ /* Enable expansion ROM BAR at stolen BAR's address */
+ movl %eax, %ecx
+ orb $0x1, %cl
+ movw $PCI_BAR_EXPROM, %di
+ call pci_write_config_dword
+
+ /* Locate our ROM image */
+1: movl $0xaa55, %ecx /* 55aa signature */
+ addr32 es cmpw %cx, (%eax)
+ jne 2f
+ movl $PCIR_SIGNATURE, %ecx /* PCIR signature */
+ addr32 es movzwl 0x18(%eax), %edx
+ addr32 es cmpl %ecx, (%eax,%edx)
+ jne 2f
+ addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */
+ je 3f
+ movl $0x80, %ecx /* Last image */
+ addr32 es testb %cl, 0x15(%eax,%edx)
+ jnz 2f
+ addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */
+ shll $9, %ecx
+ addl %ecx, %eax
+ jmp 1b
+2: /* Failure */
+ stc
+ movl %eax, %esi /* Report failure address */
+ jmp 99f
+3:
+
+ /* Copy payload to buffer, or set buffer address to BAR address */
+ testl %esi, %esi
+ jz 1f
+ /* We have a buffer; copy payload to it. Since .mrom is
+ * designed specifically for real hardware, we assume that
+ * flat real mode is working properly. (In the unlikely event
+ * that this code is run inside a hypervisor that doesn't
+ * properly support flat real mode, it will die horribly.)
+ */
+ pushl %esi
+ movl %esi, %edi
+ movl %eax, %esi
+ addr32 es movzbl 2(%esi), %ecx
+ shll $7, %ecx
+ addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx
+ shll $7, %edx
+ addl %edx, %ecx
+ addr32 es rep movsl
+ popl %esi
+ jmp 2f
+1: /* We have no buffer; set %esi to the BAR address */
+ movl %eax, %esi
+2:
+
+ /* Locate first payload block (after the dummy ROM header) */
+ addr32 es movzbl 2(%esi), %ecx
+ shll $9, %ecx
+ addl $_pprefix_skip, %ecx
+
+ clc
+ /* Restore registers and return */
+99: popw %ds
+ popw %es
+ popw %bp
+ popl %edi
+ popl %edx
+ popw %bx
+ popl %eax
+ lret
+ .size open_payload, . - open_payload
+
+ .section ".text16.early.data", "aw", @progbits
+payload_pci_busdevfn:
+ .word 0
+ .size payload_pci_busdevfn, . - payload_pci_busdevfn
+
+ .section ".text16.early.data", "aw", @progbits
+rom_bar_orig_value:
+ .long 0
+ .size rom_bar_orig_value, . - rom_bar_orig_value
+
+ .section ".text16.early.data", "aw", @progbits
+rom_bar_size:
+ .long 0
+ .size rom_bar_size, . - rom_bar_size
+
+ .section ".text16.early.data", "aw", @progbits
+stolen_bar_register:
+ .word 0
+ .size stolen_bar_register, . - stolen_bar_register
+
+ .section ".text16.early.data", "aw", @progbits
+stolen_bar_orig_value:
+ .long 0
+ .size stolen_bar_orig_value, . - stolen_bar_orig_value
+
+/* Restore original BAR values
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * none
+ */
+ .section ".text16.early", "awx", @progbits
+ .globl close_payload
+close_payload:
+ /* Preserve registers */
+ pushw %bx
+ pushw %di
+ pushl %ecx
+ pushw %ds
+
+ /* Set up %ds for access to .text16.early */
+ pushw %cs
+ popw %ds
+
+ /* Retrieve stored bus:dev.fn */
+ movw payload_pci_busdevfn, %bx
+
+ /* Restore expansion ROM BAR original value */
+ movw $PCI_BAR_EXPROM, %di
+ movl rom_bar_orig_value, %ecx
+ call pci_write_config_dword
+
+ /* Restore stolen BAR original value */
+ movw stolen_bar_register, %di
+ movl stolen_bar_orig_value, %ecx
+ call pci_write_config_dword
+
+ /* Restore registers and return */
+ popw %ds
+ popl %ecx
+ popw %di
+ popw %bx
+ lret
+ .size close_payload, . - close_payload
+
+/* Get PCI BAR value
+ *
+ * Parameters:
+ * %bx : PCI bus:dev.fn
+ * %di : PCI BAR register number
+ * Returns:
+ * %edx:%eax : PCI BAR value
+ */
+ .section ".text16.early", "awx", @progbits
+pci_read_bar:
+ /* Preserve registers */
+ pushl %ecx
+ pushw %di
+
+ /* Read low dword value */
+ call pci_read_config_dword
+ movl %ecx, %eax
+
+ /* Read high dword value, if applicable */
+ xorl %edx, %edx
+ andb $0x07, %cl
+ cmpb $0x04, %cl
+ jne 1f
+ addw $4, %di
+ call pci_read_config_dword
+ movl %ecx, %edx
+1:
+ /* Restore registers and return */
+ popw %di
+ popl %ecx
+ ret
+ .size pci_read_bar, . - pci_read_bar
+
+/* Get low dword of PCI memory BAR size
+ *
+ * Parameters:
+ * %bx : PCI bus:dev.fn
+ * %di : PCI BAR register number
+ * %eax : Low dword of current PCI BAR value
+ * Returns:
+ * %ecx : PCI BAR size
+ */
+ .section ".text16.early", "awx", @progbits
+pci_size_mem_bar_low:
+ /* Preserve registers */
+ pushw %dx
+
+ /* Disable memory accesses */
+ xorw %dx, %dx
+ call pci_set_mem_access
+
+ /* Write all ones to BAR */
+ xorl %ecx, %ecx
+ decl %ecx
+ call pci_write_config_dword
+
+ /* Read back BAR */
+ call pci_read_config_dword
+
+ /* Calculate size */
+ notl %ecx
+ orb $0x0f, %cl
+ incl %ecx
+
+ /* Restore original value */
+ pushl %ecx
+ movl %eax, %ecx
+ call pci_write_config_dword
+ popl %ecx
+
+ /* Enable memory accesses */
+ movw $PCI_COMMAND_MEM, %dx
+ call pci_set_mem_access
+
+ /* Restore registers and return */
+ popw %dx
+ ret
+ .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
+
+/* Read PCI config dword
+ *
+ * Parameters:
+ * %bx : PCI bus:dev.fn
+ * %di : PCI register number
+ * Returns:
+ * %ecx : Dword value
+ */
+ .section ".text16.early", "awx", @progbits
+pci_read_config_dword:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebx
+ pushl %edx
+
+ /* Issue INT 0x1a,b10a */
+ movw $PCIBIOS_READ_CONFIG_DWORD, %ax
+ int $0x1a
+
+ /* Restore registers and return */
+ popl %edx
+ popl %ebx
+ popl %eax
+ ret
+ .size pci_read_config_dword, . - pci_read_config_dword
+
+/* Write PCI config dword
+ *
+ * Parameters:
+ * %bx : PCI bus:dev.fn
+ * %di : PCI register number
+ * %ecx : PCI BAR value
+ * Returns:
+ * none
+ */
+ .section ".text16.early", "awx", @progbits
+pci_write_config_dword:
+ /* Preserve registers */
+ pushal
+
+ /* Issue INT 0x1a,b10d */
+ movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
+ int $0x1a
+
+ /* Restore registers and return */
+ popal
+ ret
+ .size pci_write_config_dword, . - pci_write_config_dword
+
+/* Enable/disable memory access response in PCI command word
+ *
+ * Parameters:
+ * %bx : PCI bus:dev.fn
+ * %dx : PCI_COMMAND_MEM, or zero
+ * Returns:
+ * none
+ */
+ .section ".text16.early", "awx", @progbits
+pci_set_mem_access:
+ /* Preserve registers */
+ pushal
+
+ /* Read current value of command register */
+ pushw %bx
+ pushw %dx
+ movw $PCI_COMMAND, %di
+ movw $PCIBIOS_READ_CONFIG_WORD, %ax
+ int $0x1a
+ popw %dx
+ popw %bx
+
+ /* Set memory access enable as appropriate */
+ andw $~PCI_COMMAND_MEM, %cx
+ orw %dx, %cx
+
+ /* Write new value of command register */
+ movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
+ int $0x1a
+
+ /* Restore registers and return */
+ popal
+ ret
+ .size pci_set_mem_access, . - pci_set_mem_access
+
+/* Payload prefix
+ *
+ * We include a dummy ROM header to cover the "hidden" portion of the
+ * overall ROM image.
+ */
+ .globl _payload_align
+ .equ _payload_align, 512
+ .section ".pprefix", "ax", @progbits
+ .org 0x00
+mromheader:
+ .word 0xaa55 /* BIOS extension signature */
+ .byte 0x01 /* Dummy size (BIOS bug workaround) */
+ .org 0x18
+ .word mpciheader
+ .org 0x1a
+ .word 0
+ .size mromheader, . - mromheader
+
+mpciheader:
+ .ascii "PCIR" /* Signature */
+ .word pci_vendor_id /* Vendor identification */
+ .word pci_device_id /* Device identification */
+ .word 0x0000 /* Device list pointer */
+ .word mpciheader_len /* PCI data structure length */
+ .byte 0x03 /* PCI data structure revision */
+ .byte 0x02, 0x00, 0x00 /* Class code */
+mpciheader_image_length:
+ .word 0 /* Image length */
+ .word 0x0001 /* Revision level */
+ .byte 0xff /* Code type */
+ .byte 0x80 /* Last image indicator */
+mpciheader_runtime_length:
+ .word 0 /* Maximum run-time image length */
+ .word 0x0000 /* Configuration utility code header */
+ .word 0x0000 /* DMTF CLP entry point */
+ .equ mpciheader_len, . - mpciheader
+ .size mpciheader, . - mpciheader
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "APPW"
+ .long mpciheader_image_length
+ .long 512
+ .long 0
+ .ascii "APPW"
+ .long mpciheader_runtime_length
+ .long 512
+ .long 0
+ .previous
+
+/* Fix up additional image source size
+ *
+ */
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADPW"
+ .long extra_size
+ .long 512
+ .long 0
+ .previous
diff --git a/roms/ipxe/src/arch/i386/prefix/nbiprefix.S b/roms/ipxe/src/arch/i386/prefix/nbiprefix.S
new file mode 100644
index 000000000..16c79566c
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/nbiprefix.S
@@ -0,0 +1,84 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .arch i386
+ .code16
+ .section ".prefix", "ax", @progbits
+ .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 _nbi_start, 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 -512
+memlen: .long -512
+ .size segment_header, . - segment_header
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDL"
+ .long imglen
+ .long 1
+ .long 0
+ .ascii "ADDL"
+ .long memlen
+ .long 1
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * NBI entry point
+ *****************************************************************************
+ */
+ .globl _nbi_start
+_nbi_start:
+ /* Install iPXE */
+ 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 %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Reboot system */
+ int $0x19
+
+ .previous
+ .size _nbi_start, . - _nbi_start
+
+nbi_header_end:
+ .org 512
diff --git a/roms/ipxe/src/arch/i386/prefix/nullprefix.S b/roms/ipxe/src/arch/i386/prefix/nullprefix.S
new file mode 100644
index 000000000..bd0ff339e
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/nullprefix.S
@@ -0,0 +1,15 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .org 0
+ .text
+ .arch i386
+
+ .section ".prefix", "ax", @progbits
+ .code16
+_prefix:
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/roms/ipxe/src/arch/i386/prefix/pciromprefix.S b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S
new file mode 100644
index 000000000..5a5a49647
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define BUSTYPE "PCIR"
+#define _rom_start _pcirom_start
+#include "romprefix.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/pxeprefix.S b/roms/ipxe/src/arch/i386/prefix/pxeprefix.S
new file mode 100644
index 000000000..465ce4345
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/pxeprefix.S
@@ -0,0 +1,862 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+#define PXENV_STOP_UNDI 0x0015
+#define PXENV_UNLOAD_STACK 0x0070
+#define PXENV_GET_CACHED_INFO 0x0071
+#define PXENV_PACKET_TYPE_DHCP_ACK 0x0002
+#define PXENV_FILE_CMDLINE 0x00e8
+
+#define PXE_HACK_EB54 0x0001
+
+ .text
+ .arch i386
+ .org 0
+ .code16
+
+#include <undi.h>
+
+#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
+#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
+
+/* Prefix memory layout:
+ *
+ * iPXE binary image
+ * Temporary stack
+ * Temporary copy of DHCPACK packet
+ * Temporary copy of command line
+ */
+#define PREFIX_STACK_SIZE 2048
+#define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE
+#define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ )
+#define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE )
+#define PREFIX_TEMP_CMDLINE_SIZE 4096
+
+/*****************************************************************************
+ * Entry point: set operating context, print welcome message
+ *****************************************************************************
+ */
+ .section ".prefix", "ax", @progbits
+ .globl _pxe_start
+_pxe_start:
+ jmp $0x7c0, $1f
+1:
+ /* Preserve registers for possible return to PXE */
+ pushfl
+ pushal
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+
+ /* Store magic word on PXE stack and remember PXE %ss:esp */
+ pushl $STACK_MAGIC
+ movw %ss, %cs:pxe_ss
+ movl %esp, %cs:pxe_esp
+
+ /* Set up segments */
+ movw %cs, %ax
+ movw %ax, %ds
+ movw $0x40, %ax /* BIOS data segment access */
+ movw %ax, %fs
+ /* Set up temporary stack immediately after the iPXE image */
+ movw %cs, %ax
+ addw image_size_pgh, %ax
+ movw %ax, %ss
+ movl $PREFIX_STACK_SIZE, %esp
+ /* Clear direction flag, for the sake of sanity */
+ cld
+ /* Print welcome message */
+ movw $10f, %si
+ xorw %di, %di
+ call print_message
+ .section ".prefix.data", "aw", @progbits
+10: .asciz "PXE->EB:"
+ .previous
+
+ /* Image size (for stack placement calculation) */
+ .section ".prefix.data", "aw", @progbits
+image_size_pgh:
+ .word 0
+ .previous
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADDW"
+ .long image_size_pgh
+ .long 16
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * Find us a usable !PXE or PXENV+ entry point
+ *****************************************************************************
+ */
+detect_pxe:
+ /* Plan A: !PXE pointer from the stack */
+ lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
+ lesw %gs:52(%bp), %bx
+ call is_valid_ppxe
+ je have_ppxe
+
+ /* Plan B: PXENV+ pointer from initial ES:BX */
+ movw %gs:32(%bp),%bx
+ movw %gs:8(%bp),%es
+ call is_valid_pxenv
+ je have_pxenv
+
+ /* Plan C: PXENV+ structure via INT 1Ah */
+ movw $0x5650, %ax
+ int $0x1a
+ jc 1f
+ cmpw $0x564e, %ax
+ jne 1f
+ call is_valid_pxenv
+ je have_pxenv
+1:
+ /* Plan D: scan base memory for !PXE */
+ call memory_scan_ppxe
+ je have_ppxe
+
+ /* Plan E: scan base memory for PXENV+ */
+ call memory_scan_pxenv
+ jne stack_not_found
+
+have_pxenv:
+ movw %bx, pxenv_offset
+ movw %es, pxenv_segment
+
+ cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
+ jb 1f
+ cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
+ jb 2f
+
+ lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
+ call is_valid_ppxe
+ je have_ppxe
+2:
+ call memory_scan_ppxe /* We are *supposed* to have !PXE... */
+ je have_ppxe
+1:
+ lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
+
+ /* Record entry point and UNDI segments */
+ pushl %es:0x0a(%bx) /* Entry point */
+ pushw %es:0x24(%bx) /* UNDI code segment */
+ pushw %es:0x26(%bx) /* UNDI code size */
+ pushw %es:0x20(%bx) /* UNDI data segment */
+ pushw %es:0x22(%bx) /* UNDI data size */
+
+ /* Print "PXENV+ at <address>" */
+ movw $10f, %si
+ jmp check_have_stack
+ .section ".prefix.data", "aw", @progbits
+10: .asciz " PXENV+ at "
+ .previous
+
+have_ppxe:
+ movw %bx, ppxe_offset
+ movw %es, ppxe_segment
+
+ pushl %es:0x10(%bx) /* Entry point */
+ pushw %es:0x30(%bx) /* UNDI code segment */
+ pushw %es:0x36(%bx) /* UNDI code size */
+ pushw %es:0x28(%bx) /* UNDI data segment */
+ pushw %es:0x2e(%bx) /* UNDI data size */
+
+ /* Print "!PXE at <address>" */
+ movw $10f, %si
+ jmp check_have_stack
+ .section ".prefix.data", "aw", @progbits
+10: .asciz " !PXE at "
+ .previous
+
+is_valid_ppxe:
+ cmpl $0x45585021, %es:(%bx)
+ jne 1f
+ movzbw %es:4(%bx), %cx
+ cmpw $0x58, %cx
+ jae is_valid_checksum
+1:
+ ret
+
+is_valid_pxenv:
+ cmpl $0x4e455850, %es:(%bx)
+ jne 1b
+ cmpw $0x2b56, %es:4(%bx)
+ jne 1b
+ movzbw %es:8(%bx), %cx
+ cmpw $0x28, %cx
+ jb 1b
+
+is_valid_checksum:
+ pushw %ax
+ movw %bx, %si
+ xorw %ax, %ax
+2:
+ es lodsb
+ addb %al, %ah
+ loopw 2b
+ popw %ax
+ ret
+
+memory_scan_ppxe:
+ movw $is_valid_ppxe, %dx
+ jmp memory_scan_common
+
+memory_scan_pxenv:
+ movw $is_valid_pxenv, %dx
+
+memory_scan_common:
+ movw %fs:(0x13), %ax
+ shlw $6, %ax
+ decw %ax
+1: incw %ax
+ cmpw $( 0xa000 - 1 ), %ax
+ ja 2f
+ movw %ax, %es
+ xorw %bx, %bx
+ call *%dx
+ jne 1b
+2: ret
+
+/*****************************************************************************
+ * Sanity check: we must have an entry point
+ *****************************************************************************
+ */
+check_have_stack:
+ /* Save common values pushed onto the stack */
+ popl undi_data_segoff
+ popl undi_code_segoff
+ popl entry_segoff
+
+ /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
+ call print_message
+ call print_segoff
+ movb $( ',' ), %al
+ call print_character
+
+ /* Check for entry point */
+ movl entry_segoff, %eax
+ testl %eax, %eax
+ jnz 99f
+ /* No entry point: print message and skip everything else */
+stack_not_found:
+ movw $10f, %si
+ call print_message
+ jmp finished
+ .section ".prefix.data", "aw", @progbits
+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, %bx
+ call print_segoff
+ .section ".prefix.data", "aw", @progbits
+10: .asciz " entry point at "
+ .previous
+ /* Print UNDI code segment */
+ movw $10f, %si
+ call print_message
+ les undi_code_segoff, %bx
+ call print_segoff
+ .section ".prefix.data", "aw", @progbits
+10: .asciz "\n UNDI code segment "
+ .previous
+ /* Print UNDI data segment */
+ movw $10f, %si
+ call print_message
+ les undi_data_segoff, %bx
+ call print_segoff
+ .section ".prefix.data", "aw", @progbits
+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", "aw", @progbits
+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
+ jmp 99f
+ .section ".prefix.data", "aw", @progbits
+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", "aw", @progbits
+10: .asciz " Unable to determine UNDI physical device"
+ .previous
+
+99:
+
+/*****************************************************************************
+ * Determine interface type
+ *****************************************************************************
+ */
+get_iface_type:
+ /* Issue PXENV_UNDI_GET_IFACE_INFO */
+ movw $PXENV_UNDI_GET_IFACE_INFO, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Print interface type */
+ movw $10f, %si
+ call print_message
+ leaw ( pxe_parameter_structure + 0x02 ), %si
+ call print_message
+ .section ".prefix.data", "aw", @progbits
+10: .asciz ", type "
+ .previous
+ /* Check for "Etherboot" interface type */
+ cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
+ jne 99f
+ cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
+ jne 99f
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data", "aw", @progbits
+10: .asciz " (workaround enabled)"
+ .previous
+ /* Flag Etherboot workarounds as required */
+ orw $PXE_HACK_EB54, pxe_hacks
+
+99: movb $0x0a, %al
+ call print_character
+
+/*****************************************************************************
+ * Get cached DHCP_ACK packet
+ *****************************************************************************
+ */
+get_dhcpack:
+ /* Issue PXENV_GET_CACHED_INFO */
+ xorl %esi, %esi
+ movw %ss, %si
+ movw %si, ( pxe_parameter_structure + 0x08 )
+ movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 )
+ movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 )
+ movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 )
+ movw $PXENV_GET_CACHED_INFO, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Store physical address of packet */
+ shll $4, %esi
+ addl $PREFIX_TEMP_DHCPACK, %esi
+ movl %esi, pxe_cached_dhcpack
+99:
+ .section ".prefix.data", "aw", @progbits
+pxe_cached_dhcpack:
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * Check for a command line
+ *****************************************************************************
+ */
+get_cmdline:
+ /* Issue PXENV_FILE_CMDLINE */
+ xorl %esi, %esi
+ movw %ss, %si
+ movw %si, ( pxe_parameter_structure + 0x06 )
+ movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 )
+ movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 )
+ movw $PXENV_FILE_CMDLINE, %bx
+ call pxe_call
+ jc 99f /* Suppress errors; this is an iPXE extension API call */
+ /* Check for non-NULL command line */
+ movw ( pxe_parameter_structure + 0x02 ), %ax
+ testw %ax, %ax
+ jz 99f
+ /* Record command line */
+ shll $4, %esi
+ addl $PREFIX_TEMP_CMDLINE, %esi
+ movl %esi, pxe_cmdline
+99:
+ .section ".prefix.data", "aw", @progbits
+pxe_cmdline:
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * Leave NIC in a safe state
+ *****************************************************************************
+ */
+#ifndef PXELOADER_KEEP_PXE
+shutdown_nic:
+ /* Issue PXENV_UNDI_SHUTDOWN */
+ movw $PXENV_UNDI_SHUTDOWN, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+1:
+unload_base_code:
+ /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
+ * we must not issue this call if the underlying stack is
+ * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
+ */
+#ifdef PXELOADER_KEEP_UNDI
+ testw $PXE_HACK_EB54, pxe_hacks
+ jnz 99f
+#endif /* PXELOADER_KEEP_UNDI */
+ /* 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 undi_fbms_start, %ax
+ movw %fs:(0x13), %bx
+ call free_basemem
+99:
+ andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
+#endif /* PXELOADER_KEEP_PXE */
+
+/*****************************************************************************
+ * Unload UNDI driver
+ *****************************************************************************
+ */
+#ifndef PXELOADER_KEEP_UNDI
+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_end, %ax
+ movw undi_fbms_start, %bx
+ call free_basemem
+ /* Clear UNDI_FL_STARTED */
+ andw $~UNDI_FL_STARTED, flags
+99:
+#endif /* PXELOADER_KEEP_UNDI */
+
+/*****************************************************************************
+ * 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", "aw", @progbits
+10: .asciz " "
+20: .asciz "kB free base memory after PXE unload\n"
+ .previous
+
+/*****************************************************************************
+ * Exit point
+ *****************************************************************************
+ */
+finished:
+ jmp run_ipxe
+
+/*****************************************************************************
+ * Subroutine: print segment:offset address
+ *
+ * Parameters:
+ * %es:%bx : segment:offset address to print
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+print_segoff:
+ /* Preserve registers */
+ pushw %ax
+ /* Print "<segment>:offset" */
+ movw %es, %ax
+ call print_hex_word
+ movb $( ':' ), %al
+ call print_character
+ movw %bx, %ax
+ call print_hex_word
+ /* Restore registers and return */
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: print decimal word
+ *
+ * Parameters:
+ * %ax : word to print
+ * %ds:di : output buffer (or %di=0 to print to console)
+ * Returns:
+ * %ds:di : next character in output buffer (if applicable)
+ *****************************************************************************
+ */
+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: zero 1kB block of base memory
+ *
+ * Parameters:
+ * %bx : block to zero (in kB)
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+zero_kb:
+ /* Preserve registers */
+ pushw %ax
+ pushw %cx
+ pushw %di
+ pushw %es
+ /* Zero block */
+ movw %bx, %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:
+ * %ax : Desired new free base memory counter (in kB)
+ * %bx : Expected current free base memory counter (in kB)
+ * %fs : BIOS data segment (0x40)
+ * Returns:
+ * None
+ *
+ * The base memory from %bx kB to %ax kB is unconditionally zeroed.
+ * It will be freed if and only if the expected current free base
+ * memory counter (%bx) 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 %bx
+1: cmpw %bx, %ax
+ je 2f
+ call zero_kb
+ incw %bx
+ jmp 1b
+2: popw %bx
+ /* Free base memory */
+ cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */
+ jne 1f /* is correct */
+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: /* Clear direction flag, for the sake of sanity */
+ cld
+ /* 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", "aw", @progbits
+10: .asciz " UNDI API call "
+20: .asciz " failed: status code "
+30: .asciz "\n"
+ .previous
+
+/*****************************************************************************
+ * PXE data structures
+ *****************************************************************************
+ */
+ .section ".prefix.data"
+
+pxe_esp: .long 0
+pxe_ss: .word 0
+
+pxe_parameter_structure: .fill 64
+
+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
+
+pxe_hacks: .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
+
+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_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
+
+ .equ undi_device_size, ( . - undi_device )
+
+/*****************************************************************************
+ * Run iPXE main code
+ *****************************************************************************
+ */
+ .section ".prefix"
+run_ipxe:
+ /* Install iPXE */
+ 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
+
+ /* Retrieve PXE %ss:esp */
+ movw pxe_ss, %di
+ movl pxe_esp, %ebp
+
+ /* Retrieve PXE command line, if any */
+ movl pxe_cmdline, %esi
+
+ /* Retrieve cached DHCPACK, if any */
+ movl pxe_cached_dhcpack, %ecx
+
+ /* Jump to .text16 segment with %ds pointing to .data16 */
+ movw %bx, %ds
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "ax", @progbits
+1:
+ /* Update the exit hook */
+ movw %cs, ( pxe_exit_hook + 2 )
+
+ /* Store command-line pointer */
+ movl %esi, cmdline_phys
+
+ /* Store cached DHCPACK pointer */
+ movl %ecx, cached_dhcpack_phys
+
+ /* Run main program */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %ecx /* discard */
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Restore PXE stack */
+ movw %di, %ss
+ movl %ebp, %esp
+
+ /* Jump to hook if applicable */
+ ljmpw *pxe_exit_hook
+
+ .section ".data16", "aw", @progbits
+ .globl pxe_exit_hook
+pxe_exit_hook:
+ .word exit_ipxe, 0
+ .previous
+
+exit_ipxe:
+ /* Check PXE stack magic */
+ popl %eax
+ cmpl $STACK_MAGIC, %eax
+ jne 1f
+
+ /* PXE stack OK: return to caller */
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ popal
+ popfl
+ xorw %ax, %ax /* Return success */
+ lret
+
+1: /* PXE stack corrupt or removed: use INT 18 */
+ int $0x18
+ .previous
diff --git a/roms/ipxe/src/arch/i386/prefix/romprefix.S b/roms/ipxe/src/arch/i386/prefix/romprefix.S
new file mode 100644
index 000000000..18dda2b37
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/romprefix.S
@@ -0,0 +1,895 @@
+/* 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#include <config/general.h>
+#include <config/branding.h>
+
+#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
+#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
+#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
+#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define PMM_ALLOCATE 0x0000
+#define PMM_FIND 0x0001
+#define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \
+ ( ( 'E' - 'A' + 1 ) << 21 ) + \
+ ( ( 'N' - 'A' + 1 ) << 16 ) )
+#define PMM_HANDLE_BASE_IMAGE_SOURCE \
+ ( PMM_HANDLE_BASE | 0x00001000 )
+#define PMM_HANDLE_BASE_DECOMPRESS_TO \
+ ( PMM_HANDLE_BASE | 0x00002000 )
+#define PCI_FUNC_MASK 0x07
+
+/* ROM banner timeout, converted to a number of (18Hz) timer ticks. */
+#define ROM_BANNER_TIMEOUT_TICKS ( ( 18 * ROM_BANNER_TIMEOUT ) / 10 )
+
+/* Allow payload to be excluded from ROM size
+ */
+#if ROMPREFIX_EXCLUDE_PAYLOAD
+#define ZINFO_TYPE_ADxB "ADHB"
+#define ZINFO_TYPE_ADxW "ADHW"
+#else
+#define ZINFO_TYPE_ADxB "ADDB"
+#define ZINFO_TYPE_ADxW "ADDW"
+#endif
+
+/* Allow ROM to be marked as containing multiple images
+ */
+#if ROMPREFIX_MORE_IMAGES
+#define INDICATOR 0x00
+#else
+#define INDICATOR 0x80
+#endif
+
+/* Default to building a PCI ROM if no bus type is specified
+ */
+#ifndef BUSTYPE
+#define BUSTYPE "PCIR"
+#endif
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .globl _rom_start
+_rom_start:
+
+ .org 0x00
+romheader:
+ .word 0xAA55 /* BIOS extension signature */
+romheader_size: .byte 0 /* Size in 512-byte blocks */
+ jmp init /* Initialisation vector */
+checksum:
+ .byte 0
+ .org 0x10
+ .word ipxeheader
+ .org 0x16
+ .word undiheader
+.ifeqs BUSTYPE, "PCIR"
+ .org 0x18
+ .word pciheader
+.endif
+ .org 0x1a
+ .word pnpheader
+ .size romheader, . - romheader
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii ZINFO_TYPE_ADxB
+ .long romheader_size
+ .long 512
+ .long 0
+ .previous
+
+.ifeqs BUSTYPE, "PCIR"
+pciheader:
+ .ascii "PCIR" /* Signature */
+ .word pci_vendor_id /* Vendor identification */
+ .word pci_device_id /* Device identification */
+ .word ( pci_devlist - pciheader ) /* Device list pointer */
+ .word pciheader_len /* PCI data structure length */
+ .byte 0x03 /* PCI data structure revision */
+ .byte 0x02, 0x00, 0x00 /* Class code */
+pciheader_image_length:
+ .word 0 /* Image length */
+ .word 0x0001 /* Revision level */
+ .byte 0x00 /* Code type */
+ .byte INDICATOR /* Last image indicator */
+pciheader_runtime_length:
+ .word 0 /* Maximum run-time image length */
+ .word 0x0000 /* Configuration utility code header */
+ .word 0x0000 /* DMTF CLP entry point */
+ .equ pciheader_len, . - pciheader
+ .size pciheader, . - pciheader
+
+ /* PCI additional device list (filled in by linker) */
+ .section ".pci_devlist.00000000", "a", @progbits
+pci_devlist:
+ .previous
+ .section ".pci_devlist.ffffffff", "a", @progbits
+pci_devlist_end:
+ .short 0x0000 /* List terminator */
+ .previous
+ /* Ensure that terminator is always present */
+ .reloc pciheader, RELOC_TYPE_NONE, pci_devlist_end
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii ZINFO_TYPE_ADxW
+ .long pciheader_image_length
+ .long 512
+ .long 0
+ .ascii ZINFO_TYPE_ADxW
+ .long pciheader_runtime_length
+ .long 512
+ .long 0
+ .previous
+.endif /* PCIR */
+
+ /* PnP doesn't require any particular alignment, but IBM
+ * BIOSes will scan on 16-byte boundaries rather than using
+ * the offset stored at 0x1a
+ */
+ .align 16
+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 0xf4 /* 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
+
+/* Manufacturer string */
+mfgstr:
+ .asciz "http://ipxe.org"
+ .size mfgstr, . - mfgstr
+
+/* Product string
+ *
+ * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
+ * initialisation time, it will be filled in to include the PCI
+ * bus:dev.fn number of the card as well.
+ */
+prodstr:
+ .ascii PRODUCT_SHORT_NAME
+.ifeqs BUSTYPE, "PCIR"
+prodstr_separator:
+ .byte 0
+ .ascii "(PCI "
+prodstr_pci_id:
+ .ascii "xx:xx.x)" /* Filled in by init code */
+.endif /* PCIR */
+ .byte 0
+ .size prodstr, . - prodstr
+
+ .globl undiheader
+ .weak undiloader
+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_memsz /* Stack segment size */
+ .word _data16_memsz /* Data segment size */
+ .word _text16_memsz /* Code segment size */
+ .ascii BUSTYPE /* Bus type */
+ .equ undiheader_len, . - undiheader
+ .size undiheader, . - undiheader
+
+ipxeheader:
+ .ascii "iPXE" /* Signature */
+ .byte ipxeheader_len /* Length of structure */
+ .byte 0 /* Checksum */
+shrunk_rom_size:
+ .byte 0 /* Shrunk size (in 512-byte blocks) */
+ .byte 0 /* Reserved */
+build_id:
+ .long _build_id /* Randomly-generated build ID */
+ .equ ipxeheader_len, . - ipxeheader
+ .size ipxeheader, . - ipxeheader
+
+ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+ .ascii "ADHB"
+ .long shrunk_rom_size
+ .long 512
+ .long 0
+ .previous
+
+/* 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
+ pushw %fs
+ pushw %gs
+ cld
+ pushw %cs
+ popw %ds
+
+ /* Print message as early as possible */
+ movw $init_message, %si
+ xorw %di, %di
+ call print_message
+
+ /* Store PCI 3.0 runtime segment address for later use, if
+ * applicable.
+ */
+.ifeqs BUSTYPE, "PCIR"
+ movw %bx, %gs
+.endif
+
+ /* Store PCI bus:dev.fn address, print PCI bus:dev.fn, and add
+ * PCI bus:dev.fn to product name string, if applicable.
+ */
+.ifeqs BUSTYPE, "PCIR"
+ xorw %di, %di
+ call print_space
+ movw %ax, init_pci_busdevfn
+ call print_pci_busdevfn
+ movw $prodstr_pci_id, %di
+ call print_pci_busdevfn
+ movb $( ' ' ), prodstr_separator
+.endif
+
+ /* Print segment address */
+ xorw %di, %di
+ call print_space
+ movw %cs, %ax
+ call print_hex_word
+
+ /* Check for PCI BIOS version, if applicable */
+.ifeqs BUSTYPE, "PCIR"
+ pushl %ebx
+ pushl %edx
+ pushl %edi
+ stc
+ movw $0xb101, %ax
+ int $0x1a
+ jc no_pci3
+ cmpl $PCI_SIGNATURE, %edx
+ jne no_pci3
+ testb %ah, %ah
+ jnz no_pci3
+ movw $init_message_pci, %si
+ xorw %di, %di
+ call print_message
+ movb %bh, %al
+ call print_hex_nibble
+ movb $( '.' ), %al
+ call print_character
+ movb %bl, %al
+ call print_hex_byte
+ cmpb $3, %bh
+ jb no_pci3
+ /* PCI >=3.0: leave %gs as-is if sane */
+ movw %gs, %ax
+ cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
+ jb pci3_insane
+ movw %cs, %bx /* Sane if %cs == %gs */
+ cmpw %bx, %ax
+ je 1f
+ movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
+ shlw $5, %cx
+ addw %cx, %bx
+ cmpw %bx, %ax
+ jae 1f
+ movw %cs, %bx /* Sane if %gs+len <= %cs */
+ addw %cx, %ax
+ cmpw %bx, %ax
+ jbe 1f
+pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
+ movb $( '!' ), %al
+ call print_character
+ movw %gs, %ax
+ call print_hex_word
+no_pci3:
+ /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
+ pushw %cs
+ popw %gs
+1: popl %edi
+ popl %edx
+ popl %ebx
+.endif /* PCIR */
+
+ /* Check for PnP BIOS. Although %es:di should point to the
+ * PnP BIOS signature on entry, some BIOSes fail to do this.
+ */
+ movw $( 0xf000 - 1 ), %bx
+pnp_scan:
+ incw %bx
+ jz no_pnp
+ movw %bx, %es
+ cmpl $PNP_SIGNATURE, %es:0
+ jne pnp_scan
+ xorw %dx, %dx
+ xorw %si, %si
+ movzbw %es:5, %cx
+1: es lodsb
+ addb %al, %dl
+ loop 1b
+ jnz pnp_scan
+ /* Is PnP: print PnP message */
+ movw $init_message_pnp, %si
+ xorw %di, %di
+ call print_message
+ jmp pnp_done
+no_pnp: /* Not PnP-compliant - hook INT 19 */
+#ifdef NONPNP_HOOK_INT19
+ movw $init_message_int19, %si
+ xorw %di, %di
+ call print_message
+ xorw %ax, %ax
+ movw %ax, %es
+ pushl %es:( 0x19 * 4 )
+ popl orig_int19
+ pushw %gs /* %gs contains runtime %cs */
+ pushw $int19_entry
+ popl %es:( 0x19 * 4 )
+#endif /* NONPNP_HOOK_INT19 */
+pnp_done:
+
+ /* Check for PMM */
+ movw $( 0xe000 - 1 ), %bx
+pmm_scan:
+ incw %bx
+ jz no_pmm
+ movw %bx, %es
+ cmpl $PMM_SIGNATURE, %es:0
+ jne pmm_scan
+ xorw %dx, %dx
+ xorw %si, %si
+ movzbw %es:5, %cx
+1: es lodsb
+ addb %al, %dl
+ loop 1b
+ jnz pmm_scan
+ /* PMM found: print PMM message */
+ movw $init_message_pmm, %si
+ xorw %di, %di
+ call print_message
+ /* We have PMM and so a 1kB stack: preserve whole registers */
+ pushal
+ /* Allocate image source PMM block. Round up the size to the
+ * nearest 4kB (8 512-byte sectors) to work around AMI BIOS bugs.
+ */
+ movzbl romheader_size, %ecx
+ addw extra_size, %cx
+ addw $0x0007, %cx /* Round up to multiple of 8 512-byte sectors */
+ andw $0xfff8, %cx
+ shll $5, %ecx
+ movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx
+ movw $get_pmm_image_source, %bp
+ call get_pmm
+ movl %esi, image_source
+ jz 1f
+ /* Copy ROM to image source PMM block */
+ pushw %es
+ xorw %ax, %ax
+ movw %ax, %es
+ movl %esi, %edi
+ xorl %esi, %esi
+ movzbl romheader_size, %ecx
+ shll $7, %ecx
+ addr32 rep movsl /* PMM presence implies flat real mode */
+ popw %es
+ /* Shrink ROM */
+ movb shrunk_rom_size, %al
+ movb %al, romheader_size
+1: /* Allocate decompression PMM block. Round up the size to the
+ * nearest 128kB and use the size within the PMM handle; this
+ * allows the same decompression area to be shared between
+ * multiple iPXE ROMs even with differing build IDs
+ */
+ movl $_textdata_memsz_pgh, %ecx
+ addl $0x00001fff, %ecx
+ andl $0xffffe000, %ecx
+ movl %ecx, %ebx
+ shrw $12, %bx
+ orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx
+ movw $get_pmm_decompress_to, %bp
+ call get_pmm
+ movl %esi, decompress_to
+ /* Restore registers */
+ popal
+no_pmm:
+
+ /* Update checksum */
+ xorw %bx, %bx
+ xorw %si, %si
+ movzbw romheader_size, %cx
+ shlw $9, %cx
+1: lodsb
+ addb %al, %bl
+ loop 1b
+ subb %bl, checksum
+
+ /* Copy self to option ROM space, if applicable. Required for
+ * PCI3.0, which loads us to a temporary location in low
+ * memory. Will be a no-op for lower PCI versions.
+ */
+.ifeqs BUSTYPE, "PCIR"
+ xorw %di, %di
+ call print_space
+ movw %gs, %ax
+ call print_hex_word
+ movzbw romheader_size, %cx
+ shlw $9, %cx
+ movw %ax, %es
+ xorw %si, %si
+ xorw %di, %di
+ cs rep movsb
+.endif
+
+ /* Skip prompt if this is not the first PCI function, if applicable */
+.ifeqs BUSTYPE, "PCIR"
+ testb $PCI_FUNC_MASK, init_pci_busdevfn
+ jnz no_shell
+.endif
+ /* Prompt for POST-time shell */
+ movw $init_message_prompt, %si
+ xorw %di, %di
+ call print_message
+ movw $prodstr, %si
+ call print_message
+ movw $init_message_dots, %si
+ call print_message
+ /* Wait for Ctrl-B */
+ movw $0xff02, %bx
+ call wait_for_key
+ /* Clear prompt */
+ pushf
+ xorw %di, %di
+ call print_kill_line
+ movw $init_message_done, %si
+ call print_message
+ popf
+ jnz no_shell
+ /* Ctrl-B was pressed: invoke iPXE. The keypress will be
+ * picked up by the initial shell prompt, and we will drop
+ * into a shell.
+ */
+ xorl %ebp, %ebp /* Inhibit use of INT 15,e820 and INT 15,e801 */
+ pushw %cs
+ call exec
+no_shell:
+ movb $( '\n' ), %al
+ xorw %di, %di
+ call print_character
+
+ /* Restore registers */
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ popaw
+
+ /* Indicate boot capability to PnP BIOS, if present */
+ movw $0x20, %ax
+ lret
+ .size init, . - init
+
+/* Attempt to find or allocate PMM block
+ *
+ * Parameters:
+ * %ecx : size of block to allocate, in paragraphs
+ * %ebx : PMM handle base
+ * %bp : routine to check acceptability of found blocks
+ * %es:0000 : PMM structure
+ * Returns:
+ * %ebx : PMM handle
+ * %esi : allocated block address, or zero (with ZF set) if allocation failed
+ */
+get_pmm:
+ /* Preserve registers */
+ pushl %eax
+ pushw %di
+ movw $( ' ' ), %di
+get_pmm_find:
+ /* Try to find existing block */
+ pushl %ebx /* PMM handle */
+ pushw $PMM_FIND
+ lcall *%es:7
+ addw $6, %sp
+ pushw %dx
+ pushw %ax
+ popl %esi
+ /* Treat 0xffffffff (not supported) as 0x00000000 (not found) */
+ incl %esi
+ jz get_pmm_allocate
+ decl %esi
+ jz get_pmm_allocate
+ /* Block found - check acceptability */
+ call *%bp
+ jnc get_pmm_done
+ /* Block not acceptable - increment handle and retry */
+ incl %ebx
+ jmp get_pmm_find
+get_pmm_allocate:
+ /* Block not found - try to allocate new block */
+ pushw $0x0002 /* Extended memory */
+ pushl %ebx /* PMM handle */
+ pushl %ecx /* Length */
+ pushw $PMM_ALLOCATE
+ lcall *%es:7
+ addw $12, %sp
+ pushw %dx
+ pushw %ax
+ popl %esi
+ movw $( '+' ), %di /* Indicate allocation attempt */
+get_pmm_done:
+ /* Print block address */
+ movw %di, %ax
+ xorw %di, %di
+ call print_character
+ movl %esi, %eax
+ call print_hex_dword
+ /* Treat 0xffffffff (not supported) as 0x00000000 (allocation
+ * failed), and set ZF to indicate a zero result.
+ */
+ incl %esi
+ jz 1f
+ decl %esi
+1: /* Restore registers and return */
+ popw %di
+ popl %eax
+ ret
+ .size get_pmm, . - get_pmm
+
+ /* Check acceptability of image source block */
+get_pmm_image_source:
+ pushw %es
+ xorw %ax, %ax
+ movw %ax, %es
+ movl build_id, %eax
+ addr32 cmpl %es:build_id(%esi), %eax
+ je 1f
+ stc
+1: popw %es
+ ret
+ .size get_pmm_image_source, . - get_pmm_image_source
+
+ /* Check acceptability of decompression block */
+get_pmm_decompress_to:
+ clc
+ ret
+ .size get_pmm_decompress_to, . - get_pmm_decompress_to
+
+/*
+ * Note to hardware vendors:
+ *
+ * If you wish to brand this boot ROM, please do so by defining the
+ * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/branding.h.
+ *
+ * While nothing in the GPL prevents you from removing all references
+ * to iPXE or http://ipxe.org, we prefer you not to do so.
+ *
+ * If you have an OEM-mandated branding requirement that cannot be
+ * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
+ * please contact us.
+ *
+ * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
+ * bypassing the spirit of this request! ]
+ */
+init_message:
+ .ascii "\n"
+ .ascii PRODUCT_NAME
+ .ascii "\n"
+ .ascii PRODUCT_SHORT_NAME
+ .ascii " ("
+ .ascii PRODUCT_URI
+ .asciz ")"
+ .size init_message, . - init_message
+.ifeqs BUSTYPE, "PCIR"
+init_message_pci:
+ .asciz " PCI"
+ .size init_message_pci, . - init_message_pci
+.endif /* PCIR */
+init_message_pnp:
+ .asciz " PnP"
+ .size init_message_pnp, . - init_message_pnp
+init_message_pmm:
+ .asciz " PMM"
+ .size init_message_pmm, . - init_message_pmm
+init_message_int19:
+ .asciz " INT19"
+ .size init_message_int19, . - init_message_int19
+init_message_prompt:
+ .asciz "\nPress Ctrl-B to configure "
+ .size init_message_prompt, . - init_message_prompt
+init_message_dots:
+ .asciz "..."
+ .size init_message_dots, . - init_message_dots
+init_message_done:
+ .asciz "\n\n"
+ .size init_message_done, . - init_message_done
+
+/* PCI bus:dev.fn
+ *
+ */
+.ifeqs BUSTYPE, "PCIR"
+init_pci_busdevfn:
+ .word 0
+ .size init_pci_busdevfn, . - init_pci_busdevfn
+.endif /* PCIR */
+
+/* Image source area
+ *
+ * May be either zero (indicating to use option ROM space as source),
+ * or within a PMM-allocated block.
+ */
+ .globl image_source
+image_source:
+ .long 0
+ .size image_source, . - image_source
+
+/* Additional image source size (in 512-byte sectors)
+ *
+ */
+extra_size:
+ .word 0
+ .size extra_size, . - extra_size
+
+/* Temporary decompression area
+ *
+ * May be either zero (indicating to use default decompression area in
+ * high memory), or within a PMM-allocated block.
+ */
+ .globl decompress_to
+decompress_to:
+ .long 0
+ .size decompress_to, . - decompress_to
+
+/* Boot Execution Vector entry point
+ *
+ * Called by the PnP BIOS when it wants to boot us.
+ */
+bev_entry:
+ orl $0xffffffff, %ebp /* Allow arbitrary relocation */
+ 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. We
+ * attempt to return via the original INT 19 vector (if we were able
+ * to store it).
+ */
+int19_entry:
+ pushw %cs
+ popw %ds
+ /* Prompt user to press B to boot */
+ movw $int19_message_prompt, %si
+ xorw %di, %di
+ call print_message
+ movw $prodstr, %si
+ call print_message
+ movw $int19_message_dots, %si
+ call print_message
+ movw $0xdf4e, %bx
+ call wait_for_key
+ pushf
+ xorw %di, %di
+ call print_kill_line
+ movw $int19_message_done, %si
+ call print_message
+ popf
+ jz 1f
+ /* Leave keypress in buffer and start iPXE. The keypress will
+ * cause the usual initial Ctrl-B prompt to be skipped.
+ */
+ orl $0xffffffff, %ebp /* Allow arbitrary relocation */
+ pushw %cs
+ call exec
+1: /* Try to call original INT 19 vector */
+ movl %cs:orig_int19, %eax
+ testl %eax, %eax
+ je 2f
+ ljmp *%cs:orig_int19
+2: /* No chained vector: issue INT 18 as a last resort */
+ int $0x18
+ .size int19_entry, . - int19_entry
+orig_int19:
+ .long 0
+ .size orig_int19, . - orig_int19
+
+int19_message_prompt:
+ .asciz "Press N to skip booting from "
+ .size int19_message_prompt, . - int19_message_prompt
+int19_message_dots:
+ .asciz "..."
+ .size int19_message_dots, . - int19_message_dots
+int19_message_done:
+ .asciz "\n\n"
+ .size int19_message_done, . - int19_message_done
+
+/* Execute as a boot device
+ *
+ */
+exec: /* Set %ds = %cs */
+ pushw %cs
+ popw %ds
+
+ /* Print message as soon as possible */
+ movw $prodstr, %si
+ xorw %di, %di
+ call print_message
+ movw $exec_message_pre_install, %si
+ call print_message
+
+ /* Store magic word on BIOS stack and remember BIOS %ss:sp */
+ pushl $STACK_MAGIC
+ movw %ss, %cx
+ movw %sp, %dx
+
+ /* Obtain a reasonably-sized temporary stack */
+ xorw %bx, %bx
+ movw %bx, %ss
+ movw $0x7c00, %sp
+
+ /* Install iPXE */
+ call alloc_basemem
+ movl image_source, %esi
+ movl decompress_to, %edi
+ call install_prealloc
+
+ /* Print message indicating successful installation */
+ movw $exec_message_post_install, %si
+ xorw %di, %di
+ call print_message
+
+ /* 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:
+ /* Retrieve PCI bus:dev.fn, if applicable */
+.ifeqs BUSTYPE, "PCIR"
+ movw init_pci_busdevfn, %ax
+.endif
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+ /* Store PCI bus:dev.fn, if applicable */
+.ifeqs BUSTYPE, "PCIR"
+#ifdef AUTOBOOT_ROM_FILTER
+ movw %ax, autoboot_busdevfn
+#endif /* AUTOBOOT_ROM_FILTER */
+.endif
+
+ /* Call main() */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Set up flat real mode for return to BIOS */
+ call flatten_real_mode
+
+ /* Uninstall iPXE */
+ call uninstall
+
+ /* Restore BIOS stack */
+ movw %cx, %ss
+ movw %dx, %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_pre_install:
+ .asciz " starting execution..."
+ .size exec_message_pre_install, . - exec_message_pre_install
+exec_message_post_install:
+ .asciz "ok\n"
+ .size exec_message_post_install, . - exec_message_post_install
+
+/* Wait for key press specified by %bl (masked by %bh)
+ *
+ * Used by init and INT19 code when prompting user. If the specified
+ * key is pressed, it is left in the keyboard buffer.
+ *
+ * Returns with ZF set iff specified key is pressed.
+ */
+wait_for_key:
+ /* Preserve registers */
+ pushw %cx
+ pushw %ax
+1: /* Empty the keyboard buffer before waiting for input */
+ movb $0x01, %ah
+ int $0x16
+ jz 2f
+ xorw %ax, %ax
+ int $0x16
+ jmp 1b
+2: /* Wait for a key press */
+ movw $ROM_BANNER_TIMEOUT_TICKS, %cx
+3: decw %cx
+ js 99f /* Exit with ZF clear */
+ /* Wait for timer tick to be updated */
+ call wait_for_tick
+ /* Check to see if a key was pressed */
+ movb $0x01, %ah
+ int $0x16
+ jz 3b
+ /* Check to see if key was the specified key */
+ andb %bh, %al
+ cmpb %al, %bl
+ je 99f /* Exit with ZF set */
+ /* Not the specified key: remove from buffer and stop waiting */
+ pushfw
+ xorw %ax, %ax
+ int $0x16
+ popfw /* Exit with ZF clear */
+99: /* Restore registers and return */
+ popw %ax
+ popw %cx
+ ret
+ .size wait_for_key, . - wait_for_key
+
+/* Wait for timer tick
+ *
+ * Used by wait_for_key
+ */
+wait_for_tick:
+ pushl %eax
+ pushw %fs
+ movw $0x40, %ax
+ movw %ax, %fs
+ movl %fs:(0x6c), %eax
+1: pushf
+ sti
+ hlt
+ popf
+ cmpl %fs:(0x6c), %eax
+ je 1b
+ popw %fs
+ popl %eax
+ ret
+ .size wait_for_tick, . - wait_for_tick
+
+/* Drag in objects via _rom_start */
+REQUIRING_SYMBOL ( _rom_start )
+
+/* Drag in ROM configuration */
+REQUIRE_OBJECT ( config_romprefix )
diff --git a/roms/ipxe/src/arch/i386/prefix/undiloader.S b/roms/ipxe/src/arch/i386/prefix/undiloader.S
new file mode 100644
index 000000000..5cace44b7
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/undiloader.S
@@ -0,0 +1,54 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+/* UNDI loader
+ *
+ * Called by an external program to load our PXE stack.
+ */
+ .globl undiloader
+undiloader:
+ /* Save registers */
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ pushw %ds
+ pushw %es
+ pushw %bx
+ /* ROM segment address to %ds */
+ pushw %cs
+ popw %ds
+ /* UNDI loader parameter structure address into %es:%di */
+ movw %sp, %bx
+ movw %ss:22(%bx), %di
+ movw %ss:24(%bx), %es
+ /* Install to specified real-mode addresses */
+ pushw %di
+ movw %es:12(%di), %bx
+ movw %es:14(%di), %ax
+ movl image_source, %esi
+ movl decompress_to, %edi
+ orl $0xffffffff, %ebp /* Allow arbitrary relocation */
+ 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
+ popw %ds
+ popl %ebp
+ popl %edi
+ popl %esi
+ lret
+ .size undiloader, . - undiloader
diff --git a/roms/ipxe/src/arch/i386/prefix/unlzma.S b/roms/ipxe/src/arch/i386/prefix/unlzma.S
new file mode 100644
index 000000000..8d4b3c1a8
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/unlzma.S
@@ -0,0 +1,942 @@
+/*
+ * Copyright (C) 2015 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 (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/****************************************************************************
+ *
+ * This file provides the decompress() and decompress16() functions
+ * which can be called in order to decompress an LZMA-compressed
+ * image. The code is modelled on the public-domain "XZ Embedded"
+ * implementation as used by the Linux kernel. Symbol names are
+ * chosen to match the XZ Embedded implementation where possible, for
+ * ease of reference.
+ *
+ * This code is optimised for size rather than speed, since the amount
+ * of data to be decompressed is trivially small by modern standards.
+ *
+ * The same basic assembly code is used to compile both decompress()
+ * and decompress16().
+ *
+ * Note that these functions require large amounts of stack space.
+ *
+ ****************************************************************************
+ */
+
+ .text
+ .arch i586
+ .section ".prefix.lib", "ax", @progbits
+
+#ifdef CODE16
+#define ADDR16
+#define ADDR32 addr32
+#define decompress decompress16
+ .code16
+#else /* CODE16 */
+#define ADDR16 addr16
+#define ADDR32
+ .code32
+#endif /* CODE16 */
+
+/****************************************************************************
+ * Debugging
+ ****************************************************************************
+ *
+ * This code will usually run in 16-bit protected mode, in which case
+ * only the 0xe9 debug port (present on some virtual machines) can be
+ * used.
+ *
+ * To debug on real hardware, build with DEBUG=libprefix. This will
+ * cause this code to be called in flat real mode, and so DEBUG_INT10
+ * may be used.
+ */
+
+/* Enable debugging via 0xe9 debug port */
+#define DEBUG_E9 0
+
+/* Enable debugging via BIOS INT 10 (works only when in flat real mode) */
+#define DEBUG_INT10 0
+
+#if ( DEBUG_E9 || DEBUG_INT10 )
+ .macro print_character, reg
+ pushfl
+ pushw %ax
+ pushw %bx
+ pushw %bp
+ movb \reg, %al
+ movw $0x0007, %bx
+ movb $0x0e, %ah
+#if DEBUG_E9
+ outb %al, $0xe9
+#endif
+#if DEBUG_INT10
+ cmpb $('\n'), %al
+ jne L\@
+ int $0x10
+ movb $('\r'), %al
+L\@: int $0x10
+#endif
+ popw %bp
+ popw %bx
+ popw %ax
+ popfl
+ .endm
+
+ .macro print_hex_nibble
+ pushfl
+ pushw %ax
+ cmpb $10, %al
+ sbb $0x69, %al
+ das
+ print_character %al
+ popw %ax
+ popfl
+ .endm
+
+ .macro print_hex_byte, reg
+ pushfl
+ pushw %ax
+ movb \reg, %al
+ pushw %ax
+ shrb $4, %al
+ print_hex_nibble
+ popw %ax
+ andb $0x0f, %al
+ print_hex_nibble
+ popw %ax
+ popfl
+ .endm
+
+ .macro print_hex_word, reg
+ pushw %ax
+ movw \reg, %ax
+ print_hex_byte %ah
+ print_hex_byte %al
+ popw %ax
+ .endm
+
+ .macro print_hex_dword, reg
+ pushl %eax
+ movl \reg, %eax
+ rorl $16, %eax
+ print_hex_word %ax
+ rorl $16, %eax
+ print_hex_word %ax
+ popl %eax
+ .endm
+#else
+ .macro print_character, char
+ .endm
+ .macro print_hex_byte, reg
+ .endm
+ .macro print_hex_word, reg
+ .endm
+ .macro print_hex_dword, reg
+ .endm
+#endif
+
+/****************************************************************************
+ * LZMA parameters and data structures
+ ****************************************************************************
+ */
+
+/* LZMA decompressor states (as used in XZ Embedded) */
+#define STATE_LIT_LIT 0x00
+#define STATE_MATCH_LIT_LIT 0x01
+#define STATE_REP_LIT_LIT 0x02
+#define STATE_SHORTREP_LIT_LIT 0x03
+#define STATE_MATCH_LIT 0x04
+#define STATE_REP_LIT 0x05
+#define STATE_SHORTREP_LIT 0x06
+#define STATE_LIT_MATCH 0x07
+#define STATE_LIT_LONGREP 0x08
+#define STATE_LIT_SHORTREP 0x09
+#define STATE_NONLIT_MATCH 0x0a
+#define STATE_NONLIT_REP 0x0b
+
+/* LZMA maximum decompressor state in which most recent symbol was a literal */
+#define STATE_LIT_MAX 0x06
+
+/* LZMA number of literal context bits ("lc=" parameter) */
+#define LZMA_LC 2
+
+ .struct 0
+lzma_len_dec:
+choice: .word 0
+choice2: .word 0
+low: .rept ( 1 << 3 )
+ .word 0
+ .endr
+mid: .rept ( 1 << 3 )
+ .word 0
+ .endr
+high: .rept ( 1 << 8 )
+ .word 0
+ .endr
+ .equ sizeof__lzma_len_dec, . - lzma_len_dec
+ .previous
+
+ .struct 0
+lzma_dec:
+out_start: .long 0
+rc_code: .long 0
+rc_range: .long 0
+len: .word 0
+reps:
+rep0: .long 0
+rep1: .long 0
+rep2: .long 0
+rep3: .long 0
+probs:
+is_match: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+is_rep: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+is_rep0: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+is_rep1: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+is_rep2: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+is_rep0_long: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+dist_slot: .rept ( 4 * ( 1 << 6 ) )
+ .word 0
+ .endr
+dist_special: .rept ( ( 1 << ( 14 / 2 ) ) - 14 )
+ .word 0
+ .endr
+dist_align: .rept ( 1 << 4 )
+ .word 0
+ .endr
+match_len_dec: .space sizeof__lzma_len_dec
+rep_len_dec: .space sizeof__lzma_len_dec
+literal: .rept ( ( 1 << LZMA_LC ) * 0x300 )
+ .word 0
+ .endr
+ .align 4
+ .equ sizeof__lzma_dec, . - lzma_dec
+ .previous
+
+ /* Some binutils versions seem not to handle .struct/.previous */
+ .section ".prefix.lib", "ax", @progbits
+
+/*****************************************************************************
+ * Normalise range encoder
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %eax : current range
+ *****************************************************************************
+ */
+rc_normalise:
+ /* Check if rc_range is less than 1<<24 */
+ testb $0xff, (rc_range+3)(%ebp)
+ jnz 1f
+ /* If it is, shift in a new byte from the compressed input data */
+ shll $8, rc_range(%ebp)
+ shll $8, rc_code(%ebp)
+ ADDR32 lodsb
+ movb %al, (rc_code+0)(%ebp)
+1: /* Return current range */
+ movl rc_range(%ebp), %eax
+ ret
+ .size rc_normalise, . - rc_normalise
+
+/*****************************************************************************
+ * Decode single range-encoded bit using a probability estimate
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %ebx : probability estimate pointer (offset from %ebp)
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * CF : decoded bit
+ * ZF : inverse of decoded bit
+ * Corrupts:
+ * none
+ *****************************************************************************
+ */
+rc_bit:
+ /* Preserve registers */
+ pushl %eax
+ pushl %edx
+ /* Perform normalisation */
+ call rc_normalise
+ /* Calculate bound in %eax and probability estimate in %dx */
+ shrl $11, %eax
+ movzwl (%ebp,%ebx), %edx
+ mul %edx /* will zero %edx */
+ movw (%ebp,%ebx), %dx
+ /* Compare code against bound */
+ cmpl %eax, rc_code(%ebp)
+ jae 2f
+1: /* Code is less than bound */
+ movl %eax, rc_range(%ebp)
+ negw %dx
+ addw $(1<<11), %dx
+ shrw $5, %dx
+ addw %dx, (%ebp,%ebx)
+ xorw %ax, %ax /* Clear CF, set ZF */
+ jmp 99f
+2: /* Code is greater than or equal to bound */
+ subl %eax, rc_range(%ebp)
+ subl %eax, rc_code(%ebp)
+ shrw $5, %dx
+ subw %dx, (%ebp,%ebx)
+ incw %dx /* Clear ZF (%dx is 11-bit; can never wrap) */
+ stc /* Set CF */
+99: /* Restore registers and return */
+ popl %edx
+ popl %eax
+ ret
+ .size rc_bit, . - rc_bit
+
+/*****************************************************************************
+ * Decode MSB-first bittree
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %ebx : probability estimate set pointer (offset from %ebp)
+ * %cx : number of bits to decode
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %eax : decoded bittree
+ * Corrupts:
+ * none
+ *****************************************************************************
+ */
+rc_bittree:
+ /* Preserve registers */
+ pushl %edi
+ pushw %cx
+ movl %ebx, %edi
+ /* Initialise registers */
+ movl $1, %eax
+1: /* Decode bit */
+ leaw (%edi,%eax,2), %bx /* high word always zero anyway */
+ call rc_bit
+ rclw %ax
+ ADDR16 loop 1b
+ /* Restore registers, clear unwanted high bit of result, and return */
+ movl %edi, %ebx
+ popw %cx
+ popl %edi
+ btrw %cx, %ax
+ ret
+ .size rc_bittree, . - rc_bittree
+
+/*****************************************************************************
+ * Decode LSB-first bittree
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %ebx : probability estimate set pointer (offset from %ebp)
+ * %cx : number of bits to decode
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %eax : decoded bittree
+ * Corrupts:
+ * none
+ *****************************************************************************
+ */
+rc_bittree_reverse:
+ /* Preserve registers */
+ pushw %cx
+ /* Decode bittree */
+ call rc_bittree
+1: /* Reverse result */
+ rcrb %al
+ rclb %ah
+ ADDR16 loop 1b
+ shrw $8, %ax
+ /* Restore registers and return */
+ popw %cx
+ ret
+ .size rc_bittree_reverse, . - rc_bittree_reverse
+
+/*****************************************************************************
+ * Decode MSB-first bittree with optional match byte
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %ebx : probability estimate set pointer (offset from %ebp)
+ * %cl : match byte
+ * %ch : 1 to use match byte, 0 to ignore match byte
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %eax : decoded bittree
+ * Corrupts:
+ * none
+ *****************************************************************************
+ */
+rc_bittree_match:
+ /* Preserve registers */
+ pushl %edi
+ pushw %cx
+ pushw %dx
+ movl %ebx, %edi
+ /* Initialise registers */
+ movl $1, %eax
+1: /* Decode bit */
+ rolb $1, %cl
+ movw %cx, %dx
+ andb %dh, %dl /* match_bit in %dl */
+ movw %dx, %bx
+ addb %bl, %bh
+ xorb %bl, %bl
+ addw %ax, %bx /* offset + match_bit + symbol */
+ leaw (%edi,%ebx,2), %bx /* high word always zero anyway */
+ call rc_bit
+ rclw %ax
+ movb %al, %dh
+ notb %dh
+ xorb %dh, %dl
+ andb %dl, %ch /* offset &= ( match_bit ^ bit ) */
+ testb %ah, %ah
+ jz 1b
+ /* Restore registers, clear unwanted high bit of result, and return */
+ movl %edi, %ebx
+ popw %dx
+ popw %cx
+ popl %edi
+ xorb %ah, %ah
+ ret
+ .size rc_bittree_match, . - rc_bittree_match
+
+/*****************************************************************************
+ * Decode direct bits (no probability estimates)
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %cx : number of bits to decode
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %eax : decoded bits
+ * Corrupts:
+ * none
+ *****************************************************************************
+ */
+rc_direct:
+ /* Preserve registers */
+ pushl %ebx
+ pushw %cx
+ pushl %edx
+ /* Initialise registers */
+ xorl %edx, %edx
+1: /* Perform normalisation */
+ call rc_normalise
+ /* Decode bit */
+ shrl $1, %eax
+ movl %eax, rc_range(%ebp)
+ movl rc_code(%ebp), %ebx
+ subl %eax, %ebx
+ js 2f
+ movl %ebx, rc_code(%ebp)
+2: rcll %ebx
+ rcll %edx
+ xorb $1, %dl
+ ADDR16 loop 1b
+ /* Restore registers and return */
+ movl %edx, %eax
+ popl %edx
+ popw %cx
+ popl %ebx
+ ret
+ .size rc_direct, . - rc_direct
+
+/*****************************************************************************
+ * Decode an LZMA literal
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %es:%edi : uncompressed output data pointer (updated)
+ * %edx : LZMA state
+ * CF : end of payload marker found (always zero)
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ *****************************************************************************
+ *
+ * Literals are coded as an eight-bit tree, using a match byte if the
+ * previous symbol was not a literal.
+ *
+ */
+lzma_literal:
+ /* Get most recent output byte, if available */
+ xorl %ebx, %ebx
+ cmpl %edi, out_start(%ebp)
+ je 1f
+ movb %es:-1(%edi), %bh
+1: /* Locate probability estimate set */
+ shrb $( 8 - LZMA_LC ), %bh
+ shlb $1, %bh
+ leaw literal(%ebx,%ebx,2), %bx
+ /* Get match byte, if applicable */
+ xorw %cx, %cx
+ cmpb $STATE_LIT_MAX, %dl
+ jbe 1f
+ movl rep0(%ebp), %eax
+ notl %eax
+ movb %es:(%edi,%eax), %cl
+ movb $1, %ch
+1: /* Decode bittree */
+ call rc_bittree_match
+ /* Store output byte */
+ ADDR32 stosb
+ print_hex_byte %al
+ print_character $(' ')
+ /* Update LZMA state */
+ subb $3, %dl
+ jns 1f
+ xorb %dl, %dl
+1: cmpb $7, %dl
+ jb 1f
+ subb $3, %dl
+1: /* Clear CF and return */
+ clc
+ ret
+ .size lzma_literal, . - lzma_literal
+
+/*****************************************************************************
+ * Decode an LZMA length
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %ebx : length parameter pointer (offset from %ebp)
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * Corrupts:
+ * %ebx
+ *****************************************************************************
+ *
+ * Lengths are encoded as:
+ *
+ * "0" + 3 bits : lengths 2-9 ("low")
+ * "10" + 3 bits : lengths 10-17 ("mid")
+ * "11" + 8 bits : lengths 18-273 ("high")
+ */
+lzma_len:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ecx
+ pushl %edi
+ movl %ebx, %edi
+ /* Start by assuming three bits and a base length of 2 */
+ movw $3, %cx
+ movw $2, len(%ebp)
+ /* Check low-length choice bit */
+ leal choice(%edi), %ebx
+ call rc_bit
+ leal low(%edi), %ebx
+ jz 1f
+ /* Check high-length choice bit */
+ leal choice2(%edi), %ebx
+ call rc_bit
+ leal mid(%edi), %ebx
+ movb $10, len(%ebp)
+ jz 1f
+ leal high(%edi), %ebx
+ movb $8, %cl
+ movb $18, len(%ebp)
+1: /* Get encoded length */
+ call rc_bittree
+ addw %ax, len(%ebp)
+ /* Restore registers and return */
+ movl %edi, %ebx
+ popl %edi
+ popl %ecx
+ popl %eax
+ ret
+ .size lzma_len, . - lzma_len
+
+/*****************************************************************************
+ * Copy (possibly repeated) matched data
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %es:%edi : uncompressed output data pointer
+ * %cl : repeated match distance index (for repeated matches)
+ * %eax : match distance (for non-repeated matches)
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %es:%edi : uncompressed output data pointer
+ * CF : match distance is out of range
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ *****************************************************************************
+ */
+match: /* Update repeated match list */
+ print_character $('[')
+ movl $3, %ecx
+ jmp 1f
+match_rep:
+ print_character $('[')
+ print_character $('R')
+ print_hex_byte %cl
+ print_character $('=')
+ movzbl %cl, %ecx
+ movl reps(%ebp,%ecx,4), %eax
+ jcxz 2f
+1: movl (reps-4)(%ebp,%ecx,4), %ebx
+ movl %ebx, reps(%ebp,%ecx,4)
+ loop 1b
+ movl %eax, rep0(%ebp)
+2: /* Preserve registers */
+ pushl %esi
+ /* Get stored match length */
+ movzwl len(%ebp), %ecx
+ print_hex_dword %eax
+ print_character $('+')
+ print_hex_word %cx
+ print_character $(']')
+ print_character $(' ')
+ /* Abort with CF set if match distance is out of range */
+ movl out_start(%ebp), %esi
+ negl %esi
+ leal -1(%edi,%esi), %esi
+ cmpl %eax, %esi
+ jc 99f
+ /* Perform copy */
+ notl %eax
+ leal (%edi,%eax), %esi
+ ADDR32 es rep movsb
+99: /* Restore registers and return */
+ popl %esi
+ ret
+ .size match, . - match
+
+/*****************************************************************************
+ * Decode an LZMA match
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * CF : end of payload marker found
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ *****************************************************************************
+ *
+ * Matches are encoded as an LZMA length followed by a 6-bit "distance
+ * slot" code, 0-26 fixed-probability bits, and 0-5 context encoded
+ * bits.
+ */
+lzma_match:
+ /* Preserve registers */
+ pushl %edi
+ /* Update LZMA state */
+ cmpb $STATE_LIT_MAX, %dl
+ movb $STATE_LIT_MATCH, %dl
+ jbe 1f
+ movb $STATE_NONLIT_MATCH, %dl
+1: /* Decode length */
+ movl $match_len_dec, %ebx
+ call lzma_len
+ /* Decode distance slot */
+ movw len(%ebp), %bx
+ subw $2, %bx
+ cmpw $4, %bx
+ jb 1f
+ movw $3, %bx
+1: shlw $7, %bx
+ addw $dist_slot, %bx
+ movw $6, %cx
+ call rc_bittree
+ /* Distance slots 0-3 are literal distances */
+ cmpb $4, %al
+ jb 99f
+ /* Determine initial bits: 10/11 for even/odd distance codes */
+ movl %eax, %edi
+ andw $1, %di
+ orw $2, %di
+ /* Determine number of context-encoded bits */
+ movw %ax, %cx
+ shrb $1, %cl
+ decb %cl
+ /* Select context to be used in absence of fixed-probability bits */
+ movl %edi, %ebx
+ shlw %cl, %bx
+ subw %ax, %bx
+ leaw (dist_special-2)(%ebx,%ebx), %bx
+ /* Decode fixed-probability bits, if any */
+ cmpb $6, %cl
+ jb 1f
+ subb $4, %cl
+ shll %cl, %edi
+ call rc_direct
+ orl %eax, %edi
+ /* Select context to be used in presence of fixed-probability bits */
+ movb $4, %cl
+ movl $dist_align, %ebx
+1: /* Decode context-encoded bits */
+ shll %cl, %edi
+ call rc_bittree_reverse
+ orl %edi, %eax
+99: /* Restore registers and tail-call */
+ popl %edi
+ jmp match
+ .size lzma_match, . - lzma_match
+
+/*****************************************************************************
+ * Decode an LZMA repeated match
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * CF : end of payload marker found
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ *****************************************************************************
+ *
+ * Repeated matches are encoded as:
+ *
+ * "00" : shortrep0 (implicit length 1)
+ * "01" + len : longrep0
+ * "10" + len : longrep1
+ * "110" + len : longrep2
+ * "111" + len : longrep3
+ */
+lzma_rep_match:
+ /* Initially assume longrep0 */
+ movw $(STATE_LIT_LONGREP << 8), %cx
+ /* Get is_rep0 bit */
+ leal is_rep0(,%edx,2), %ebx
+ call rc_bit
+ jnz 1f
+ /* Get is_rep0_long bit */
+ leal is_rep0_long(,%edx,2), %ebx
+ call rc_bit
+ jnz 98f
+ movw $1, len(%ebp)
+ movb $STATE_LIT_SHORTREP, %ch
+ jmp 99f
+1: /* Get is_rep1 bit */
+ incb %cl
+ leal is_rep1(,%edx,2), %ebx
+ call rc_bit
+ jz 98f
+ /* Get is_rep2 bit */
+ incb %cl
+ leal is_rep2(,%edx,2), %ebx
+ call rc_bit
+ adcb $0, %cl
+98: /* Decode length */
+ movl $rep_len_dec, %ebx
+ call lzma_len
+99: /* Update LZMA state */
+ cmpb $STATE_LIT_MAX, %dl
+ movb %ch, %dl
+ jbe 1f
+ movb $STATE_NONLIT_REP, %dl
+1: /* Tail call */
+ jmp match_rep
+ .size lzma_match, . - lzma_match
+
+/*****************************************************************************
+ * Decode one LZMA symbol
+ *
+ * Parameters:
+ * %ss:%ebp : LZMA parameter block
+ * %ds:%esi : compressed input data pointer
+ * %es:%edi : uncompressed output data pointer
+ * %edx : LZMA state
+ * Returns:
+ * %ds:%esi : compressed input data pointer (possibly updated)
+ * %es:%edi : uncompressed output data pointer (updated)
+ * %edx : LZMA state
+ * CF : end of payload marker found
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ *****************************************************************************
+ */
+lzma_decode:
+ /* Get is_match bit */
+ leal is_match(,%edx,2), %ebx
+ call rc_bit
+ jz lzma_literal
+ /* Get is_rep bit */
+ leal is_rep(,%edx,2), %ebx
+ call rc_bit
+ jz lzma_match
+ jmp lzma_rep_match
+ .size lzma_decode, . - lzma_decode
+
+/****************************************************************************
+ * Undo effect of branch-call-jump (BCJ) filter
+ *
+ * Parameters:
+ * %es:%esi : start of uncompressed output data (note %es)
+ * %es:%edi : end of uncompressed output data
+ * Returns:
+ * Corrupts:
+ * %eax
+ * %ebx
+ * %ecx
+ * %edx
+ * %esi
+ *****************************************************************************
+ */
+bcj_filter:
+ /* Store (negative) start of data in %edx */
+ movl %esi, %edx
+ negl %edx
+ /* Calculate limit in %ecx */
+ leal -5(%edi,%edx), %ecx
+1: /* Calculate offset in %ebx */
+ leal (%esi,%edx), %ebx
+ /* Check for end of data */
+ cmpl %ecx, %ebx
+ ja 99f
+ /* Check for an opcode which would be followed by a rel32 address */
+ ADDR32 es lodsb
+ andb $0xfe, %al
+ cmpb $0xe8, %al
+ jne 1b
+ /* Get current jump target value in %eax */
+ ADDR32 es lodsl
+ /* Convert absolute addresses in the range [0,limit) back to
+ * relative addresses in the range [-offset,limit-offset).
+ */
+ cmpl %ecx, %eax
+ jae 2f
+ subl %ebx,%es:-4(%esi)
+2: /* Convert negative numbers in the range [-offset,0) back to
+ * positive numbers in the range [limit-offset,limit).
+ */
+ notl %eax /* Range is now [0,offset) */
+ cmpl %ebx, %eax
+ jae 1b
+ addl %ecx,%es:-4(%esi)
+ jmp 1b
+99: /* Return */
+ ret
+ .size bcj_filter, . - bcj_filter
+
+/****************************************************************************
+ * decompress (real-mode or 16/32-bit protected-mode near call)
+ *
+ * Decompress data
+ *
+ * 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 16-bit registers
+ * where possible.
+ ****************************************************************************
+ */
+ .globl decompress
+decompress:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %ebp
+ /* Allocate parameter block */
+ subl $sizeof__lzma_dec, %esp
+ movl %esp, %ebp
+ /* Zero parameter block and set all probabilities to 0.5 */
+ pushl %edi
+ pushw %es
+ pushw %ss
+ popw %es
+ movl %ebp, %edi
+ xorl %eax, %eax
+ movl $( sizeof__lzma_dec / 4 ), %ecx
+ ADDR32 rep stosl
+ leal probs(%ebp), %edi
+ movw $( ( 1 << 11 ) / 2 ), %ax
+ movl $( ( sizeof__lzma_dec - probs ) / 2 ), %ecx
+ ADDR32 rep stosw
+ popw %es
+ popl %edi
+ /* Initialise remaining parameters */
+ movl %edi, out_start(%ebp)
+ print_character $('\n')
+ ADDR32 lodsb /* discard initial byte */
+ print_hex_byte %al
+ ADDR32 lodsl
+ bswapl %eax
+ print_hex_dword %eax
+ print_character $('\n')
+ movl %eax, rc_code(%ebp)
+ decl rc_range(%ebp)
+ movl $STATE_LIT_LIT, %edx
+1: /* Decompress until we reach end of buffer */
+ call lzma_decode
+ jnc 1b
+ call rc_normalise
+ print_character $('\n')
+ /* Undo BCJ filter */
+ pushl %esi
+ movl out_start(%ebp), %esi
+ call bcj_filter
+ popl %esi
+ /* Restore registers and return */
+ addl $sizeof__lzma_dec, %esp
+ popl %ebp
+ popl %edx
+ popl %ecx
+ popl %ebx
+ popl %eax
+ ret
+
+ /* Specify minimum amount of stack space required */
+ .globl _min_decompress_stack
+ .equ _min_decompress_stack, ( sizeof__lzma_dec + 512 /* margin */ )
diff --git a/roms/ipxe/src/arch/i386/prefix/unlzma16.S b/roms/ipxe/src/arch/i386/prefix/unlzma16.S
new file mode 100644
index 000000000..32b43f0dc
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/unlzma16.S
@@ -0,0 +1,9 @@
+/*
+ * 16-bit version of the decompressor
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+#define CODE16
+#include "unlzma.S"
diff --git a/roms/ipxe/src/arch/i386/prefix/usbdisk.S b/roms/ipxe/src/arch/i386/prefix/usbdisk.S
new file mode 100644
index 000000000..9676406e2
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/prefix/usbdisk.S
@@ -0,0 +1,34 @@
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+#include "mbr.S"
+
+/* Partition table: 64 heads, 32 sectors/track (ZIP-drive compatible) */
+ .org 446
+ .space 16
+ .space 16
+ /* Partition 3: log partition (for CONSOLE_INT13) */
+ .byte 0x00, 0x01, 0x01, 0x00
+ .byte 0xe0, 0x3f, 0x20, 0x00
+ .long 0x00000020
+ .long 0x000007e0
+ /* Partition 4: boot partition */
+ .byte 0x80, 0x00, 0x01, 0x01
+ .byte 0xeb, 0x3f, 0x20, 0x02
+ .long 0x00000800
+ .long 0x00001000
+
+ .org 510
+ .byte 0x55, 0xaa
+
+/* Skip to start of log partition */
+ .org 32 * 512
+ .ascii "iPXE LOG\n\n"
+
+/* Skip to start of boot partition */
+ .org 2048 * 512
diff --git a/roms/ipxe/src/arch/i386/scripts/i386.lds b/roms/ipxe/src/arch/i386/scripts/i386.lds
new file mode 100644
index 000000000..38c89e14b
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/scripts/i386.lds
@@ -0,0 +1,254 @@
+/* -*- ld-script -*- */
+
+/*
+ * Linker script for i386 images
+ *
+ */
+
+SECTIONS {
+
+ /* Each section starts at a virtual address of zero.
+ *
+ * We guarantee alignment of virtual addresses to any alignment
+ * specified by the constituent object files (e.g. via
+ * __attribute__((aligned(x)))). Load addresses are guaranteed
+ * only up to _max_align. Provided that all loader and relocation
+ * code honours _max_align, this means that physical addresses are
+ * also guaranteed up to _max_align.
+ *
+ * Note that when using -DKEEP_IT_REAL, the UNDI segments are only
+ * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte
+ * alignment). Using _max_align>16 will therefore not guarantee
+ * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is
+ * used (though virtual addresses will still be fully aligned).
+ *
+ */
+
+ PROVIDE ( _max_align = 16 );
+
+ /*
+ * Allow decompressor to require a minimum amount of temporary stack
+ * space.
+ *
+ */
+ PROVIDE ( _min_decompress_stack = 0 );
+
+ /*
+ * The prefix
+ *
+ */
+
+ .prefix 0x0 : AT ( _prefix_lma ) {
+ _prefix = .;
+ *(.prefix)
+ *(SORT(.pci_devlist.*))
+ *(.prefix.*)
+ _mprefix = .;
+ } .bss.prefix (NOLOAD) : AT ( _end_lma ) {
+ _eprefix = .;
+ }
+ _prefix_filesz = ABSOLUTE ( _mprefix ) - ABSOLUTE ( _prefix );
+ _prefix_memsz = ABSOLUTE ( _eprefix ) - ABSOLUTE ( _prefix );
+
+ /*
+ * The 16-bit (real-mode) code section
+ *
+ */
+
+ .text16.early 0x0 : AT ( _text16_early_lma ) {
+ _text16 = .;
+ KEEP(*(.text16.null))
+ KEEP(*(.text16.null.*))
+ . += 1; /* Prevent NULL being valid */
+ *(.text16.early)
+ *(.text16.early.*)
+ _etext16_early = .;
+ } .text16.late ALIGN ( _max_align ) : AT ( _text16_late_lma ) {
+ _text16_late = .;
+ *(.text16)
+ *(.text16.*)
+ _mtext16 = .;
+ } .bss.text16 (NOLOAD) : AT ( _end_lma ) {
+ _etext16 = .;
+ }
+ _text16_early_filesz = ABSOLUTE ( _etext16_early ) - ABSOLUTE ( _text16 );
+ _text16_early_memsz = ABSOLUTE ( _etext16_early ) - ABSOLUTE ( _text16 );
+ _text16_late_filesz = ABSOLUTE ( _mtext16 ) - ABSOLUTE ( _text16_late );
+ _text16_late_memsz = ABSOLUTE ( _etext16 ) - ABSOLUTE ( _text16_late );
+ _text16_memsz = ABSOLUTE ( _etext16 ) - ABSOLUTE ( _text16 );
+
+ /*
+ * The 16-bit (real-mode) data section
+ *
+ */
+
+ .data16 0x0 : AT ( _data16_lma ) {
+ _data16 = .;
+ . += 1; /* Prevent NULL being valid */
+ *(.rodata16)
+ *(.rodata16.*)
+ *(.data16)
+ *(.data16.*)
+ _mdata16 = .;
+ } .bss.data16 (NOLOAD) : AT ( _end_lma ) {
+ *(.bss16)
+ *(.bss16.*)
+ *(.stack16)
+ *(.stack16.*)
+ . = MAX ( ., _mdata16 + _min_decompress_stack );
+ _edata16 = .;
+ }
+ _data16_filesz = ABSOLUTE ( _mdata16 ) - ABSOLUTE ( _data16 );
+ _data16_memsz = ABSOLUTE ( _edata16 ) - ABSOLUTE ( _data16 );
+
+ /*
+ * The 32-bit sections
+ *
+ */
+
+ .textdata 0x0 : AT ( _textdata_lma ) {
+ _textdata = .;
+ KEEP(*(.text.null_trap))
+ KEEP(*(.text.null_trap.*))
+ . += 1; /* Prevent NULL being valid */
+ *(.text)
+ *(.text.*)
+ *(.rodata)
+ *(.rodata.*)
+ *(.data)
+ *(.data.*)
+ KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */
+ KEEP(*(.provided))
+ KEEP(*(.provided.*))
+ _mtextdata = .;
+ } .bss.textdata (NOLOAD) : AT ( _end_lma ) {
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ *(.stack)
+ *(.stack.*)
+ _etextdata = .;
+ }
+ _textdata_filesz = ABSOLUTE ( _mtextdata ) - ABSOLUTE ( _textdata );
+ _textdata_memsz = ABSOLUTE ( _etextdata ) - ABSOLUTE ( _textdata );
+
+ /*
+ * Payload prefix
+ *
+ * If present, this will be placed between .text16.early and .text16.late.
+ *
+ */
+ .pprefix 0x0 : AT ( _pprefix_lma ) {
+ _pprefix = .;
+ KEEP(*(.pprefix))
+ KEEP(*(.pprefix.*))
+ _mpprefix = .;
+ } .bss.pprefix (NOLOAD) : AT ( _end_lma ) {
+ _epprefix = .;
+ }
+ _pprefix_filesz = ABSOLUTE ( _mpprefix ) - ABSOLUTE ( _pprefix );
+ _pprefix_memsz = ABSOLUTE ( _epprefix ) - ABSOLUTE ( _pprefix );
+
+ /*
+ * Compressor information block
+ *
+ */
+
+ .zinfo 0x0 : AT ( _zinfo_lma ) {
+ _zinfo = .;
+ KEEP(*(.zinfo))
+ KEEP(*(.zinfo.*))
+ _mzinfo = .;
+ } .bss.zinfo (NOLOAD) : AT ( _end_lma ) {
+ _ezinfo = .;
+ }
+ _zinfo_filesz = ABSOLUTE ( _mzinfo ) - ABSOLUTE ( _zinfo );
+ _zinfo_memsz = ABSOLUTE ( _ezinfo ) - ABSOLUTE ( _zinfo );
+
+ /*
+ * Weak symbols that need zero values if not otherwise defined
+ *
+ */
+
+ .weak 0x0 : AT ( _end_lma ) {
+ _weak = .;
+ *(.weak)
+ *(.weak.*)
+ _eweak = .;
+ }
+ _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
+
+ /*
+ * Dispose of the comment and note sections to make the link map
+ * easier to read
+ *
+ */
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.comment.*)
+ *(.note)
+ *(.note.*)
+ *(.eh_frame)
+ *(.eh_frame.*)
+ *(.rel)
+ *(.rel.*)
+ *(.einfo)
+ *(.einfo.*)
+ *(.discard)
+ *(.discard.*)
+ }
+
+ /*
+ * Load address calculations. In older versions of ld, ALIGN()
+ * can operate only on the location counter, so we use that.
+ *
+ */
+
+ . = 0;
+
+ . = ALIGN ( _max_align );
+ _prefix_lma = .;
+ . += _prefix_filesz;
+
+ . = ALIGN ( _max_align );
+ _text16_early_lma = .;
+ . += _text16_early_filesz;
+
+ . = ALIGN ( _max_align );
+ . = ALIGN ( _payload_align );
+ _pprefix_lma = .;
+ . += _pprefix_filesz;
+
+ . = ALIGN ( _max_align );
+ _payload_lma = .;
+ _pprefix_skip = ABSOLUTE ( _payload_lma ) - ABSOLUTE ( _pprefix_lma );
+ _text16_late_lma = .;
+ . += _text16_late_filesz;
+
+ . = ALIGN ( _max_align );
+ _data16_lma = .;
+ . += _data16_filesz;
+
+ . = ALIGN ( _max_align );
+ _textdata_lma = .;
+ . += _textdata_filesz;
+
+ _filesz = .; /* Do not include zinfo block in file size */
+
+ . = ALIGN ( _max_align );
+ _zinfo_lma = .;
+ . += _zinfo_filesz;
+
+ . = ALIGN ( _max_align );
+ _end_lma = .;
+
+ /*
+ * Values calculated to save code from doing it
+ *
+ */
+ _text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 );
+ _data16_memsz_pgh = ( ( _data16_memsz + 15 ) / 16 );
+ _textdata_memsz_pgh = ( ( _textdata_memsz + 15 ) / 16 );
+ _textdata_memsz_kb = ( ( _textdata_memsz + 1023 ) / 1024 );
+}
diff --git a/roms/ipxe/src/arch/i386/tests/gdbstub_test.S b/roms/ipxe/src/arch/i386/tests/gdbstub_test.S
deleted file mode 100644
index 739b0527a..000000000
--- a/roms/ipxe/src/arch/i386/tests/gdbstub_test.S
+++ /dev/null
@@ -1,54 +0,0 @@
- .arch i386
-
- .section ".data", "aw", @progbits
-watch_me:
- .long 0xfeedbeef
-
- .section ".text", "ax", @progbits
- .code32
-gdbstub_test:
- /* 1. Read registers test */
- movl $0xea010203, %eax
- movl $0xeb040506, %ebx
- movl $0xec070809, %ecx
- movl $0xed0a0b0c, %edx
- movl $0x510d0e0f, %esi
- movl $0xd1102030, %edi
- int $3
-
- /* 2. Write registers test */
- int $3
-
- /* 3. Read memory test */
- subl $8, %esp
- movl $0x11223344, 4(%esp)
- movw $0x5566, 2(%esp)
- movb $0x77, (%esp)
- int $3
-
- /* 4. Write memory test */
- int $3
- addl $8, %esp
-
- /* 5. Step test */
- int $3
- nop
-
- /* 6. Access watch test */
- movl $0x600d0000, %ecx
- movl watch_me, %eax
- movl $0xbad00000, %ecx
- int $3
- movl $0x600d0001, %ecx
- movl %eax, watch_me
- movl $0xbad00001, %ecx
- int $3
-
- /* 7. Write watch test */
- movl $0x600d0002, %ecx
- movl %eax, watch_me
- movl $0xbad00002, %ecx
- int $3
-
-1:
- jmp 1b
diff --git a/roms/ipxe/src/arch/i386/tests/gdbstub_test.gdb b/roms/ipxe/src/arch/i386/tests/gdbstub_test.gdb
deleted file mode 100755
index bcfa07dae..000000000
--- a/roms/ipxe/src/arch/i386/tests/gdbstub_test.gdb
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/gdb -x
-# Test suite for GDB remote debugging
-# Run:
-# make bin/ipxe.hd.tmp
-# make
-# gdb
-# (gdb) target remote :TCPPORT
-# OR
-# (gdb) target remote udp:IP:UDPPORT
-# (gdb) source tests/gdbstub_test.gdb
-
-define ipxe_load_symbols
- file bin/ipxe.hd.tmp
-end
-
-define ipxe_assert
- if $arg0 != $arg1
- echo FAIL $arg2\n
- else
- echo PASS $arg2\n
- end
-end
-
-define ipxe_start_tests
- jump gdbstub_test
-end
-
-define ipxe_test_regs_read
- ipxe_assert $eax 0xea010203 "ipxe_test_regs_read eax"
- ipxe_assert $ebx 0xeb040506 "ipxe_test_regs_read ebx"
- ipxe_assert $ecx 0xec070809 "ipxe_test_regs_read ecx"
- ipxe_assert $edx 0xed0a0b0c "ipxe_test_regs_read edx"
- ipxe_assert $esi 0x510d0e0f "ipxe_test_regs_read esi"
- ipxe_assert $edi 0xd1102030 "ipxe_test_regs_read edi"
-end
-
-define ipxe_test_regs_write
- set $eax = 0xea112233
- set $ebx = 0xeb445566
- set $ecx = 0xec778899
- set $edx = 0xedaabbcc
- set $esi = 0x51ddeeff
- set $edi = 0xd1010203
- c
- ipxe_assert $eax 0xea112233 "ipxe_test_regs_write eax"
- ipxe_assert $ebx 0xeb445566 "ipxe_test_regs_write ebx"
- ipxe_assert $ecx 0xec778899 "ipxe_test_regs_write ecx"
- ipxe_assert $edx 0xedaabbcc "ipxe_test_regs_write edx"
- ipxe_assert $esi 0x51ddeeff "ipxe_test_regs_write esi"
- ipxe_assert $edi 0xd1010203 "ipxe_test_regs_write edi"
-
- # This assumes segment selectors are always 0x10 or 0x8 (for code).
- ipxe_assert $cs 0x08 "ipxe_test_regs_write cs"
- ipxe_assert $ds 0x10 "ipxe_test_regs_write ds"
-end
-
-define ipxe_test_mem_read
- c
- ipxe_assert ({int}($esp+4)) 0x11223344 "ipxe_test_mem_read int"
- ipxe_assert ({short}($esp+2)) 0x5566 "ipxe_test_mem_read short"
- ipxe_assert ({char}($esp)) 0x77 "ipxe_test_mem_read char"
-end
-
-define ipxe_test_mem_write
- set ({int}($esp+4)) = 0xaabbccdd
- set ({short}($esp+2)) = 0xeeff
- set ({char}($esp)) = 0x99
- c
- ipxe_assert ({int}($esp+4)) 0xaabbccdd "ipxe_test_mem_write int"
- ipxe_assert ({short}($esp+2)) (short)0xeeff "ipxe_test_mem_write short"
- ipxe_assert ({char}($esp)) (char)0x99 "ipxe_test_mem_write char"
-end
-
-define ipxe_test_step
- c
- si
- ipxe_assert ({char}($eip-1)) (char)0x90 "ipxe_test_step" # nop = 0x90
-end
-
-define ipxe_test_awatch
- awatch watch_me
-
- c
- ipxe_assert $ecx 0x600d0000 "ipxe_test_awatch read"
- if $ecx == 0x600d0000
- c
- end
-
- c
- ipxe_assert $ecx 0x600d0001 "ipxe_test_awatch write"
- if $ecx == 0x600d0001
- c
- end
-
- delete
-end
-
-define ipxe_test_watch
- watch watch_me
- c
- ipxe_assert $ecx 0x600d0002 "ipxe_test_watch"
- if $ecx == 0x600d0002
- c
- end
- delete
-end
-
-ipxe_load_symbols
-ipxe_start_tests
-ipxe_test_regs_read
-ipxe_test_regs_write
-ipxe_test_mem_read
-ipxe_test_mem_write
-ipxe_test_step
-ipxe_test_awatch
-ipxe_test_watch
diff --git a/roms/ipxe/src/arch/i386/transitions/liba20.S b/roms/ipxe/src/arch/i386/transitions/liba20.S
new file mode 100644
index 000000000..6c1e1f62f
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/liba20.S
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+ .arch i386
+
+/****************************************************************************
+ * test_a20_short, test_a20_long
+ *
+ * Check to see if A20 line is enabled
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if A20 line is not enabled
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+#define TEST_A20_SHORT_MAX_RETRIES 0x20
+#define TEST_A20_LONG_MAX_RETRIES 0x200000
+ .section ".text16.early", "awx", @progbits
+ .code16
+test_a20_short:
+ pushl %ecx
+ movl $TEST_A20_SHORT_MAX_RETRIES, %ecx
+ jmp 1f
+ .size test_a20_short, . - test_a20_short
+test_a20_long:
+ pushl %ecx
+ movl $TEST_A20_LONG_MAX_RETRIES, %ecx
+1: pushw %ax
+ pushw %ds
+ pushw %es
+
+ /* Set up segment registers for access across the 1MB boundary */
+ xorw %ax, %ax
+ movw %ax, %ds
+ decw %ax
+ movw %ax, %es
+
+2: /* Modify and check test pattern; succeed if we see a difference */
+ pushfw
+ cli
+ xchgw %ds:0, %cx
+ movw %es:0x10, %ax
+ xchgw %ds:0, %cx
+ popfw
+ cmpw %ax, %cx
+ clc
+ jnz 99f
+
+ /* Delay and retry */
+ outb %al, $0x80
+ addr32 loop 2b
+ stc
+
+99: /* Restore registers and return */
+ popw %es
+ popw %ds
+ popw %ax
+ popl %ecx
+ ret
+ .size test_a20_long, . - test_a20_long
+
+/****************************************************************************
+ * enable_a20_bios
+ *
+ * Try enabling A20 line via BIOS
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if A20 line is not enabled
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16.early", "awx", @progbits
+ .code16
+enable_a20_bios:
+
+ /* Preserve registers. Be very paranoid, since some BIOSes
+ * are reported to clobber %ebx
+ */
+ pushal
+
+ /* Attempt INT 15,2401 */
+ movw $0x2401, %ax
+ int $0x15
+ jc 99f
+
+ /* Check that success was really successful */
+ call test_a20_short
+
+99: /* Restore registers and return */
+ popal
+ ret
+ .size enable_a20_bios, . - enable_a20_bios
+
+/****************************************************************************
+ * enable_a20_kbc
+ *
+ * Try enabling A20 line via keyboard controller
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if A20 line is not enabled
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+#define KC_RDWR 0x60
+#define KC_RDWR_SET_A20 0xdf
+#define KC_CMD 0x64
+#define KC_CMD_WOUT 0xd1
+#define KC_CMD_NULL 0xff
+#define KC_STATUS 0x64
+#define KC_STATUS_OBUF_FULL 0x01
+#define KC_STATUS_IBUF_FULL 0x02
+#define KC_MAX_RETRIES 100000
+ .section ".text16.early", "awx", @progbits
+ .code16
+enable_a20_kbc:
+ /* Preserve registers */
+ pushw %ax
+
+ /* Try keyboard controller */
+ call empty_kbc
+ movb $KC_CMD_WOUT, %al
+ outb %al, $KC_CMD
+ call empty_kbc
+ movb $KC_RDWR_SET_A20, %al
+ outb %al, $KC_RDWR
+ call empty_kbc
+ movb $KC_CMD_NULL, %al
+ outb %al, $KC_CMD
+ call empty_kbc
+
+ /* Check to see if it worked */
+ call test_a20_long
+
+ /* Restore registers and return */
+ popw %ax
+ ret
+ .size enable_a20_kbc, . - enable_a20_kbc
+
+ .section ".text16.early", "awx", @progbits
+ .code16
+empty_kbc:
+ /* Preserve registers */
+ pushl %ecx
+ pushw %ax
+
+ /* Wait for KBC to become empty */
+ movl $KC_MAX_RETRIES, %ecx
+1: outb %al, $0x80
+ inb $KC_STATUS, %al
+ testb $( KC_STATUS_OBUF_FULL | KC_STATUS_IBUF_FULL ), %al
+ jz 99f
+ testb $KC_STATUS_OBUF_FULL, %al
+ jz 2f
+ outb %al, $0x80
+ inb $KC_RDWR, %al
+2: addr32 loop 1b
+
+99: /* Restore registers and return */
+ popw %ax
+ popl %ecx
+ ret
+ .size empty_kbc, . - empty_kbc
+
+/****************************************************************************
+ * enable_a20_fast
+ *
+ * Try enabling A20 line via "Fast Gate A20"
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if A20 line is not enabled
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+#define SCP_A 0x92
+ .section ".text16.early", "awx", @progbits
+ .code16
+enable_a20_fast:
+ /* Preserve registers */
+ pushw %ax
+
+ /* Try "Fast Gate A20" */
+ inb $SCP_A, %al
+ orb $0x02, %al
+ andb $~0x01, %al
+ outb %al, $SCP_A
+
+ /* Check to see if it worked */
+ call test_a20_long
+
+ /* Restore registers and return */
+ popw %ax
+ ret
+ .size enable_a20_fast, . - enable_a20_fast
+
+/****************************************************************************
+ * enable_a20
+ *
+ * Try enabling A20 line via any available method
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if A20 line is not enabled
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+#define ENABLE_A20_RETRIES 255
+ .section ".text16.early", "awx", @progbits
+ .code16
+ .globl enable_a20
+enable_a20:
+ /* Preserve registers */
+ pushl %ecx
+ pushw %ax
+
+ /* Check to see if A20 is already enabled */
+ call test_a20_short
+ jnc 99f
+
+ /* Use known working method, if we have one */
+ movw %cs:enable_a20_method, %ax
+ testw %ax, %ax
+ jz 1f
+ call *%ax
+ jmp 99f
+1:
+ /* Try all methods in turn until one works */
+ movl $ENABLE_A20_RETRIES, %ecx
+2: movw $enable_a20_bios, %ax
+ movw %ax, %cs:enable_a20_method
+ call *%ax
+ jnc 99f
+ movw $enable_a20_kbc, %ax
+ movw %ax, %cs:enable_a20_method
+ call *%ax
+ jnc 99f
+ movw $enable_a20_fast, %ax
+ movw %ax, %cs:enable_a20_method
+ call *%ax
+ jnc 99f
+ addr32 loop 2b
+ /* Failure; exit with carry set */
+ movw $0, %cs:enable_a20_method
+ stc
+
+99: /* Restore registers and return */
+ popw %ax
+ popl %ecx
+ ret
+
+ .section ".text16.early.data", "aw", @progbits
+ .align 2
+enable_a20_method:
+ .word 0
+ .size enable_a20_method, . - enable_a20_method
+
+/****************************************************************************
+ * access_highmem (real mode far call)
+ *
+ * Open up access to high memory with A20 enabled
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * CF set if high memory could not be accessed
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16.early", "awx", @progbits
+ .code16
+ .globl access_highmem
+access_highmem:
+ /* Enable A20 line */
+ call enable_a20
+ lret
+ .size access_highmem, . - access_highmem
diff --git a/roms/ipxe/src/arch/i386/transitions/libkir.S b/roms/ipxe/src/arch/i386/transitions/libkir.S
new file mode 100644
index 000000000..fa9459d52
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/libkir.S
@@ -0,0 +1,256 @@
+/*
+ * libkir: a transition library for -DKEEP_IT_REAL
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/****************************************************************************
+ * This file defines libkir: an interface between external and
+ * internal environments when -DKEEP_IT_REAL is used, so that both
+ * internal and external environments are in real mode. It deals with
+ * switching data segments and the stack. It provides the following
+ * functions:
+ *
+ * ext_to_kir & switch between external and internal (kir)
+ * kir_to_ext environments, preserving all non-segment
+ * registers
+ *
+ * kir_call issue a call to an internal routine from external
+ * code
+ *
+ * libkir is written to avoid assuming that segments are anything
+ * other than opaque data types, and also avoids assuming that the
+ * stack pointer is 16-bit. This should enable it to run just as well
+ * in 16:16 or 16:32 protected mode as in real mode.
+ ****************************************************************************
+ */
+
+/* Breakpoint for when debugging under bochs */
+#define BOCHSBP xchgw %bx, %bx
+
+ .text
+ .arch i386
+ .section ".text16", "awx", @progbits
+ .code16
+
+/****************************************************************************
+ * init_libkir (real-mode or 16:xx protected-mode far call)
+ *
+ * Initialise libkir ready for transitions to the kir environment
+ *
+ * Parameters:
+ * %cs : .text16 segment
+ * %ds : .data16 segment
+ ****************************************************************************
+ */
+ .globl init_libkir
+init_libkir:
+ /* Record segment registers */
+ pushw %ds
+ popw %cs:kir_ds
+ lret
+
+/****************************************************************************
+ * ext_to_kir (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from external stack and segment registers to internal stack
+ * and segment registers. %ss:sp is restored from the saved kir_ds
+ * and kir_sp. %ds, %es, %fs and %gs are all restored from the saved
+ * kir_ds. All other registers are preserved.
+ *
+ * %cs:0000 must point to the start of the runtime image code segment
+ * on entry.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl ext_to_kir
+ext_to_kir:
+ /* Record external segment registers */
+ movw %ds, %cs:ext_ds
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ movw %es, %ds:ext_es
+ movw %fs, %ds:ext_fs
+ movw %gs, %ds:ext_fs
+
+ /* Preserve registers */
+ movw %ax, %ds:save_ax
+
+ /* Extract near return address from stack */
+ popw %ds:save_retaddr
+
+ /* Record external %ss:esp */
+ movw %ss, %ds:ext_ss
+ movl %esp, %ds:ext_esp
+
+ /* Load internal segment registers and stack pointer */
+ movw %ds:kir_ds, %ax
+ movw %ax, %ss
+ movzwl %ds:kir_sp, %esp
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+1:
+
+ /* Place return address on new stack */
+ pushw %cs:save_retaddr
+
+ /* Restore registers and return */
+ movw %cs:save_ax, %ax
+ ret
+
+/****************************************************************************
+ * kir_to_ext (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from internal stack and segment registers to external stack
+ * and segment registers. %ss:%esp is restored from the saved ext_ss
+ * and ext_esp. Other segment registers are restored from the
+ * corresponding locations. All other registers are preserved.
+ *
+ * Note that it is actually %ss that is recorded as kir_ds, on the
+ * assumption that %ss == %ds when kir_to_ext is called.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl kir_to_ext
+kir_to_ext:
+ /* Record near return address */
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ popw %ds:save_retaddr
+
+ /* Record internal segment registers and %sp */
+ movw %ss, %ds:kir_ds
+ movw %sp, %ds:kir_sp
+
+ /* Load external segment registers and stack pointer */
+ movw %ds:ext_ss, %ss
+ movl %ds:ext_esp, %esp
+ movw %ds:ext_gs, %gs
+ movw %ds:ext_fs, %fs
+ movw %ds:ext_es, %es
+ movw %ds:ext_ds, %ds
+
+ /* Return */
+ pushw %cs:save_retaddr
+ ret
+
+/****************************************************************************
+ * kir_call (real-mode or 16:xx protected-mode far call)
+ *
+ * Call a specific C function in the internal code. The prototype of
+ * the C function must be
+ * void function ( struct i386_all_resg *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
+ * at entry to kir_call.
+ *
+ * All registers will be preserved across kir_call(), unless the C
+ * function explicitly overwrites values in ix86. Interrupt status
+ * will also be preserved.
+ *
+ * Parameters:
+ * function : (32-bit) virtual address of C function to call
+ *
+ * Example usage:
+ * pushl $pxe_api_call
+ * lcall $UNDI_CS, $kir_call
+ * addw $4, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
+ ****************************************************************************
+ */
+
+ .globl kir_call
+kir_call:
+ /* Preserve flags. Must do this before any operation that may
+ * affect flags.
+ */
+ pushfl
+ popl %cs:save_flags
+
+ /* Disable interrupts. We do funny things with the stack, and
+ * we're not re-entrant.
+ */
+ cli
+
+ /* Extract address of internal routine from stack. We must do
+ * this without using (%bp), because we may be called with
+ * either a 16-bit or a 32-bit stack segment.
+ */
+ popl %cs:save_retaddr /* Scratch location */
+ popl %cs:save_function
+ subl $8, %esp /* Restore %esp */
+
+ /* Switch to internal stack. Note that the external stack is
+ * inaccessible once we're running internally (since we have
+ * no concept of 48-bit far pointers)
+ */
+ call ext_to_kir
+
+ /* Store external registers on internal stack */
+ pushl %cs:save_flags
+ pushal
+ pushl %cs:ext_fs_and_gs
+ pushl %cs:ext_ds_and_es
+ pushl %cs:ext_cs_and_ss
+
+ /* Push &ix86 on stack and call function */
+ sti
+ pushl %esp
+ data32 call *%cs:save_function
+ popl %eax /* discard */
+
+ /* Restore external registers from internal stack */
+ popl %cs:ext_cs_and_ss
+ popl %cs:ext_ds_and_es
+ popl %cs:ext_fs_and_gs
+ popal
+ popl %cs:save_flags
+
+ /* Switch to external stack */
+ call kir_to_ext
+
+ /* Restore flags */
+ pushl %cs:save_flags
+ popfl
+
+ /* Return */
+ lret
+
+/****************************************************************************
+ * Stored internal and external stack and segment registers
+ ****************************************************************************
+ */
+
+ext_cs_and_ss:
+ext_cs: .word 0
+ext_ss: .word 0
+ext_ds_and_es:
+ext_ds: .word 0
+ext_es: .word 0
+ext_fs_and_gs:
+ext_fs: .word 0
+ext_gs: .word 0
+ext_esp: .long 0
+
+ .globl kir_ds
+kir_ds: .word 0
+ .globl kir_sp
+kir_sp: .word _estack
+
+/****************************************************************************
+ * Temporary variables
+ ****************************************************************************
+ */
+save_ax: .word 0
+save_retaddr: .long 0
+save_flags: .long 0
+save_function: .long 0
diff --git a/roms/ipxe/src/arch/i386/transitions/libpm.S b/roms/ipxe/src/arch/i386/transitions/libpm.S
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/libpm.S
diff --git a/roms/ipxe/src/arch/i386/transitions/librm.S b/roms/ipxe/src/arch/i386/transitions/librm.S
new file mode 100644
index 000000000..863e22415
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/librm.S
@@ -0,0 +1,671 @@
+/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/* Drag in local definitions */
+#include "librm.h"
+
+/* For switches to/from protected mode */
+#define CR0_PE 1
+
+/* Size of various C data structures */
+#define SIZEOF_I386_SEG_REGS 12
+#define SIZEOF_I386_REGS 32
+#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
+#define SIZEOF_I386_FLAGS 4
+#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
+
+ .arch i386
+
+/****************************************************************************
+ * Global descriptor table
+ *
+ * Call init_librm to set up the GDT before attempting to use any
+ * protected-mode code.
+ *
+ * NOTE: This must be located before prot_to_real, otherwise gas
+ * throws a "can't handle non absolute segment in `ljmp'" error due to
+ * not knowing the value of REAL_CS when the ljmp is encountered.
+ *
+ * Note also that putting ".word gdt_end - gdt - 1" directly into
+ * gdt_limit, rather than going via gdt_length, will also produce the
+ * "non absolute segment" error. This is most probably a bug in gas.
+ ****************************************************************************
+ */
+ .section ".data16", "aw", @progbits
+ .align 16
+gdt:
+gdtr: /* The first GDT entry is unused, the GDTR can fit here. */
+gdt_limit: .word gdt_length - 1
+gdt_base: .long 0
+ .word 0 /* padding */
+
+ .org gdt + VIRTUAL_CS, 0
+virtual_cs: /* 32 bit protected mode code segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + VIRTUAL_DS, 0
+virtual_ds: /* 32 bit protected mode data segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ .org gdt + PHYSICAL_CS, 0
+physical_cs: /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + PHYSICAL_DS, 0
+physical_ds: /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ .org gdt + REAL_CS, 0
+real_cs: /* 16 bit real mode code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9b, 0x00, 0
+
+ .org gdt + REAL_DS
+real_ds: /* 16 bit real mode data segment */
+ .word 0xffff, ( REAL_DS << 4 )
+ .byte 0, 0x93, 0x00, 0
+
+gdt_end:
+ .equ gdt_length, gdt_end - gdt
+
+/****************************************************************************
+ * init_librm (real-mode far call, 16-bit real-mode far return address)
+ *
+ * Initialise the GDT ready for transitions to protected mode.
+ *
+ * Parameters:
+ * %cs : .text16 segment
+ * %ds : .data16 segment
+ * %edi : Physical base of protected-mode code (virt_offset)
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl init_librm
+init_librm:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebx
+
+ /* Store virt_offset and set up virtual_cs and virtual_ds segments */
+ movl %edi, %eax
+ movw $virtual_cs, %bx
+ call set_seg_base
+ movw $virtual_ds, %bx
+ call set_seg_base
+ movl %edi, rm_virt_offset
+
+ /* Negate virt_offset */
+ negl %edi
+
+ /* Store rm_cs and text16, set up real_cs segment */
+ xorl %eax, %eax
+ movw %cs, %ax
+ movw %ax, %cs:rm_cs
+ shll $4, %eax
+ movw $real_cs, %bx
+ call set_seg_base
+ addr32 leal (%eax, %edi), %ebx
+ movl %ebx, rm_text16
+
+ /* Store rm_ds and data16 */
+ xorl %eax, %eax
+ movw %ds, %ax
+ movw %ax, %cs:rm_ds
+ shll $4, %eax
+ addr32 leal (%eax, %edi), %ebx
+ movl %ebx, rm_data16
+
+ /* Set GDT base */
+ movl %eax, gdt_base
+ addl $gdt, gdt_base
+
+ /* Initialise IDT */
+ pushl $init_idt
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Restore registers */
+ negl %edi
+ popl %ebx
+ popl %eax
+ lret
+
+ .section ".text16", "ax", @progbits
+ .code16
+set_seg_base:
+1: movw %ax, 2(%bx)
+ rorl $16, %eax
+ movb %al, 4(%bx)
+ movb %ah, 7(%bx)
+ roll $16, %eax
+ ret
+
+/****************************************************************************
+ * real_to_prot (real-mode near call, 32-bit virtual return address)
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with virtual
+ * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and
+ * the protected-mode %esp is restored from the saved pm_esp.
+ * Interrupts are disabled. All other registers may be destroyed.
+ *
+ * The return address for this function should be a 32-bit virtual
+ * address.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from RM stack to PM stack
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+real_to_prot:
+ /* Enable A20 line */
+ call enable_a20
+ /* A failure at this point is fatal, and there's nothing we
+ * can do about it other than lock the machine to make the
+ * problem immediately visible.
+ */
+1: jc 1b
+
+ /* Make sure we have our data segment available */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+
+ /* Add virt_offset, text16 and data16 to stack to be
+ * copied, and also copy the return address.
+ */
+ pushl rm_virt_offset
+ pushl rm_text16
+ pushl rm_data16
+ addw $16, %cx /* %ecx must be less than 64kB anyway */
+
+ /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
+ xorl %ebp, %ebp
+ movw %ss, %bp
+ movzwl %sp, %edx
+ movl %ebp, %eax
+ shll $4, %eax
+ addr32 leal (%eax,%edx), %esi
+ subl rm_virt_offset, %esi
+
+ /* Load protected-mode global descriptor table */
+ data32 lgdt gdtr
+
+ /* Zero segment registers. This wastes around 12 cycles on
+ * real hardware, but saves a substantial number of emulated
+ * instructions under KVM.
+ */
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* Switch to protected mode */
+ cli
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ data32 ljmp $VIRTUAL_CS, $r2p_pmode
+ .section ".text", "ax", @progbits
+ .code32
+r2p_pmode:
+ /* Set up protected-mode data segments and stack pointer */
+ movw $VIRTUAL_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ movl pm_esp, %esp
+
+ /* Load protected-mode interrupt descriptor table */
+ lidt idtr
+
+ /* Record real-mode %ss:sp (after removal of data) */
+ movw %bp, rm_ss
+ addl %ecx, %edx
+ movw %dx, rm_sp
+
+ /* Move data from RM stack to PM stack */
+ subl %ecx, %esp
+ movl %esp, %edi
+ rep movsb
+
+ /* Publish virt_offset, text16 and data16 for PM code to use */
+ popl data16
+ popl text16
+ popl virt_offset
+
+ /* Return to virtual address */
+ ret
+
+/****************************************************************************
+ * prot_to_real (protected-mode near call, 32-bit real-mode return address)
+ *
+ * Switch from 32-bit protected mode with virtual addresses to 16-bit
+ * real mode. The protected-mode %esp is stored in pm_esp and the
+ * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The
+ * high word of the real-mode %esp is set to zero. All real-mode data
+ * segment registers are loaded from the saved rm_ds. Interrupts are
+ * *not* enabled, since we want to be able to use prot_to_real in an
+ * ISR. All other registers may be destroyed.
+ *
+ * The return address for this function should be a 32-bit (sic)
+ * real-mode offset within .code16.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from PM stack to RM stack
+ * %esi : real-mode global and interrupt descriptor table registers
+ *
+ ****************************************************************************
+ */
+ .section ".text", "ax", @progbits
+ .code32
+prot_to_real:
+ /* Copy real-mode global descriptor table register to RM code segment */
+ movl text16, %edi
+ leal rm_gdtr(%edi), %edi
+ movsw
+ movsl
+
+ /* Load real-mode interrupt descriptor table register */
+ lidt (%esi)
+
+ /* Add return address to data to be moved to RM stack */
+ addl $4, %ecx
+
+ /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
+ movzwl rm_ss, %ebp
+ movzwl rm_sp, %edx
+ subl %ecx, %edx
+ movl %ebp, %eax
+ shll $4, %eax
+ leal (%eax,%edx), %edi
+ subl virt_offset, %edi
+
+ /* Move data from PM stack to RM stack */
+ movl %esp, %esi
+ rep movsb
+
+ /* Record protected-mode %esp (after removal of data) */
+ movl %esi, pm_esp
+
+ /* Load real-mode segment limits */
+ movw $REAL_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ ljmp $REAL_CS, $p2r_rmode
+ .section ".text16", "ax", @progbits
+ .code16
+p2r_rmode:
+ /* Load real-mode GDT */
+ data32 lgdt %cs:rm_gdtr
+ /* Switch to real mode */
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+p2r_ljmp_rm_cs:
+ ljmp $0, $1f
+1:
+ /* Set up real-mode data segments and stack pointer */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %bp, %ss
+ movl %edx, %esp
+
+ /* Return to real-mode address */
+ data32 ret
+
+
+ /* Real-mode code and data segments. Assigned by the call to
+ * init_librm. rm_cs doubles as the segment part of the jump
+ * instruction used by prot_to_real. Both are located in
+ * .text16 rather than .data16: rm_cs since it forms part of
+ * the jump instruction within the code segment, and rm_ds
+ * since real-mode code needs to be able to locate the data
+ * segment with no other reference available.
+ */
+ .globl rm_cs
+ .equ rm_cs, ( p2r_ljmp_rm_cs + 3 )
+
+ .section ".text16.data", "aw", @progbits
+ .globl rm_ds
+rm_ds: .word 0
+
+ /* Real-mode global and interrupt descriptor table registers */
+ .section ".text16.data", "aw", @progbits
+rm_gdtr:
+ .word 0 /* Limit */
+ .long 0 /* Base */
+
+/****************************************************************************
+ * prot_call (real-mode far call, 16-bit real-mode far return address)
+ *
+ * Call a specific C function in the protected-mode code. The
+ * prototype of the C function must be
+ * void function ( struct i386_all_regs *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
+ * at entry to prot_call.
+ *
+ * All registers will be preserved across prot_call(), unless the C
+ * function explicitly overwrites values in ix86. Interrupt status
+ * and GDT will also be preserved. Gate A20 will be enabled.
+ *
+ * Note that prot_call() does not rely on the real-mode stack
+ * remaining intact in order to return, since everything relevant is
+ * copied to the protected-mode stack for the duration of the call.
+ * In particular, this means that a real-mode prefix can make a call
+ * to main() which will return correctly even if the prefix's stack
+ * gets vapourised during the Etherboot run. (The prefix cannot rely
+ * on anything else on the stack being preserved, so should move any
+ * critical data to registers before calling main()).
+ *
+ * Parameters:
+ * function : virtual address of protected-mode function to call
+ *
+ * Example usage:
+ * pushl $pxe_api_call
+ * call prot_call
+ * addw $4, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
+ ****************************************************************************
+ */
+
+#define PC_OFFSET_GDT ( 0 )
+#define PC_OFFSET_IDT ( PC_OFFSET_GDT + 6 )
+#define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 6 )
+#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
+#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
+#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
+
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl prot_call
+prot_call:
+ /* Preserve registers, flags and GDT on external RM stack */
+ pushfl
+ pushal
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ subw $PC_OFFSET_IX86, %sp
+ movw %sp, %bp
+ sidt PC_OFFSET_IDT(%bp)
+ sgdt PC_OFFSET_GDT(%bp)
+
+ /* For sanity's sake, clear the direction flag as soon as possible */
+ cld
+
+ /* Switch to protected mode and move register dump to PM stack */
+ movl $PC_OFFSET_END, %ecx
+ pushl $pc_pmode
+ jmp real_to_prot
+ .section ".text", "ax", @progbits
+ .code32
+pc_pmode:
+ /* Call function */
+ leal PC_OFFSET_IX86(%esp), %eax
+ pushl %eax
+ call *(PC_OFFSET_FUNCTION+4)(%esp)
+ popl %eax /* discard */
+
+ /* Switch to real mode and move register dump back to RM stack */
+ movl $PC_OFFSET_END, %ecx
+ movl %esp, %esi
+ pushl $pc_rmode
+ jmp prot_to_real
+ .section ".text16", "ax", @progbits
+ .code16
+pc_rmode:
+ /* Restore registers and flags and return */
+ addw $( PC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ popal
+ /* popal skips %esp. We therefore want to do "movl -20(%sp),
+ * %esp", but -20(%sp) is not a valid 80386 expression.
+ * Fortunately, prot_to_real() zeroes the high word of %esp, so
+ * we can just use -20(%esp) instead.
+ */
+ addr32 movl -20(%esp), %esp
+ popfl
+ lret
+
+/****************************************************************************
+ * real_call (protected-mode near call, 32-bit virtual return address)
+ *
+ * Call a real-mode function from protected-mode code.
+ *
+ * The non-segment register values will be passed directly to the
+ * real-mode code. The segment registers will be set as per
+ * prot_to_real. The non-segment register values set by the real-mode
+ * function will be passed back to the protected-mode caller. A
+ * result of this is that this routine cannot be called directly from
+ * C code, since it clobbers registers that the C ABI expects the
+ * callee to preserve.
+ *
+ * librm.h defines a convenient macro REAL_CODE() for using real_call.
+ * See librm.h and realmode.h for details and examples.
+ *
+ * Parameters:
+ * (32-bit) near pointer to real-mode function to call
+ *
+ * Returns: none
+ ****************************************************************************
+ */
+
+#define RC_OFFSET_PRESERVE_REGS ( 0 )
+#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
+#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
+#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
+
+ .section ".text", "ax", @progbits
+ .code32
+ .globl real_call
+real_call:
+ /* Create register dump and function pointer copy on PM stack */
+ pushal
+ pushl RC_OFFSET_FUNCTION(%esp)
+
+ /* Switch to real mode and move register dump to RM stack */
+ movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
+ pushl $rc_rmode
+ movl $rm_default_gdtr_idtr, %esi
+ jmp prot_to_real
+ .section ".text16", "ax", @progbits
+ .code16
+rc_rmode:
+ /* Call real-mode function */
+ popl rc_function
+ popal
+ call *rc_function
+ pushal
+
+ /* For sanity's sake, clear the direction flag as soon as possible */
+ cld
+
+ /* Switch to protected mode and move register dump back to PM stack */
+ movl $RC_OFFSET_RETADDR, %ecx
+ pushl $rc_pmode
+ jmp real_to_prot
+ .section ".text", "ax", @progbits
+ .code32
+rc_pmode:
+ /* Restore registers and return */
+ popal
+ ret
+
+
+ /* Function vector, used because "call xx(%sp)" is not a valid
+ * 16-bit expression.
+ */
+ .section ".data16", "aw", @progbits
+rc_function: .word 0, 0
+
+ /* Default real-mode global and interrupt descriptor table registers */
+ .section ".data", "aw", @progbits
+rm_default_gdtr_idtr:
+ .word 0 /* Global descriptor table limit */
+ .long 0 /* Global descriptor table base */
+ .word 0x03ff /* Interrupt descriptor table limit */
+ .long 0 /* Interrupt descriptor table base */
+
+/****************************************************************************
+ * flatten_real_mode (real-mode near call)
+ *
+ * Switch to flat real mode
+ *
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .code16
+ .globl flatten_real_mode
+flatten_real_mode:
+ /* Modify GDT to use flat real mode */
+ movb $0x8f, real_cs + 6
+ movb $0x8f, real_ds + 6
+ /* Call dummy protected-mode function */
+ pushl $flatten_dummy
+ pushw %cs
+ call prot_call
+ addw $4, %sp
+ /* Restore GDT */
+ movb $0x00, real_cs + 6
+ movb $0x00, real_ds + 6
+ /* Return */
+ ret
+
+ .section ".text", "ax", @progbits
+ .code32
+flatten_dummy:
+ ret
+
+/****************************************************************************
+ * Interrupt wrapper
+ *
+ * Used by the protected-mode interrupt vectors to call the
+ * interrupt() function.
+ *
+ * May be entered with either physical or virtual stack segment.
+ ****************************************************************************
+ */
+ .globl interrupt_wrapper
+interrupt_wrapper:
+ /* Preserve segment registers and original %esp */
+ pushl %ds
+ pushl %es
+ pushl %fs
+ pushl %gs
+ pushl %ss
+ pushl %esp
+
+ /* Switch to virtual addressing */
+ call _intr_to_virt
+
+ /* Expand IRQ number to whole %eax register */
+ movzbl %al, %eax
+
+ /* Call interrupt handler */
+ call interrupt
+
+ /* Restore original stack and segment registers */
+ lss (%esp), %esp
+ popl %ss
+ popl %gs
+ popl %fs
+ popl %es
+ popl %ds
+
+ /* Restore registers and return */
+ popal
+ iret
+
+/****************************************************************************
+ * Stored real-mode and protected-mode stack pointers
+ *
+ * The real-mode stack pointer is stored here whenever real_to_prot
+ * is called and restored whenever prot_to_real is called. The
+ * converse happens for the protected-mode stack pointer.
+ *
+ * Despite initial appearances this scheme is, in fact re-entrant,
+ * because program flow dictates that we always return via the point
+ * we left by. For example:
+ * PXE API call entry
+ * 1 real => prot
+ * ...
+ * Print a text string
+ * ...
+ * 2 prot => real
+ * INT 10
+ * 3 real => prot
+ * ...
+ * ...
+ * 4 prot => real
+ * PXE API call exit
+ *
+ * At point 1, the RM mode stack value, say RPXE, is stored in
+ * rm_ss,sp. We want this value to still be present in rm_ss,sp when
+ * we reach point 4.
+ *
+ * At point 2, the RM stack value is restored from RPXE. At point 3,
+ * the RM stack value is again stored in rm_ss,sp. This *does*
+ * overwrite the RPXE that we have stored there, but it's the same
+ * value, since the code between points 2 and 3 has managed to return
+ * to us.
+ ****************************************************************************
+ */
+ .section ".data", "aw", @progbits
+ .globl rm_sp
+rm_sp: .word 0
+ .globl rm_ss
+rm_ss: .word 0
+pm_esp: .long _estack
+
+/****************************************************************************
+ * Virtual address offsets
+ *
+ * These are used by the protected-mode code to map between virtual
+ * and physical addresses, and to access variables in the .text16 or
+ * .data16 segments.
+ ****************************************************************************
+ */
+ /* Internal copies, created by init_librm (which runs in real mode) */
+ .section ".data16", "aw", @progbits
+rm_virt_offset: .long 0
+rm_text16: .long 0
+rm_data16: .long 0
+
+ /* Externally-visible copies, created by real_to_prot */
+ .section ".data", "aw", @progbits
+ .globl virt_offset
+virt_offset: .long 0
+ .globl text16
+text16: .long 0
+ .globl data16
+data16: .long 0
diff --git a/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c
new file mode 100644
index 000000000..becb02677
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c
@@ -0,0 +1,158 @@
+/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/profile.h>
+#include <realmode.h>
+#include <pic8259.h>
+
+/*
+ * This file provides functions for managing librm.
+ *
+ */
+
+/** The interrupt wrapper */
+extern char interrupt_wrapper[];
+
+/** The interrupt vectors */
+static struct interrupt_vector intr_vec[NUM_INT];
+
+/** The interrupt descriptor table */
+struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) ));
+
+/** The interrupt descriptor table register */
+struct idtr idtr = {
+ .limit = ( sizeof ( idt ) - 1 ),
+};
+
+/** Timer interrupt profiler */
+static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
+
+/** Other interrupt profiler */
+static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };
+
+/**
+ * Allocate space on the real-mode stack and copy data there from a
+ * user buffer
+ *
+ * @v data User buffer
+ * @v size Size of stack data
+ * @ret sp New value of real-mode stack pointer
+ */
+uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
+ userptr_t rm_stack;
+ rm_sp -= size;
+ rm_stack = real_to_user ( rm_ss, rm_sp );
+ memcpy_user ( rm_stack, 0, data, 0, size );
+ return rm_sp;
+};
+
+/**
+ * Deallocate space on the real-mode stack, optionally copying back
+ * data to a user buffer.
+ *
+ * @v data User buffer
+ * @v size Size of stack data
+ */
+void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
+ if ( data ) {
+ userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
+ memcpy_user ( rm_stack, 0, data, 0, size );
+ }
+ rm_sp += size;
+};
+
+/**
+ * Set interrupt vector
+ *
+ * @v intr Interrupt number
+ * @v vector Interrupt vector, or NULL to disable
+ */
+void set_interrupt_vector ( unsigned int intr, void *vector ) {
+ struct interrupt_descriptor *idte;
+
+ idte = &idt[intr];
+ idte->segment = VIRTUAL_CS;
+ idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
+ idte->low = ( ( ( uint32_t ) vector ) & 0xffff );
+ idte->high = ( ( ( uint32_t ) vector ) >> 16 );
+}
+
+/**
+ * Initialise interrupt descriptor table
+ *
+ */
+void init_idt ( void ) {
+ struct interrupt_vector *vec;
+ unsigned int intr;
+
+ /* Initialise the interrupt descriptor table and interrupt vectors */
+ for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
+ vec = &intr_vec[intr];
+ vec->pushal = PUSHAL_INSN;
+ vec->movb = MOVB_INSN;
+ vec->intr = intr;
+ vec->jmp = JMP_INSN;
+ vec->offset = ( ( uint32_t ) interrupt_wrapper -
+ ( uint32_t ) vec->next );
+ set_interrupt_vector ( intr, vec );
+ }
+ DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
+ intr_vec, sizeof ( intr_vec[0] ),
+ virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );
+
+ /* Initialise the interrupt descriptor table register */
+ idtr.base = virt_to_phys ( idt );
+}
+
+/**
+ * Determine interrupt profiler (for debugging)
+ *
+ * @v intr Interrupt number
+ * @ret profiler Profiler
+ */
+static struct profiler * interrupt_profiler ( int intr ) {
+
+ switch ( intr ) {
+ case IRQ_INT ( 0 ) :
+ return &timer_irq_profiler;
+ default:
+ return &other_irq_profiler;
+ }
+}
+
+/**
+ * Interrupt handler
+ *
+ * @v intr Interrupt number
+ */
+void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int intr ) {
+ struct profiler *profiler = interrupt_profiler ( intr );
+ uint32_t discard_eax;
+
+ /* Reissue interrupt in real mode */
+ profile_start ( profiler );
+ __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
+ "\n1:\n\t"
+ "int $0x00\n\t" )
+ : "=a" ( discard_eax ) : "0" ( intr ) );
+ profile_stop ( profiler );
+ profile_exclude ( profiler );
+}
+
+PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
+PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
+PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
+PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
+PROVIDE_UACCESS_INLINE ( librm, userptr_add );
+PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
+PROVIDE_UACCESS_INLINE ( librm, memmove_user );
+PROVIDE_UACCESS_INLINE ( librm, memset_user );
+PROVIDE_UACCESS_INLINE ( librm, strlen_user );
+PROVIDE_UACCESS_INLINE ( librm, memchr_user );
diff --git a/roms/ipxe/src/arch/i386/transitions/librm_test.c b/roms/ipxe/src/arch/i386/transitions/librm_test.c
new file mode 100644
index 000000000..f1a517eda
--- /dev/null
+++ b/roms/ipxe/src/arch/i386/transitions/librm_test.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Real mode transition self-tests
+ *
+ * This file allows for easy measurement of the time taken to perform
+ * real mode transitions, which may have a substantial overhead when
+ * running under a hypervisor.
+ *
+ */
+
+/* Forcibly enable assertions */
+#undef NDEBUG
+
+#include <ipxe/test.h>
+#include <ipxe/profile.h>
+#include <realmode.h>
+
+/** Number of sample iterations for profiling */
+#define PROFILE_COUNT 4096
+
+/** Protected-to-real mode transition profiler */
+static struct profiler p2r_profiler __profiler = { .name = "p2r" };
+
+/** Real-to-protected mode transition profiler */
+static struct profiler r2p_profiler __profiler = { .name = "r2p" };
+
+/** Real-mode call profiler */
+static struct profiler real_call_profiler __profiler = { .name = "real_call" };
+
+/** Protected-mode call profiler */
+static struct profiler prot_call_profiler __profiler = { .name = "prot_call" };
+
+/**
+ * Dummy protected-mode function
+ */
+static void librm_test_prot_call ( void ) {
+ /* Do nothing */
+}
+
+/**
+ * Perform real mode transition self-tests
+ *
+ */
+static void librm_test_exec ( void ) {
+ unsigned int i;
+ unsigned long timestamp;
+ unsigned long started;
+ unsigned long stopped;
+ unsigned int discard_d;
+
+ /* Profile mode transitions. We want to profile each
+ * direction of the transition separately, so perform an RDTSC
+ * while in real mode and tweak the profilers' start/stop
+ * times appropriately.
+ */
+ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
+ profile_start ( &p2r_profiler );
+ __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" )
+ : "=a" ( timestamp ), "=d" ( discard_d )
+ : );
+ profile_start_at ( &r2p_profiler, timestamp );
+ profile_stop ( &r2p_profiler );
+ profile_stop_at ( &p2r_profiler, timestamp );
+ }
+
+ /* Profile complete real-mode call cycle */
+ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
+ profile_start ( &real_call_profiler );
+ __asm__ __volatile__ ( REAL_CODE ( "" ) : : );
+ profile_stop ( &real_call_profiler );
+ }
+
+ /* Profile complete protected-mode call cycle */
+ for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
+ __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t"
+ "movl %0, %2\n\t"
+ "pushl %3\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ "addw $4, %%sp\n\t"
+ "rdtsc\n\t" )
+ : "=a" ( stopped ), "=d" ( discard_d ),
+ "=r" ( started )
+ : "i" ( librm_test_prot_call ) );
+ profile_start_at ( &prot_call_profiler, started );
+ profile_stop_at ( &prot_call_profiler, stopped );
+ }
+}
+
+/** Real mode transition self-test */
+struct self_test librm_test __self_test = {
+ .name = "librm",
+ .exec = librm_test_exec,
+};
+
+REQUIRING_SYMBOL ( librm_test );
+REQUIRE_OBJECT ( test );