diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/arch/i386 | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/arch/i386')
182 files changed, 31054 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/arch/i386/Makefile b/qemu/roms/ipxe/src/arch/i386/Makefile new file mode 100644 index 000000000..4925cc4e6 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/Makefile @@ -0,0 +1,109 @@ +# Force i386-only instructions +# +CFLAGS += -march=i386 + +# Code size reduction. +# +CFLAGS += -fomit-frame-pointer + +# Code size reduction. +# +ifeq ($(CCTYPE),gcc) +CFLAGS += -fstrength-reduce +endif + +# Code size reduction. gcc3 needs a different syntax to gcc2 if you +# want to avoid spurious warnings. +# +ifeq ($(CCTYPE),gcc) +GCC_VERSION := $(subst ., ,$(shell $(CC) -dumpversion)) +GCC_MAJOR := $(firstword $(GCC_VERSION)) +ifeq ($(GCC_MAJOR),2) +CFLAGS += -malign-jumps=1 -malign-loops=1 -malign-functions=1 +else +CFLAGS += -falign-jumps=1 -falign-loops=1 -falign-functions=1 +endif # gcc2 +endif # gcc + +# Code size reduction. This is almost always a win. The kernel uses +# it, too. +# +ifeq ($(CCTYPE),gcc) +CFLAGS += -mpreferred-stack-boundary=2 +endif + +# Code size reduction. Use regparm for all functions - C functions +# called from assembly (or vice versa) need __asmcall now +# +CFLAGS += -mregparm=3 + +# Code size reduction. Use -mrtd (same __asmcall requirements as above) +ifeq ($(CCTYPE),gcc) +CFLAGS += -mrtd +endif + +# Code size reduction. This is the logical complement to -mregparm=3. +# It doesn't currently buy us anything, but if anything ever tries to +# return small structures, let's be prepared +# +CFLAGS += -freg-struct-return + +# Force 32-bit code even on an x86-64 machine +# +CFLAGS += -m32 +ASFLAGS += --32 +ifeq ($(HOST_OS),FreeBSD) +LDFLAGS += -m elf_i386_fbsd +else ifeq ($(HOST_OS),OpenBSD) +LDFLAGS += -m elf_i386_obsd +else +LDFLAGS += -m elf_i386 +endif + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# We need to undefine the default macro "i386" when compiling .S +# files, otherwise ".arch i386" translates to ".arch 1"... +# +CFLAGS += -Ui386 + +# 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 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 +# +MAKEDEPS += arch/x86/Makefile +include arch/x86/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/i386/Makefile.$(PLATFORM) +include arch/i386/Makefile.$(PLATFORM) diff --git a/qemu/roms/ipxe/src/arch/i386/Makefile.efi b/qemu/roms/ipxe/src/arch/i386/Makefile.efi new file mode 100644 index 000000000..aa809eb5d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI32) + +# Use EFI ABI +# +CFLAGS += -malign-double + +# Include generic EFI Makefile +# +MAKEDEPS += arch/x86/Makefile.efi +include arch/x86/Makefile.efi diff --git a/qemu/roms/ipxe/src/arch/i386/Makefile.linux b/qemu/roms/ipxe/src/arch/i386/Makefile.linux new file mode 100644 index 000000000..46328c83b --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/Makefile.linux @@ -0,0 +1,6 @@ +LDSCRIPT = arch/i386/scripts/linux.lds + +SRCDIRS += arch/i386/core/linux + +MAKEDEPS += arch/x86/Makefile.linux +include arch/x86/Makefile.linux diff --git a/qemu/roms/ipxe/src/arch/i386/Makefile.pcbios b/qemu/roms/ipxe/src/arch/i386/Makefile.pcbios new file mode 100644 index 000000000..ff823737d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/Makefile.pcbios @@ -0,0 +1,100 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The i386 linker script +# +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/qemu/roms/ipxe/src/arch/i386/README.i386 b/qemu/roms/ipxe/src/arch/i386/README.i386 new file mode 100644 index 000000000..b9b79cc4b --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/README.i386 @@ -0,0 +1,197 @@ +Etherboot/NILO i386 initialisation path and external call interface +=================================================================== + +1. Background + +GCC compiles 32-bit code. It is capable of producing +position-independent code, but the resulting binary is about 25% +bigger than the corresponding fixed-position code. Since one main use +of Etherboot is as firmware to be burned into an EPROM, code size must +be kept as small as possible. + +This means that we want to compile fixed-position code with GCC, and +link it to have a predetermined start address. The problem then is +that we must know the address that the code will be loaded to when it +runs. There are several ways to solve this: + +1. Pick an address, link the code with this start address, then make + sure that the code gets loaded at that location. This is + problematic, because we may pick an address that we later end up + wanting to use to load the operating system that we're booting. + +2. Pick an address, link the code with this start address, then set up + virtual addressing so that the virtual addresses match the + link-time addresses regardless of the real physical address that + the code is loaded to. This enables us to relocate Etherboot to + the top of high memory, where it will be out of the way of any + loading operating system. + +3. Link the code with a text start address of zero and a data start + address also of zero. Use 16-bit real mode and the + quasi-position-independence it gives you via segment addressing. + Doing this requires that we generate 16-bit code, rather than + 32-bit code, and restricts us to a maximum of 64kB in each segment. + +There are other possible approaches (e.g. including a relocation table +and code that performs standard dynamic relocation), but the three +options listed above are probably the best available. + +Etherboot can be invoked in a variety of ways (ROM, floppy, as a PXE +NBP, etc). Several of these ways involve control being passed to +Etherboot with the CPU in 16-bit real mode. Some will involve the CPU +being in 32-bit protected mode, and there's an outside chance that +some may involve the CPU being in 16-bit protected mode. We will +almost certainly have to effect a CPU mode change in order to reach +the mode we want to be in to execute the C code. + +Additionally, Etherboot may wish to call external routines, such as +BIOS interrupts, which must be called in 16-bit real mode. When +providing a PXE API, Etherboot must provide a mechanism for external +code to call it from 16-bit real mode. + +Not all i386 builds of Etherboot will want to make real-mode calls. +For example, when built for LinuxBIOS rather than the standard PCBIOS, +no real-mode calls are necessary. + +For the ultimate in PXE compatibility, we may want to build Etherboot +to run permanently in real mode. + +There is a wide variety of potential combinations of mode switches +that we may wish to implement. There are additional complications, +such as the inability to access a high-memory stack when running in +real mode. + +2. Transition libraries + +To handle all these various combinations of mode switches, we have +several "transition" libraries in Etherboot. We also have the concept +of an "internal" and an "external" environment. The internal +environment is the environment within which we can execute C code. +The external environment is the environment of whatever external code +we're trying to interface to, such as the system BIOS or a PXE NBP. + +As well as having a separate addressing scheme, the internal +environment also has a separate stack. + +The transition libraries are: + +a) librm + +librm handles transitions between an external 16-bit real-mode +environment and an internal 32-bit protected-mode environment with +virtual addresses. + +b) libkir + +libkir handles transitions between an external 16-bit real-mode (or +16:16 or 16:32 protected-mode) environment and an internal 16-bit +real-mode (or 16:16 protected-mode) environment. + +c) libpm + +libpm handles transitions between an external 32-bit protected-mode +environment with flat physical addresses and an internal 32-bit +protected-mode environment with virtual addresses. + +The transition libraries handle the transitions required when +Etherboot is started up for the first time, the transitions required +to execute any external code, and the transitions required when +Etherboot exits (if it exits). When Etherboot provides a PXE API, +they also handle the transitions required when a PXE client makes a +PXE API call to Etherboot. + +Etherboot may use multiple transition libraries. For example, an +Etherboot ELF image does not require librm for its initial transitions +from prefix to runtime, but may require librm for calling external +real-mode functions. + +3. Setup and initialisation + +Etherboot is conceptually divided into the prefix, the decompressor, +and the runtime image. (For non-compressed images, the decompressor +is a no-op.) The complete image comprises all three parts and is +distinct from the runtime image, which exclude the prefix and the +decompressor. + +The prefix does several tasks: + + Load the complete image into memory. (For example, the floppy + prefix issues BIOS calls to load the remainder of the complete image + from the floppy disk into RAM, and the ISA ROM prefix copies the ROM + contents into RAM for faster access.) + + Call the decompressor, if the runtime image is compressed. This + decompresses the runtime image. + + Call the runtime image's setup() routine. This is a routine + implemented in assembly code which sets up the internal environment + so that C code can execute. + + Call the runtime image's arch_initialise() routine. This is a + routine implemented in C which does some basic startup tasks, such + as initialising the console device, obtaining a memory map and + relocating the runtime image to high memory. + + Call the runtime image's arch_main() routine. This records the exit + mechanism requested by the prefix and calls main(). (The prefix + needs to register an exit mechanism because by the time main() + returns, the memory occupied by the prefix has most likely been + overwritten.) + +When acting as a PXE ROM, the ROM prefix contains an UNDI loader +routine in addition to its usual code. The UNDI loader performs a +similar sequence of steps: + + Load the complete image into memory. + + Call the decompressor. + + Call the runtime image's setup() routine. + + Call the runtime image's arch_initialise() routine. + + Call the runtime image's install_pxe_stack() routine. + + Return to caller. + +The runtime image's setup() routine will perform the following steps: + + Switch to the internal environment using an appropriate transition + library. This will record the parameters of the external + environment. + + Set up the internal environment: load a stack, and set up a GDT for + virtual addressing if virtual addressing is to be used. + + Switch back to the external environment using the transition + library. This will record the parameters of the internal + environment. + +Once the setup() routine has returned, the internal environment has been +set up ready for C code to run. The prefix can call C routines using +a function from the transition library. + +The runtime image's arch_initialise() routine will perform the +following steps: + + Zero the bss + + Initialise the console device(s) and print a welcome message. + + Obtain a memory map via the INT 15,E820 BIOS call or suitable + fallback mechanism. [not done if libkir is being used] + + Relocate the runtime image to the top of high memory. [not done if + libkir is being used] + + Install librm to base memory. [done only if librm is being used] + + Call initialise(). + + Return to the prefix, setting registers to indicate to the prefix + the new location of the transition library, if applicable. Which + registers these are is specific to the transition library being + used. + +Once the arch_initialise() routine has returned, the prefix will +probably call arch_main(). diff --git a/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c b/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c new file mode 100644 index 000000000..06ffa3bbd --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/basemem_packet.c @@ -0,0 +1,33 @@ +/* + * 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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c b/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c new file mode 100644 index 000000000..3cac28e7d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/cachedhcp.c @@ -0,0 +1,171 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/core/dumpregs.c b/qemu/roms/ipxe/src/arch/i386/core/dumpregs.c new file mode 100644 index 000000000..82dc21847 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/core/gdbidt.S b/qemu/roms/ipxe/src/arch/i386/core/gdbidt.S new file mode 100644 index 000000000..a1e309d7c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/gdbidt.S @@ -0,0 +1,140 @@ +/* + * Interrupt handlers for GDB stub + */ + +#define SIZEOF_I386_REGS 32 +#define SIZEOF_I386_FLAGS 4 + +/**************************************************************************** + * Interrupt handlers + **************************************************************************** + */ + .section ".text", "ax", @progbits + .code32 + +/* 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_nocode_sigfpe +gdbmach_nocode_sigfpe: + pushl $SIGFPE + jmp gdbmach_interrupt + + .globl gdbmach_nocode_sigtrap +gdbmach_nocode_sigtrap: + pushl $SIGTRAP + jmp gdbmach_interrupt + + .globl gdbmach_nocode_sigstkflt +gdbmach_nocode_sigstkflt: + pushl $SIGSTKFLT + jmp gdbmach_interrupt + + .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 ) +#define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 ) +#define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS ) +#define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 ) +#define IH_OFFSET_SIGNO ( IH_OFFSET_GDB_END ) +#define IH_OFFSET_OLD_EIP ( IH_OFFSET_SIGNO + 4 ) +#define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 ) +#define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 ) +#define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 ) + +/* We also access the stack whilst still storing or restoring + * the register snapshot. Since ESP is in flux, we need + * special offsets. + */ +#define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 ) +#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 ) +#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 ) +#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 ) +gdbmach_interrupt: + /* Store CPU state in GDB register snapshot */ + pushw $0 + pushw %gs + pushw $0 + pushw %fs + pushw $0 + pushw %es + pushw $0 + pushw %ds + pushw $0 + pushw %ss + pushw $0 + pushw IH_OFFSET_FLUX_OLD_CS + 2(%esp) + pushl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + pushl IH_OFFSET_FLUX_OLD_EIP(%esp) + pushl %edi + pushl %esi + pushl %ebp + leal IH_OFFSET_FLUX_END(%esp), %edi + pushl %edi /* old ESP */ + pushl %ebx + pushl %edx + pushl %ecx + pushl %eax + + /* Switch to virtual addressing */ + call _intr_to_virt + + /* Call GDB stub exception handler */ + pushl %esp + pushl (IH_OFFSET_SIGNO + 4)(%esp) + call gdbmach_handler + addl $8, %esp + + /* Copy register snapshot to new stack and switch to new stack */ + movl %esp, %esi + movl (IH_OFFSET_GDB_SEG_REGS + 4)(%esp), %eax + movl %eax, %es + movl (IH_OFFSET_GDB_REGS + 16)(%esp), %edi + subl $IH_OFFSET_END, %edi + movl $(IH_OFFSET_END / 4), %ecx + pushl %edi + ss rep movsl + popl %edi + movl %eax, %ss + movl %edi, %esp + + /* Restore CPU state from GDB register snapshot */ + popl %eax + popl %ecx + popl %edx + popl %ebx + popl %ebp /* Skip %esp: already loaded */ + popl %ebp + popl %esi + popl %edi + popl IH_OFFSET_FLUX_OLD_EIP(%esp) + popl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + popl IH_OFFSET_FLUX_OLD_CS(%esp) + popl %ds /* Skip %ss: already loaded */ + popl %ds + popl %es + popl %fs + popl %gs + + addl $4, %esp /* drop signo */ + iret diff --git a/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c b/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c new file mode 100644 index 000000000..4d6897f7d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/gdbmach.c @@ -0,0 +1,180 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S b/qemu/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S new file mode 100644 index 000000000..38a3e74bd --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/linux/linux_syscall.S @@ -0,0 +1,45 @@ + + .section ".data" + .globl linux_errno + +linux_errno: .int 0 + + .section ".text" + .code32 + .globl linux_syscall + .type linux_syscall, @function + +linux_syscall: + /* Save registers */ + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + + movl 20(%esp), %eax // C arg1 -> syscall number + movl 24(%esp), %ebx // C arg2 -> syscall arg1 + movl 28(%esp), %ecx // C arg3 -> syscall arg2 + movl 32(%esp), %edx // C arg4 -> syscall arg3 + movl 36(%esp), %esi // C arg5 -> syscall arg4 + movl 40(%esp), %edi // C arg6 -> syscall arg5 + movl 44(%esp), %ebp // C arg7 -> syscall arg6 + + int $0x80 + + /* Restore registers */ + popl %ebp + popl %edi + popl %esi + popl %ebx + + cmpl $-4095, %eax + jae 1f + ret + +1: + negl %eax + movl %eax, linux_errno + movl $-1, %eax + ret + + .size linux_syscall, . - linux_syscall diff --git a/qemu/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S b/qemu/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S new file mode 100644 index 000000000..398d3cb21 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/linux/linuxprefix.S @@ -0,0 +1,28 @@ +#include <linux/unistd.h> + + .section ".text" + .code32 + .globl _linux_start + .type _linux_start, @function + +_linux_start: + xorl %ebp, %ebp + + popl %esi // save argc + movl %esp, %edi // save argv + + andl $~15, %esp // 16-byte align the stack + + pushl %edi // argv -> C arg2 + pushl %esi // argc -> C arg1 + + call save_args + + /* Our main doesn't use any arguments */ + call main + + movl %eax, %ebx // rc -> syscall arg1 + movl $__NR_exit, %eax + int $0x80 + + .size _linux_start, . - _linux_start diff --git a/qemu/roms/ipxe/src/arch/i386/core/nulltrap.c b/qemu/roms/ipxe/src/arch/i386/core/nulltrap.c new file mode 100644 index 000000000..3046fbecf --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/nulltrap.c @@ -0,0 +1,51 @@ +#include <stdint.h> +#include <stdio.h> + +__attribute__ (( noreturn, section ( ".text.null_trap" ) )) +void null_function_trap ( void ) { + void *stack; + + /* 128 bytes of NOPs; the idea of this is that if something + * dereferences a NULL pointer and overwrites us, we at least + * have some chance of still getting to execute the printf() + * statement. + */ + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + + __asm__ __volatile__ ( "movl %%esp, %0" : "=r" ( stack ) ); + printf ( "NULL method called from %p (stack %p)\n", + __builtin_return_address ( 0 ), stack ); + DBG_HD ( stack, 256 ); + while ( 1 ) {} +} diff --git a/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S b/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S new file mode 100644 index 000000000..97a62f494 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/patch_cf.S @@ -0,0 +1,38 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c new file mode 100644 index 000000000..a3eb1f97d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/pci_autoboot.c @@ -0,0 +1,44 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/core/pic8259.c b/qemu/roms/ipxe/src/arch/i386/core/pic8259.c new file mode 100644 index 000000000..0a9ea2e03 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/pic8259.c @@ -0,0 +1,67 @@ +/* + * 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/io.h> +#include <pic8259.h> + +/** @file + * + * Minimal support for the 8259 Programmable Interrupt Controller + * + */ + +/** + * Send non-specific EOI(s) + * + * @v irq IRQ number + * + * This seems to be inherently unsafe. + */ +static inline void send_nonspecific_eoi ( unsigned int irq ) { + DBG ( "Sending non-specific EOI for IRQ %d\n", irq ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR ); + } + outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR ); +} + +/** + * Send specific EOI(s) + * + * @v irq IRQ number + */ +static inline void send_specific_eoi ( unsigned int irq ) { + DBG ( "Sending specific EOI for IRQ %d\n", irq ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ), + ICR_REG ( CHAINED_IRQ ) ); + } + outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) ); +} + +/** + * Send End-Of-Interrupt to the PIC + * + * @v irq IRQ number + */ +void send_eoi ( unsigned int irq ) { + send_specific_eoi ( irq ); +} diff --git a/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c b/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c new file mode 100644 index 000000000..2f31afc66 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/rdtsc_timer.c @@ -0,0 +1,90 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * RDTSC timer + * + */ + +#include <assert.h> +#include <ipxe/timer.h> +#include <ipxe/timer2.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 timer2 and calibrate + * based on result. + */ + timer2_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/qemu/roms/ipxe/src/arch/i386/core/relocate.c b/qemu/roms/ipxe/src/arch/i386/core/relocate.c new file mode 100644 index 000000000..5fbf2d2c2 --- /dev/null +++ b/qemu/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 ); + +/* + * 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/qemu/roms/ipxe/src/arch/i386/core/runtime.c b/qemu/roms/ipxe/src/arch/i386/core/runtime.c new file mode 100644 index 000000000..18ca7936e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/runtime.c @@ -0,0 +1,265 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/arch/i386/core/setjmp.S b/qemu/roms/ipxe/src/arch/i386/core/setjmp.S new file mode 100644 index 000000000..03727148c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/setjmp.S @@ -0,0 +1,42 @@ +/* setjmp and longjmp. Use of these functions is deprecated. */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .text + .arch i386 + .code32 + +/************************************************************************** +SETJMP - Save stack context for non-local goto +**************************************************************************/ + .globl setjmp +setjmp: + movl 4(%esp),%ecx /* jmpbuf */ + movl 0(%esp),%edx /* return address */ + movl %edx,0(%ecx) + movl %ebx,4(%ecx) + movl %esp,8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + movl $0,%eax + ret + +/************************************************************************** +LONGJMP - Non-local jump to a saved stack context +**************************************************************************/ + .globl longjmp +longjmp: + movl 4(%esp),%edx /* jumpbuf */ + movl 8(%esp),%eax /* result */ + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret diff --git a/qemu/roms/ipxe/src/arch/i386/core/stack.S b/qemu/roms/ipxe/src/arch/i386/core/stack.S new file mode 100644 index 000000000..737ec0eed --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/stack.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + + .arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ + .section ".stack", "aw", @nobits + .align 8 + .globl _stack +_stack: + .space 4096 + .globl _estack +_estack: diff --git a/qemu/roms/ipxe/src/arch/i386/core/stack16.S b/qemu/roms/ipxe/src/arch/i386/core/stack16.S new file mode 100644 index 000000000..523f0288b --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/stack16.S @@ -0,0 +1,15 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + + .arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ + .section ".stack16", "aw", @nobits + .align 8 + .globl _stack16 +_stack16: + .space 4096 + .globl _estack16 +_estack16: diff --git a/qemu/roms/ipxe/src/arch/i386/core/timer2.c b/qemu/roms/ipxe/src/arch/i386/core/timer2.c new file mode 100644 index 000000000..077866562 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/core/timer2.c @@ -0,0 +1,87 @@ +/* + * arch/i386/core/i386_timer.c + * + * Use the "System Timer 2" to implement the udelay callback in + * the BIOS timer driver. Also used to calibrate the clock rate + * in the RTDSC timer driver. + * + * 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, or (at + * your option) any later version. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <ipxe/timer2.h> +#include <ipxe/io.h> + +/* Timers tick over at this rate */ +#define TIMER2_TICKS_PER_SEC 1193180U + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A + +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +static void load_timer2 ( unsigned int ticks ) { + /* + * Now let's take care of PPC channel 2 + * + * Set the Gate high, program PPC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + * + * Note some implementations have a bug where the high bits byte + * of channel 2 is ignored. + */ + /* Set up the timer gate, turn off the speaker */ + /* Set the Gate high, disable speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); + /* LSB of ticks */ + outb(ticks & 0xFF, TIMER2_PORT); + /* MSB of ticks */ + outb(ticks >> 8, TIMER2_PORT); +} + +static int timer2_running ( void ) { + return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +void timer2_udelay ( unsigned long usecs ) { + load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); + while (timer2_running()) { + /* Do nothing */ + } +} diff --git a/qemu/roms/ipxe/src/arch/i386/core/video_subr.c b/qemu/roms/ipxe/src/arch/i386/core/video_subr.c new file mode 100644 index 000000000..3f701bd96 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S b/qemu/roms/ipxe/src/arch/i386/core/virtaddr.S new file mode 100644 index 000000000..5e5d77352 --- /dev/null +++ b/qemu/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 ) + +#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/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c new file mode 100644 index 000000000..2bc54824c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undi.c @@ -0,0 +1,149 @@ +/* + * 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; + + /* Ignore non-network devices */ + if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK ) + return -ENOTTY; + + /* 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] ) ), + .probe = undipci_probe, + .remove = undipci_remove, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/drivers/net/undiisr.S b/qemu/roms/ipxe/src/arch/i386/drivers/net/undiisr.S new file mode 100644 index 000000000..b27effe1d --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c new file mode 100644 index 000000000..77134dcb8 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undiload.c @@ -0,0 +1,180 @@ +/* + * 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 <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/qemu/roms/ipxe/src/arch/i386/drivers/net/undinet.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undinet.c new file mode 100644 index 000000000..6450665ff --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c new file mode 100644 index 000000000..028fac5d9 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undionly.c @@ -0,0 +1,138 @@ +/* + * 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/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/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c new file mode 100644 index 000000000..81d7a80eb --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/drivers/net/undipreload.c @@ -0,0 +1,38 @@ +/* + * 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 <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/qemu/roms/ipxe/src/arch/i386/drivers/net/undirom.c b/qemu/roms/ipxe/src/arch/i386/drivers/net/undirom.c new file mode 100644 index 000000000..b54c6170f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c new file mode 100644 index 000000000..b23f2c356 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c @@ -0,0 +1,47 @@ +/* + * 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 <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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c new file mode 100644 index 000000000..bd73838b5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -0,0 +1,385 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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_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 == 22 ) { + bios_attr &= ~ATTR_BOLD; + } 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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S new file mode 100644 index 000000000..cea17ef8e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -0,0 +1,585 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c new file mode 100644 index 000000000..e5f713728 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c new file mode 100644 index 000000000..8f3069e18 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -0,0 +1,231 @@ +/* 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c new file mode 100644 index 000000000..0937a7ce2 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c @@ -0,0 +1,339 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c new file mode 100644 index 000000000..5c74b0431 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c @@ -0,0 +1,110 @@ +/* + * 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 <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/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c b/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c new file mode 100644 index 000000000..523724ab0 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#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 ); + +/** @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/qemu/roms/ipxe/src/arch/i386/image/bootsector.c b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c new file mode 100644 index 000000000..9a089e6bb --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/bootsector.c @@ -0,0 +1,137 @@ +/* + * 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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/image/bzimage.c b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c new file mode 100644 index 000000000..4865c394c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/bzimage.c @@ -0,0 +1,665 @@ +/* + * 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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/image/com32.c b/qemu/roms/ipxe/src/arch/i386/image/com32.c new file mode 100644 index 000000000..c12ffb684 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/image/comboot.c b/qemu/roms/ipxe/src/arch/i386/image/comboot.c new file mode 100644 index 000000000..1ec02331d --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/image/elfboot.c b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c new file mode 100644 index 000000000..0f6957f02 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/elfboot.c @@ -0,0 +1,110 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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? */ +} + +/** + * 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, + }; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { + DBG ( "Invalid ELF identifier\n" ); + return -ENOEXEC; + } + + 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/qemu/roms/ipxe/src/arch/i386/image/initrd.c b/qemu/roms/ipxe/src/arch/i386/image/initrd.c new file mode 100644 index 000000000..eaba3a645 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/initrd.c @@ -0,0 +1,300 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/image/multiboot.c b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c new file mode 100644 index 000000000..86b0bc12d --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/multiboot.c @@ -0,0 +1,488 @@ +/* + * 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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/image/nbi.c b/qemu/roms/ipxe/src/arch/i386/image/nbi.c new file mode 100644 index 000000000..99046144d --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c new file mode 100644 index 000000000..dc28f6082 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/pxe_image.c @@ -0,0 +1,129 @@ +/* + * 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 ); + +/** + * @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> + +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; +} + +/** PXE image type */ +struct image_type pxe_image_type __image_type ( PROBE_PXE ) = { + .name = "PXE", + .probe = pxe_probe, + .exec = pxe_exec, +}; diff --git a/qemu/roms/ipxe/src/arch/i386/image/sdi.c b/qemu/roms/ipxe/src/arch/i386/image/sdi.c new file mode 100644 index 000000000..df1c3a868 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/image/sdi.c @@ -0,0 +1,136 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/include/basemem.h b/qemu/roms/ipxe/src/arch/i386/include/basemem.h new file mode 100644 index 000000000..c477c7fe2 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h b/qemu/roms/ipxe/src/arch/i386/include/basemem_packet.h new file mode 100644 index 000000000..3cb477671 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/bios.h b/qemu/roms/ipxe/src/arch/i386/include/bios.h new file mode 100644 index 000000000..0754b1168 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bios.h @@ -0,0 +1,14 @@ +#ifndef BIOS_H +#define BIOS_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/include/bios_disks.h b/qemu/roms/ipxe/src/arch/i386/include/bios_disks.h new file mode 100644 index 000000000..0dd7c4ebb --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/biosint.h b/qemu/roms/ipxe/src/arch/i386/include/biosint.h new file mode 100644 index 000000000..ab466af3c --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h b/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h new file mode 100644 index 000000000..0d9cb967c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/byteswap.h @@ -0,0 +1,70 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include <stdint.h> + +FILE_LICENCE ( GPL2_OR_LATER ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "xchgb %b0,%h0" : "=q" ( x ) : "0" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + __asm__ ( "rorw $8, %0" : "+m" ( *x ) ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "bswapl %0" : "=r" ( x ) : "0" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + __asm__ ( "bswapl %0" : "=r" ( *x ) : "0" ( *x ) ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + uint32_t in_high = ( x >> 32 ); + uint32_t in_low = ( x & 0xffffffffUL ); + uint32_t out_high; + uint32_t out_low; + + __asm__ ( "bswapl %0\n\t" + "bswapl %1\n\t" + "xchgl %0,%1\n\t" + : "=r" ( out_high ), "=r" ( out_low ) + : "0" ( in_high ), "1" ( in_low ) ); + + return ( ( ( ( uint64_t ) out_high ) << 32 ) | + ( ( uint64_t ) out_low ) ); +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + struct { + uint32_t __attribute__ (( may_alias )) low; + uint32_t __attribute__ (( may_alias )) high; + } __attribute__ (( may_alias )) *dwords = ( ( void * ) x ); + uint32_t discard; + + __asm__ ( "movl %0,%2\n\t" + "bswapl %2\n\t" + "xchgl %2,%1\n\t" + "bswapl %2\n\t" + "movl %2,%0\n\t" + : "+m" ( dwords->low ), "+m" ( dwords->high ), + "=r" ( discard ) ); +} + +#endif /* _BITS_BYTESWAP_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h b/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h new file mode 100644 index 000000000..d48b4b385 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/compiler.h @@ -0,0 +1,38 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef ASSEMBLY + +/** Declare a function with standard calling conventions */ +#define __asmcall __attribute__ (( cdecl, regparm(0) )) + +/** + * Declare a function with libgcc implicit linkage + * + * It seems as though gcc expects its implicit arithmetic functions to + * be cdecl, even if -mrtd is specified. This is somewhat + * inconsistent; for example, if -mregparm=3 is used then the implicit + * functions do become regparm(3). + * + * The implicit calls to memcpy() and memset() which gcc can generate + * do not seem to have this inconsistency; -mregparm and -mrtd affect + * them in the same way as any other function. + * + * Update (25/4/14): it appears that more recent gcc versions do allow + * -mrtd to affect calls to the implicit arithmetic functions. There + * is nothing obvious in the gcc changelogs to indicate precisely when + * this happened. From experimentation with available gcc versions, + * the change occurred sometime between v4.6.3 and v4.7.2. We assume + * that only versions up to v4.6.x require the special treatment. + */ +#if ( __GNUC__ < 4 ) || ( ( __GNUC__ == 4 ) && ( __GNUC_MINOR__ <= 6 ) ) +#define __libgcc __attribute__ (( cdecl )) +#else +#define __libgcc +#endif + +#endif /* ASSEMBLY */ + +#endif /* _BITS_COMPILER_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h b/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h new file mode 100644 index 000000000..841885424 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/endian.h @@ -0,0 +1,8 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h b/qemu/roms/ipxe/src/arch/i386/include/bits/entropy.h new file mode 100644 index 000000000..6dcceec6d --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/rtc_entropy.h> + +#endif /* _BITS_ENTROPY_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/linux_api.h b/qemu/roms/ipxe/src/arch/i386/include/bits/linux_api.h new file mode 100644 index 000000000..dc6e7416e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/linux_api.h @@ -0,0 +1,6 @@ +#ifndef _I386_LINUX_API_H +#define _I386_LINUX_API_H + +#define __SYSCALL_mmap __NR_mmap2 + +#endif /* _I386_LINUX_API_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h b/qemu/roms/ipxe/src/arch/i386/include/bits/nap.h new file mode 100644 index 000000000..64066e6ab --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/bios_nap.h> +#include <ipxe/efi/efix86_nap.h> + +#endif /* _BITS_MAP_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h b/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h new file mode 100644 index 000000000..f3ee54ae9 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/profile.h @@ -0,0 +1,28 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint64_t tsc; + + /* Read timestamp counter */ + __asm__ __volatile__ ( "rdtsc" : "=A" ( tsc ) ); + return tsc; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h b/qemu/roms/ipxe/src/arch/i386/include/bits/reboot.h new file mode 100644 index 000000000..5b09e95f7 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/bios_reboot.h> + +#endif /* _BITS_REBOOT_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h b/qemu/roms/ipxe/src/arch/i386/include/bits/sanboot.h new file mode 100644 index 000000000..9c77a4d42 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/bios_sanboot.h> + +#endif /* _BITS_SANBOOT_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h b/qemu/roms/ipxe/src/arch/i386/include/bits/smbios.h new file mode 100644 index 000000000..cc79eec51 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/bios_smbios.h> + +#endif /* _BITS_SMBIOS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h b/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h new file mode 100644 index 000000000..8edf13192 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/stdint.h @@ -0,0 +1,23 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h b/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h new file mode 100644 index 000000000..092bcb593 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bits/strings.h @@ -0,0 +1,50 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + long msb_minus_one; + + /* If the input value is zero, the BSR instruction returns + * ZF=1 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsrl %1, %0" + : "=r" ( msb_minus_one ) + : "rm" ( value ) ); + return ( msb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( high ) { + return ( 32 + __flsl ( high ) ); + } else if ( low ) { + return ( __flsl ( low ) ); + } else { + return 0; + } +} + +#endif /* _BITS_STRINGS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/time.h b/qemu/roms/ipxe/src/arch/i386/include/bits/time.h new file mode 100644 index 000000000..24dd020e9 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/rtc_time.h> + +#endif /* _BITS_TIME_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h b/qemu/roms/ipxe/src/arch/i386/include/bits/timer.h new file mode 100644 index 000000000..50b676b77 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/bios_timer.h> +#include <ipxe/rdtsc_timer.h> + +#endif /* _BITS_TIMER_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h b/qemu/roms/ipxe/src/arch/i386/include/bits/uaccess.h new file mode 100644 index 000000000..2bb52e021 --- /dev/null +++ b/qemu/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 ); + +#include <librm.h> + +#endif /* _BITS_UACCESS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h b/qemu/roms/ipxe/src/arch/i386/include/bits/umalloc.h new file mode 100644 index 000000000..54fb006f0 --- /dev/null +++ b/qemu/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 ); + +#include <ipxe/memtop_umalloc.h> + +#endif /* _BITS_UMALLOC_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bochs.h b/qemu/roms/ipxe/src/arch/i386/include/bochs.h new file mode 100644 index 000000000..9d090fc12 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/bootsector.h b/qemu/roms/ipxe/src/arch/i386/include/bootsector.h new file mode 100644 index 000000000..8730fbfcc --- /dev/null +++ b/qemu/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 ); + +extern int call_bootsector ( unsigned int segment, unsigned int offset, + unsigned int drive ); + +#endif /* _BOOTSECTOR_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/bzimage.h b/qemu/roms/ipxe/src/arch/i386/include/bzimage.h new file mode 100644 index 000000000..7e42e3188 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/bzimage.h @@ -0,0 +1,142 @@ +#ifndef _BZIMAGE_H +#define _BZIMAGE_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/include/comboot.h b/qemu/roms/ipxe/src/arch/i386/include/comboot.h new file mode 100644 index 000000000..2d2f04fe1 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 000000000..184177219 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/dhcp.h> + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_IA32 ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) + +#endif diff --git a/qemu/roms/ipxe/src/arch/i386/include/fakee820.h b/qemu/roms/ipxe/src/arch/i386/include/fakee820.h new file mode 100644 index 000000000..9d00fb670 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/fakee820.h @@ -0,0 +1,9 @@ +#ifndef _FAKEE820_H +#define _FAKEE820_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +extern void fake_e820 ( void ); +extern void unfake_e820 ( void ); + +#endif /* _FAKEE820_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/gdbmach.h b/qemu/roms/ipxe/src/arch/i386/include/gdbmach.h new file mode 100644 index 000000000..416ae341a --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/gdbmach.h @@ -0,0 +1,74 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include <stdint.h> + +typedef unsigned long gdbreg_t; + +/* The register snapshot, this must be in sync with interrupt handler and the + * GDB protocol. */ +enum { + GDBMACH_EAX, + GDBMACH_ECX, + GDBMACH_EDX, + GDBMACH_EBX, + GDBMACH_ESP, + GDBMACH_EBP, + GDBMACH_ESI, + GDBMACH_EDI, + GDBMACH_EIP, + GDBMACH_EFLAGS, + GDBMACH_CS, + GDBMACH_SS, + GDBMACH_DS, + GDBMACH_ES, + GDBMACH_FS, + GDBMACH_GS, + GDBMACH_NREGS, + GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) +}; + +/* Breakpoint types */ +enum { + GDBMACH_BPMEM, + GDBMACH_BPHW, + GDBMACH_WATCH, + GDBMACH_RWATCH, + GDBMACH_AWATCH, +}; + +/* Interrupt vectors */ +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; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); +} + +static inline void gdbmach_breakpoint ( void ) { + __asm__ __volatile__ ( "int $3\n" ); +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ); + +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/initrd.h b/qemu/roms/ipxe/src/arch/i386/include/initrd.h new file mode 100644 index 000000000..a5659f43c --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/int13.h b/qemu/roms/ipxe/src/arch/i386/include/int13.h new file mode 100644 index 000000000..e337ca1d1 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h new file mode 100644 index 000000000..5b684c041 --- /dev/null +++ b/qemu/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 ); + +#ifdef NAP_PCBIOS +#define NAP_PREFIX_pcbios +#else +#define NAP_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _IPXE_BIOS_NAP_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h new file mode 100644 index 000000000..a0845328d --- /dev/null +++ b/qemu/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 ); + +#ifdef REBOOT_PCBIOS +#define REBOOT_PREFIX_pcbios +#else +#define REBOOT_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _IPXE_BIOS_REBOOT_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h new file mode 100644 index 000000000..689227b70 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h new file mode 100644 index 000000000..d8c7f648a --- /dev/null +++ b/qemu/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 ); + +#ifdef SMBIOS_PCBIOS +#define SMBIOS_PREFIX_pcbios +#else +#define SMBIOS_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _IPXE_BIOS_SMBIOS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h new file mode 100644 index 000000000..f9fc80412 --- /dev/null +++ b/qemu/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 ); + +#ifdef TIMER_PCBIOS +#define TIMER_PREFIX_pcbios +#else +#define TIMER_PREFIX_pcbios __pcbios_ +#endif + +#include <ipxe/timer2.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 timer2 + */ + timer2_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/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h new file mode 100644 index 000000000..3a9eb2495 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h new file mode 100644 index 000000000..72a0f714f --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h new file mode 100644 index 000000000..001648fe5 --- /dev/null +++ b/qemu/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 ); + +#ifdef UMALLOC_MEMTOP +#define UMALLOC_PREFIX_memtop +#else +#define UMALLOC_PREFIX_memtop __memtop_ +#endif + +#endif /* _IPXE_MEMTOP_UMALLOC_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h new file mode 100644 index 000000000..c88e26a39 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/msr.h @@ -0,0 +1,38 @@ +#ifndef _IPXE_MSR_H +#define _IPXE_MSR_H + +/** @file + * + * Model-specific registers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Read model-specific register + * + * @v msr Model-specific register + * @ret value Value + */ +static inline __attribute__ (( always_inline )) uint64_t +rdmsr ( unsigned int msr ) { + uint64_t value; + + __asm__ __volatile__ ( "rdmsr" : "=A" ( value ) : "c" ( msr ) ); + return value; +} + +/** + * Write model-specific register + * + * @v msr Model-specific register + * @v value Value + */ +static inline __attribute__ (( always_inline )) void +wrmsr ( unsigned int msr, uint64_t value ) { + + __asm__ __volatile__ ( "wrmsr" : : "c" ( msr ), "A" ( value ) ); +} + +#endif /* _IPXE_MSR_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h new file mode 100644 index 000000000..472e14007 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h new file mode 100644 index 000000000..6c3cf2104 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h new file mode 100644 index 000000000..c0dfe3f88 --- /dev/null +++ b/qemu/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 ); + +#ifdef TIME_RTC +#define TIME_PREFIX_rtc +#else +#define TIME_PREFIX_rtc __rtc_ +#endif + +#endif /* _IPXE_RTC_TIME_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h new file mode 100644 index 000000000..322a3ed59 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/ipxe/timer2.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_TIMER2_H +#define _IPXE_TIMER2_H + +/** @file + * + * Timer chip control + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +extern void timer2_udelay ( unsigned long usecs ); + +#endif /* _IPXE_TIMER2_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h new file mode 100644 index 000000000..48cd6a7b7 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h b/qemu/roms/ipxe/src/arch/i386/include/ipxe/vmware.h new file mode 100644 index 000000000..2ac65f436 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/kir.h b/qemu/roms/ipxe/src/arch/i386/include/kir.h new file mode 100644 index 000000000..84633d26f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/libkir.h b/qemu/roms/ipxe/src/arch/i386/include/libkir.h new file mode 100644 index 000000000..1f5b13504 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/librm.h b/qemu/roms/ipxe/src/arch/i386/include/librm.h new file mode 100644 index 000000000..c8ba72b53 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/librm.h @@ -0,0 +1,279 @@ +#ifndef LIBRM_H +#define LIBRM_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +/* 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 ) + +/** + * 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 ) ); +} + +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/qemu/roms/ipxe/src/arch/i386/include/limits.h b/qemu/roms/ipxe/src/arch/i386/include/limits.h new file mode 100644 index 000000000..031b6c57a --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/limits.h @@ -0,0 +1,61 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +FILE_LICENCE ( GPL2_OR_LATER ); + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/memsizes.h b/qemu/roms/ipxe/src/arch/i386/include/memsizes.h new file mode 100644 index 000000000..7b217494a --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/memsizes.h @@ -0,0 +1,19 @@ +#ifndef _MEMSIZES_H +#define _MEMSIZES_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/include/multiboot.h b/qemu/roms/ipxe/src/arch/i386/include/multiboot.h new file mode 100644 index 000000000..44614c73a --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h new file mode 100644 index 000000000..a36d9cfa1 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/dhcp.h> + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) + +#endif diff --git a/qemu/roms/ipxe/src/arch/i386/include/pic8259.h b/qemu/roms/ipxe/src/arch/i386/include/pic8259.h new file mode 100644 index 000000000..a07e97d30 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/pic8259.h @@ -0,0 +1,73 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef PIC8259_H +#define PIC8259_H + +#include <ipxe/io.h> + +/* For segoff_t */ +#include "realmode.h" + +#define IRQ_PIC_CUTOFF 8 + +/* 8259 register locations */ +#define PIC1_ICW1 0x20 +#define PIC1_OCW2 0x20 +#define PIC1_OCW3 0x20 +#define PIC1_ICR 0x20 +#define PIC1_IRR 0x20 +#define PIC1_ISR 0x20 +#define PIC1_ICW2 0x21 +#define PIC1_ICW3 0x21 +#define PIC1_ICW4 0x21 +#define PIC1_IMR 0x21 +#define PIC2_ICW1 0xa0 +#define PIC2_OCW2 0xa0 +#define PIC2_OCW3 0xa0 +#define PIC2_ICR 0xa0 +#define PIC2_IRR 0xa0 +#define PIC2_ISR 0xa0 +#define PIC2_ICW2 0xa1 +#define PIC2_ICW3 0xa1 +#define PIC2_ICW4 0xa1 +#define PIC2_IMR 0xa1 + +/* Register command values */ +#define OCW3_ID 0x08 +#define OCW3_READ_IRR 0x03 +#define OCW3_READ_ISR 0x02 +#define ICR_EOI_NON_SPECIFIC 0x20 +#define ICR_EOI_NOP 0x40 +#define ICR_EOI_SPECIFIC 0x60 +#define ICR_EOI_SET_PRIORITY 0xc0 + +/* Macros to enable/disable IRQs */ +#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) +#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) +#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) +#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) +#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) + +/* Macros for acknowledging IRQs */ +#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) +#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF ) +#define CHAINED_IRQ 2 + +/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ +#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f ) + +/* Other constants */ +#define IRQ_MAX 15 +#define IRQ_NONE -1U + +/* Function prototypes + */ +void send_eoi ( unsigned int irq ); + +#endif /* PIC8259_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h b/qemu/roms/ipxe/src/arch/i386/include/pnpbios.h new file mode 100644 index 000000000..4c20e73ed --- /dev/null +++ b/qemu/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 ); + +/* BIOS segment address */ +#define BIOS_SEG 0xf000 + +extern int find_pnp_bios ( void ); + +#endif /* _PNPBIOS_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/pxe.h b/qemu/roms/ipxe/src/arch/i386/include/pxe.h new file mode 100644 index 000000000..b95b0cce5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe.h @@ -0,0 +1,199 @@ +#ifndef PXE_H +#define PXE_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h new file mode 100644 index 000000000..e4396efb2 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/pxe_api.h @@ -0,0 +1,1819 @@ +#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. + * + * 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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_call.h new file mode 100644 index 000000000..45af46549 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_error.h new file mode 100644 index 000000000..a1398cbd4 --- /dev/null +++ b/qemu/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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h b/qemu/roms/ipxe/src/arch/i386/include/pxe_types.h new file mode 100644 index 000000000..db8214591 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/pxeparent.h b/qemu/roms/ipxe/src/arch/i386/include/pxeparent.h new file mode 100644 index 000000000..b31e24a76 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/include/realmode.h b/qemu/roms/ipxe/src/arch/i386/include/realmode.h new file mode 100644 index 000000000..dafc5a32a --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/realmode.h @@ -0,0 +1,127 @@ +#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 ); + +/* + * 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. + */ + +/** + * 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/qemu/roms/ipxe/src/arch/i386/include/registers.h b/qemu/roms/ipxe/src/arch/i386/include/registers.h new file mode 100644 index 000000000..06d236524 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/rtc.h b/qemu/roms/ipxe/src/arch/i386/include/rtc.h new file mode 100644 index 000000000..2a6abbae5 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/sdi.h b/qemu/roms/ipxe/src/arch/i386/include/sdi.h new file mode 100644 index 000000000..fc486402d --- /dev/null +++ b/qemu/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 ); + +/** 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/qemu/roms/ipxe/src/arch/i386/include/setjmp.h b/qemu/roms/ipxe/src/arch/i386/include/setjmp.h new file mode 100644 index 000000000..5d3c11b69 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/include/setjmp.h @@ -0,0 +1,40 @@ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <realmode.h> + +/** A jump buffer */ +typedef struct { + uint32_t retaddr; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; +} jmp_buf[1]; + +/** A real-mode-extended jump buffer */ +typedef struct { + jmp_buf env; + uint16_t rm_ss; + uint16_t rm_sp; +} rmjmp_buf[1]; + +extern int __asmcall setjmp ( jmp_buf env ); +extern void __asmcall longjmp ( jmp_buf env, int val ); + +#define rmsetjmp( _env ) ( { \ + (_env)->rm_ss = rm_ss; \ + (_env)->rm_sp = rm_sp; \ + setjmp ( (_env)->env ); } ) \ + +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_ss; \ + rm_sp = (_env)->rm_sp; \ + longjmp ( (_env)->env, (_val) ); \ + } while ( 0 ) + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/qemu/roms/ipxe/src/arch/i386/include/undi.h b/qemu/roms/ipxe/src/arch/i386/include/undi.h new file mode 100644 index 000000000..325fcbbf9 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/undiload.h b/qemu/roms/ipxe/src/arch/i386/include/undiload.h new file mode 100644 index 000000000..426830e8d --- /dev/null +++ b/qemu/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 ); + +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/qemu/roms/ipxe/src/arch/i386/include/undinet.h b/qemu/roms/ipxe/src/arch/i386/include/undinet.h new file mode 100644 index 000000000..c3c17c11a --- /dev/null +++ b/qemu/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 ); + +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/qemu/roms/ipxe/src/arch/i386/include/undipreload.h b/qemu/roms/ipxe/src/arch/i386/include/undipreload.h new file mode 100644 index 000000000..de9b8fb52 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/undirom.h b/qemu/roms/ipxe/src/arch/i386/include/undirom.h new file mode 100644 index 000000000..86d7077b5 --- /dev/null +++ b/qemu/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 ); + +#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/qemu/roms/ipxe/src/arch/i386/include/vga.h b/qemu/roms/ipxe/src/arch/i386/include/vga.h new file mode 100644 index 000000000..01fc39d86 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c new file mode 100644 index 000000000..3b13e1cd0 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/apm.c @@ -0,0 +1,108 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c new file mode 100644 index 000000000..1e7de756b --- /dev/null +++ b/qemu/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 ); + +/** + * 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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c new file mode 100644 index 000000000..68546b2e5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c @@ -0,0 +1,48 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c new file mode 100644 index 000000000..dd7897e29 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c @@ -0,0 +1,61 @@ +/* + * 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 <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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c new file mode 100644 index 000000000..65bbf9e01 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c @@ -0,0 +1,66 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c new file mode 100644 index 000000000..a193defa3 --- /dev/null +++ b/qemu/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 ); + +/** + * 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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c new file mode 100644 index 000000000..1c7a8128f --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/int13.c @@ -0,0 +1,1989 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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 ( ¶ms, 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 ( ¶ms.dpte, 0xff, sizeof ( params.dpte ) ); + if ( ( rc = int13_device_path_info ( int13, ¶ms.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, ¶ms, len ); + + /* Return drive parameters */ + if ( len > bufsize ) + len = bufsize; + copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms, 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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c new file mode 100644 index 000000000..c382e3c36 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c @@ -0,0 +1,178 @@ +/* + * 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 ); + +/** + * @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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c new file mode 100644 index 000000000..61873039f --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c @@ -0,0 +1,115 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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; + + __asm__ __volatile__ ( REAL_CODE ( "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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c new file mode 100644 index 000000000..fad421c2a --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c @@ -0,0 +1,199 @@ +/* + * 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 + * + * 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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c new file mode 100644 index 000000000..67041d4ca --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c new file mode 100644 index 000000000..2adc7b040 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c @@ -0,0 +1,538 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c new file mode 100644 index 000000000..657d47b6c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c @@ -0,0 +1,349 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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 ( ¶ms, 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 ( ¶ms ); + + /* Copy modified parameter block back to caller and return */ + copy_to_user ( uparams, 0, ¶ms, 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 ( ¶ms, 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 ( ¶ms ); + + /* Copy modified parameter block back to caller and return */ + copy_to_user ( uparams, 0, ¶ms, 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; +} + +REQUIRE_OBJECT ( pxe_preboot ); +REQUIRE_OBJECT ( pxe_undi ); +REQUIRE_OBJECT ( pxe_udp ); +REQUIRE_OBJECT ( pxe_tftp ); +REQUIRE_OBJECT ( pxe_file ); diff --git a/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S new file mode 100644 index 000000000..6274264ff --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -0,0 +1,217 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c new file mode 100644 index 000000000..9d1896507 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c @@ -0,0 +1,61 @@ +/** @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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c new file mode 100644 index 000000000..6e9610294 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c @@ -0,0 +1,342 @@ +/** @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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c new file mode 100644 index 000000000..695af3b93 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c new file mode 100644 index 000000000..534352b2b --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c @@ -0,0 +1,385 @@ +/** @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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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 cached version of packet, if not already constructed. */ + if ( ! info->dhcphdr.op ) { + /* 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; + } + } + + 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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c new file mode 100644 index 000000000..f4801bad0 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c @@ -0,0 +1,592 @@ +/** @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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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 <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 ( uint32_t ipaddress, unsigned int port, + const unsigned char *filename, size_t blksize, + int sizeonly ) { + char uri_string[PXE_TFTP_URI_LEN]; + struct in_addr address; + 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 string */ + address.s_addr = ipaddress; + if ( ! port ) + port = htons ( TFTP_PORT ); + snprintf ( uri_string, sizeof ( uri_string ), "tftp%s://%s:%d%s%s", + sizeonly ? "size" : "", inet_ntoa ( address ), + ntohs ( port ), ( ( filename[0] == '/' ) ? "" : "/" ), + filename ); + DBG ( " %s", uri_string ); + + /* Open PXE TFTP connection */ + if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer, + uri_string ) ) != 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) ) != 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 ) ) != 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, 1 ) ) != 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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c new file mode 100644 index 000000000..32bc39c8e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c @@ -0,0 +1,422 @@ +/** @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 <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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** A PXE UDP connection */ +struct pxe_udp_connection { + /** Data transfer interface to UDP stack */ + struct interface xfer; + /** Local address */ + struct sockaddr_in local; + /** Current PXENV_UDP_READ parameter block */ + struct s_PXENV_UDP_READ *pxenv_udp_read; +}; + +/** + * 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 s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read; + struct sockaddr_in *sin_src; + struct sockaddr_in *sin_dest; + userptr_t buffer; + size_t len; + int rc = 0; + + if ( ! pxenv_udp_read ) { + DBG ( "PXE discarded UDP packet\n" ); + rc = -ENOBUFS; + goto done; + } + + /* 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 */ + assert ( meta ); + sin_src = ( struct sockaddr_in * ) meta->src; + assert ( sin_src ); + assert ( sin_src->sin_family == AF_INET ); + pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr; + pxenv_udp_read->s_port = sin_src->sin_port; + sin_dest = ( struct sockaddr_in * ) meta->dest; + assert ( sin_dest ); + assert ( sin_dest->sin_family == AF_INET ); + pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr; + pxenv_udp_read->d_port = sin_dest->sin_port; + + /* Mark as received */ + pxe_udp->pxenv_udp_read = NULL; + + done: + 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, + }, +}; + +/** + * 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 ) { + DBG ( "PXENV_UDP_CLOSE\n" ); + + /* Close UDP connection */ + intf_restart ( &pxe_udp.xfer, 0 ); + + 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; + uint16_t d_port_wanted = pxenv_udp_read->d_port; + uint16_t d_port; + + /* Try receiving a packet */ + pxe_udp.pxenv_udp_read = pxenv_udp_read; + step(); + if ( pxe_udp.pxenv_udp_read ) { + /* No packet received */ + DBG2 ( "PXENV_UDP_READ\n" ); + pxe_udp.pxenv_udp_read = NULL; + goto no_packet; + } + dest_ip.s_addr = pxenv_udp_read->dest_ip; + d_port = pxenv_udp_read->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 no_packet; + } + 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 no_packet; + } + + 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 ) ); + + pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; + + 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/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c new file mode 100644 index 000000000..29e586ed2 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c @@ -0,0 +1,1080 @@ +/** @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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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/qemu/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c b/qemu/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c new file mode 100644 index 000000000..0b6be9a03 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/com32_call.c new file mode 100644 index 000000000..75dcc238f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/com32_wrapper.S new file mode 100644 index 000000000..c9d1452b4 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c new file mode 100644 index 000000000..1854501de --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c @@ -0,0 +1,714 @@ +/* + * 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> +#include "config/console.h" +#include "config/serial.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 */ + serial_putc ( 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 defined(CONSOLE_SERIAL) && !defined(COMPRESERVE) + ix86->regs.dx = COMCONSOLE; + ix86->regs.cx = 115200 / COMSPEED; + ix86->regs.bx = 0; +#else + ix86->regs.dx = 0; +#endif + + ix86->flags &= ~CF; + 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 ); +} diff --git a/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c b/qemu/roms/ipxe/src/arch/i386/interface/syslinux/comboot_resolv.c new file mode 100644 index 000000000..03bbfd04a --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c new file mode 100644 index 000000000..a0530c8d1 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c new file mode 100644 index 000000000..390fc5545 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c @@ -0,0 +1,328 @@ +/* + * 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 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/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c new file mode 100644 index 000000000..c6b9fff12 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c @@ -0,0 +1,134 @@ +/* + * 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 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/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c new file mode 100644 index 000000000..8074e6118 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/vmware.c @@ -0,0 +1,58 @@ +/* + * 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 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/qemu/roms/ipxe/src/arch/i386/kir-Makefile b/qemu/roms/ipxe/src/arch/i386/kir-Makefile new file mode 100644 index 000000000..bbfc1a3a6 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/kir-Makefile @@ -0,0 +1,26 @@ +# Makefile to build a KEEP_IT_REAL flavour +# +# KEEP_IT_REAL, by its nature, requires a different build of every +# single object file, since the inclusion of ".code16gcc" will +# generate different machine code from the assembly. Unlike the other +# config options, there is no way that this global dependency can ever +# be reduced, so it makes sense to be able to build both the normal +# and the KIR versions without having to force a full rebuild each +# time. + +# Add this Makefile to MAKEDEPS +# +MAKEDEPS += arch/i386/kir-Makefile + +# Place binaries in bin-kir +# +BIN = bin-kir + +# Compile with -DKEEP_IT_REAL, forcibly include kir.h at the start of +# each file to drag in ".code16gcc" +# +CFLAGS += -DKEEP_IT_REAL -include kir.h + +include Makefile + +LDSCRIPT = arch/i386/scripts/i386-kir.lds diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S b/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S new file mode 100644 index 000000000..968da1a38 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/bootpart.S @@ -0,0 +1,218 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + +#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/qemu/roms/ipxe/src/arch/i386/prefix/dskprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/dskprefix.S new file mode 100644 index 000000000..7aa017ccd --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S new file mode 100644 index 000000000..cb61287d3 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/exeprefix.S @@ -0,0 +1,157 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +/* 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/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S new file mode 100644 index 000000000..876bfe1be --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/hdprefix.S @@ -0,0 +1,111 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S new file mode 100644 index 000000000..e28208089 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/isaromprefix.S @@ -0,0 +1,25 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#define BUSTYPE "ISAR" +#define _rom_start _isarom_start +#include "romprefix.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S new file mode 100644 index 000000000..27ed231e7 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S @@ -0,0 +1,19 @@ +/***************************************************************************** + * 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 ) + +/* Since we have the whole stack, we can use cached DHCP information */ +REQUIRE_OBJECT ( pxeparent_dhcp ) + +/* Provide the PXENV_FILE_EXIT_HOOK API call */ +REQUIRE_OBJECT ( pxe_exit_hook ) + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#define _pxe_start _kkkpxe_start +#include "pxeprefix.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S new file mode 100644 index 000000000..d177d7d62 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S @@ -0,0 +1,14 @@ +/***************************************************************************** + * PXE prefix that keeps the whole PXE stack present + ***************************************************************************** + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +/* Since we have the whole stack, we can use cached DHCP information */ +REQUEST_OBJECT ( pxeparent_dhcp ) + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#define _pxe_start _kkpxe_start +#include "pxeprefix.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S new file mode 100644 index 000000000..c75608172 --- /dev/null +++ b/qemu/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 ) + +#define PXELOADER_KEEP_UNDI +#define _pxe_start _kpxe_start +#include "pxeprefix.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S new file mode 100644 index 000000000..7c1ece791 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/libprefix.S @@ -0,0 +1,969 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .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: + * %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 +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 + + /* Call memcpy()-like function */ + call *%bx + + /* 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 + + /* 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, %cx + shll $4, %eax + addl %eax, %esi + xorl %eax, %eax + movw %es, %cx + 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 */ + pushal + pushw %ds + pushw %es + cld /* Sanity: clear the direction flag asap */ + + /* 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_memsz, %edx + call install_block /* .data16 */ + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + + /* Restore decompression temporary area physical address */ + popl %edi + +#ifdef KEEP_IT_REAL + /* Initialise libkir */ + movw %ax, (init_libkir_vector+2) + lcall *init_libkir_vector +#else + /* 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 + + /* 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 +#endif + + /* 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/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S new file mode 100644 index 000000000..259bc6ba5 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S @@ -0,0 +1,236 @@ +FILE_LICENCE ( GPL_ANY ) + +#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/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S b/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S new file mode 100644 index 000000000..adfe20410 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/mbr.S @@ -0,0 +1,13 @@ + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +mbr: + movw $exec_sector, %bp + jmp find_active_partition +exec_sector: + ljmp $0x0000, $0x7c00 + +#include "bootpart.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S new file mode 100644 index 000000000..4c94457c2 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/mromprefix.S @@ -0,0 +1,511 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#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 */ + .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/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S new file mode 100644 index 000000000..06e7df5b7 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/nbiprefix.S @@ -0,0 +1,82 @@ + .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/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S new file mode 100644 index 000000000..032d41e0f --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/nullprefix.S @@ -0,0 +1,13 @@ + .org 0 + .text + .arch i386 + + .section ".prefix", "ax", @progbits + .code16 +_prefix: + + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S new file mode 100644 index 000000000..45ba31f50 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/pciromprefix.S @@ -0,0 +1,25 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#define BUSTYPE "PCIR" +#define _rom_start _pcirom_start +#include "romprefix.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 000000000..6e29c7949 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,862 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + +#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/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S b/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S new file mode 100644 index 000000000..7bc4fe8cd --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/romprefix.S @@ -0,0 +1,872 @@ +/* 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 ) + +#include <config/general.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 0x0000 /* 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 + + .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/general.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" + .asciz "iPXE (http://ipxe.org)" + .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" + movw %ax, autoboot_busdevfn +.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 diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S b/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S new file mode 100644 index 000000000..74bb59041 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/undiloader.S @@ -0,0 +1,54 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S new file mode 100644 index 000000000..f5724c134 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b.S @@ -0,0 +1,184 @@ +/* + * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Originally this code was part of ucl the data compression library + * for upx the ``Ultimate Packer of eXecutables''. + * + * - Converted to gas assembly, and refitted to work with etherboot. + * Eric Biederman 20 Aug 2002 + * + * - Structure modified to be a subroutine call rather than an + * executable prefix. + * Michael Brown 30 Mar 2004 + * + * - Modified to be compilable as either 16-bit or 32-bit code. + * Michael Brown 9 Mar 2005 + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +/**************************************************************************** + * This file provides the decompress() and decompress16() functions + * which can be called in order to decompress an image compressed with + * the nrv2b utility in src/util. + * + * These functions are designed to be called by the prefix. They are + * position-independent code. + * + * The same basic assembly code is used to compile both + * decompress() and decompress16(). + **************************************************************************** + */ + + .text + .arch i386 + .section ".prefix.lib", "ax", @progbits + +#ifdef CODE16 +/**************************************************************************** + * decompress16 (real-mode near call, position independent) + * + * Decompress data in 16-bit mode + * + * Parameters (passed via registers): + * %ds:%esi - Start of compressed input data + * %es:%edi - Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + * + * NOTE: It would be possible to build a smaller version of the + * decompression code for -DKEEP_IT_REAL by using + * #define REG(x) x + * to use 16-bit registers where possible. This would impose limits + * that the compressed data size must be in the range [1,65533-%si] + * and the uncompressed data size must be in the range [1,65536-%di] + * (where %si and %di are the input values for those registers). Note + * particularly that the lower limit is 1, not 0, and that the upper + * limit on the input (compressed) data really is 65533, since the + * algorithm may read up to three bytes beyond the end of the input + * data, since it reads dwords. + **************************************************************************** + */ + +#define REG(x) e ## x +#define ADDR32 addr32 + + .code16 + .globl decompress16 +decompress16: + +#else /* CODE16 */ + +/**************************************************************************** + * decompress (32-bit protected-mode near call, position independent) + * + * Parameters (passed via registers): + * %ds:%esi - Start of compressed input data + * %es:%edi - Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + **************************************************************************** + */ + +#define REG(x) e ## x +#define ADDR32 + + .code32 + .globl decompress +decompress: + +#endif /* CODE16 */ + +#define xAX REG(ax) +#define xCX REG(cx) +#define xBP REG(bp) +#define xSI REG(si) +#define xDI REG(di) + + /* Save registers */ + push %xAX + pushl %ebx + push %xCX + push %xBP + /* Do the decompression */ + cld + xor %xBP, %xBP + dec %xBP /* last_m_off = -1 */ + jmp dcl1_n2b + +decompr_literals_n2b: + ADDR32 movsb +decompr_loop_n2b: + addl %ebx, %ebx + jnz dcl2_n2b +dcl1_n2b: + call getbit32 +dcl2_n2b: + jc decompr_literals_n2b + xor %xAX, %xAX + inc %xAX /* m_off = 1 */ +loop1_n2b: + call getbit1 + adc %xAX, %xAX /* m_off = m_off*2 + getbit() */ + call getbit1 + jnc loop1_n2b /* while(!getbit()) */ + sub $3, %xAX + jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + shl $8, %xAX + ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ + inc %xSI + xor $-1, %xAX + jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + mov %xAX, %xBP /* last_m_off = m_off ?*/ +decompr_ebpeax_n2b: + xor %xCX, %xCX + call getbit1 + adc %xCX, %xCX /* m_len = getbit() */ + call getbit1 + adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */ + jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + inc %xCX /* m_len++ */ +loop2_n2b: + call getbit1 + adc %xCX, %xCX /* m_len = m_len*2 + getbit() */ + call getbit1 + jnc loop2_n2b /* while(!getbit()) */ + inc %xCX + inc %xCX /* m_len += 2 */ +decompr_got_mlen_n2b: + cmp $-0xd00, %xBP + adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */ + push %xSI + ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */ + rep + es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ + pop %xSI + jmp decompr_loop_n2b + + +getbit1: + addl %ebx, %ebx + jnz 1f +getbit32: + ADDR32 movl (%xSI), %ebx + sub $-4, %xSI /* sets carry flag */ + adcl %ebx, %ebx +1: + ret + +decompr_end_n2b: + /* Restore registers and return */ + pop %xBP + pop %xCX + popl %ebx + pop %xAX + ret diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S new file mode 100644 index 000000000..b24c2846f --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S @@ -0,0 +1,9 @@ +/* + * 16-bit version of the decompressor + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#define CODE16 +#include "unnrv2b.S" diff --git a/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S b/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S new file mode 100644 index 000000000..fa7d1956e --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/prefix/usbdisk.S @@ -0,0 +1,23 @@ + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +#include "mbr.S" + +/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */ + .org 446 + .space 16 + .space 16 + .space 16 + .byte 0x80, 0x01, 0x01, 0x00 + .byte 0xeb, 0x3f, 0x20, 0x01 + .long 0x00000020 + .long 0x00000fe0 + + .org 510 + .byte 0x55, 0xaa + +/* Skip to start of partition */ + .org 32 * 512 diff --git a/qemu/roms/ipxe/src/arch/i386/scripts/i386-kir.lds b/qemu/roms/ipxe/src/arch/i386/scripts/i386-kir.lds new file mode 100644 index 000000000..66bf804e6 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/scripts/i386-kir.lds @@ -0,0 +1,202 @@ +/* -*- sh -*- */ + +/* + * Linker script for i386 images + * + */ + +OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" ) +OUTPUT_ARCH ( i386 ) + +SECTIONS { + + /* All sections in the resulting file have consecutive load + * addresses, but may have individual link addresses depending on + * the memory model being used. + * + * The linker symbols _prefix_link_addr, load_addr, and + * _max_align may be specified explicitly. If not specified, they + * will default to: + * + * _prefix_link_addr = 0 + * _load_addr = 0 + * _max_align = 16 + * + * 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). + * + */ + + /* + * The prefix + */ + + _prefix_link_addr = DEFINED ( _prefix_link_addr ) ? _prefix_link_addr : 0; + . = _prefix_link_addr; + _prefix = .; + + .prefix : AT ( _prefix_load_offset + __prefix ) { + __prefix = .; + _entry = .; + *(.prefix) + *(.prefix.*) + _eprefix_progbits = .; + } + + _eprefix = .; + + /* + * The 16-bit sections + */ + + _text16_link_addr = 0; + . = _text16_link_addr; + _text16 = .; + + . += 1; /* Prevent NULL being valid */ + + .text16 : AT ( _text16_load_offset + __text16 ) { + __text16 = .; + KEEP(*(.text.null_trap)) + KEEP(*(.text.null_trap.*)) + *(.text16) + *(.text16.*) + *(.text) + *(.text.*) + _etext16_progbits = .; + } = 0x9090 + + _etext16 = .; + + _data16_link_addr = 0; + . = _data16_link_addr; + _data16 = .; + + . += 1; /* Prevent NULL being valid */ + + .rodata16 : AT ( _data16_load_offset + __rodata16 ) { + __rodata16 = .; + *(.rodata16) + *(.rodata16.*) + *(.rodata) + *(.rodata.*) + } + .data16 : AT ( _data16_load_offset + __data16 ) { + __data16 = .; + *(.data16) + *(.data16.*) + *(.data) + *(.data.*) + KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) + _edata16_progbits = .; + } + .bss16 : AT ( _data16_load_offset + __bss16 ) { + __bss16 = .; + _bss16 = .; + *(.bss16) + *(.bss16.*) + *(.bss) + *(.bss.*) + *(COMMON) + _ebss16 = .; + } + .stack16 : AT ( _data16_load_offset + __stack16 ) { + __stack16 = .; + *(.stack16) + *(.stack16.*) + *(.stack) + *(.stack.*) + } + + _edata16 = .; + + _end = .; + + /* + * Dispose of the comment and note sections to make the link map + * easier to read + */ + + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.note) + *(.note.*) + *(.discard) + *(.discard.*) + } + + /* + * Load address calculations. The slightly obscure nature of the + * calculations is because ALIGN(x) can only operate on the + * location counter. + */ + + _max_align = DEFINED ( _max_align ) ? _max_align : 16; + _load_addr = DEFINED ( _load_addr ) ? _load_addr : 0; + + . = _load_addr; + + . -= _prefix_link_addr; + _prefix_load_offset = ALIGN ( _max_align ); + _prefix_load_addr = _prefix_link_addr + _prefix_load_offset; + _prefix_size = _eprefix - _prefix; + _prefix_progbits_size = _eprefix_progbits - _prefix; + . = _prefix_load_addr + _prefix_progbits_size; + + . -= _text16_link_addr; + _text16_load_offset = ALIGN ( _max_align ); + _text16_load_addr = _text16_link_addr + _text16_load_offset; + _text16_size = _etext16 - _text16; + _text16_progbits_size = _etext16_progbits - _text16; + . = _text16_load_addr + _text16_progbits_size; + + . -= _data16_link_addr; + _data16_load_offset = ALIGN ( _max_align ); + _data16_load_addr = _data16_link_addr + _data16_load_offset; + _data16_size = _edata16 - _data16; + _data16_progbits_size = _edata16_progbits - _data16; + . = _data16_load_addr + _data16_progbits_size; + + . = ALIGN ( _max_align ); + + _load_size = . - _load_addr; + + /* + * Alignment checks. ALIGN() can only operate on the location + * counter, so we set the location counter to each value we want + * to check. + */ + + . = _prefix_load_addr - _prefix_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_prefix is badly aligned" ); + + . = _text16_load_addr - _text16_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_text16 is badly aligned" ); + + . = _data16_load_addr - _data16_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_data16 is badly aligned" ); + + /* + * Values calculated to save code from doing it + */ + _text16_size_pgh = ( ( _text16_size + 15 ) / 16 ); + _data16_size_pgh = ( ( _data16_size + 15 ) / 16 ); + _load_size_pgh = ( ( _load_size + 15 ) / 16 ); + _load_size_sect = ( ( _load_size + 511 ) / 512 ); +} diff --git a/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds b/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds new file mode 100644 index 000000000..98f95cb23 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/scripts/i386.lds @@ -0,0 +1,245 @@ +/* -*- 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 ); + + /* + * The prefix + * + */ + + .prefix 0x0 : AT ( _prefix_lma ) { + _prefix = .; + *(.prefix) + *(.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.*) + _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/qemu/roms/ipxe/src/arch/i386/scripts/linux.lds b/qemu/roms/ipxe/src/arch/i386/scripts/linux.lds new file mode 100644 index 000000000..9f2eeaf3c --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/scripts/linux.lds @@ -0,0 +1,104 @@ +/* -*- sh -*- */ + +/* + * Linker script for i386 Linux images + * + */ + +OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" ) +OUTPUT_ARCH ( i386 ) + +SECTIONS { + _max_align = 32; + + . = 0x08048000; + + /* + * The text section + * + */ + + . = ALIGN ( _max_align ); + .text : { + _text = .; + *(.text) + *(.text.*) + _etext = .; + } + + /* + * The rodata section + * + */ + + . = ALIGN ( _max_align ); + .rodata : { + _rodata = .; + *(.rodata) + *(.rodata.*) + _erodata = .; + } + + /* + * The data section + * + * Adjust the address for the data segment. We want to adjust up to + * the same address within the page on the next page up. + */ + + . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + .data : { + _data = .; + *(.data) + *(.data.*) + KEEP(*(SORT(.tbl.*))) + KEEP(*(.provided)) + KEEP(*(.provided.*)) + _edata = .; + } + + /* + * The bss section + * + */ + + . = ALIGN ( _max_align ); + .bss : { + _bss = .; + *(.bss) + *(.bss.*) + *(COMMON) + _ebss = .; + } + + /* + * Weak symbols that need zero values if not otherwise defined + * + */ + + .weak 0x0 : { + _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.*) + *(.rel) + *(.rel.*) + *(.discard) + *(.discard.*) + } +} diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S b/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S new file mode 100644 index 000000000..684697525 --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/transitions/liba20.S @@ -0,0 +1,309 @@ +/* + * 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. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + + .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/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S b/qemu/roms/ipxe/src/arch/i386/transitions/libkir.S new file mode 100644 index 000000000..1176fcced --- /dev/null +++ b/qemu/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 ) + +/**************************************************************************** + * 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/qemu/roms/ipxe/src/arch/i386/transitions/libpm.S b/qemu/roms/ipxe/src/arch/i386/transitions/libpm.S new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/transitions/libpm.S diff --git a/qemu/roms/ipxe/src/arch/i386/transitions/librm.S b/qemu/roms/ipxe/src/arch/i386/transitions/librm.S new file mode 100644 index 000000000..2e447b030 --- /dev/null +++ b/qemu/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 ) + +/* 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/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c b/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c new file mode 100644 index 000000000..cc4765de2 --- /dev/null +++ b/qemu/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 ); + +#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+%xn (phys %#lx+%xn)\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/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c b/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c new file mode 100644 index 000000000..e07cfccdd --- /dev/null +++ b/qemu/roms/ipxe/src/arch/i386/transitions/librm_test.c @@ -0,0 +1,117 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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, +}; + +REQUIRE_OBJECT ( test ); |