diff options
Diffstat (limited to 'gpxe/src/arch')
75 files changed, 2111 insertions, 1925 deletions
diff --git a/gpxe/src/arch/i386/Config b/gpxe/src/arch/i386/Config deleted file mode 100644 index 1c086ecc..00000000 --- a/gpxe/src/arch/i386/Config +++ /dev/null @@ -1,148 +0,0 @@ -# -*- makefile -*- - -############################################################################## -############################################################################## -# -# IMPORTANT! -# -# The use of this file to set options that affect only single object -# files is deprecated, because changing anything in this file results -# in a complete rebuild, which is slow. All options are gradually -# being migrated to config.h, which does not suffer from this problem. -# -# Only options that affect the entire build (e.g. overriding the $(CC) -# Makefile variable) should be placed in here. -# -############################################################################## -############################################################################## - - -# Config for i386 Etherboot -# -# Do not delete the tag OptionDescription and /OptionDescription -# It is used to automatically generate the documentation. -# -# @OptionDescrition@ -# -# BIOS interface options: -# -# -DPCBIOS -# Compile in support for the normal pcbios -# -DLINUXBIOS -# Compile in support for LinuxBIOS -# -DBBS_BUT_NOT_PNP_COMPLIANT -# Some BIOSes claim to be PNP but they don't conform -# to the BBS spec which specifies that ES:DI must -# point to the string $PnP on entry. This option -# works around those. This option must be added to -# LCONFIG. -# -DNO_DELAYED_INT -# Take control as soon as BIOS detects the ROM. -# Normally hooks onto INT18H or INT19H. Use only if you -# have a very non-conformant BIOS as it bypasses -# BIOS initialisation of devices. This only works for -# legacy ROMs, i.e. PCI_PNP_HEADER not defined. -# This option was formerly called NOINT19H. -# -DBOOT_INT18H -# Etherboot normally hooks onto INT19H for legacy ROMs. -# You can choose to hook onto INT18H (BASIC interpreter -# entry point) instead. This entry point is used when -# all boot devices have been exhausted. This option must -# be added to LCONFIG. -# -DCONFIG_PCI_DIRECT -# Define this for PCI BIOSes that do not implement -# BIOS32 or not correctly. Normally not needed. -# Only works for BIOSes of a certain era. -# -DCONFIG_TSC_CURRTICKS -# Uses the processor time stamp counter instead of reading -# the BIOS time counter. This allows Etherboot to work -# even without a BIOS. This only works on late model -# 486s and above. -# -DCONFIG_NO_TIMER2 -# Some systems do not have timer2 implemented. -# If you have a RTC this will allow you to roughly calibrate -# it using outb instructions. -# -# Extended cpu options - -# -DCONFIG_X86_64 -# Compile in support for booting x86_64 64bit binaries. -# -# PXE loader options: -# -# -DPXELOADER_KEEP_ALL -# Prevent PXE loader (prefix) from unloading the -# PXE stack. You will want to use this if, for -# example, you are booting via PXE-on-floppy. -# You may want to use it under certain -# circumstances when using the Etherboot UNDI -# driver; these are complex and best practice is -# not yet established. -# -# Obscure options you probably don't need to touch: -# -# -DIGNORE_E820_MAP -# Ignore the memory map returned by the E820 BIOS -# call. May be necessary on some buggy BIOSes. -# -DT503_AUI -# Use AUI by default on 3c503 cards. -# -DFLATTEN_REAL_MODE -# Use 4GB segment limits when calling out to or -# returning to real-mode code. This is necessary to -# work around some buggy code (e.g. OpenBSD's pxeboot) -# that uses flat real-mode without being sufficiently -# paranoid about the volatility of its segment limits. - -# -# @/OptionDescription@ - -# BIOS select don't change unless you know what you are doing -# CFLAGS+= -DPCBIOS - -# Compile in k8/hammer support -# CFLAGS+= -DCONFIG_X86_64 - -# Options to make a version of Etherboot that will work under linuxBIOS. -# CFLAGS+= -DLINUXBIOS -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 -DCOMPRESERVE -DCONFIG_PCI_DIRECT -DELF_IMAGE - -# These options affect the loader that is prepended to the Etherboot image -# LCONFIG+= -DBBS_BUT_NOT_PNP_COMPLIANT -# LCONFIG+= -DBOOT_INT18H - -# Produce code that will work with OpenBSD's pxeboot -# CFLAGS+= -DFLATTEN_REAL_MODE - -CFLAGS+= -fstrength-reduce -fomit-frame-pointer -march=i386 -# Squeeze the code in as little space as possible. -# gcc3 needs a different syntax to gcc2 if you want to avoid spurious warnings. -GCC_VERSION = $(subst ., ,$(shell $(CC) -dumpversion)) -GCC_MAJORVERSION = $(firstword $(GCC_VERSION)) -ifeq ($(GCC_MAJORVERSION),2) -CFLAGS+= -malign-jumps=1 -malign-loops=1 -malign-functions=1 -else -CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1 -endif - -# this is almost always a win. the kernel uses it, too. -CFLAGS+= -mpreferred-stack-boundary=2 - -# use regparm for all functions - C functions called from assembly (or -# vice versa) need __cdecl now -CFLAGS+= -mregparm=3 - -# use -mrtd (same __cdecl requirements as above) -CFLAGS+= -mrtd - -# 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 - -LDFLAGS+= -N --no-check-sections - -ifeq "$(shell uname -s)" "FreeBSD" -CFLAGS+= -DIMAGE_FREEBSD -DELF_IMAGE -DAOUT_IMAGE -endif - -# An alternate location for isolinux.bin can be set here -# ISOLINUX_BIN=/path/to/isolinux.bin diff --git a/gpxe/src/arch/i386/Makefile b/gpxe/src/arch/i386/Makefile index 97ca0774..7d3e7639 100644 --- a/gpxe/src/arch/i386/Makefile +++ b/gpxe/src/arch/i386/Makefile @@ -1,3 +1,59 @@ +# Force i386-only instructions +# +CFLAGS += -march=i386 + +# Code size reduction. +# +CFLAGS += -fstrength-reduce -fomit-frame-pointer + +# Code size reduction. gcc3 needs a different syntax to gcc2 if you +# want to avoid spurious warnings. +# +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 + +# Code size reduction. This is almost always a win. The kernel uses it, too. +# +CFLAGS += -mpreferred-stack-boundary=2 + +# Code size reduction. Use regparm for all functions - C functions +# called from assembly (or vice versa) need __cdecl now +# +CFLAGS += -mregparm=3 + +# Code size reduction. Use -mrtd (same __cdecl requirements as above) +CFLAGS += -mrtd + +# 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 +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 + # Locations of utilities # ISOLINUX_BIN = /usr/lib/syslinux/isolinux.bin @@ -12,6 +68,7 @@ SRCDIRS += arch/i386/drivers/net SRCDIRS += arch/i386/interface/pcbios SRCDIRS += arch/i386/interface/pxe SRCDIRS += arch/i386/interface/syslinux +SRCDIRS += arch/i386/interface/efi # The various xxx_loader.c files are #included into core/loader.c and # should not be compiled directly. @@ -22,85 +79,32 @@ NON_AUTO_SRCS += arch/i386/core/wince_loader.c # unnrv2b.S is used to generate a 16-bit as well as a 32-bit object. # -OBJS_unnrv2b = unnrv2b unnrv2b16 -CFLAGS_unnrv2b16 = -DCODE16 - -# We need to undefine the default macro "i386" when compiling .S -# files, otherwise ".arch i386" translates to ".arch 1"... -# -CFLAGS_S += -Ui386 +OBJS_unnrv2b = unnrv2b unnrv2b16 +CFLAGS_unnrv2b16 = -DCODE16 -# The i386 linker script +# Include platform-specific Makefile # -LDSCRIPT = arch/i386/scripts/i386.lds - -# Media types. -# -MEDIA += rom -MEDIA += pxe -MEDIA += kpxe -MEDIA += elf -MEDIA += elfd -MEDIA += lmelf -MEDIA += lmelfd -MEDIA += lkrn -MEDIA += bImage -MEDIA += dsk -MEDIA += nbi -MEDIA += hd -MEDIA += raw -MEDIA += com -MEDIA += exe - -# Special target for building Master Boot Record binary -$(BIN)/mbr.bin : $(BIN)/mbr.o - $(OBJCOPY) -O binary $< $@ +MAKEDEPS += arch/i386/Makefile.$(PLATFORM) +include arch/i386/Makefile.$(PLATFORM) # Some suffixes (e.g. %.fd0) are generated directly from other # finished files (e.g. %.dsk), rather than having their own prefix. # rule to write disk images to /dev/fd0 -NON_AUTO_MEDIA += fd0 +NON_AUTO_MEDIA += fd0 %fd0 : %dsk - dd if=$< bs=512 conv=sync of=/dev/fd0 - sync + $(QM)$(ECHO) " [DD] $@" + $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 + $(Q)sync # rule to create padded disk images -NON_AUTO_MEDIA += pdsk +NON_AUTO_MEDIA += pdsk %pdsk : %dsk - cp $< $@ - $(PERL) ./util/dskpad.pl $@ - -# rule to make a non-emulation ISO boot image -NON_AUTO_MEDIA += iso -%iso: %lkrn util/geniso - ISOLINUX_BIN=$(ISOLINUX_BIN) bash util/geniso $@ $< - -# rule to make a floppy emulation ISO boot image -NON_AUTO_MEDIA += liso -%liso: %lkrn util/genliso - bash util/genliso $@ $< - -# rule to make a USB disk image -$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o - $(OBJCOPY) -O binary $< $@ - -NON_AUTO_MEDIA += usb -%usb: $(BIN)/usbdisk.bin %hd - cat $^ > $@ + $(QM)$(ECHO) " [DSKPAD] $@" + $(Q)cp $< $@ + $(Q)$(PERL) ./util/dskpad.pl $@ # Add NON_AUTO_MEDIA to the media list, so that they show up in the # output of "make" # MEDIA += $(NON_AUTO_MEDIA) - -# Shortcut to allow typing just -# make bin-kir/% -# rather than -# make -f arch/i386/kir-Makefile bin-kir/% -# for building a KEEP_IT_REAL flavour. -# -$(BIN)-kir/% : kir-target - $(MAKE) -f arch/i386/kir-Makefile $(MAKECMDGOALS) - -.PHONY : kir-target diff --git a/gpxe/src/arch/i386/Makefile.efi b/gpxe/src/arch/i386/Makefile.efi new file mode 100644 index 00000000..f1eb6fdf --- /dev/null +++ b/gpxe/src/arch/i386/Makefile.efi @@ -0,0 +1,24 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The EFI linker script +# +LDSCRIPT = arch/i386/scripts/efi.lds + +# Use a relocatable link; we perform final relocations in the efilink utility. +# +LDFLAGS += -r -d -S + +# Media types. +# +NON_AUTO_MEDIA += efi + +# Rule for building EFI files +# +$(BIN)/%.efi.tmp-reloc : $(BIN)/%.efi.tmp $(EFILINK) + $(QM)$(ECHO) " [EFILINK] $@" + $(Q)$(LD) -e 0 -o /dev/null $< # Check for unresolved symbols + $(Q)$(EFILINK) $< $@ + +$(BIN)/%.efi : $(BIN)/%.efi.tmp-reloc + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(OBJCOPY) -Obinary $< $@ diff --git a/gpxe/src/arch/i386/Makefile.pcbios b/gpxe/src/arch/i386/Makefile.pcbios new file mode 100644 index 00000000..64b3dac2 --- /dev/null +++ b/gpxe/src/arch/i386/Makefile.pcbios @@ -0,0 +1,55 @@ +# -*- 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 + +# Media types. +# +MEDIA += rom +MEDIA += pxe +MEDIA += kpxe +MEDIA += kkpxe +MEDIA += elf +MEDIA += elfd +MEDIA += lmelf +MEDIA += lmelfd +MEDIA += lkrn +MEDIA += bImage +MEDIA += dsk +MEDIA += nbi +MEDIA += hd +MEDIA += raw +MEDIA += com +MEDIA += exe + +# 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) bash util/geniso $@ $< + +# rule to make a floppy emulation ISO boot image +NON_AUTO_MEDIA += liso +%liso: %lkrn util/genliso + $(QM)$(ECHO) " [GENLISO] $@" + $(Q)bash util/genliso $@ $< + +# 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 $^ > $@ diff --git a/gpxe/src/arch/i386/core/gdbidt.S b/gpxe/src/arch/i386/core/gdbidt.S index 860f7b01..64c29e4b 100644 --- a/gpxe/src/arch/i386/core/gdbidt.S +++ b/gpxe/src/arch/i386/core/gdbidt.S @@ -2,7 +2,7 @@ * Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub. */ -#include <virtaddr.h> +#include <librm.h> #define SIZEOF_I386_REGS 32 #define SIZEOF_I386_FLAGS 4 diff --git a/gpxe/src/arch/i386/core/gdbmach.c b/gpxe/src/arch/i386/core/gdbmach.c index 5e72e4d0..d07663c4 100644 --- a/gpxe/src/arch/i386/core/gdbmach.c +++ b/gpxe/src/arch/i386/core/gdbmach.c @@ -19,7 +19,7 @@ #include <stddef.h> #include <stdio.h> #include <assert.h> -#include <virtaddr.h> +#include <gpxe/uaccess.h> #include <gpxe/gdbstub.h> #include <gdbmach.h> diff --git a/gpxe/src/arch/i386/core/nap.c b/gpxe/src/arch/i386/core/nap.c deleted file mode 100644 index 12bb5699..00000000 --- a/gpxe/src/arch/i386/core/nap.c +++ /dev/null @@ -1,12 +0,0 @@ - -#include <realmode.h> -#include <bios.h> - -/************************************************************************** - * Save power by halting the CPU until the next interrupt - **************************************************************************/ -void cpu_nap ( void ) { - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "hlt\n\t" - "cli\n\t" ) : : ); -} diff --git a/gpxe/src/arch/i386/core/pcidirect.c b/gpxe/src/arch/i386/core/pcidirect.c index 2ed8c2ad..fec2e3c1 100644 --- a/gpxe/src/arch/i386/core/pcidirect.c +++ b/gpxe/src/arch/i386/core/pcidirect.c @@ -17,7 +17,6 @@ */ #include <gpxe/pci.h> -#include <pcidirect.h> /** @file * @@ -36,3 +35,10 @@ void pcidirect_prepare ( struct pci_device *pci, int where ) { ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS ); } +PROVIDE_PCIAPI_INLINE ( direct, pci_max_bus ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_dword ); diff --git a/gpxe/src/arch/i386/core/pic8259.c b/gpxe/src/arch/i386/core/pic8259.c index defe2e7d..8a0433dd 100644 --- a/gpxe/src/arch/i386/core/pic8259.c +++ b/gpxe/src/arch/i386/core/pic8259.c @@ -16,6 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <gpxe/io.h> #include <pic8259.h> /** @file diff --git a/gpxe/src/arch/i386/core/rdtsc_timer.c b/gpxe/src/arch/i386/core/rdtsc_timer.c new file mode 100644 index 00000000..443c8ada --- /dev/null +++ b/gpxe/src/arch/i386/core/rdtsc_timer.c @@ -0,0 +1,87 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * + * RDTSC timer + * + */ + +#include <assert.h> +#include <gpxe/timer.h> +#include <gpxe/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/gpxe/src/arch/i386/core/relocate.c b/gpxe/src/arch/i386/core/relocate.c index aa58ad65..fd8df087 100644 --- a/gpxe/src/arch/i386/core/relocate.c +++ b/gpxe/src/arch/i386/core/relocate.c @@ -1,4 +1,4 @@ -#include <io.h> +#include <gpxe/io.h> #include <registers.h> #include <gpxe/memmap.h> @@ -18,8 +18,8 @@ extern char _max_align[]; #define max_align ( ( unsigned int ) _max_align ) /* Linker symbols */ -extern char _text[]; -extern char _end[]; +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. @@ -47,8 +47,8 @@ __cdecl void relocate ( struct i386_all_regs *ix86 ) { /* Get memory map and current location */ get_memmap ( &memmap ); - start = virt_to_phys ( _text ); - end = virt_to_phys ( _end ); + start = virt_to_phys ( _textdata ); + end = virt_to_phys ( _etextdata ); size = ( end - start ); padded_size = ( size + max_align - 1 ); diff --git a/gpxe/src/arch/i386/core/start32.S b/gpxe/src/arch/i386/core/start32.S deleted file mode 100644 index 37ef5eb9..00000000 --- a/gpxe/src/arch/i386/core/start32.S +++ /dev/null @@ -1,325 +0,0 @@ -#include "virtaddr.h" - - .equ MSR_K6_EFER, 0xC0000080 - .equ EFER_LME, 0x00000100 - .equ X86_CR4_PAE, 0x00000020 - .equ CR0_PG, 0x80000000 - -#ifdef GAS291 -#define DATA32 data32; -#define ADDR32 addr32; -#define LJMPI(x) ljmp x -#else -#define DATA32 data32 -#define ADDR32 addr32 -/* newer GAS295 require #define LJMPI(x) ljmp *x */ -#define LJMPI(x) ljmp x -#endif - -/* - * NOTE: if you write a subroutine that is called from C code (gcc/egcs), - * then you only have to take care of %ebx, %esi, %edi and %ebp. These - * registers must not be altered under any circumstance. All other registers - * may be clobbered without any negative side effects. If you don't follow - * this rule then you'll run into strange effects that only occur on some - * gcc versions (because the register allocator may use different registers). - * - * All the data32 prefixes for the ljmp instructions are necessary, because - * the assembler emits code with a relocation address of 0. This means that - * all destinations are initially negative, which the assembler doesn't grok, - * because for some reason negative numbers don't fit into 16 bits. The addr32 - * prefixes are there for the same reasons, because otherwise the memory - * references are only 16 bit wide. Theoretically they are all superfluous. - * One last note about prefixes: the data32 prefixes on all call _real_to_prot - * instructions could be removed if the _real_to_prot function is changed to - * deal correctly with 16 bit return addresses. I tried it, but failed. - */ - - .text - .arch i386 - .code32 - - /* This is a struct os_entry_regs */ - .globl os_regs -os_regs: .space 56 - -/************************************************************************** -XSTART32 - Transfer control to the kernel just loaded -**************************************************************************/ - .globl xstart32 -xstart32: - /* Save the callee save registers */ - movl %ebp, os_regs + 32 - movl %esi, os_regs + 36 - movl %edi, os_regs + 40 - movl %ebx, os_regs + 44 - - /* save the return address */ - popl %eax - movl %eax, os_regs + 48 - - /* save the stack pointer */ - movl %esp, os_regs + 52 - - /* Get the new destination address */ - popl %ecx - - /* Store the physical address of xend on the stack */ - movl $xend32, %ebx - addl virt_offset, %ebx - pushl %ebx - - /* Store the destination address on the stack */ - pushl $PHYSICAL_CS - pushl %ecx - - /* Cache virt_offset */ - movl virt_offset, %ebp - - /* Switch to using physical addresses */ - call _virt_to_phys - - /* Save the target stack pointer */ - movl %esp, os_regs + 12(%ebp) - leal os_regs(%ebp), %esp - - /* Store the pointer to os_regs */ - movl %esp, os_regs_ptr(%ebp) - - /* Load my new registers */ - popal - movl (-32 + 12)(%esp), %esp - - /* Jump to the new kernel - * The lret switches to a flat code segment - */ - lret - - .balign 4 - .globl xend32 -xend32: - /* Fixup %eflags */ - nop - cli - cld - - /* Load %esp with &os_regs + virt_offset */ - .byte 0xbc /* movl $0, %esp */ -os_regs_ptr: - .long 0 - - /* Save the result registers */ - addl $32, %esp - pushal - - /* Compute virt_offset */ - movl %esp, %ebp - subl $os_regs, %ebp - - /* Load the stack pointer and convert it to physical address */ - movl 52(%esp), %esp - addl %ebp, %esp - - /* Enable the virtual addresses */ - leal _phys_to_virt(%ebp), %eax - call *%eax - - /* Restore the callee save registers */ - movl os_regs + 32, %ebp - movl os_regs + 36, %esi - movl os_regs + 40, %edi - movl os_regs + 44, %ebx - movl os_regs + 48, %edx - movl os_regs + 52, %esp - - /* Get the C return value */ - movl os_regs + 28, %eax - - jmpl *%edx - -#ifdef CONFIG_X86_64 - .arch sledgehammer -/************************************************************************** -XSTART_lm - Transfer control to the kernel just loaded in long mode -**************************************************************************/ - .globl xstart_lm -xstart_lm: - /* Save the callee save registers */ - pushl %ebp - pushl %esi - pushl %edi - pushl %ebx - - /* Cache virt_offset && (virt_offset & 0xfffff000) */ - movl virt_offset, %ebp - movl %ebp, %ebx - andl $0xfffff000, %ebx - - /* Switch to using physical addresses */ - call _virt_to_phys - - /* Initialize the page tables */ - /* Level 4 */ - leal 0x23 + pgt_level3(%ebx), %eax - leal pgt_level4(%ebx), %edi - movl %eax, (%edi) - - /* Level 3 */ - leal 0x23 + pgt_level2(%ebx), %eax - leal pgt_level3(%ebx), %edi - movl %eax, 0x00(%edi) - addl $4096, %eax - movl %eax, 0x08(%edi) - addl $4096, %eax - movl %eax, 0x10(%edi) - addl $4096, %eax - movl %eax, 0x18(%edi) - - /* Level 2 */ - movl $0xe3, %eax - leal pgt_level2(%ebx), %edi - leal 16384(%edi), %esi -pgt_level2_loop: - movl %eax, (%edi) - addl $8, %edi - addl $0x200000, %eax - cmp %esi, %edi - jne pgt_level2_loop - - /* Point at the x86_64 page tables */ - leal pgt_level4(%ebx), %edi - movl %edi, %cr3 - - - /* Setup for the return from 64bit mode */ - /* 64bit align the stack */ - movl %esp, %ebx /* original stack pointer + 16 */ - andl $0xfffffff8, %esp - - /* Save original stack pointer + 16 */ - pushl %ebx - - /* Save virt_offset */ - pushl %ebp - - /* Setup for the jmp to 64bit long mode */ - leal start_lm(%ebp), %eax - movl %eax, 0x00 + start_lm_addr(%ebp) - movl $LM_CODE_SEG, %eax - movl %eax, 0x04 + start_lm_addr(%ebp) - - /* Setup for the jump out of 64bit long mode */ - leal end_lm(%ebp), %eax - movl %eax, 0x00 + end_lm_addr(%ebp) - movl $FLAT_CODE_SEG, %eax - movl %eax, 0x04 + end_lm_addr(%ebp) - - /* Enable PAE mode */ - movl %cr4, %eax - orl $X86_CR4_PAE, %eax - movl %eax, %cr4 - - /* Enable long mode */ - movl $MSR_K6_EFER, %ecx - rdmsr - orl $EFER_LME, %eax - wrmsr - - /* Start paging, entering 32bit compatiblity mode */ - movl %cr0, %eax - orl $CR0_PG, %eax - movl %eax, %cr0 - - /* Enter 64bit long mode */ - ljmp *start_lm_addr(%ebp) - .code64 -start_lm: - /* Load 64bit data segments */ - movl $LM_DATA_SEG, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %ss - - andq $0xffffffff, %rbx - /* Get the address to jump to */ - movl 20(%rbx), %edx - andq $0xffffffff, %rdx - - /* Get the argument pointer */ - movl 24(%rbx), %ebx - andq $0xffffffff, %rbx - - /* Jump to the 64bit code */ - call *%rdx - - /* Preserve the result */ - movl %eax, %edx - - /* Fixup %eflags */ - cli - cld - - /* Switch to 32bit compatibility mode */ - ljmp *end_lm_addr(%rip) - - .code32 -end_lm: - /* Disable paging */ - movl %cr0, %eax - andl $~CR0_PG, %eax - movl %eax, %cr0 - - /* Disable long mode */ - movl $MSR_K6_EFER, %ecx - rdmsr - andl $~EFER_LME, %eax - wrmsr - - /* Disable PAE */ - movl %cr4, %eax - andl $~X86_CR4_PAE, %eax - movl %eax, %cr4 - - /* Compute virt_offset */ - popl %ebp - - /* Compute the original stack pointer + 16 */ - popl %ebx - movl %ebx, %esp - - /* Enable the virtual addresses */ - leal _phys_to_virt(%ebp), %eax - call *%eax - - /* Restore the callee save registers */ - popl %ebx - popl %esi - popl %edi - popl %ebp - - /* Get the C return value */ - movl %edx, %eax - - /* Return */ - ret - - .arch i386 -#endif /* CONFIG_X86_64 */ - -#ifdef CONFIG_X86_64 - .section ".bss" - .p2align 12 - /* Include a dummy space in case we are loaded badly aligned */ - .space 4096 - /* Reserve enough space for a page table convering 4GB with 2MB pages */ -pgt_level4: - .space 4096 -pgt_level3: - .space 4096 -pgt_level2: - .space 16384 -start_lm_addr: - .space 8 -end_lm_addr: - .space 8 -#endif diff --git a/gpxe/src/arch/i386/core/i386_timer.c b/gpxe/src/arch/i386/core/timer2.c index 8f90ae05..bb589ecc 100644 --- a/gpxe/src/arch/i386/core/i386_timer.c +++ b/gpxe/src/arch/i386/core/timer2.c @@ -12,12 +12,11 @@ */ #include <stddef.h> -#include <bits/timer2.h> -#include <gpxe/timer.h> -#include <io.h> +#include <gpxe/timer2.h> +#include <gpxe/io.h> /* Timers tick over at this rate */ -#define TIMER2_TICK_RATE 1193180U +#define TIMER2_TICKS_PER_SEC 1193180U /* Parallel Peripheral Controller Port B */ #define PPC_PORTB 0x61 @@ -52,8 +51,7 @@ #define BINARY_COUNT 0x00 #define BCD_COUNT 0x01 -static void load_timer2(unsigned int ticks) -{ +static void load_timer2 ( unsigned int ticks ) { /* * Now let's take care of PPC channel 2 * @@ -75,15 +73,13 @@ static void load_timer2(unsigned int ticks) outb(ticks >> 8, TIMER2_PORT); } -static int timer2_running(void) -{ +static int timer2_running ( void ) { return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); } -void i386_timer2_udelay(unsigned int usecs) -{ - load_timer2((usecs * TIMER2_TICK_RATE)/USECS_IN_SEC); - while (timer2_running()) - ; +void timer2_udelay ( unsigned long usecs ) { + load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); + while (timer2_running()) { + /* Do nothing */ + } } - diff --git a/gpxe/src/arch/i386/core/video_subr.c b/gpxe/src/arch/i386/core/video_subr.c index bf82cc61..c821cd02 100644 --- a/gpxe/src/arch/i386/core/video_subr.c +++ b/gpxe/src/arch/i386/core/video_subr.c @@ -7,7 +7,7 @@ #include "stddef.h" #include "string.h" -#include "io.h" +#include <gpxe/io.h> #include "console.h" #include <gpxe/init.h> #include "vga.h" diff --git a/gpxe/src/arch/i386/core/virtaddr.S b/gpxe/src/arch/i386/core/virtaddr.S index 5d762375..cf6da4f6 100644 --- a/gpxe/src/arch/i386/core/virtaddr.S +++ b/gpxe/src/arch/i386/core/virtaddr.S @@ -4,7 +4,7 @@ * */ -#include "virtaddr.h" +#include "librm.h" .arch i386 .text diff --git a/gpxe/src/arch/i386/core/x86_io.c b/gpxe/src/arch/i386/core/x86_io.c new file mode 100644 index 00000000..424a96cc --- /dev/null +++ b/gpxe/src/arch/i386/core/x86_io.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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <gpxe/io.h> +#include <gpxe/x86_io.h> + +/** @file + * + * gPXE I/O API for x86 + * + */ + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + * + * This routine uses MMX instructions. + */ +static uint64_t x86_readq ( volatile uint64_t *io_addr ) { + uint64_t data; + __asm__ __volatile__ ( "pushl %%edx\n\t" + "pushl %%eax\n\t" + "movq (%1), %%mm0\n\t" + "movq %%mm0, (%%esp)\n\t" + "popl %%eax\n\t" + "popl %%edx\n\t" + "emms\n\t" + : "=A" ( data ) : "r" ( io_addr ) ); + return data; +} + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + * + * This routine uses MMX instructions. + */ +static void x86_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + __asm__ __volatile__ ( "pushl %%edx\n\t" + "pushl %%eax\n\t" + "movq (%%esp), %%mm0\n\t" + "movq %%mm0, (%1)\n\t" + "popl %%eax\n\t" + "popl %%edx\n\t" + "emms\n\t" + : : "A" ( data ), "r" ( io_addr ) ); +} + +PROVIDE_IOAPI_INLINE ( x86, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( x86, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( x86, ioremap ); +PROVIDE_IOAPI_INLINE ( x86, iounmap ); +PROVIDE_IOAPI_INLINE ( x86, io_to_bus ); +PROVIDE_IOAPI_INLINE ( x86, readb ); +PROVIDE_IOAPI_INLINE ( x86, readw ); +PROVIDE_IOAPI_INLINE ( x86, readl ); +PROVIDE_IOAPI ( x86, readq, x86_readq ); +PROVIDE_IOAPI_INLINE ( x86, writeb ); +PROVIDE_IOAPI_INLINE ( x86, writew ); +PROVIDE_IOAPI_INLINE ( x86, writel ); +PROVIDE_IOAPI ( x86, writeq, x86_writeq ); +PROVIDE_IOAPI_INLINE ( x86, inb ); +PROVIDE_IOAPI_INLINE ( x86, inw ); +PROVIDE_IOAPI_INLINE ( x86, inl ); +PROVIDE_IOAPI_INLINE ( x86, outb ); +PROVIDE_IOAPI_INLINE ( x86, outw ); +PROVIDE_IOAPI_INLINE ( x86, outl ); +PROVIDE_IOAPI_INLINE ( x86, insb ); +PROVIDE_IOAPI_INLINE ( x86, insw ); +PROVIDE_IOAPI_INLINE ( x86, insl ); +PROVIDE_IOAPI_INLINE ( x86, outsb ); +PROVIDE_IOAPI_INLINE ( x86, outsw ); +PROVIDE_IOAPI_INLINE ( x86, outsl ); +PROVIDE_IOAPI_INLINE ( x86, iodelay ); +PROVIDE_IOAPI_INLINE ( x86, mb ); diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c index 9576ad60..6ce4d924 100644 --- a/gpxe/src/arch/i386/drivers/net/undinet.c +++ b/gpxe/src/arch/i386/drivers/net/undinet.c @@ -23,6 +23,7 @@ #include <biosint.h> #include <pnpbios.h> #include <basemem_packet.h> +#include <gpxe/io.h> #include <gpxe/iobuf.h> #include <gpxe/netdevice.h> #include <gpxe/if_ether.h> @@ -554,7 +555,7 @@ static int undinet_open ( struct net_device *netdev ) { DBGC ( undinic, "UNDINIC %p opened\n", undinic ); return 0; -err: + err: undinet_close ( netdev ); return rc; } @@ -595,10 +596,6 @@ static void undinet_close ( struct net_device *netdev ) { /* Disable interrupt and unhook ISR */ disable_irq ( undinic->irq ); undinet_unhook_isr ( undinic->irq ); -#if 0 - enable_irq ( undinic->irq ); - send_eoi ( undinic->irq ); -#endif DBGC ( undinic, "UNDINIC %p closed\n", undinic ); } @@ -642,9 +639,7 @@ int undinet_probe ( struct undi_device *undi ) { struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface; struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; struct s_PXENV_UNDI_CLEANUP undi_cleanup; -#if 0 struct s_PXENV_STOP_UNDI stop_undi; -#endif int rc; /* Allocate net device */ @@ -671,20 +666,23 @@ int undinet_probe ( struct undi_device *undi ) { &start_undi, sizeof ( start_undi ) ) ) != 0 ) goto err_start_undi; - /* Bring up UNDI stack */ + } + undi->flags |= UNDI_FL_STARTED; + + /* Bring up UNDI stack */ + if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) { memset ( &undi_startup, 0, sizeof ( undi_startup ) ); if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, &undi_startup, sizeof ( undi_startup ) ) ) != 0 ) - goto err_undi_startup; - + goto err_undi_startup; memset ( &undi_initialize, 0, sizeof ( undi_initialize ) ); if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE, &undi_initialize, - sizeof ( undi_initialize ) ) ) != 0 ) - goto err_undi_initialize; + sizeof ( undi_initialize ))) != 0 ) + goto err_undi_initialize; } - undi->flags |= UNDI_FL_STARTED; + undi->flags |= UNDI_FL_INITIALIZED; /* Get device information */ memset ( &undi_info, 0, sizeof ( undi_info ) ); @@ -731,7 +729,6 @@ int undinet_probe ( struct undi_device *undi ) { err_bad_irq: err_undi_get_information: err_undi_initialize: - /* Shut down UNDI stack */ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, @@ -739,13 +736,13 @@ int undinet_probe ( struct undi_device *undi ) { memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, sizeof ( undi_cleanup ) ); + undi->flags &= ~UNDI_FL_INITIALIZED; err_undi_startup: -#if 0 /* Unhook UNDI stack */ memset ( &stop_undi, 0, sizeof ( stop_undi ) ); undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, sizeof ( stop_undi ) ); -#endif + undi->flags &= ~UNDI_FL_STARTED; err_start_undi: netdev_nullify ( netdev ); netdev_put ( netdev ); @@ -761,30 +758,33 @@ int undinet_probe ( struct undi_device *undi ) { void undinet_remove ( struct undi_device *undi ) { struct net_device *netdev = undi_get_drvdata ( undi ); struct undi_nic *undinic = netdev->priv; -#if 0 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; struct s_PXENV_UNDI_CLEANUP undi_cleanup; struct s_PXENV_STOP_UNDI stop_undi; -#endif /* Unregister net device */ unregister_netdev ( netdev ); - /* Shut down UNDI stack */ -#if 0 - memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); - undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, - sizeof ( undi_shutdown ) ); - memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); - undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, - sizeof ( undi_cleanup ) ); - - /* Unhook UNDI stack */ - memset ( &stop_undi, 0, sizeof ( stop_undi ) ); - undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, - sizeof ( stop_undi ) ); - undi->flags &= ~UNDI_FL_STARTED; -#endif + /* 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 ) ); + undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, + sizeof ( undi_shutdown ) ); + memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); + undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, + sizeof ( undi_cleanup ) ); + undi->flags &= ~UNDI_FL_INITIALIZED; + + /* Unhook UNDI stack */ + memset ( &stop_undi, 0, sizeof ( stop_undi ) ); + undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, + sizeof ( stop_undi ) ); + undi->flags &= ~UNDI_FL_STARTED; + } /* Clear entry point */ memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) ); diff --git a/gpxe/src/arch/i386/drivers/net/undionly.c b/gpxe/src/arch/i386/drivers/net/undionly.c index ee361493..4cdce677 100644 --- a/gpxe/src/arch/i386/drivers/net/undionly.c +++ b/gpxe/src/arch/i386/drivers/net/undionly.c @@ -20,6 +20,7 @@ #include <stdlib.h> #include <string.h> #include <gpxe/device.h> +#include <gpxe/init.h> #include <undi.h> #include <undinet.h> #include <undipreload.h> @@ -107,3 +108,20 @@ struct root_device undi_root_device __root_device = { .dev = { .name = "UNDI" }, .driver = &undi_root_driver, }; + +/** + * Prepare for exit + * + * @v flags Shutdown flags + */ +static void undionly_shutdown ( int flags ) { + /* If we are shutting down to boot an OS, clear the "keep PXE + * stack" flag. + */ + if ( flags & SHUTDOWN_BOOT ) + preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL; +} + +struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = { + .shutdown = undionly_shutdown, +}; diff --git a/gpxe/src/arch/i386/drivers/timer_bios.c b/gpxe/src/arch/i386/drivers/timer_bios.c deleted file mode 100644 index f9caf8d9..00000000 --- a/gpxe/src/arch/i386/drivers/timer_bios.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Etherboot routines for PCBIOS firmware. - * - * Body of routines taken from old pcbios.S - */ - -#include <gpxe/init.h> -#include <gpxe/timer.h> -#include <stdio.h> -#include <realmode.h> -#include <bios.h> -#include <bits/timer2.h> - -/* A bit faster actually, but we don't care. */ -#define TIMER2_TICKS_PER_SEC 18 - -/* - * 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 tick_t 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__ ( REAL_CODE ( "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) * (USECS_IN_SEC / TIMER2_TICKS_PER_SEC) ); -} - -static int bios_ts_init(void) -{ - DBG("BIOS timer installed\n"); - return 0; -} - -struct timer bios_ts __timer ( 02 ) = { - .init = bios_ts_init, - .udelay = i386_timer2_udelay, - .currticks = bios_currticks, -}; - diff --git a/gpxe/src/arch/i386/drivers/timer_rdtsc.c b/gpxe/src/arch/i386/drivers/timer_rdtsc.c deleted file mode 100644 index 09b7df2f..00000000 --- a/gpxe/src/arch/i386/drivers/timer_rdtsc.c +++ /dev/null @@ -1,69 +0,0 @@ - -#include <gpxe/init.h> -#include <gpxe/timer.h> -#include <errno.h> -#include <stdio.h> -#include <bits/cpu.h> -#include <bits/timer2.h> -#include <io.h> - - -#define rdtsc(low,high) \ - __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) - -#define rdtscll(val) \ - __asm__ __volatile__ ("rdtsc" : "=A" (val)) - - -/* Measure how many clocks we get in one microsecond */ -static inline uint64_t calibrate_tsc(void) -{ - - uint64_t rdtsc_start; - uint64_t rdtsc_end; - - rdtscll(rdtsc_start); - i386_timer2_udelay(USECS_IN_MSEC); - rdtscll(rdtsc_end); - - return (rdtsc_end - rdtsc_start) / USECS_IN_MSEC; -} - -static uint32_t clocks_per_usec = 0; - -/* We measure time in microseconds. */ -static tick_t rdtsc_currticks(void) -{ - uint64_t clocks; - - /* Read the Time Stamp Counter */ - rdtscll(clocks); - - return clocks / clocks_per_usec; -} - -static int rdtsc_ts_init(void) -{ - - struct cpuinfo_x86 cpu_info; - - get_cpuinfo(&cpu_info); - if (cpu_info.features & X86_FEATURE_TSC) { - clocks_per_usec= calibrate_tsc(); - if (clocks_per_usec) { - DBG("RDTSC ticksource installed. CPU running at %ld Mhz\n", - clocks_per_usec); - return 0; - } - } - - DBG("RDTSC ticksource not available on this machine.\n"); - return -ENODEV; -} - -struct timer rdtsc_ts __timer (01) = { - .init = rdtsc_ts_init, - .udelay = generic_currticks_udelay, - .currticks = rdtsc_currticks, -}; - diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S index 4fbd6563..53e2d7c5 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S +++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -66,7 +66,7 @@ .align 16 .globl hidemem_base .globl hidemem_umalloc - .globl hidemem_text + .globl hidemem_textdata memory_windows: base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */ @@ -76,7 +76,7 @@ ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */ hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */ .long 0xffffffff, 0xffffffff /* Changes at runtime */ -hidemem_text: .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 */ @@ -268,8 +268,10 @@ get_underlying_e820: 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 %di pushw %ds popw %es movw $underlying_e820_cache, %di @@ -280,8 +282,10 @@ get_underlying_e820: stc pushfw lcall *%cs:int15_vector - popw %di popw %es + popl %ebp + popl %edi + popl %esi /* Check for error return from underlying e820 call */ jc 2f /* CF set: error */ cmpl $SMAP, %eax diff --git a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c index a14e3416..34e3ac52 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c +++ b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <realmode.h> #include <bios.h> +#include <gpxe/io.h> #include <gpxe/timer.h> #define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ @@ -48,9 +49,9 @@ static void empty_8042 ( void ) { time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */ while ( ( inb ( K_CMD ) & ( K_IBUF_FUL | K_OBUF_FUL ) ) && currticks() < time ) { - SLOW_DOWN_IO; - ( void ) inb ( K_RDWR ); - SLOW_DOWN_IO; + iodelay(); + ( void ) inb_p ( K_RDWR ); + iodelay(); } } @@ -77,7 +78,7 @@ static int gateA20_is_set ( int retries ) { /* Avoid false negatives */ test_pattern++; - SLOW_DOWN_IO; + iodelay(); /* Always retry at least once, to avoid false negatives */ } while ( retries-- >= 0 ); @@ -145,9 +146,9 @@ void gateA20_set ( void ) { scp_a = inb ( SCP_A ); scp_a &= ~0x01; /* Avoid triggering a reset */ scp_a |= 0x02; /* Enable A20 */ - SLOW_DOWN_IO; + iodelay(); outb ( scp_a, SCP_A ); - SLOW_DOWN_IO; + iodelay(); if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) { DBG ( "Enabled gate A20 using " "Fast Gate A20\n" ); diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c index c9df7bd0..620b62e0 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c +++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -55,8 +55,8 @@ extern struct hidden_region __data16 ( hidemem_umalloc ); #define hidemem_umalloc __use_data16 ( hidemem_umalloc ) /** Hidden text memory */ -extern struct hidden_region __data16 ( hidemem_text ); -#define hidemem_text __use_data16 ( hidemem_text ) +extern struct hidden_region __data16 ( hidemem_textdata ); +#define hidemem_textdata __use_data16 ( hidemem_textdata ) /** Assembly routine in e820mangler.S */ extern void int15(); @@ -66,12 +66,12 @@ extern struct segoff __text16 ( int15_vector ); #define int15_vector __use_text16 ( int15_vector ) /* The linker defines these symbols for us */ -extern char _text[]; -extern char _end[]; -extern char _text16_size[]; -#define _text16_size ( ( unsigned int ) _text16_size ) -extern char _data16_size[]; -#define _data16_size ( ( unsigned int ) _data16_size ) +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 @@ -110,7 +110,7 @@ void hide_basemem ( void ) { * */ void hide_umalloc ( physaddr_t start, physaddr_t end ) { - assert ( end <= virt_to_phys ( _text ) ); + assert ( end <= virt_to_phys ( _textdata ) ); hide_region ( &hidemem_umalloc, start, end ); } @@ -118,9 +118,9 @@ void hide_umalloc ( physaddr_t start, physaddr_t end ) { * Hide .text and .data * */ -void hide_text ( void ) { - hide_region ( &hidemem_text, virt_to_phys ( _text ), - virt_to_phys ( _end ) ); +void hide_textdata ( void ) { + hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ), + virt_to_phys ( _etextdata ) ); } /** @@ -148,8 +148,8 @@ static void hide_etherboot ( void ) { /* Initialise the hidden regions */ hide_basemem(); - hide_umalloc ( virt_to_phys ( _text ), virt_to_phys ( _text ) ); - hide_text(); + 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 @@ -161,8 +161,8 @@ static void hide_etherboot ( void ) { * We use a heuristic to guess whether or not we are being * loaded sensibly. */ - rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_size + 1024 - 1 ) >> 10 ); - rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_size + 1024 - 1 ) >> 10 ); + 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 " diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c index 9de10a7a..2e9627c0 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/memmap.c +++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c @@ -158,7 +158,7 @@ static int meme820 ( struct memory_map *memmap ) { uint32_t smap; size_t size; unsigned int flags; - unsigned int discard_d, discard_D; + unsigned int discard_D; /* Clear the E820 buffer. Do this once before starting, * rather than on each call; some BIOSes rely on the contents @@ -167,18 +167,24 @@ static int meme820 ( struct memory_map *memmap ) { memset ( &e820buf, 0, sizeof ( e820buf ) ); do { - __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + /* 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 %w0\n\t" ) - : "=r" ( flags ), "=a" ( smap ), - "=b" ( next ), "=D" ( discard_D ), - "=c" ( size ), "=d" ( discard_d ) + "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 ) - : "memory" ); + : "esi", "memory" ); if ( smap != SMAP ) { DBG ( "INT 15,e820 failed SMAP signature check\n" ); diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c index b088e51d..3238fb19 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c @@ -24,6 +24,16 @@ #include <gpxe/uuid.h> #include <smbios.h> +/** SMBIOS settings tag magic number */ +#define SMBIOS_TAG_MAGIC 0x5B /* "SmBios" */ + +/** + * Construct SMBIOS empty tag + * + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_EMPTY_TAG ( SMBIOS_TAG_MAGIC << 24 ) + /** * Construct SMBIOS raw-data tag * @@ -33,7 +43,8 @@ * @ret tag SMBIOS setting tag */ #define SMBIOS_RAW_TAG( _type, _structure, _field ) \ - ( ( (_type) << 16 ) | \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_type) << 16 ) | \ ( offsetof ( _structure, _field ) << 8 ) | \ ( sizeof ( ( ( _structure * ) 0 )->_field ) ) ) @@ -46,7 +57,8 @@ * @ret tag SMBIOS setting tag */ #define SMBIOS_STRING_TAG( _type, _structure, _field ) \ - ( ( (_type) << 16 ) | \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_type) << 16 ) | \ ( offsetof ( _structure, _field ) << 8 ) ) /** @@ -78,16 +90,18 @@ static int smbios_fetch ( struct settings *settings __unused, struct setting *setting, void *data, size_t len ) { struct smbios_structure structure; + unsigned int tag_magic; unsigned int tag_type; unsigned int tag_offset; unsigned int tag_len; int rc; /* Split tag into type, offset and length */ - tag_type = ( setting->tag >> 16 ); + tag_magic = ( setting->tag >> 24 ); + tag_type = ( ( setting->tag >> 16 ) & 0xff ); tag_offset = ( ( setting->tag >> 8 ) & 0xff ); tag_len = ( setting->tag & 0xff ); - if ( ! tag_type ) + if ( tag_magic != SMBIOS_TAG_MAGIC ) return -ENOENT; /* Find SMBIOS structure */ @@ -127,6 +141,7 @@ static struct settings_operations smbios_settings_operations = { static struct settings smbios_settings = { .refcnt = NULL, .name = "smbios", + .tag_magic = SMBIOS_EMPTY_TAG, .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ), .children = LIST_HEAD_INIT ( smbios_settings.children ), .op = &smbios_settings_operations, diff --git a/gpxe/src/arch/i386/image/multiboot.c b/gpxe/src/arch/i386/image/multiboot.c index a4a340fd..49adf951 100644 --- a/gpxe/src/arch/i386/image/multiboot.c +++ b/gpxe/src/arch/i386/image/multiboot.c @@ -282,11 +282,13 @@ static int multiboot_exec ( struct image *image ) { /* Jump to OS with flat physical addressing */ DBGC ( image, "MULTIBOOT %p starting execution at %lx\n", image, entry ); - __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" ) + __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", "ebp", "memory" ); + : "ecx", "edx", "esi", "memory" ); DBGC ( image, "MULTIBOOT %p returned\n", image ); diff --git a/gpxe/src/arch/i386/image/nbi.c b/gpxe/src/arch/i386/image/nbi.c index e6a0ab0f..ea8375cb 100644 --- a/gpxe/src/arch/i386/image/nbi.c +++ b/gpxe/src/arch/i386/image/nbi.c @@ -372,11 +372,12 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { * @ret netdev Boot network device */ static struct net_device * guess_boot_netdev ( void ) { - struct net_device *boot_netdev; + struct net_device *netdev; /* Just use the first network device */ - for_each_netdev ( boot_netdev ) { - return boot_netdev; + for_each_netdev ( netdev ) { + if ( netdev->state & NETDEV_OPEN ) + return netdev; } return NULL; diff --git a/gpxe/src/arch/i386/include/bios.h b/gpxe/src/arch/i386/include/bios.h index 630a898b..979a092c 100644 --- a/gpxe/src/arch/i386/include/bios.h +++ b/gpxe/src/arch/i386/include/bios.h @@ -5,7 +5,4 @@ #define BDA_FBMS 0x0013 #define BDA_NUM_DRIVES 0x0075 -extern unsigned long currticks ( void ); -extern void cpu_nap ( void ); - #endif /* BIOS_H */ diff --git a/gpxe/src/arch/i386/include/biosint.h b/gpxe/src/arch/i386/include/biosint.h index d4e34963..d365cf01 100644 --- a/gpxe/src/arch/i386/include/biosint.h +++ b/gpxe/src/arch/i386/include/biosint.h @@ -6,9 +6,22 @@ * */ +#include <realmode.h> + struct segoff; -extern int hooked_bios_interrupts; +/** + * 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, diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h index 99927c28..70c78eaf 100644 --- a/gpxe/src/arch/i386/include/bits/errfile.h +++ b/gpxe/src/arch/i386/include/bits/errfile.h @@ -6,7 +6,7 @@ * @{ */ -#define ERRFILE_umalloc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 ) +#define ERRFILE_memtop_umalloc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 ) #define ERRFILE_memmap ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 ) #define ERRFILE_pnpbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 ) #define ERRFILE_smbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 ) diff --git a/gpxe/src/arch/i386/include/bits/io.h b/gpxe/src/arch/i386/include/bits/io.h new file mode 100644 index 00000000..dd0ee444 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/io.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * i386-specific I/O API implementations + * + */ + +#include <gpxe/x86_io.h> + +#endif /* _BITS_IO_H */ diff --git a/gpxe/src/arch/i386/include/bits/nap.h b/gpxe/src/arch/i386/include/bits/nap.h new file mode 100644 index 00000000..f8ba7a7c --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/nap.h @@ -0,0 +1,13 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * i386-specific CPU sleeping API implementations + * + */ + +#include <gpxe/bios_nap.h> +#include <gpxe/efi/efix86_nap.h> + +#endif /* _BITS_MAP_H */ diff --git a/gpxe/src/arch/i386/include/bits/pci_io.h b/gpxe/src/arch/i386/include/bits/pci_io.h new file mode 100644 index 00000000..0fbb439d --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/pci_io.h @@ -0,0 +1,13 @@ +#ifndef _BITS_PCI_IO_H +#define _BITS_PCI_IO_H + +/** @file + * + * i386-specific PCI I/O API implementations + * + */ + +#include <gpxe/pcibios.h> +#include <gpxe/pcidirect.h> + +#endif /* _BITS_PCI_IO_H */ diff --git a/gpxe/src/arch/i386/include/bits/timer.h b/gpxe/src/arch/i386/include/bits/timer.h new file mode 100644 index 00000000..99666d84 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/timer.h @@ -0,0 +1,13 @@ +#ifndef _BITS_TIMER_H +#define _BITS_TIMER_H + +/** @file + * + * i386-specific timer API implementations + * + */ + +#include <gpxe/bios_timer.h> +#include <gpxe/rdtsc_timer.h> + +#endif /* _BITS_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/bits/timer2.h b/gpxe/src/arch/i386/include/bits/timer2.h deleted file mode 100644 index 83923b29..00000000 --- a/gpxe/src/arch/i386/include/bits/timer2.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef BITS_TIMER2_H -#define BITS_TIMER2_H - -#include <stddef.h> - -void i386_timer2_udelay(unsigned int usecs); - -#endif diff --git a/gpxe/src/arch/i386/include/bits/uaccess.h b/gpxe/src/arch/i386/include/bits/uaccess.h index 9c6d0c21..0ecc5028 100644 --- a/gpxe/src/arch/i386/include/bits/uaccess.h +++ b/gpxe/src/arch/i386/include/bits/uaccess.h @@ -1,6 +1,12 @@ #ifndef _BITS_UACCESS_H #define _BITS_UACCESS_H -#include <realmode.h> +/** @file + * + * i386-specific user access API implementations + * + */ + +#include <librm.h> #endif /* _BITS_UACCESS_H */ diff --git a/gpxe/src/arch/i386/include/bits/umalloc.h b/gpxe/src/arch/i386/include/bits/umalloc.h new file mode 100644 index 00000000..dcbd0a6b --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/umalloc.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * i386-specific user memory allocation API implementations + * + */ + +#include <gpxe/memtop_umalloc.h> + +#endif /* _BITS_UMALLOC_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/bios_nap.h b/gpxe/src/arch/i386/include/gpxe/bios_nap.h new file mode 100644 index 00000000..f1c721e9 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/bios_nap.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_BIOS_NAP_H +#define _GPXE_BIOS_NAP_H + +/** @file + * + * BIOS CPU sleeping + * + */ + +#ifdef NAP_PCBIOS +#define NAP_PREFIX_pcbios +#else +#define NAP_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _GPXE_BIOS_NAP_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/bios_timer.h b/gpxe/src/arch/i386/include/gpxe/bios_timer.h new file mode 100644 index 00000000..7e3caa3c --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/bios_timer.h @@ -0,0 +1,42 @@ +#ifndef _GPXE_BIOS_TIMER_H +#define _GPXE_BIOS_TIMER_H + +/** @file + * + * BIOS timer + * + */ + +#ifdef TIMER_PCBIOS +#define TIMER_PREFIX_pcbios +#else +#define TIMER_PREFIX_pcbios __pcbios_ +#endif + +#include <gpxe/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 /* _GPXE_BIOS_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/efi/efix86_nap.h b/gpxe/src/arch/i386/include/gpxe/efi/efix86_nap.h new file mode 100644 index 00000000..91424c54 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/efi/efix86_nap.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_EFIX86_NAP_H +#define _GPXE_EFIX86_NAP_H + +/** @file + * + * EFI CPU sleeping + * + */ + +#ifdef NAP_EFIX86 +#define NAP_PREFIX_efix86 +#else +#define NAP_PREFIX_efix86 __efix86_ +#endif + +#endif /* _GPXE_EFIX86_NAP_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h new file mode 100644 index 00000000..a3cd2c01 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_MEMTOP_UMALLOC_H +#define _GPXE_MEMTOP_UMALLOC_H + +/** @file + * + * External memory allocation + * + */ + +#ifdef UMALLOC_MEMTOP +#define UMALLOC_PREFIX_memtop +#else +#define UMALLOC_PREFIX_memtop __memtop_ +#endif + +#endif /* _GPXE_MEMTOP_UMALLOC_H */ diff --git a/gpxe/src/arch/i386/include/pcibios.h b/gpxe/src/arch/i386/include/gpxe/pcibios.h index 3d08d135..b86f5abd 100644 --- a/gpxe/src/arch/i386/include/pcibios.h +++ b/gpxe/src/arch/i386/include/gpxe/pcibios.h @@ -1,5 +1,5 @@ -#ifndef _PCIBIOS_H -#define _PCIBIOS_H +#ifndef _GPXE_PCIBIOS_H +#define _GPXE_PCIBIOS_H #include <stdint.h> @@ -9,6 +9,12 @@ * */ +#ifdef PCIAPI_PCBIOS +#define PCIAPI_PREFIX_pcbios +#else +#define PCIAPI_PREFIX_pcbios __pcbios_ +#endif + struct pci_device; #define PCIBIOS_INSTALLATION_CHECK 0xb1010000 @@ -19,7 +25,6 @@ struct pci_device; #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c0000 #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d0000 -extern int pcibios_max_bus ( void ); extern int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ); extern int pcibios_write ( struct pci_device *pci, uint32_t command, @@ -33,9 +38,10 @@ extern int pcibios_write ( struct pci_device *pci, uint32_t command, * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_read_config_byte ( struct pci_device *pci, unsigned int where, - uint8_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { uint32_t tmp; int rc; @@ -52,9 +58,10 @@ pcibios_read_config_byte ( struct pci_device *pci, unsigned int where, * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_read_config_word ( struct pci_device *pci, unsigned int where, - uint16_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { uint32_t tmp; int rc; @@ -71,9 +78,10 @@ pcibios_read_config_word ( struct pci_device *pci, unsigned int where, * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_read_config_dword ( struct pci_device *pci, unsigned int where, - uint32_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { return pcibios_read ( pci, PCIBIOS_READ_CONFIG_DWORD | where, value ); } @@ -85,9 +93,10 @@ pcibios_read_config_dword ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_write_config_byte ( struct pci_device *pci, unsigned int where, - uint8_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value ); } @@ -99,9 +108,10 @@ pcibios_write_config_byte ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_write_config_word ( struct pci_device *pci, unsigned int where, - uint16_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_WORD | where, value ); } @@ -113,10 +123,11 @@ pcibios_write_config_word ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcibios_write_config_dword ( struct pci_device *pci, unsigned int where, - uint32_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_DWORD | where, value); } -#endif /* _PCIBIOS_H */ +#endif /* _GPXE_PCIBIOS_H */ diff --git a/gpxe/src/arch/i386/include/pcidirect.h b/gpxe/src/arch/i386/include/gpxe/pcidirect.h index 4e2e9d12..fe433c6f 100644 --- a/gpxe/src/arch/i386/include/pcidirect.h +++ b/gpxe/src/arch/i386/include/gpxe/pcidirect.h @@ -2,7 +2,13 @@ #define _PCIDIRECT_H #include <stdint.h> -#include <io.h> +#include <gpxe/io.h> + +#ifdef PCIAPI_DIRECT +#define PCIAPI_PREFIX_direct +#else +#define PCIAPI_PREFIX_direct __direct_ +#endif /** @file * @@ -22,7 +28,8 @@ extern void pcidirect_prepare ( struct pci_device *pci, int where ); * * @ret max_bus Maximum bus number */ -static inline int pcidirect_max_bus ( void ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_max_bus ) ( void ) { /* No way to work this out via Type 1 accesses */ return 0xff; } @@ -35,9 +42,10 @@ static inline int pcidirect_max_bus ( void ) { * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_read_config_byte ( struct pci_device *pci, unsigned int where, - uint8_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { pcidirect_prepare ( pci, where ); *value = inb ( PCIDIRECT_CONFIG_DATA + ( where & 3 ) ); return 0; @@ -51,9 +59,10 @@ pcidirect_read_config_byte ( struct pci_device *pci, unsigned int where, * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_read_config_word ( struct pci_device *pci, unsigned int where, - uint16_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { pcidirect_prepare ( pci, where ); *value = inw ( PCIDIRECT_CONFIG_DATA + ( where & 2 ) ); return 0; @@ -67,9 +76,10 @@ pcidirect_read_config_word ( struct pci_device *pci, unsigned int where, * @v value Value read * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_read_config_dword ( struct pci_device *pci, unsigned int where, - uint32_t *value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { pcidirect_prepare ( pci, where ); *value = inl ( PCIDIRECT_CONFIG_DATA ); return 0; @@ -83,9 +93,10 @@ pcidirect_read_config_dword ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_write_config_byte ( struct pci_device *pci, unsigned int where, - uint8_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { pcidirect_prepare ( pci, where ); outb ( value, PCIDIRECT_CONFIG_DATA + ( where & 3 ) ); return 0; @@ -99,9 +110,10 @@ pcidirect_write_config_byte ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_write_config_word ( struct pci_device *pci, unsigned int where, - uint16_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { pcidirect_prepare ( pci, where ); outw ( value, PCIDIRECT_CONFIG_DATA + ( where & 2 ) ); return 0; @@ -115,9 +127,10 @@ pcidirect_write_config_word ( struct pci_device *pci, unsigned int where, * @v value Value to be written * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) int -pcidirect_write_config_dword ( struct pci_device *pci, unsigned int where, - uint32_t value ) { +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { pcidirect_prepare ( pci, where ); outl ( value, PCIDIRECT_CONFIG_DATA ); return 0; diff --git a/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h new file mode 100644 index 00000000..0e03d707 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h @@ -0,0 +1,37 @@ +#ifndef _GPXE_RDTSC_TIMER_H +#define _GPXE_RDTSC_TIMER_H + +/** @file + * + * RDTSC timer + * + */ + +#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 /* _GPXE_RDTSC_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/timer2.h b/gpxe/src/arch/i386/include/gpxe/timer2.h new file mode 100644 index 00000000..59705fa2 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/timer2.h @@ -0,0 +1,12 @@ +#ifndef _GPXE_TIMER2_H +#define _GPXE_TIMER2_H + +/** @file + * + * Timer chip control + * + */ + +extern void timer2_udelay ( unsigned long usecs ); + +#endif /* _GPXE_TIMER2_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/x86_io.h b/gpxe/src/arch/i386/include/gpxe/x86_io.h new file mode 100644 index 00000000..b1ae3bac --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/x86_io.h @@ -0,0 +1,151 @@ +#ifndef _GPXE_X86_IO_H +#define _GPXE_X86_IO_H + +/** @file + * + * gPXE I/O API for x86 + * + * i386 uses direct pointer dereferences for accesses to memory-mapped + * I/O space, and the inX/outX instructions for accesses to + * port-mapped I/O space. + * + * 64-bit atomic accesses (readq() and writeq()) use MMX instructions, + * and will crash original Pentium and earlier CPUs. Fortunately, no + * hardware that requires atomic 64-bit accesses will physically fit + * into a machine with such an old CPU anyway. + */ + +#ifdef IOAPI_X86 +#define IOAPI_PREFIX_x86 +#else +#define IOAPI_PREFIX_x86 __x86_ +#endif + +/* + * Memory space mappings + * + */ + +/* + * Physical<->Bus and Bus<->I/O address mappings + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +static inline __always_inline void * +IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { + return phys_to_virt ( bus_addr ); +} + +static inline __always_inline void +IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) { + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) { + return virt_to_phys ( io_addr ); +} + +/* + * MMIO reads and writes up to 32 bits + * + */ + +#define X86_READX( _api_func, _type ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( x86, _api_func ) ( volatile _type *io_addr ) { \ + return *io_addr; \ +} +X86_READX ( readb, uint8_t ); +X86_READX ( readw, uint16_t ); +X86_READX ( readl, uint32_t ); + +#define X86_WRITEX( _api_func, _type ) \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, _api_func ) ( _type data, \ + volatile _type *io_addr ) { \ + *io_addr = data; \ +} +X86_WRITEX ( writeb, uint8_t ); +X86_WRITEX ( writew, uint16_t ); +X86_WRITEX ( writel, uint32_t ); + +/* + * PIO reads and writes up to 32 bits + * + */ + +#define X86_INX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( x86, in ## _insn_suffix ) ( volatile _type *io_addr ) { \ + _type data; \ + __asm__ __volatile__ ( "in" #_insn_suffix " %w1, %" _reg_prefix "0" \ + : "=a" ( data ) : "Nd" ( io_addr ) ); \ + return data; \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, ins ## _insn_suffix ) ( volatile _type *io_addr, \ + _type *data, \ + unsigned int count ) { \ + unsigned int discard_D; \ + __asm__ __volatile__ ( "rep ins" #_insn_suffix \ + : "=D" ( discard_D ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +X86_INX ( b, uint8_t, "b" ); +X86_INX ( w, uint16_t, "w" ); +X86_INX ( l, uint32_t, "k" ); + +#define X86_OUTX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, out ## _insn_suffix ) ( _type data, \ + volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "out" #_insn_suffix " %" _reg_prefix "0, %w1" \ + : : "a" ( data ), "Nd" ( io_addr ) ); \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, outs ## _insn_suffix ) ( volatile _type *io_addr, \ + const _type *data, \ + unsigned int count ) { \ + unsigned int discard_S; \ + __asm__ __volatile__ ( "rep outs" #_insn_suffix \ + : "=S" ( discard_S ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +X86_OUTX ( b, uint8_t, "b" ); +X86_OUTX ( w, uint16_t, "w" ); +X86_OUTX ( l, uint32_t, "k" ); + +/* + * Slow down I/O + * + */ + +static inline __always_inline void +IOAPI_INLINE ( x86, iodelay ) ( void ) { + __asm__ __volatile__ ( "outb %al, $0x80" ); +} + +/* + * Memory barrier + * + */ + +static inline __always_inline void +IOAPI_INLINE ( x86, mb ) ( void ) { + __asm__ __volatile__ ( "lock; addl $0, 0(%%esp)" : : : "memory" ); +} + +#endif /* _GPXE_X86_IO_H */ diff --git a/gpxe/src/arch/i386/include/int13.h b/gpxe/src/arch/i386/include/int13.h index 72ca97d7..bf6d0318 100644 --- a/gpxe/src/arch/i386/include/int13.h +++ b/gpxe/src/arch/i386/include/int13.h @@ -9,6 +9,7 @@ #include <stdint.h> #include <gpxe/list.h> +#include <realmode.h> struct block_device; diff --git a/gpxe/src/arch/i386/include/io.h b/gpxe/src/arch/i386/include/io.h deleted file mode 100644 index c26fdf7e..00000000 --- a/gpxe/src/arch/i386/include/io.h +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef ETHERBOOT_IO_H -#define ETHERBOOT_IO_H - -#include <stdint.h> -#include "virtaddr.h" - -/* virt_to_bus converts an addresss inside of etherboot [_start, _end] - * into a memory access cards can use. - */ -#define virt_to_bus virt_to_phys - - -/* bus_to_virt reverses virt_to_bus, the address must be output - * from virt_to_bus to be valid. This function does not work on - * all bus addresses. - */ -#define bus_to_virt phys_to_virt - -/* ioremap converts a random 32bit bus address into something - * etherboot can access. - */ -static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused) -{ - return bus_to_virt(bus_addr); -} - -/* iounmap cleans up anything ioremap had to setup */ -static inline void iounmap(void *virt_addr __unused) -{ - return; -} - -/* - * This file contains the definitions for the x86 IO instructions - * inb/inw/inl/outb/outw/outl and the "string versions" of the same - * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" - * versions of the single-IO instructions (inb_p/inw_p/..). - * - * This file is not meant to be obfuscating: it's just complicated - * to (a) handle it all in a way that makes gcc able to optimize it - * as well as possible and (b) trying to avoid writing the same thing - * over and over again with slight variations and possibly making a - * mistake somewhere. - */ - -/* - * Thanks to James van Artsdalen for a better timing-fix than - * the two short jumps: using outb's to a nonexistent port seems - * to guarantee better timings even on fast machines. - * - * On the other hand, I'd like to be sure of a non-existent port: - * I feel a bit unsafe about using 0x80 (should be safe, though) - * - * Linus - */ - -#ifdef SLOW_IO_BY_JUMPING -#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") -#else -#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") -#endif - -#ifdef REALLY_SLOW_IO -#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } -#else -#define SLOW_DOWN_IO __SLOW_DOWN_IO -#endif - -/* - * readX/writeX() are used to access memory mapped devices. On some - * architectures the memory mapped IO stuff needs to be accessed - * differently. On the x86 architecture, we just read/write the - * memory location directly. - */ -static inline __attribute__ (( always_inline )) unsigned long -_readb ( volatile uint8_t *addr ) { - unsigned long data = *addr; - DBGIO ( "[%08lx] => %02lx\n", virt_to_phys ( addr ), data ); - return data; -} -static inline __attribute__ (( always_inline )) unsigned long -_readw ( volatile uint16_t *addr ) { - unsigned long data = *addr; - DBGIO ( "[%08lx] => %04lx\n", virt_to_phys ( addr ), data ); - return data; -} -static inline __attribute__ (( always_inline )) unsigned long -_readl ( volatile uint32_t *addr ) { - unsigned long data = *addr; - DBGIO ( "[%08lx] => %08lx\n", virt_to_phys ( addr ), data ); - return data; -} -#define readb( addr ) _readb ( ( volatile uint8_t * ) (addr) ) -#define readw( addr ) _readw ( ( volatile uint16_t * ) (addr) ) -#define readl( addr ) _readl ( ( volatile uint32_t * ) (addr) ) - -static inline __attribute__ (( always_inline )) void -_writeb ( unsigned long data, volatile uint8_t *addr ) { - DBGIO ( "[%08lx] <= %02lx\n", virt_to_phys ( addr ), data ); - *addr = data; -} -static inline __attribute__ (( always_inline )) void -_writew ( unsigned long data, volatile uint16_t *addr ) { - DBGIO ( "[%08lx] <= %04lx\n", virt_to_phys ( addr ), data ); - *addr = data; -} -static inline __attribute__ (( always_inline )) void -_writel ( unsigned long data, volatile uint32_t *addr ) { - DBGIO ( "[%08lx] <= %08lx\n", virt_to_phys ( addr ), data ); - *addr = data; -} -#define writeb( b, addr ) _writeb ( (b), ( volatile uint8_t * ) (addr) ) -#define writew( b, addr ) _writew ( (b), ( volatile uint16_t * ) (addr) ) -#define writel( b, addr ) _writel ( (b), ( volatile uint32_t * ) (addr) ) - -#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) -#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) - -/* - * Force strict CPU ordering. - * And yes, this is required on UP too when we're talking - * to devices. - * - * For now, "wmb()" doesn't actually do anything, as all - * Intel CPU's follow what Intel calls a *Processor Order*, - * in which all writes are seen in the program order even - * outside the CPU. - * - * I expect future Intel CPU's to have a weaker ordering, - * but I'd also expect them to finally get their act together - * and add some real memory barriers if so. - * - * Some non intel clones support out of order store. wmb() ceases to be a - * nop for these. - */ - -#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") -#define rmb() mb() -#define wmb() mb(); - - -/* - * Talk about misusing macros.. - */ - -#define __OUT1(s,x) \ -extern void __out##s(unsigned x value, unsigned short port); \ -extern inline void __out##s(unsigned x value, unsigned short port) { - -#define __OUT2(s,s1,s2) \ -__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" - -#define __OUT(s,s1,x) \ -__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ -__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ -__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ -__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } - -#define __IN1(s,x) \ -extern unsigned x __in##s(unsigned short port); \ -extern inline unsigned x __in##s(unsigned short port) { unsigned x _v; - -#define __IN2(s,s1,s2) \ -__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" - -#define __IN(s,s1,x,i...) \ -__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ -__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ -__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ -__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } - -#define __INS(s) \ -extern void ins##s(unsigned short port, void * addr, unsigned long count); \ -extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \ -{ __asm__ __volatile__ ("cld ; rep ; ins" #s \ -: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } - -#define __OUTS(s) \ -extern void outs##s(unsigned short port, const void * addr, unsigned long count); \ -extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ -{ __asm__ __volatile__ ("cld ; rep ; outs" #s \ -: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } - -__IN(b,"", char) -__IN(w,"",short) -__IN(l,"", long) - -__OUT(b,"b",char) -__OUT(w,"w",short) -__OUT(l,,int) - -__INS(b) -__INS(w) -__INS(l) - -__OUTS(b) -__OUTS(w) -__OUTS(l) - -/* - * Note that due to the way __builtin_constant_p() works, you - * - can't use it inside a inline function (it will never be true) - * - you don't have to worry about side effects within the __builtin.. - */ -#define outb(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outbc((val),(port)) : \ - __outb((val),(port))) - -#define inb(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inbc(port) : \ - __inb(port)) - -#define outb_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outbc_p((val),(port)) : \ - __outb_p((val),(port))) - -#define inb_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inbc_p(port) : \ - __inb_p(port)) - -#define outw(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outwc((val),(port)) : \ - __outw((val),(port))) - -#define inw(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inwc(port) : \ - __inw(port)) - -#define outw_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outwc_p((val),(port)) : \ - __outw_p((val),(port))) - -#define inw_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inwc_p(port) : \ - __inw_p(port)) - -#define outl(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outlc((val),(port)) : \ - __outl((val),(port))) - -#define inl(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inlc(port) : \ - __inl(port)) - -#define outl_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outlc_p((val),(port)) : \ - __outl_p((val),(port))) - -#define inl_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inlc_p(port) : \ - __inl_p(port)) - -#endif /* ETHERBOOT_IO_H */ diff --git a/gpxe/src/arch/i386/include/librm.h b/gpxe/src/arch/i386/include/librm.h index 07a85c59..9eb2767a 100644..100755 --- a/gpxe/src/arch/i386/include/librm.h +++ b/gpxe/src/arch/i386/include/librm.h @@ -1,21 +1,109 @@ #ifndef LIBRM_H #define LIBRM_H -/* Drag in protected-mode segment selector values */ -#include "virtaddr.h" -#include "realmode.h" +/* 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 -#include "stddef.h" -#include "string.h" +#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 ); +} -/* - * Data structures and type definitions +/** + * 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 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 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 * */ -/* Access to variables in .data16 and .text16 */ extern char *data16; extern char *text16; @@ -72,178 +160,6 @@ extern uint16_t __text16 ( rm_ds ); */ extern void gateA20_set ( void ); -/* - * librm_mgmt: functions for manipulating base memory and executing - * real-mode code. - * - * Full API documentation for these functions is in realmode.h. - * - */ - -/* Macro for obtaining a physical address from a segment:offset pair. */ -#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) ) - -/* Copy to/from base memory */ -static inline __attribute__ (( always_inline )) void -copy_to_real_librm ( unsigned int dest_seg, unsigned int dest_off, - void *src, size_t n ) { - memcpy ( VIRTUAL ( dest_seg, dest_off ), src, n ); -} -static inline __attribute__ (( always_inline )) void -copy_from_real_librm ( void *dest, unsigned int src_seg, - unsigned int src_off, size_t n ) { - memcpy ( dest, VIRTUAL ( src_seg, src_off ), n ); -} -#define put_real_librm( var, dest_seg, dest_off ) \ - do { \ - * ( ( typeof(var) * ) VIRTUAL ( dest_seg, dest_off ) ) = var; \ - } while ( 0 ) -#define get_real_librm( var, src_seg, src_off ) \ - do { \ - var = * ( ( typeof(var) * ) VIRTUAL ( src_seg, src_off ) ); \ - } while ( 0 ) -#define copy_to_real copy_to_real_librm -#define copy_from_real copy_from_real_librm -#define put_real put_real_librm -#define get_real get_real_librm - -/** - * A pointer to a user buffer - * - * Even though we could just use a void *, we use an intptr_t so that - * attempts to use normal pointers show up as compiler warnings. Such - * code is actually valid for librm, but not for libkir (i.e. under - * KEEP_IT_REAL), so it's good to have the warnings even under librm. - */ -typedef intptr_t userptr_t; - -/** - * Add offset to user pointer - * - * @v ptr User pointer - * @v offset Offset - * @ret new_ptr New pointer value - */ -static inline __attribute__ (( always_inline )) userptr_t -userptr_add ( userptr_t ptr, off_t offset ) { - return ( ptr + offset ); -} - -/** - * 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 ) { - memcpy ( ( ( void * ) buffer + 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 ) { - memcpy ( dest, ( ( void * ) buffer + offset ), len ); -} - -/** - * Copy data between user buffers - * - * @v dest Destination user buffer - * @v dest_off Offset within destination buffer - * @v src Source user buffer - * @v src_off Offset within source buffer - * @v len Length - */ -static inline __attribute__ (( always_inline )) void -memcpy_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, - size_t len ) { - memcpy ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ), - len ); -} - -/** - * Copy data between user buffers, allowing for overlap - * - * @v dest Destination user buffer - * @v dest_off Offset within destination buffer - * @v src Source user buffer - * @v src_off Offset within source buffer - * @v len Length - */ -static inline __attribute__ (( always_inline )) void -memmove_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, - size_t len ) { - memmove ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ), - len ); -} - -/** - * Fill user buffer with a constant byte - * - * @v buffer User buffer - * @v offset Offset within buffer - * @v c Constant byte with which to fill - * @v len Length - */ -static inline __attribute__ (( always_inline )) void -memset_user ( userptr_t buffer, off_t offset, int c, size_t len ) { - memset ( ( ( void * ) buffer + offset ), c, len ); -} - -/** - * Find length of NUL-terminated string in user buffer - * - * @v buffer User buffer - * @v offset Offset within buffer - * @ret len Length of string (excluding NUL) - */ -static inline __attribute__ (( always_inline )) size_t -strlen_user ( userptr_t buffer, off_t offset ) { - return strlen ( ( void * ) buffer + offset ); -} - -/** - * Find character in user buffer - * - * @v buffer User buffer - * @v offset Starting offset within buffer - * @v c Character to search for - * @v len Length of user buffer - * @ret offset Offset of character, or <0 if not found - */ -static inline __attribute__ (( always_inline )) off_t -memchr_user ( userptr_t buffer, off_t offset, int c, size_t len ) { - void *found; - - found = memchr ( ( ( void * ) buffer + offset ), c, len ); - return ( found ? ( found - ( void * ) buffer ) : -1 ); -} - -/** - * 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 ( ( intptr_t ) virtual ); -} - /** * Convert segment:offset address to user buffer * @@ -251,32 +167,9 @@ virt_to_user ( void * virtual ) { * @v offset Real-mode offset * @ret buffer User buffer */ -static inline __attribute__ (( always_inline )) userptr_t +static inline __always_inline userptr_t real_to_user ( unsigned int segment, unsigned int offset ) { - return virt_to_user ( VIRTUAL ( segment, offset ) ); -} - -/** - * Convert physical address to user buffer - * - * @v physical Physical address - * @ret buffer User buffer - */ -static inline __attribute__ (( always_inline )) userptr_t -phys_to_user ( physaddr_t physical ) { - return virt_to_user ( phys_to_virt ( physical ) ); -} - -/** - * Convert user buffer to physical address - * - * @v buffer User buffer - * @v offset Offset within user buffer - * @ret physical Physical address - */ -static inline __attribute__ (( always_inline )) physaddr_t -user_to_phys ( userptr_t buffer, off_t offset ) { - return virt_to_phys ( ( void * ) buffer + offset ); + return ( phys_to_user ( ( segment << 4 ) + offset ) ); } extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); diff --git a/gpxe/src/arch/i386/include/pci_io.h b/gpxe/src/arch/i386/include/pci_io.h deleted file mode 100644 index 4888d557..00000000 --- a/gpxe/src/arch/i386/include/pci_io.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _PCI_IO_H -#define _PCI_IO_H - -#include <pcibios.h> -#include <pcidirect.h> - -/** @file - * - * i386 PCI configuration space access - * - * We have two methods of PCI configuration space access: the PCI BIOS - * and direct Type 1 accesses. Selecting between them is via the - * compile-time switch -DCONFIG_PCI_DIRECT. - * - */ - -#if CONFIG_PCI_DIRECT -#define pci_max_bus pcidirect_max_bus -#define pci_read_config_byte pcidirect_read_config_byte -#define pci_read_config_word pcidirect_read_config_word -#define pci_read_config_dword pcidirect_read_config_dword -#define pci_write_config_byte pcidirect_write_config_byte -#define pci_write_config_word pcidirect_write_config_word -#define pci_write_config_dword pcidirect_write_config_dword -#else /* CONFIG_PCI_DIRECT */ -#define pci_max_bus pcibios_max_bus -#define pci_read_config_byte pcibios_read_config_byte -#define pci_read_config_word pcibios_read_config_word -#define pci_read_config_dword pcibios_read_config_dword -#define pci_write_config_byte pcibios_write_config_byte -#define pci_write_config_word pcibios_write_config_word -#define pci_write_config_dword pcibios_write_config_dword -#endif /* CONFIG_PCI_DIRECT */ - -#endif /* _PCI_IO_H */ diff --git a/gpxe/src/arch/i386/include/pxe_addr.h b/gpxe/src/arch/i386/include/pxe_addr.h deleted file mode 100644 index 954551e8..00000000 --- a/gpxe/src/arch/i386/include/pxe_addr.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Architecture-specific portion of pxe.h for Etherboot - * - * This file has to define the types SEGOFF16_t, SEGDESC_t and - * SEGSEL_t for use in other PXE structures. See pxe.h for details. - */ - -#ifndef PXE_ADDR_H -#define PXE_ADDR_H - -#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) ) -#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) ) -#define PTR_TO_SEGOFF16(ptr,segoff16) \ - (segoff16).segment = SEGMENT(ptr); \ - (segoff16).offset = OFFSET(ptr); - -#endif /* PXE_ADDR_H */ diff --git a/gpxe/src/arch/i386/include/realmode.h b/gpxe/src/arch/i386/include/realmode.h index 5d3ddf50..26e6dd77 100644 --- a/gpxe/src/arch/i386/include/realmode.h +++ b/gpxe/src/arch/i386/include/realmode.h @@ -1,45 +1,15 @@ #ifndef REALMODE_H #define REALMODE_H -#ifndef ASSEMBLY - -#include "stdint.h" -#include "registers.h" -#include "io.h" +#include <stdint.h> +#include <registers.h> +#include <gpxe/uaccess.h> /* * Data structures and type definitions * */ -/* 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; - -/* Macro hackery needed to stringify bits of inline assembly */ -#define RM_XSTR(x) #x -#define RM_STR(x) RM_XSTR(x) - -/* Drag in the selected real-mode transition library header */ -#ifdef KEEP_IT_REAL -#include "libkir.h" -#else -#include "librm.h" -#endif - -/* - * The API to some functions is identical between librm and libkir, so - * they are documented here, even though the prototypes are in librm.h - * and libkir.h. - * - */ - /* * Declaration of variables in .data16 * @@ -92,24 +62,53 @@ typedef struct segoff segoff_t; * assembler output to make sure that it's doing the right thing. */ -/* - * void copy_to_real ( uint16_t dest_seg, uint16_t dest_off, - * void *src, size_t n ) - * void copy_from_real ( void *dest, uint16_t src_seg, uint16_t src_off, - * size_t n ) +/** + * Copy data to base memory * - * These functions can be used to copy data to and from arbitrary - * locations in 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 ); +} -/* - * put_real ( variable, uint16_t dest_seg, uint16_t dest_off ) - * get_real ( variable, uint16_t src_seg, uint16_t src_off ) +/** + * 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 * - * These macros can be used to read or write single variables to and - * from arbitrary locations in base memory. "variable" must be a - * variable of either 1, 2 or 4 bytes in length. + * @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 ) @@ -123,6 +122,4 @@ typedef struct segoff segoff_t; * */ -#endif /* ASSEMBLY */ - #endif /* REALMODE_H */ diff --git a/gpxe/src/arch/i386/include/registers.h b/gpxe/src/arch/i386/include/registers.h index 2b9b2b43..e68fa85a 100644 --- a/gpxe/src/arch/i386/include/registers.h +++ b/gpxe/src/arch/i386/include/registers.h @@ -10,8 +10,7 @@ * */ -#include "compiler.h" /* for doxygen */ -#include "stdint.h" +#include <stdint.h> /** * A 16-bit general register. @@ -184,4 +183,14 @@ struct i386_all_regs { #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; +} PACKED; + +typedef struct segoff segoff_t; + #endif /* REGISTERS_H */ diff --git a/gpxe/src/arch/i386/include/undi.h b/gpxe/src/arch/i386/include/undi.h index 9936e17f..c6253d0a 100644 --- a/gpxe/src/arch/i386/include/undi.h +++ b/gpxe/src/arch/i386/include/undi.h @@ -24,10 +24,6 @@ struct undi_device { SEGOFF16_t ppxe; /** Entry point */ SEGOFF16_t entry; - /** Return stack */ - UINT16_t return_stack[3]; - /** Return type */ - UINT16_t return_type; /** Free base memory after load */ UINT16_t fbms; /** Free base memory prior to load */ @@ -99,4 +95,10 @@ static inline void * undi_get_drvdata ( struct undi_device *undi ) { /** 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/gpxe/src/arch/i386/include/virtaddr.h b/gpxe/src/arch/i386/include/virtaddr.h deleted file mode 100644 index f2ffa2a1..00000000 --- a/gpxe/src/arch/i386/include/virtaddr.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VIRTADDR_H -#define VIRTADDR_H - -/* 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 - -#include "stdint.h" -#include "string.h" - -#ifndef KEEP_IT_REAL - -/* - * Without -DKEEP_IT_REAL, we are in 32-bit protected mode with a - * fixed link address but an unknown physical start address. Our GDT - * sets up code and data segments with an offset of virt_offset, so - * that link-time addresses can still work. - * - */ - -/* C-callable function prototypes */ - -extern void relocate_to ( uint32_t new_phys_addr ); - -/* Variables in virtaddr.S */ -extern unsigned long virt_offset; - -/* - * Convert between virtual and physical addresses - * - */ -static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) { - return ( ( unsigned long ) virt_addr ) + virt_offset; -} - -static inline void * phys_to_virt ( unsigned long phys_addr ) { - return ( void * ) ( phys_addr - virt_offset ); -} - -static inline void copy_to_phys ( physaddr_t dest, const void *src, - size_t len ) { - memcpy ( phys_to_virt ( dest ), src, len ); -} - -static inline void copy_from_phys ( void *dest, physaddr_t src, size_t len ) { - memcpy ( dest, phys_to_virt ( src ), len ); -} - -static inline void copy_phys_to_phys ( physaddr_t dest, physaddr_t src, - size_t len ) { - memcpy ( phys_to_virt ( dest ), phys_to_virt ( src ), len ); -} - -#else /* KEEP_IT_REAL */ - -/* - * With -DKEEP_IT_REAL, we are in 16-bit real mode with fixed link - * addresses and a segmented memory model. We have separate code and - * data segments. - * - * Because we may be called in 16-bit protected mode (damn PXE spec), - * we cannot simply assume that physical = segment * 16 + offset. - * Instead, we have to look up the physical start address of the - * segment in the !PXE structure. We have to assume that - * virt_to_phys() is called only on pointers within the data segment, - * because nothing passes segment information to us. - * - * We don't implement phys_to_virt at all, because there will be many - * addresses that simply cannot be reached via a virtual address when - * the virtual address space is limited to 64kB! - */ - -static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) { - /* Cheat: just for now, do the segment*16+offset calculation */ - uint16_t ds; - - __asm__ ( "movw %%ds, %%ax" : "=a" ( ds ) : ); - return ( 16 * ds + ( ( unsigned long ) virt_addr ) ); -} - -/* Define it as a deprecated function so that we get compile-time - * warnings, rather than just the link-time errors. - */ -extern void * phys_to_virt ( unsigned long phys_addr ) - __attribute__ ((deprecated)); - -#endif /* KEEP_IT_REAL */ - -#endif /* ASSEMBLY */ - -#endif /* VIRTADDR_H */ diff --git a/gpxe/src/arch/i386/interface/efi/efix86_nap.c b/gpxe/src/arch/i386/interface/efi/efix86_nap.c new file mode 100644 index 00000000..45e99a68 --- /dev/null +++ b/gpxe/src/arch/i386/interface/efi/efix86_nap.c @@ -0,0 +1,46 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <gpxe/nap.h> +#include <gpxe/efi/efi.h> + +/** @file + * + * gPXE CPU sleeping API for EFI + * + */ + +/** + * Sleep until next interrupt + * + */ +static void efix86_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + */ + __asm__ __volatile__ ( "hlt" ); +} + +PROVIDE_NAP ( efix86, cpu_nap, efix86_cpu_nap ); diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_nap.c b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c new file mode 100644 index 00000000..2f4a0513 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c @@ -0,0 +1,14 @@ +#include <gpxe/nap.h> +#include <realmode.h> + +/** + * Save power by halting the CPU until the next interrupt + * + */ +static void bios_cpu_nap ( void ) { + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "hlt\n\t" + "cli\n\t" ) : : ); +} + +PROVIDE_NAP ( pcbios, cpu_nap, bios_cpu_nap ); diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_timer.c b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c new file mode 100644 index 00000000..0b475ea3 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c @@ -0,0 +1,63 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * + * BIOS timer + * + */ + +#include <gpxe/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__ ( REAL_CODE ( "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/gpxe/src/arch/i386/interface/pcbios/biosint.c b/gpxe/src/arch/i386/interface/pcbios/biosint.c index 8ef2d7ab..1306f918 100644 --- a/gpxe/src/arch/i386/interface/pcbios/biosint.c +++ b/gpxe/src/arch/i386/interface/pcbios/biosint.c @@ -8,17 +8,6 @@ */ /** - * 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). - */ -int hooked_bios_interrupts = 0; - -/** * Hook INT vector * * @v interrupt INT number diff --git a/gpxe/src/arch/i386/core/umalloc.c b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c index 3990488c..2eb7f76d 100644 --- a/gpxe/src/arch/i386/core/umalloc.c +++ b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c @@ -36,9 +36,6 @@ /** Equivalent of NOWHERE for user pointers */ #define UNOWHERE ( ~UNULL ) -/** Start of Etherboot text, as defined by the linker */ -extern char _text[]; - /** An external memory block */ struct external_memory { /** Size of this memory block (excluding this header) */ @@ -135,7 +132,7 @@ static void ecollect_free ( void ) { * Calling realloc() with a new size of zero is a valid way to free a * memory block. */ -userptr_t urealloc ( userptr_t ptr, size_t new_size ) { +static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) { struct external_memory extmem; userptr_t new = ptr; size_t align; @@ -200,25 +197,4 @@ userptr_t urealloc ( userptr_t ptr, size_t new_size ) { return ( new_size ? new : UNOWHERE ); } -/** - * Allocate external memory - * - * @v size Requested size - * @ret ptr Memory, or UNULL - * - * Memory is guaranteed to be aligned to a page boundary. - */ -userptr_t umalloc ( size_t size ) { - return urealloc ( UNULL, size ); -} - -/** - * Free external memory - * - * @v ptr Memory allocated by umalloc(), or UNULL - * - * If @c ptr is UNULL, no action is taken. - */ -void ufree ( userptr_t ptr ) { - urealloc ( ptr, 0 ); -} +PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc ); diff --git a/gpxe/src/arch/i386/core/pcibios.c b/gpxe/src/arch/i386/interface/pcbios/pcibios.c index 1c93e4be..81b4fd3c 100644 --- a/gpxe/src/arch/i386/core/pcibios.c +++ b/gpxe/src/arch/i386/interface/pcbios/pcibios.c @@ -18,7 +18,6 @@ #include <stdint.h> #include <gpxe/pci.h> -#include <pcibios.h> #include <realmode.h> /** @file @@ -32,7 +31,7 @@ * * @ret max_bus Maximum bus number */ -int pcibios_max_bus ( void ) { +static int pcibios_max_bus ( void ) { int discard_a, discard_D; uint8_t max_bus; @@ -104,3 +103,11 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ return ( ( status >> 8 ) & 0xff ); } + +PROVIDE_PCIAPI ( pcbios, pci_max_bus, pcibios_max_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/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S index e5d327a5..8dd1a2ea 100644 --- a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S +++ b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -44,10 +44,10 @@ ppxe: .byte SegDescCnt /* SegDescCnt */ .word 0 /* FirstSelector */ pxe_segments: - .word 0, 0, 0, _data16_size /* Stack */ - .word 0, 0, 0, _data16_size /* UNDIData */ - .word 0, 0, 0, _text16_size /* UNDICode */ - .word 0, 0, 0, _text16_size /* UNDICodeWrite */ + .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 */ @@ -76,15 +76,15 @@ pxenv: .long 0 /* PMEntry */ .word 0 /* PMSelector */ .word 0 /* StackSeg */ - .word _data16_size /* StackSize */ + .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_size /* UNDIDataSize */ + .word _data16_memsz /* UNDIDataSize */ .word 0 /* UNDICodeSeg */ - .word _text16_size /* UNDICodeSize */ + .word _text16_memsz /* UNDICodeSize */ .word ppxe, 0 /* PXEPtr */ .equ pxenv_length, . - pxenv .size pxenv, . - pxenv diff --git a/gpxe/src/arch/i386/prefix/dskprefix.S b/gpxe/src/arch/i386/prefix/dskprefix.S index cdc43b37..0156812a 100644 --- a/gpxe/src/arch/i386/prefix/dskprefix.S +++ b/gpxe/src/arch/i386/prefix/dskprefix.S @@ -144,8 +144,8 @@ got_sectors: /* Jump to loaded copy */ ljmp $SYSSEG, $start_runtime -endseg: .word SYSSEG + _load_size_pgh - .section ".zinfo.fixup", "a" /* Compressor fixup information */ +endseg: .word SYSSEG + _filesz_pgh + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBW" .long endseg .long 16 @@ -353,6 +353,7 @@ msg1end: .word 0xAA55 start_runtime: + /* Install gPXE */ call install /* Set up real-mode stack */ @@ -368,7 +369,10 @@ start_runtime: pushl $main pushw %cs call prot_call - popl %eax /* discard */ + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall /* Boot next device */ int $0x18 diff --git a/gpxe/src/arch/i386/prefix/efiprefix.S b/gpxe/src/arch/i386/prefix/efiprefix.S new file mode 100644 index 00000000..c4cf68e4 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/efiprefix.S @@ -0,0 +1,175 @@ + .text + .code32 + .arch i386 + .section ".prefix", "a", @progbits + .org 0x00 + + /* DOS (.com) header + * + * EFI executables seem to leave most of this empty + */ +mzhdr: + .ascii "MZ" /* Magic number */ + .word 0 /* Bytes on last page of file */ + .word 0 /* Pages in file */ + .word 0 /* Relocations */ + .word 0 /* Size of header in paragraphs */ + .word 0 /* Minimum extra paragraphs needed */ + .word 0 /* Maximum extra paragraphs needed */ + .word 0 /* Initial (relative) SS value */ + .word 0 /* Initial SP value */ + .word 0 /* "Checksum" */ + .word 0 /* Initial IP value */ + .word 0 /* Initial (relative) CS value */ + .word 0 /* File address of relocation table */ + .word 0 /* Ovesrlay number */ + .word 0, 0, 0, 0 /* Reserved words */ + .word 0 /* OEM identifier (for e_oeminfo) */ + .word 0 /* OEM information; e_oemid specific */ + .word 0, 0, 0, 0, 0 /* Reserved words */ + .word 0, 0, 0, 0, 0 /* Reserved words */ + .long pehdr_lma /* File address of new exe header */ + .size mzhdr, . - mzhdr + + /* PE header */ + .org 0xc0 /* For compatibility with MS toolchain */ +pehdr: + .ascii "PE\0\0" /* Magic number */ + .word 0x014c /* CPU architecture: i386 */ + .word num_pe_sections /* Number of sections */ + .long 0x10d1a884 /* Timestamp */ + .long 0 /* Symbol table */ + .long 0 /* Number of symbols */ + .word opthdr_size /* Size of optional header */ + .word 0x2102 /* Characteristics */ + .size pehdr, . - pehdr + .equ pehdr_lma, pehdr - mzhdr + + /* "Optional" header */ +opthdr: + .word 0x010b /* Magic number */ + .byte 0 /* Linker major version number */ + .byte 0 /* Linker minor version number */ + .long _text_filesz /* Size of text section */ + .long _data_filesz /* Size of data section */ + .long _bss_filesz /* Size of bss section */ + .long efi_entry_lma /* Entry point */ + .long _text_lma /* Text section start RVA */ + .long _data_lma /* Data section start RVA */ + .long 0 /* Image base address */ + .long _max_align /* Section alignment */ + .long _max_align /* File alignment */ + .word 0 /* Operating system major version number */ + .word 0 /* Operating system minor version number */ + .word 0 /* Image major version number */ + .word 0 /* Image minor version number */ + .word 0 /* Subsystem major version number */ + .word 0 /* Subsystem minor version number */ + .long 0 /* Reserved */ + .long _filesz /* Total image size */ + .long _prefix_filesz /* Total header size */ + .long 0 /* "Checksum" */ + .word 0x0a /* Subsystem: EFI */ + .word 0 /* DLL characteristics */ + .long 0 /* Size of stack reserve */ + .long 0 /* Size of stack commit */ + .long 0 /* Size of heap reserve */ + .long 0 /* Size of heap commit */ + .long 0 /* Loader flags */ + .long 16 /* Number of data directory entries */ + .long 0, 0 /* Export directory */ + .long 0, 0 /* Import directory */ + .long 0, 0 /* Resource directory */ + .long 0, 0 /* Exception directory */ + .long 0, 0 /* Security directory */ + .long _reloc_lma, _reloc_filesz /* Base relocation directory */ + .long debugdir_lma, debugdir_size /* Debug directory */ + .long 0, 0 /* Description directory */ + .long 0, 0 /* Special directory */ + .long 0, 0 /* Thread storage directory */ + .long 0, 0 /* Load configuration directory */ + .long 0, 0 /* Bound import directory */ + .long 0, 0 /* Import address table directory */ + .long 0, 0 /* Delay import directory */ + .long 0, 0 /* Reserved */ + .long 0, 0 /* Reserved */ + .size opthdr, . - opthdr + .equ opthdr_size, . - opthdr + + /* PE sections */ +pe_sections: +text_section: + .asciz ".text" /* Section name */ + .align 8 + .long _text_filesz /* Section size */ + .long _text_lma /* Relative Virtual Address */ + .long _text_filesz /* Section size (rounded up) */ + .long _text_lma /* Pointer to raw data */ + .long 0 /* Link-time relocations */ + .long 0 /* Line numbers */ + .word 0 /* Number of link-time relocations */ + .word 0 /* Number of line numbers */ + .long 0x68000020 /* Characteristics */ +rodata_section: + .asciz ".rodata" /* Section name */ + .align 8 + .long _rodata_filesz /* Section size */ + .long _rodata_lma /* Relative Virtual Address */ + .long _rodata_filesz /* Section size (rounded up) */ + .long _rodata_lma /* Pointer to raw data */ + .long 0 /* Link-time relocations */ + .long 0 /* Line numbers */ + .word 0 /* Number of link-time relocations */ + .word 0 /* Number of line numbers */ + .long 0x48000040 /* Characteristics */ +data_section: + .asciz ".data" /* Section name */ + .align 8 + .long _data_filesz /* Section size */ + .long _data_lma /* Relative Virtual Address */ + .long _data_filesz /* Section size (rounded up) */ + .long _data_lma /* Pointer to raw data */ + .long 0 /* Link-time relocations */ + .long 0 /* Line numbers */ + .word 0 /* Number of link-time relocations */ + .word 0 /* Number of line numbers */ + .long 0xc8000040 /* Characteristics */ +reloc_section: + .asciz ".reloc" /* Section name */ + .align 8 + .long _reloc_filesz /* Section size */ + .long _reloc_lma /* Relative Virtual Address */ + .long _reloc_filesz /* Section size (rounded up) */ + .long _reloc_lma /* Pointer to raw data */ + .long 0 /* Link-time relocations */ + .long 0 /* Line numbers */ + .word 0 /* Number of link-time relocations */ + .word 0 /* Number of line numbers */ + .long 0x42000040 /* Characteristics */ + +pe_sections_end: + .size pe_sections, . - pe_sections + .equ num_pe_sections, ( ( . - pe_sections ) / 0x28 ) + + /* Debug directory */ + .section ".rodata" + .globl debugdir +debugdir: + .long 0 /* Characteristics */ + .long 0x10d1a884 /* Timestamp */ + .word 0 /* Major version */ + .word 0 /* Minor version */ + .long 0x02 /* RSDS? */ + .long codeview_rsds_size /* Size of data */ + .long codeview_rsds_lma /* RVA */ + .long codeview_rsds_lma /* File offset */ + .size debugdir, . - debugdir + .equ debugdir_size, . - debugdir + /* Codeview structure */ + .globl codeview_rsds +codeview_rsds: + .ascii "RSDS" /* Magic number */ + .long 0, 0, 0, 0, 0 /* Unused by EFI */ + .asciz "efiprefix.pdb" + .size codeview_rsds, . - codeview_rsds + .equ codeview_rsds_size, . - codeview_rsds diff --git a/gpxe/src/arch/i386/prefix/hdprefix.S b/gpxe/src/arch/i386/prefix/hdprefix.S index 56fcb36d..086d7f45 100644 --- a/gpxe/src/arch/i386/prefix/hdprefix.S +++ b/gpxe/src/arch/i386/prefix/hdprefix.S @@ -63,9 +63,9 @@ max_sector: max_head: .byte 0 load_length: - .long _load_size_sect + .long _filesz_sect - .section ".zinfo.fixup", "a" /* Compressor fixup information */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBL" .long load_length .long 512 @@ -82,6 +82,7 @@ load_failed: .byte 0x55, 0xaa start_image: + /* Install gPXE */ call install /* Set up real-mode stack */ @@ -97,7 +98,10 @@ start_image: pushl $main pushw %cs call prot_call - popl %eax /* discard */ + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall /* Boot next device */ int $0x18 diff --git a/gpxe/src/arch/i386/prefix/kkpxeprefix.S b/gpxe/src/arch/i386/prefix/kkpxeprefix.S new file mode 100644 index 00000000..38300bed --- /dev/null +++ b/gpxe/src/arch/i386/prefix/kkpxeprefix.S @@ -0,0 +1,8 @@ +/***************************************************************************** + * PXE prefix that keep the whole PXE stack present + ***************************************************************************** + */ + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#include "pxeprefix.S" diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S index ae2a491f..56ca64d9 100644 --- a/gpxe/src/arch/i386/prefix/libprefix.S +++ b/gpxe/src/arch/i386/prefix/libprefix.S @@ -199,6 +199,39 @@ print_pci_busdevfn: 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" + .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 */ + movb $' ', %al + movw $79, %cx +1: call print_character + loop 1b + /* Print CR */ + movb $'\r', %al + call print_character + /* Restore registers and return */ + popw %cx + popw %ax + ret + .size print_kill_line, . - print_kill_line + /**************************************************************************** * pm_call (real-mode near call) * @@ -308,7 +341,7 @@ pm_call: /* Switch CPU to protected mode and load up segment registers */ pushl %eax cli - lgdt PM_CALL_VAR(gdt)(%bp) + data32 lgdt PM_CALL_VAR(gdt)(%bp) movl %cr0, %eax orb $CR0_PE, %al movl %eax, %cr0 @@ -344,7 +377,7 @@ pm_call: popw %es popw %fs popw %gs - lgdt PM_CALL_VAR(pm_saved_gdt)(%bp) + data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp) popfl movw %bp, %sp popw %bp @@ -504,31 +537,89 @@ install_block: .code16 .globl alloc_basemem alloc_basemem: + /* Preserve registers */ + pushw %fs + /* FBMS => %ax as segment address */ - movw $0x40, %ax - movw %ax, %fs + pushw $0x40 + popw %fs movw %fs:0x13, %ax shlw $6, %ax - /* .data16 segment address */ - subw $_data16_size_pgh, %ax + /* Calculate .data16 segment address */ + subw $_data16_memsz_pgh, %ax pushw %ax - /* .text16 segment address */ - subw $_text16_size_pgh, %ax + /* Calculate .text16 segment address */ + subw $_text16_memsz_pgh, %ax pushw %ax /* Update FBMS */ shrw $6, %ax movw %ax, %fs:0x13 - /* Return */ + /* Retrieve .text16 and .data16 segment addresses */ popw %ax popw %bx + + /* Restore registers and return */ + popw %fs ret .size alloc_basemem, . - alloc_basemem /**************************************************************************** + * free_basemem (real-mode near call) + * + * Free space allocated with alloc_basemem. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Returns: + * %ax : 0 if successfully freed + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16" + .code16 + .globl free_basemem +free_basemem: + /* Preserve registers */ + pushw %fs + + /* Check FBMS counter */ + pushw %ax + shrw $6, %ax + pushw $0x40 + popw %fs + cmpw %ax, %fs:0x13 + popw %ax + jne 1f + + /* Check hooked interrupt count */ + cmpw $0, %cs:hooked_bios_interrupts + jne 1f + + /* OK to free memory */ + 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 %fs + ret + .size free_basemem, . - free_basemem + + .section ".text16.data" + .globl hooked_bios_interrupts +hooked_bios_interrupts: + .word 0 + .size hooked_bios_interrupts, . - hooked_bios_interrupts + +/**************************************************************************** * install (real-mode near call) * * Install all text and data segments. @@ -594,19 +685,19 @@ install_prealloc: jnz 1f movw %cs, %si shll $4, %esi -1: addl $_payload_offset, %esi +1: addl $_payload_lma, %esi /* Install .text16 and .data16 */ pushl %edi movzwl %ax, %edi shll $4, %edi - movl $_text16_size, %ecx + movl $_text16_memsz, %ecx movl %ecx, %edx call install_block /* .text16 */ movzwl %bx, %edi shll $4, %edi - movl $_data16_progbits_size, %ecx - movl $_data16_size, %edx + movl $_data16_filesz, %ecx + movl $_data16_memsz, %edx call install_block /* .data16 */ popl %edi @@ -622,8 +713,8 @@ install_prealloc: * prior to reading the E820 memory map and relocating * properly. */ - movl $_textdata_progbits_size, %ecx - movl $_textdata_size, %edx + movl $_textdata_filesz, %ecx + movl $_textdata_memsz, %edx call install_block /* Initialise librm at current location */ @@ -676,30 +767,53 @@ prot_call_vector: .size prot_call_vector, . - prot_call_vector #endif +/**************************************************************************** + * uninstall (real-mode near call) + * + * Uninstall all text and data segments. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Returns: + * none + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16" + .code16 + .globl uninstall +uninstall: + call free_basemem + ret + .size uninstall, . - uninstall + + /* File split information for the compressor */ #if COMPRESS - .section ".zinfo", "a" + .section ".zinfo", "a", @progbits .ascii "COPY" - .long _prefix_load_offset - .long _prefix_progbits_size + .long _prefix_lma + .long _prefix_filesz .long _max_align .ascii "PACK" - .long _text16_load_offset - .long _text16_progbits_size + .long _text16_lma + .long _text16_filesz .long _max_align .ascii "PACK" - .long _data16_load_offset - .long _data16_progbits_size + .long _data16_lma + .long _data16_filesz .long _max_align .ascii "PACK" - .long _textdata_load_offset - .long _textdata_progbits_size + .long _textdata_lma + .long _textdata_filesz .long _max_align #else /* COMPRESS */ - .section ".zinfo", "a" + .section ".zinfo", "a", @progbits .ascii "COPY" - .long _prefix_load_offset - .long _load_size + .long _prefix_lma + .long _filesz .long _max_align #endif /* COMPRESS */ diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S index c1e92f57..094263d2 100644 --- a/gpxe/src/arch/i386/prefix/lkrnprefix.S +++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S @@ -92,9 +92,9 @@ setup_sects: root_flags: .word 0 syssize: - .long _load_size_pgh - PREFIXPGH + .long _filesz_pgh - PREFIXPGH - .section ".zinfo.fixup", "a" /* Compressor fixup information */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBL" .long syssize .long 16 @@ -189,6 +189,7 @@ run_gpxe: movw %ax, %ss movw $0x7c00, %sp + /* Install gPXE */ call install /* Set up real-mode stack */ @@ -204,7 +205,10 @@ run_gpxe: pushl $main pushw %cs call prot_call - popl %eax /* discard */ + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall /* Boot next device */ int $0x18 diff --git a/gpxe/src/arch/i386/prefix/nbiprefix.S b/gpxe/src/arch/i386/prefix/nbiprefix.S index d4904b73..d1753e30 100644 --- a/gpxe/src/arch/i386/prefix/nbiprefix.S +++ b/gpxe/src/arch/i386/prefix/nbiprefix.S @@ -32,11 +32,11 @@ segment_header: .byte 0 .byte 0x04 /* Last segment */ .long 0x00007e00 -imglen: .long _load_size - 512 -memlen: .long _load_size - 512 +imglen: .long _filesz - 512 +memlen: .long _filesz - 512 .size segment_header, . - segment_header - .section ".zinfo.fixup", "a" /* Compressor fixup information */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBL" .long imglen .long 1 @@ -52,7 +52,7 @@ memlen: .long _load_size - 512 ***************************************************************************** */ entry: - /* Install low and high memory regions */ + /* Install gPXE */ call install /* Jump to .text16 segment */ @@ -64,7 +64,10 @@ entry: pushl $main pushw %cs call prot_call - popl %eax /* discard */ + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall /* Reboot system */ int $0x19 diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S index 302f8e5d..a5f0c711 100644 --- a/gpxe/src/arch/i386/prefix/pxeprefix.S +++ b/gpxe/src/arch/i386/prefix/pxeprefix.S @@ -2,7 +2,7 @@ #define PXENV_UNDI_GET_NIC_TYPE 0x0012 #define PXENV_STOP_UNDI 0x0015 #define PXENV_UNLOAD_STACK 0x0070 - + .text .arch i386 .org 0 @@ -12,36 +12,39 @@ #include <undi.h> +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) + /***************************************************************************** * Entry point: set operating context, print welcome message ***************************************************************************** */ .section ".prefix" - /* Set up our non-stack segment registers */ jmp $0x7c0, $1f -1: pushfl - /* %ax here is the default return type... */ - movw $5, %ax /* Keep PXE+UNDI */ +1: + /* Preserve registers for possible return to PXE */ + pushfl pushal - pushw %ds - pushw %es - pushw %fs 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 our non-stack segment registers */ movw %cs, %ax movw %ax, %ds - movw $0x40, %ax /* BIOS data segment access */ movw %ax, %fs - - pushw %fs:0x13 /* Record PXENV+ and !PXE nominal addresses */ - movw %es, pxenv_segment + movw %es, %ax /* PXENV+ address */ + movw %ax, pxenv_segment movw %bx, pxenv_offset - movw %sp, %bp - movw %ss, return_stack_segment - movl %esp, return_stack_offset - movl 50(%bp), %eax - movl %eax, ppxe_segoff /* !PXE address */ + popl %eax /* Discard return address */ + popl ppxe_segoff /* !PXE address */ /* Set up stack just below 0x7c00 */ xorw %ax, %ax movw %ax, %ss @@ -60,7 +63,7 @@ * Verify PXENV+ structure and record parameters of interest ***************************************************************************** */ -detect_pxenv: +ect_pxenv: /* Signature check */ les pxenv_segoff, %bx cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */ @@ -264,7 +267,7 @@ no_physical_device: * Leave NIC in a safe state ***************************************************************************** */ -#ifndef PXELOADER_KEEP_UNDI +#ifndef PXELOADER_KEEP_PXE shutdown_nic: /* Issue PXENV_UNDI_SHUTDOWN */ movw $PXENV_UNDI_SHUTDOWN, %bx @@ -272,11 +275,6 @@ shutdown_nic: jnc 1f call print_pxe_error 1: - -/***************************************************************************** - * Unload PXE base code - ***************************************************************************** - */ unload_base_code: /* Issue PXENV_UNLOAD_STACK */ movw $PXENV_UNLOAD_STACK, %bx @@ -289,12 +287,14 @@ unload_base_code: 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 @@ -309,6 +309,7 @@ unload_undi: /* Clear UNDI_FL_STARTED */ andw $~UNDI_FL_STARTED, flags 99: +#endif /* PXELOADER_KEEP_UNDI */ /***************************************************************************** * Print remaining free base memory @@ -325,15 +326,14 @@ print_free_basemem: 10: .asciz " " 20: .asciz "kB free base memory after PXE unload\n" .previous -#endif /* PXELOADER_KEEP_UNDI */ /***************************************************************************** * Exit point ***************************************************************************** */ finished: - jmp run_etherboot - + jmp run_gpxe + /***************************************************************************** * Subroutine: print segment:offset address * @@ -527,6 +527,10 @@ print_pxe_error: * PXE data structures ***************************************************************************** */ + .section ".prefix.data" + +pxe_ss: .word 0 +pxe_esp: .long 0 pxe_parameter_structure: .fill 20 @@ -554,12 +558,6 @@ entry_segoff: entry_offset: .word 0 entry_segment: .word 0 -return_stack_segoff: -return_stack_offset: .long 0 -return_stack_segment: .word 0 - -return_type: .word 0 /* Default: unload PXE and boot next */ - undi_fbms_start: .word 0 undi_fbms_end: .word 0 @@ -569,16 +567,18 @@ isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT pci_vendor: .word 0 pci_device: .word 0 -flags: .word UNDI_FL_STARTED +flags: + .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL ) .equ undi_device_size, ( . - undi_device ) /***************************************************************************** - * Run Etherboot main code + * Run gPXE main code ***************************************************************************** - */ -run_etherboot: - /* Install Etherboot */ + */ + .section ".prefix" +run_gpxe: + /* Install gPXE */ call install /* Set up real-mode stack */ @@ -594,6 +594,10 @@ run_etherboot: rep movsb #endif + /* Retrieve PXE %ss:esp */ + movw pxe_ss, %di + movl pxe_esp, %ebp + /* Jump to .text16 segment with %ds pointing to .data16 */ movw %bx, %ds pushw %ax @@ -605,29 +609,30 @@ run_etherboot: pushl $main pushw %cs call prot_call - popl %eax /* discard */ + popl %ecx /* discard */ -#ifdef PXELOADER_KEEP_UNDI - /* Boot next device */ - movw $0x40, %ax - movw %ax, %fs - movw $preloaded_undi,%bx - - cli - movw %ss:return_type-undi_device(%bx),%ax - lssl %ss:return_stack_segoff-undi_device(%bx), %esp - movw %sp,%bp - movw %ax,38(%bp) /* Overwrite return AX value */ - popw %fs:0x13 /* 0 */ - popw %gs /* 2 */ - popw %fs /* 4 */ - popw %es /* 6 */ - popw %ds /* 8 */ - popal /* 10, 14, 18, 22, 26, 30, 34, 38 */ - popfl /* 42 */ - lret /* 46 */ -#else - int $0x18 -#endif + /* Uninstall gPXE */ + call uninstall + + /* Restore PXE stack */ + movw %di, %ss + movl %ebp, %esp + /* 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/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S index 92a931cd..a6431cd9 100644 --- a/gpxe/src/arch/i386/prefix/romprefix.S +++ b/gpxe/src/arch/i386/prefix/romprefix.S @@ -14,6 +14,7 @@ #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) #define PNP_GET_BBS_VERSION 0x60 #define PMM_ALLOCATE 0x0000 +#define PMM_DEALLOCATE 0x0002 /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in * config.h, but converted to a number of (18Hz) timer ticks, and @@ -30,7 +31,7 @@ .org 0x00 romheader: .word 0xAA55 /* BIOS extension signature */ -romheader_size: .byte _load_size_sect /* Size in 512-byte blocks */ +romheader_size: .byte _filesz_sect /* Size in 512-byte blocks */ jmp init /* Initialisation vector */ checksum: .byte 0 @@ -42,7 +43,7 @@ checksum: .word pnpheader .size romheader, . - romheader - .section ".zinfo.fixup", "a" /* Compressor fixup information */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBB" .long romheader_size .long 512 @@ -58,18 +59,18 @@ pciheader: .byte 0x03 /* PCI data structure revision */ .byte 0x02, 0x00, 0x00 /* Class code */ pciheader_image_length: - .word _load_size_sect /* Image length */ + .word _filesz_sect /* Image length */ .word 0x0001 /* Revision level */ .byte 0x00 /* Code type */ .byte 0x80 /* Last image indicator */ pciheader_runtime_length: - .word _load_size_sect /* Maximum run-time image length */ + .word _filesz_sect /* 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" /* Compressor fixup information */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "SUBW" .long pciheader_image_length .long 512 @@ -109,12 +110,12 @@ mfgstr: /* Product string * - * Defaults to "gPXE". 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. + * 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 "gPXE" + .ascii PRODUCT_SHORT_NAME prodstr_separator: .byte 0 .ascii "(PCI " @@ -130,9 +131,9 @@ undiheader: .byte 0 /* Structure revision */ .byte 0,1,2 /* PXE version: 2.1.0 */ .word undiloader /* Offset to loader routine */ - .word _data16_size /* Stack segment size */ - .word _data16_size /* Data segment size */ - .word _text16_size /* Code segment size */ + .word _data16_memsz /* Stack segment size */ + .word _data16_memsz /* Data segment size */ + .word _text16_memsz /* Code segment size */ .ascii "PCIR" /* Bus type */ .equ undiheader_len, . - undiheader .size undiheader, . - undiheader @@ -190,11 +191,11 @@ init: stc movw $0xb101, %ax int $0x1a - jc 1f + jc no_pci3 cmpl $PCI_SIGNATURE, %edx - jne 1f + jne no_pci3 testb %ah, %ah - jnz 1f + jnz no_pci3 movw $init_message_pci, %si xorw %di, %di call print_message @@ -205,11 +206,33 @@ init: movb %bl, %al call print_hex_byte cmpb $3, %bh - jae 2f -1: /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ + 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 -2: popl %edi +1: popl %edi popl %edx popl %ebx @@ -268,21 +291,52 @@ pmm_scan: movw $init_message_pmm, %si xorw %di, %di call print_message - /* Try to allocate 2MB block via PMM */ + /* We have PMM and so a 1kB stack: preserve upper register halves */ + pushal + /* Calculate required allocation size in %esi */ + movzbl romheader_size, %eax + shll $9, %eax + addl $_textdata_memsz, %eax + orw $0xffff, %ax /* Ensure allocation size is at least 64kB */ + bsrl %eax, %ecx + subw $15, %cx /* Round up and convert to 64kB count */ + movw $1, %si + shlw %cl, %si +pmm_loop: + /* Try to allocate block via PMM */ pushw $0x0006 /* Aligned, extended memory */ pushl $0xffffffff /* No handle */ - pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */ + movzwl %si, %eax + shll $12, %eax + pushl %eax /* Allocation size in paragraphs */ pushw $PMM_ALLOCATE lcall *%es:7 addw $12, %sp + /* Abort if allocation fails */ + testw %dx, %dx /* %ax==0 even on success, since align>=64kB */ + jz pmm_fail + /* If block has A20==1, free block and try again with twice + * the allocation size (and hence alignment). + */ + testw $0x0010, %dx + jz got_pmm + pushw %dx + pushw $0 + pushw $PMM_DEALLOCATE + lcall *%es:7 + addw $6, %sp + addw %si, %si + jmp pmm_loop +got_pmm: /* PMM allocation succeeded */ + movw %dx, ( image_source + 2 ) movw %dx, %ax xorw %di, %di call print_hex_word - movw %dx, ( image_source + 2 ) - testw %dx, %dx /* %ax==0 even on success, since align=2MB */ - jz no_pmm - /* PMM allocation succeeded: copy ROM to PMM block */ - pushal /* PMM presence implies 1kB stack */ + movb $'@', %al + call print_character + movw %si, %ax + call print_hex_byte + /* Copy ROM to PMM block */ xorw %ax, %ax movw %ax, %es movl image_source, %edi @@ -294,13 +348,15 @@ pmm_scan: /* Shrink ROM and update checksum */ xorw %bx, %bx xorw %si, %si - movw $_prefix_size_sect, %cx + movw $_prefix_memsz_sect, %cx movb %cl, romheader_size shlw $9, %cx 1: lodsb addb %al, %bl loop 1b subb %bl, checksum +pmm_fail: + /* Restore upper register halves */ popal no_pmm: @@ -324,23 +380,28 @@ no_pmm: 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 - movw $clear_message, %si xorw %di, %di + call print_kill_line + movw $init_message_done, %si call print_message popf - jnz 1f + jnz 2f /* Ctrl-B was pressed: invoke gPXE. The keypress will be * picked up by the initial shell prompt, and we will drop * into a shell. */ pushw %cs call exec -1: +2: /* Restore registers */ popw %gs popw %fs @@ -353,7 +414,26 @@ no_pmm: lret .size init, . - init +/* + * 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 gPXE or http://etherboot.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 "gPXE (http://etherboot.org) - " .size init_message, . - init_message init_message_pci: @@ -372,11 +452,14 @@ init_message_int19: .asciz " INT19" .size init_message_int19, . - init_message_int19 init_message_prompt: - .asciz "\nPress Ctrl-B to configure gPXE..." + .asciz "\nPress Ctrl-B to configure " .size init_message_prompt, . - init_message_prompt -clear_message: - .asciz "\r \n\n" - .size clear_message, . - clear_message +init_message_dots: + .asciz "..." + .size init_message_dots, . - init_message_dots +init_message_done: + .asciz "\n\n" + .size init_message_done, . - init_message_done /* ROM image location * @@ -432,8 +515,9 @@ int19_entry: movw $0xdf42, %bx call wait_for_key pushf - movw $clear_message, %si xorw %di, %di + call print_kill_line + movw $int19_message_done, %si call print_message popf jnz 1f @@ -460,6 +544,9 @@ 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 * @@ -504,8 +591,11 @@ exec: /* Set %ds = %cs */ pushl $main pushw %cs call prot_call - /* No need to clean up stack; we are about to reload %ss:sp */ - + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + /* Restore BIOS stack */ movw %dx, %ss movw %bp, %sp diff --git a/gpxe/src/arch/i386/scripts/efi.lds b/gpxe/src/arch/i386/scripts/efi.lds new file mode 100644 index 00000000..b6255a68 --- /dev/null +++ b/gpxe/src/arch/i386/scripts/efi.lds @@ -0,0 +1,180 @@ +/* -*- sh -*- */ + +/* + * Linker script for EFI images + * + */ + +EXTERN ( efi_entry ) + +SECTIONS { + + /* The file starts at a virtual address of zero, and sections are + * contiguous. Each section is aligned to at least _max_align, + * which defaults to 32. Load addresses are equal to virtual + * addresses. + */ + + . = 0; + PROVIDE ( _max_align = 32 ); + + /* + * The prefix + * + */ + + .prefix : { + _prefix = .; + *(.prefix) + *(.prefix.*) + _mprefix = .; + } .bss.prefix (NOLOAD) : { + _eprefix = .; + } + _prefix_filesz = ABSOLUTE ( _mprefix - _prefix ); + _prefix_memsz = ABSOLUTE ( _eprefix - _prefix ); + + /* + * The text section + * + */ + + . = ALIGN ( _max_align ); + .text : { + _text = .; + *(.text) + *(.text.*) + _mtext = .; + } .bss.text (NOLOAD) : { + _etext = .; + } + _text_filesz = ABSOLUTE ( _mtext - _text ); + _text_memsz = ABSOLUTE ( _etext - _text ); + + /* + * The rodata section + * + */ + + . = ALIGN ( _max_align ); + .rodata : { + _rodata = .; + *(.rodata) + *(.rodata.*) + _mrodata = .; + } .bss.rodata (NOLOAD) : { + _erodata = .; + } + _rodata_filesz = ABSOLUTE ( _mrodata - _rodata ); + _rodata_memsz = ABSOLUTE ( _erodata - _rodata ); + + /* + * The data section + * + */ + + . = ALIGN ( _max_align ); + .data : { + _data = .; + *(.data) + *(.data.*) + *(SORT(.tbl.*)) /* Various tables. See include/tables.h */ + /* EFI seems to not support proper bss sections */ + *(.bss) + *(.bss.*) + *(COMMON) + *(.stack) + *(.stack.*) + _mdata = .; + } .bss.data (NOLOAD) : { + _edata = .; + } + _data_filesz = ABSOLUTE ( _mdata - _data ); + _data_memsz = ABSOLUTE ( _edata - _data ); + + /* + * The bss section + * + */ + + . = ALIGN ( _max_align ); + .bss : { + _bss = .; + /* EFI seems to not support proper bss sections */ + _mbss = .; + } .bss.bss (NOLOAD) : { + _ebss = .; + } + _bss_filesz = ABSOLUTE ( _mbss - _bss ); + _bss_memsz = ABSOLUTE ( _ebss - _bss ); + + /* + * The reloc section + * + */ + + . = ALIGN ( _max_align ); + .reloc : { + _reloc = .; + /* Provide some dummy contents to force ld to include this + * section. It will be created by the efilink utility. + */ + . += 1; + _mreloc = .; + } .bss.reloc (NOLOAD) : { + _ereloc = .; + } + _reloc_filesz = ABSOLUTE ( _mreloc - _reloc ); + _reloc_memsz = ABSOLUTE ( _ereloc - _reloc ); + + _filesz = ABSOLUTE ( . ); + + /* + * Weak symbols that need zero values if not otherwise defined + * + */ + + .weak 0x0 : { + _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.*) + } + + /* + * Load address calculations. + * + */ + + _prefix_lma = ABSOLUTE ( _prefix ); + _text_lma = ABSOLUTE ( _text ); + _rodata_lma = ABSOLUTE ( _rodata ); + _data_lma = ABSOLUTE ( _data ); + _bss_lma = ABSOLUTE ( _bss ); + _reloc_lma = ABSOLUTE ( _reloc ); + + /* + * Load addresses required by the prefix + * + */ + efi_entry_lma = ABSOLUTE ( efi_entry ); + debugdir_lma = ABSOLUTE ( debugdir ); + codeview_rsds_lma = ABSOLUTE ( codeview_rsds ); +} diff --git a/gpxe/src/arch/i386/scripts/i386.lds b/gpxe/src/arch/i386/scripts/i386.lds index 729ad30a..8a0c6733 100644 --- a/gpxe/src/arch/i386/scripts/i386.lds +++ b/gpxe/src/arch/i386/scripts/i386.lds @@ -5,15 +5,9 @@ * */ -OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" ) -OUTPUT_ARCH ( i386 ) -ENTRY ( _entry ) - SECTIONS { - /* All sections in the resulting file have consecutive load - * addresses, but may have individual link addresses depending on - * the memory model being used. + /* 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 @@ -31,254 +25,185 @@ SECTIONS { */ /* - * Weak symbols that need zero values if not otherwise defined - */ - - . = 0; - .weak : AT ( 0 ) { - *(.weak) - } - _assert = ASSERT ( ( . == 0 ), ".weak is non-zero length" ); - - /* * The prefix + * */ - _prefix_link_addr = 0; - . = _prefix_link_addr; - _prefix = .; - - .prefix : AT ( _prefix_load_offset + __prefix ) { - __prefix = .; - _entry = .; + .prefix 0x0 : AT ( _prefix_lma ) { + _prefix = .; *(.prefix) *(.prefix.*) - _eprefix_progbits = .; + _mprefix = .; + } .bss.prefix (NOLOAD) : AT ( _end_lma ) { + _eprefix = .; } - - _eprefix = .; + _prefix_filesz = ABSOLUTE ( _mprefix - _prefix ); + _prefix_memsz = ABSOLUTE ( _eprefix - _prefix ); /* - * The 16-bit sections, if present + * The 16-bit (real-mode) code section + * */ - _text16_link_addr = 0; - . = _text16_link_addr; - _text16 = .; - - /* We need to allow code at the NULL address in .text16 */ - - .text16 : AT ( _text16_load_offset + __text16 ) { - __text16 = .; + .text16 0x0 : AT ( _text16_lma ) { + _text16 = .; *(.text16.null) - . += 1; /* Prevent NULL being valid */ + . += 1; /* Prevent NULL being valid */ *(.text16) *(.text16.*) - _etext16_progbits = .; - } = 0x9090 - - _etext16 = .; - - _data16_link_addr = 0; - . = _data16_link_addr; - _data16 = .; + _mtext16 = .; + } .bss.text16 (NOLOAD) : AT ( _end_lma ) { + _etext16 = .; + } + _text16_filesz = ABSOLUTE ( _mtext16 - _text16 ); + _text16_memsz = ABSOLUTE ( _etext16 - _text16 ); - . += 1; /* Prevent NULL being valid */ + /* + * The 16-bit (real-mode) data section + * + */ - .rodata16 : AT ( _data16_load_offset + __rodata16 ) { - __rodata16 = .; + .data16 0x0 : AT ( _data16_lma ) { + _data16 = .; + . += 1; /* Prevent NULL being valid */ *(.rodata16) *(.rodata16.*) - } - .data16 : AT ( _data16_load_offset + __data16 ) { - __data16 = .; *(.data16) *(.data16.*) - _edata16_progbits = .; - } - .bss16 : AT ( _data16_load_offset + __bss16 ) { - __bss16 = .; - _bss16 = .; + _mdata16 = .; + } .bss.data16 (NOLOAD) : AT ( _end_lma ) { *(.bss16) *(.bss16.*) - _ebss16 = .; - } - .stack16 : AT ( _data16_load_offset + __stack16 ) { - __stack16 = .; *(.stack16) *(.stack16.*) + _edata16 = .; } - - _edata16 = .; + _data16_filesz = ABSOLUTE ( _mdata16 - _data16 ); + _data16_memsz = ABSOLUTE ( _edata16 - _data16 ); /* * The 32-bit sections + * */ - _textdata_link_addr = 0; - . = _textdata_link_addr; - _textdata = .; - - _text = .; - - . += 1; /* Prevent NULL being valid */ - - .text : AT ( _textdata_load_offset + __text ) { - __text = .; + .textdata 0x0 : AT ( _textdata_lma ) { + _textdata = .; *(.text.null_trap) + . += 1; /* Prevent NULL being valid */ *(.text) *(.text.*) - } = 0x9090 - - _etext = .; - - _data = .; - - .rodata : AT ( _textdata_load_offset + __rodata ) { - __rodata = .; *(.rodata) *(.rodata.*) - } - .data : AT ( _textdata_load_offset + __data ) { - __data = .; *(.data) *(.data.*) *(SORT(.tbl.*)) /* Various tables. See include/tables.h */ - _etextdata_progbits = .; - } - .bss : AT ( _textdata_load_offset + __bss ) { - __bss = .; - _bss = .; + _mtextdata = .; + } .bss.textdata (NOLOAD) : AT ( _end_lma ) { *(.bss) *(.bss.*) *(COMMON) - _ebss = .; - } - .stack : AT ( _textdata_load_offset + __stack ) { - __stack = .; *(.stack) *(.stack.*) + _etextdata = .; } - - _edata = .; - - _etextdata = .; - - _end = .; + _textdata_filesz = ABSOLUTE ( _mtextdata - _textdata ); + _textdata_memsz = ABSOLUTE ( _etextdata - _textdata ); /* * Compressor information block + * */ - _zinfo_link_addr = 0; - . = _zinfo_link_addr; - _zinfo = .; - - .zinfo : AT ( _zinfo_load_offset + __zinfo ) { - __zinfo = .; - _entry = .; + .zinfo 0x0 : AT ( _zinfo_lma ) { + _zinfo = .; *(.zinfo) *(.zinfo.*) - _ezinfo_progbits = .; + _mzinfo = .; + } .bss.zinfo (NOLOAD) : AT ( _end_lma ) { + _ezinfo = .; } - - _ezinfo = .; + _zinfo_filesz = ABSOLUTE ( _mzinfo - _zinfo ); + _zinfo_memsz = ABSOLUTE ( _ezinfo - _zinfo ); + + /* + * Weak symbols that need zero values if not otherwise defined + * + */ + + .weak 0x0 : AT ( _end_lma ) { + _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.*) } /* - * Load address calculations. The slightly obscure nature of the - * calculations is because ALIGN(x) can only operate on the - * location counter. + * Load address calculations. In older versions of ld, ALIGN() + * can operate only on the location counter, so we use that. + * */ - _max_align = 16; - _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; + PROVIDE ( _max_align = 16 ); + . = 0; - . -= _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 ); + _prefix_lma = .; + . += _prefix_filesz; - . -= _textdata_link_addr; - _textdata_load_offset = ALIGN ( _max_align ); - _textdata_load_addr = _textdata_link_addr + _textdata_load_offset; - _textdata_size = _etextdata - _textdata; - _textdata_progbits_size = _etextdata_progbits - _textdata; - . = _textdata_load_addr + _textdata_progbits_size; + . = ALIGN ( _max_align ); + _payload_lma = .; + _text16_lma = .; + . += _text16_filesz; - _load_size = . - _load_addr; + . = ALIGN ( _max_align ); + _data16_lma = .; + . += _data16_filesz; - . -= _zinfo_link_addr; - _zinfo_load_offset = ALIGN ( _max_align ); - _zinfo_load_addr = _zinfo_link_addr + _zinfo_load_offset; - _zinfo_size = _ezinfo - _zinfo; - _zinfo_progbits_size = _ezinfo_progbits - _zinfo; - . = _zinfo_load_addr + _zinfo_progbits_size; + . = ALIGN ( _max_align ); + _textdata_lma = .; + . += _textdata_filesz; - _payload_offset = _text16_load_offset; + _filesz = .; /* Do not include zinfo block in file size */ - /* - * 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" ); + . = ALIGN ( _max_align ); + _zinfo_lma = .; + . += _zinfo_filesz; - . = _data16_load_addr - _data16_link_addr; - _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), - "_data16 is badly aligned" ); - - . = _textdata_load_addr - _textdata_link_addr; - _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), - "_text is badly aligned" ); + . = ALIGN ( _max_align ); + _end_lma = .; /* * Values calculated to save code from doing it + * */ - _prefix_size_pgh = ( ( _prefix_size + 15 ) / 16 ); - _prefix_size_sect = ( ( _prefix_size + 511 ) / 512 ); - _text16_size_pgh = ( ( _text16_size + 15 ) / 16 ); - _data16_size_pgh = ( ( _data16_size + 15 ) / 16 ); + _prefix_memsz_pgh = ( ( _prefix_memsz + 15 ) / 16 ); + _prefix_memsz_sect = ( ( _prefix_memsz + 511 ) / 512 ); + _text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 ); + _data16_memsz_pgh = ( ( _data16_memsz + 15 ) / 16 ); /* - * Load sizes in paragraphs and sectors. Note that wherever the - * _load_size variables are used, there must be a corresponding + * File size in paragraphs and sectors. Note that wherever the + * _filesz variables are used, there must be a corresponding * .zinfo.fixup section. + * */ - _load_size_pgh = ( ( _load_size + 15 ) / 16 ); - _load_size_sect = ( ( _load_size + 511 ) / 512 ); + _filesz_pgh = ( ( _filesz + 15 ) / 16 ); + _filesz_sect = ( ( _filesz + 511 ) / 512 ); } diff --git a/gpxe/src/arch/i386/transitions/librm.S b/gpxe/src/arch/i386/transitions/librm.S index ff4b1d97..7e9fd45d 100644..100755 --- a/gpxe/src/arch/i386/transitions/librm.S +++ b/gpxe/src/arch/i386/transitions/librm.S @@ -203,8 +203,8 @@ real_to_prot: /* Switch to protected mode */ cli - data32 lgdt gdtr - data32 lidt idtr + data32 lgdt gdtr + data32 lidt idtr movl %cr0, %eax orb $CR0_PE, %al movl %eax, %cr0 @@ -316,7 +316,7 @@ p2r_jump_target: movl %edx, %esp /* Reset IDTR to the real-mode defaults */ - lidt rm_idtr + data32 lidt rm_idtr /* Return to real-mode address */ data32 ret @@ -424,8 +424,8 @@ prot_call: 1: /* Reload GDT and IDT, restore registers and flags and return */ movw %sp, %bp - lgdt (%bp) - lidt 8(%bp) + data32 lgdt (%bp) + data32 lidt 8(%bp) addw $20, %sp /* also skip %cs and %ss */ popw %ds popw %es diff --git a/gpxe/src/arch/i386/transitions/librm_mgmt.c b/gpxe/src/arch/i386/transitions/librm_mgmt.c index 59b2eabc..50569f8e 100644..100755 --- a/gpxe/src/arch/i386/transitions/librm_mgmt.c +++ b/gpxe/src/arch/i386/transitions/librm_mgmt.c @@ -1,45 +1,56 @@ -/*
- * librm: a library for interfacing to real-mode code
- *
- * Michael Brown <mbrown@fensystems.co.uk>
- *
- */
-
-#include <stdint.h>
-#include <librm.h>
-
-/*
- * This file provides functions for managing librm.
- *
- */
-
-/**
- * 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;
-};
+/* + * librm: a library for interfacing to real-mode code + * + * Michael Brown <mbrown@fensystems.co.uk> + * + */ + +#include <stdint.h> +#include <realmode.h> + +/* + * This file provides functions for managing librm. + * + */ + +/** + * 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; +}; + +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 ); |