[bios] Add bin-x86_64-pcbios build platform

Move most arch/i386 files to arch/x86, and adjust the contents of the
Makefiles and the include/bits/*.h headers to reflect the new
locations.

This patch makes no substantive code changes, as can be seen using a
rename-aware diff (e.g. "git show -M5").

This patch does not make the pcbios platform functional for x86_64; it
merely allows it to compile without errors.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2016-02-16 15:19:01 +00:00
parent 43515f9f1a
commit f468f12b1e
155 changed files with 198 additions and 240 deletions

View File

@@ -80,20 +80,11 @@ PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie')
WORKAROUND_CFLAGS += $(PIE_FLAGS)
endif
# Define version string for lkrnprefix.S
#
CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\""
# i386-specific directories containing source files
#
SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
SRCDIRS += arch/i386/firmware/pcbios
SRCDIRS += arch/i386/core
SRCDIRS += arch/i386/image
SRCDIRS += arch/i386/interface/pcbios
SRCDIRS += arch/i386/interface/pxe
SRCDIRS += arch/i386/interface/pxeparent
SRCDIRS += arch/i386/interface/syslinux
SRCDIRS += arch/i386/hci/commands
SRCDIRS += arch/i386/interface/syslinux
# Include common x86 Makefile
#

View File

@@ -1,123 +1,6 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# The i386 linker script
# Include generic BIOS Makefile
#
LDSCRIPT = arch/i386/scripts/i386.lds
# Stop ld from complaining about our customised linker script
#
LDFLAGS += -N --no-check-sections
# pcbios specific drivers
SRCDIRS += arch/i386/drivers
SRCDIRS += arch/i386/drivers/net
# Media types.
#
MEDIA += rom
MEDIA += mrom
MEDIA += pcirom
MEDIA += isarom
MEDIA += pxe
MEDIA += kpxe
MEDIA += kkpxe
MEDIA += kkkpxe
MEDIA += lkrn
MEDIA += dsk
MEDIA += nbi
MEDIA += hd
MEDIA += raw
MEDIA += exe
# Padding rules
#
PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff
PAD_mrom = $(PAD_rom)
PAD_pcirom = $(PAD_rom)
PAD_isarom = $(PAD_rom)
PAD_dsk = $(PERL) $(PADIMG) --blksize=512
PAD_hd = $(PERL) $(PADIMG) --blksize=32768
PAD_exe = $(PERL) $(PADIMG) --blksize=512
# Finalisation rules
#
FINALISE_rom = $(PERL) $(FIXROM)
FINALISE_mrom = $(FINALISE_rom)
FINALISE_pcirom = $(FINALISE_rom)
FINALISE_isarom = $(FINALISE_rom)
# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc.
#
LIST_NAME_rom := ROMS
LIST_NAME_mrom := ROMS
LIST_NAME_pcirom := ROMS
LIST_NAME_isarom := ROMS
# Locations of isolinux files
#
SYSLINUX_DIR_LIST := \
/usr/lib/syslinux \
/usr/lib/syslinux/bios \
/usr/lib/syslinux/modules/bios \
/usr/share/syslinux \
/usr/share/syslinux/bios \
/usr/share/syslinux/modules/bios \
/usr/local/share/syslinux \
/usr/local/share/syslinux/bios \
/usr/local/share/syslinux/modules/bios \
/usr/lib/ISOLINUX
ISOLINUX_BIN_LIST := \
$(ISOLINUX_BIN) \
$(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST))
LDLINUX_C32_LIST := \
$(LDLINUX_C32) \
$(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST))
ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST)))
LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST)))
# 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) LDLINUX_C32=$(LDLINUX_C32) \
VERSION="$(VERSION)" bash util/geniso -o $@ $<
# rule to make a floppy emulation ISO boot image
NON_AUTO_MEDIA += liso
%liso: %lkrn util/geniso
$(QM)$(ECHO) " [GENISO] $@"
$(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $<
# rule to make a syslinux floppy image (mountable, bootable)
NON_AUTO_MEDIA += sdsk
%sdsk: %lkrn util/gensdsk
$(QM)$(ECHO) " [GENSDSK] $@"
$(Q)bash util/gensdsk $@ $<
# rule to write disk images to /dev/fd0
NON_AUTO_MEDIA += fd0
%fd0 : %dsk
$(QM)$(ECHO) " [DD] $@"
$(Q)dd if=$< bs=512 conv=sync of=/dev/fd0
$(Q)sync
# Special target for building Master Boot Record binary
$(BIN)/mbr.bin : $(BIN)/mbr.o
$(QM)$(ECHO) " [OBJCOPY] $@"
$(Q)$(OBJCOPY) -O binary $< $@
# rule to make a USB disk image
$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o
$(QM)$(ECHO) " [OBJCOPY] $@"
$(Q)$(OBJCOPY) -O binary $< $@
NON_AUTO_MEDIA += usb
%usb: $(BIN)/usbdisk.bin %hd
$(QM)$(ECHO) " [FINISH] $@"
$(Q)cat $^ > $@
# Padded floppy image (e.g. for iLO)
NON_AUTO_MEDIA += pdsk
%pdsk : %dsk
$(Q)cp $< $@
$(Q)$(PADIMG) --blksize=1474560 $@
MAKEDEPS += arch/x86/Makefile.pcbios
include arch/x86/Makefile.pcbios

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Packet buffer in base memory. Used by various components which
* need to pass packets to and from external real-mode code.
*
*/
#include <basemem_packet.h>
#undef basemem_packet
char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );

View File

@@ -1,178 +0,0 @@
/*
* Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <realmode.h>
#include <pxe_api.h>
/** @file
*
* Cached DHCP packet
*
*/
/** Cached DHCPACK physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cached_dhcpack_phys );
#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
/** Colour for debug messages */
#define colour &cached_dhcpack_phys
/** Cached DHCPACK */
static struct dhcp_packet *cached_dhcpack;
/**
* Cached DHCPACK startup function
*
*/
static void cachedhcp_init ( void ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
size_t max_len;
size_t len;
/* Do nothing if no cached DHCPACK is present */
if ( ! cached_dhcpack_phys ) {
DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
return;
}
/* No reliable way to determine length before parsing packet;
* start by assuming maximum length permitted by PXE.
*/
max_len = sizeof ( BOOTPLAYER_t );
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
return;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
max_len );
dhcppkt_init ( dhcppkt, dhcphdr, max_len );
/* Shrink packet to required length. If reallocation fails,
* just continue to use the original packet and waste the
* unused space.
*/
len = dhcppkt_len ( dhcppkt );
assert ( len <= max_len );
tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
if ( tmp )
dhcppkt = tmp;
/* Reinitialise packet at new address */
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
dhcppkt_init ( dhcppkt, dhcphdr, len );
/* Store as cached DHCPACK, and mark original copy as consumed */
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
cached_dhcpack_phys, len );
cached_dhcpack = dhcppkt;
cached_dhcpack_phys = 0;
}
/**
* Cached DHCPACK startup function
*
*/
static void cachedhcp_startup ( void ) {
/* If cached DHCP packet was not claimed by any network device
* during startup, then free it.
*/
if ( cached_dhcpack ) {
DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
}
}
/** Cached DHCPACK initialisation function */
struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = cachedhcp_init,
};
/** Cached DHCPACK startup function */
struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
.startup = cachedhcp_startup,
};
/**
* Apply cached DHCPACK to network device, if applicable
*
* @v netdev Network device
* @ret rc Return status code
*/
static int cachedhcp_probe ( struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Do nothing unless we have a cached DHCPACK */
if ( ! cached_dhcpack )
return 0;
/* Do nothing unless cached DHCPACK's MAC address matches this
* network device.
*/
if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
ll_protocol->ll_addr_len ) != 0 ) {
DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
netdev->name );
return 0;
}
DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
/* Register as DHCP settings for this network device */
if ( ( rc = register_settings ( &cached_dhcpack->settings,
netdev_settings ( netdev ),
DHCP_SETTINGS_NAME ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
strerror ( rc ) );
return rc;
}
/* Claim cached DHCPACK */
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
return 0;
}
/** Cached DHCP packet network device driver */
struct net_driver cachedhcp_driver __net_driver = {
.name = "cachedhcp",
.probe = cachedhcp_probe,
};

View File

@@ -1,23 +0,0 @@
#include <stdio.h>
#include <realmode.h>
void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) {
__asm__ __volatile__ (
TEXT16_CODE ( ".globl dump_regs\n\t"
"\ndump_regs:\n\t"
"pushl $_dump_regs\n\t"
"pushw %%cs\n\t"
"call prot_call\n\t"
"addr32 leal 4(%%esp), %%esp\n\t"
"ret\n\t" ) : : );
printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n",
ix86->regs.eax, ix86->regs.ebx, ix86->regs.ecx,
ix86->regs.edx, ix86->regs.esi, ix86->regs.edi,
ix86->regs.ebp, ix86->regs.esp,
ix86->segs.cs, ix86->segs.ss, ix86->segs.ds,
ix86->segs.es, ix86->segs.fs, ix86->segs.gs );
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) 2009 H. Peter Anvin <hpa@zytor.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arch i386
.code16
/****************************************************************************
* Set/clear CF on the stack as appropriate, assumes stack is as it should
* be immediately before IRET
****************************************************************************
*/
.section ".text16", "ax", @progbits
.globl patch_cf
patch_cf:
pushw %bp
movw %sp, %bp
setc 8(%bp) /* Set/reset CF; clears PF, AF, ZF, SF */
popw %bp
ret
.size patch_cf, . - patch_cf

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2014 Red Hat Inc.
* Alex Williamson <alex.williamson@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/device.h>
#include <ipxe/init.h>
#include <realmode.h>
#include <usr/autoboot.h>
uint16_t __bss16 ( autoboot_busdevfn );
#define autoboot_busdevfn __use_data16 ( autoboot_busdevfn )
/**
* Initialise PCI autoboot device
*/
static void pci_autoboot_init ( void ) {
if ( autoboot_busdevfn )
set_autoboot_busloc ( BUS_TYPE_PCI, autoboot_busdevfn );
}
/** PCI autoboot device initialisation function */
struct init_fn pci_autoboot_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = pci_autoboot_init,
};

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* RDTSC timer
*
*/
#include <assert.h>
#include <ipxe/timer.h>
#include <ipxe/pit8254.h>
/**
* Number of TSC ticks per microsecond
*
* This is calibrated on the first use of the timer.
*/
static unsigned long rdtsc_ticks_per_usec;
/**
* Delay for a fixed number of microseconds
*
* @v usecs Number of microseconds for which to delay
*/
static void rdtsc_udelay ( unsigned long usecs ) {
unsigned long start;
unsigned long elapsed;
/* Sanity guard, since we may divide by this */
if ( ! usecs )
usecs = 1;
start = currticks();
if ( rdtsc_ticks_per_usec ) {
/* Already calibrated; busy-wait until done */
do {
elapsed = ( currticks() - start );
} while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) );
} else {
/* Not yet calibrated; use 8254 PIT and calibrate
* based on result.
*/
pit8254_udelay ( usecs );
elapsed = ( currticks() - start );
rdtsc_ticks_per_usec = ( elapsed / usecs );
DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs "
"(%ld MHz)\n", elapsed, usecs,
( rdtsc_ticks_per_usec << TSC_SHIFT ) );
}
}
/**
* Get number of ticks per second
*
* @ret ticks_per_sec Number of ticks per second
*/
static unsigned long rdtsc_ticks_per_sec ( void ) {
/* Calibrate timer, if not already done */
if ( ! rdtsc_ticks_per_usec )
udelay ( 1 );
/* Sanity check */
assert ( rdtsc_ticks_per_usec != 0 );
return ( rdtsc_ticks_per_usec * 1000 * 1000 );
}
PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay );
PROVIDE_TIMER_INLINE ( rdtsc, currticks );
PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec );

View File

@@ -1,138 +0,0 @@
#include <ipxe/io.h>
#include <registers.h>
/*
* Originally by Eric Biederman
*
* Heavily modified by Michael Brown
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/*
* The linker passes in the symbol _max_align, which is the alignment
* that we must preserve, in bytes.
*
*/
extern char _max_align[];
#define max_align ( ( size_t ) _max_align )
/* Linker symbols */
extern char _textdata[];
extern char _etextdata[];
/* within 1MB of 4GB is too close.
* MAX_ADDR is the maximum address we can easily do DMA to.
*
* Not sure where this constraint comes from, but kept it from Eric's
* old code - mcb30
*/
#define MAX_ADDR (0xfff00000UL)
/**
* Relocate iPXE
*
* @v ebp Maximum address to use for relocation
* @ret esi Current physical address
* @ret edi New physical address
* @ret ecx Length to copy
*
* This finds a suitable location for iPXE near the top of 32-bit
* address space, and returns the physical address of the new location
* to the prefix in %edi.
*/
__asmcall void relocate ( struct i386_all_regs *ix86 ) {
struct memory_map memmap;
uint32_t start, end, size, padded_size, max;
uint32_t new_start, new_end;
unsigned i;
/* Get memory map and current location */
get_memmap ( &memmap );
start = virt_to_phys ( _textdata );
end = virt_to_phys ( _etextdata );
size = ( end - start );
padded_size = ( size + max_align - 1 );
DBG ( "Relocate: currently at [%x,%x)\n"
"...need %x bytes for %zd-byte alignment\n",
start, end, padded_size, max_align );
/* Determine maximum usable address */
max = MAX_ADDR;
if ( ix86->regs.ebp < max ) {
max = ix86->regs.ebp;
DBG ( "Limiting relocation to [0,%x)\n", max );
}
/* Walk through the memory map and find the highest address
* below 4GB that iPXE will fit into.
*/
new_end = end;
for ( i = 0 ; i < memmap.count ; i++ ) {
struct memory_region *region = &memmap.regions[i];
uint32_t r_start, r_end;
DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
/* Truncate block to maximum address. This will be
* less than 4GB, which means that we can get away
* with using just 32-bit arithmetic after this stage.
*/
if ( region->start > max ) {
DBG ( "...starts after max=%x\n", max );
continue;
}
r_start = region->start;
if ( region->end > max ) {
DBG ( "...end truncated to max=%x\n", max );
r_end = max;
} else {
r_end = region->end;
}
DBG ( "...usable portion is [%x,%x)\n", r_start, r_end );
/* If we have rounded down r_end below r_ start, skip
* this block.
*/
if ( r_end < r_start ) {
DBG ( "...truncated to negative size\n" );
continue;
}
/* Check that there is enough space to fit in iPXE */
if ( ( r_end - r_start ) < size ) {
DBG ( "...too small (need %x bytes)\n", size );
continue;
}
/* If the start address of the iPXE we would
* place in this block is higher than the end address
* of the current highest block, use this block.
*
* Note that this avoids overlaps with the current
* iPXE, as well as choosing the highest of all viable
* blocks.
*/
if ( ( r_end - size ) > new_end ) {
new_end = r_end;
DBG ( "...new best block found.\n" );
}
}
/* Calculate new location of iPXE, and align it to the
* required alignemnt.
*/
new_start = new_end - padded_size;
new_start += ( start - new_start ) & ( max_align - 1 );
new_end = new_start + size;
DBG ( "Relocating from [%x,%x) to [%x,%x)\n",
start, end, new_start, new_end );
/* Let prefix know what to copy */
ix86->regs.esi = start;
ix86->regs.edi = new_start;
ix86->regs.ecx = size;
}

View File

@@ -1,269 +0,0 @@
/*
* Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Command line and initrd passed to iPXE at runtime
*
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/init.h>
#include <ipxe/image.h>
#include <ipxe/script.h>
#include <ipxe/umalloc.h>
#include <realmode.h>
/** Command line physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cmdline_phys );
#define cmdline_phys __use_data16 ( cmdline_phys )
/** initrd physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( initrd_phys );
#define initrd_phys __use_data16 ( initrd_phys )
/** initrd length
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( initrd_len );
#define initrd_len __use_data16 ( initrd_len )
/** Internal copy of the command line */
static char *cmdline_copy;
/** Free command line image */
static void cmdline_image_free ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt );
DBGC ( image, "RUNTIME freeing command line\n" );
free ( cmdline_copy );
}
/** Embedded script representing the command line */
static struct image cmdline_image = {
.refcnt = REF_INIT ( cmdline_image_free ),
.name = "<CMDLINE>",
.type = &script_image_type,
};
/** Colour for debug messages */
#define colour &cmdline_image
/**
* Strip unwanted cruft from command line
*
* @v cmdline Command line
* @v cruft Initial substring of cruft to strip
*/
static void cmdline_strip ( char *cmdline, const char *cruft ) {
char *strip;
char *strip_end;
/* Find unwanted cruft, if present */
if ( ! ( strip = strstr ( cmdline, cruft ) ) )
return;
/* Strip unwanted cruft */
strip_end = strchr ( strip, ' ' );
if ( strip_end ) {
*strip_end = '\0';
DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
strcpy ( strip, ( strip_end + 1 ) );
} else {
DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
*strip = '\0';
}
}
/**
* Initialise command line
*
* @ret rc Return status code
*/
static int cmdline_init ( void ) {
userptr_t cmdline_user;
char *cmdline;
size_t len;
int rc;
/* Do nothing if no command line was specified */
if ( ! cmdline_phys ) {
DBGC ( colour, "RUNTIME found no command line\n" );
return 0;
}
cmdline_user = phys_to_user ( cmdline_phys );
len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
/* Allocate and copy command line */
cmdline_copy = malloc ( len );
if ( ! cmdline_copy ) {
DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
"command line\n", len );
rc = -ENOMEM;
goto err_alloc_cmdline_copy;
}
cmdline = cmdline_copy;
copy_from_user ( cmdline, cmdline_user, 0, len );
DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
cmdline, cmdline_phys );
/* Mark command line as consumed */
cmdline_phys = 0;
/* Strip unwanted cruft from the command line */
cmdline_strip ( cmdline, "BOOT_IMAGE=" );
cmdline_strip ( cmdline, "initrd=" );
while ( isspace ( *cmdline ) )
cmdline++;
DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );
/* Prepare and register image */
cmdline_image.data = virt_to_user ( cmdline );
cmdline_image.len = strlen ( cmdline );
if ( cmdline_image.len ) {
if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not register command "
"line: %s\n", strerror ( rc ) );
goto err_register_image;
}
}
/* Drop our reference to the image */
image_put ( &cmdline_image );
return 0;
err_register_image:
image_put ( &cmdline_image );
err_alloc_cmdline_copy:
return rc;
}
/**
* Initialise initrd
*
* @ret rc Return status code
*/
static int initrd_init ( void ) {
struct image *image;
int rc;
/* Do nothing if no initrd was specified */
if ( ! initrd_phys ) {
DBGC ( colour, "RUNTIME found no initrd\n" );
return 0;
}
if ( ! initrd_len ) {
DBGC ( colour, "RUNTIME found empty initrd\n" );
return 0;
}
DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n",
initrd_phys, ( initrd_phys + initrd_len ) );
/* Allocate image */
image = alloc_image ( NULL );
if ( ! image ) {
DBGC ( colour, "RUNTIME could not allocate image for "
"initrd\n" );
rc = -ENOMEM;
goto err_alloc_image;
}
if ( ( rc = image_set_name ( image, "<INITRD>" ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not set image name: %s\n",
strerror ( rc ) );
goto err_set_name;
}
/* Allocate and copy initrd content */
image->data = umalloc ( initrd_len );
if ( ! image->data ) {
DBGC ( colour, "RUNTIME could not allocate %d bytes for "
"initrd\n", initrd_len );
rc = -ENOMEM;
goto err_umalloc;
}
image->len = initrd_len;
memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
initrd_len );
/* Mark initrd as consumed */
initrd_phys = 0;
/* Register image */
if ( ( rc = register_image ( image ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not register initrd: %s\n",
strerror ( rc ) );
goto err_register_image;
}
/* Drop our reference to the image */
image_put ( image );
return 0;
err_register_image:
err_umalloc:
err_set_name:
image_put ( image );
err_alloc_image:
return rc;
}
/**
* Initialise command line and initrd
*
*/
static void runtime_init ( void ) {
int rc;
/* Initialise command line */
if ( ( rc = cmdline_init() ) != 0 ) {
/* No way to report failure */
return;
}
/* Initialise initrd */
if ( ( rc = initrd_init() ) != 0 ) {
/* No way to report failure */
return;
}
}
/** Command line and initrd initialisation function */
struct startup_fn runtime_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.startup = runtime_init,
};

View File

@@ -1,15 +0,0 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.arch i386
/****************************************************************************
* Internal stack
****************************************************************************
*/
.section ".stack", "aw", @nobits
.align 8
.globl _stack
_stack:
.space 4096
.globl _estack
_estack:

View File

@@ -1,15 +0,0 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.arch i386
/****************************************************************************
* Internal stack
****************************************************************************
*/
.section ".stack16", "aw", @nobits
.align 8
.globl _stack16
_stack16:
.space 4096
.globl _estack16
_estack16:

View File

@@ -1,113 +0,0 @@
/*
*
* modified from linuxbios code
* by Cai Qiang <rimy2000@hotmail.com>
*
*/
#include "stddef.h"
#include "string.h"
#include <ipxe/io.h>
#include <ipxe/console.h>
#include <ipxe/init.h>
#include "vga.h"
#include <config/console.h>
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_DIRECT_VGA ) && \
CONSOLE_EXPLICIT ( CONSOLE_DIRECT_VGA ) )
#undef CONSOLE_DIRECT_VGA
#define CONSOLE_DIRECT_VGA ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
struct console_driver vga_console __console_driver;
static char *vidmem; /* The video buffer */
static int video_line, video_col;
#define VIDBUFFER 0xB8000
static void memsetw(void *s, int c, unsigned int n)
{
unsigned int i;
u16 *ss = (u16 *) s;
for (i = 0; i < n; i++) {
ss[i] = ( u16 ) c;
}
}
static void video_init(void)
{
static int inited=0;
vidmem = (char *)phys_to_virt(VIDBUFFER);
if (!inited) {
video_line = 0;
video_col = 0;
memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
inited=1;
}
}
static void video_scroll(void)
{
int i;
memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
vidmem[i] = ' ';
}
static void vga_putc(int byte)
{
if (byte == '\n') {
video_line++;
video_col = 0;
} else if (byte == '\r') {
video_col = 0;
} else if (byte == '\b') {
video_col--;
} else if (byte == '\t') {
video_col += 4;
} else if (byte == '\a') {
//beep
//beep(500);
} else {
vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
video_col++;
}
if (video_col < 0) {
video_col = 0;
}
if (video_col >= COLS) {
video_line++;
video_col = 0;
}
if (video_line >= LINES) {
video_scroll();
video_line--;
}
// move the cursor
write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
}
struct console_driver vga_console __console_driver = {
.putchar = vga_putc,
.disabled = CONSOLE_DISABLED,
.usage = CONSOLE_DIRECT_VGA,
};
struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = {
.initialise = video_init,
};

View File

@@ -1,145 +0,0 @@
/*
* Functions to support the virtual addressing method of relocation
* that Etherboot uses.
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
#include "librm.h"
.arch i386
.text
.code32
/****************************************************************************
* _virt_to_phys (virtual addressing)
*
* Switch from virtual to flat physical addresses. %esp is adjusted
* to a physical value. Segment registers are set to flat physical
* selectors. All other registers are preserved. Flags are
* preserved.
*
* Parameters: none
* Returns: none
****************************************************************************
*/
.globl _virt_to_phys
_virt_to_phys:
/* Preserve registers and flags */
pushfl
pushl %eax
pushl %ebp
/* Change return address to a physical address */
movl virt_offset, %ebp
addl %ebp, 12(%esp)
/* Switch to physical code segment */
cli
pushl $PHYSICAL_CS
leal 1f(%ebp), %eax
pushl %eax
lret
1:
/* Reload other segment registers and adjust %esp */
movl $PHYSICAL_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
addl %ebp, %esp
/* Restore registers and flags, and return */
popl %ebp
popl %eax
popfl
ret
/****************************************************************************
* _phys_to_virt (flat physical addressing)
*
* Switch from flat physical to virtual addresses. %esp is adjusted
* to a virtual value. Segment registers are set to virtual
* selectors. All other registers are preserved. Flags are
* preserved.
*
* Parameters: none
* Returns: none
****************************************************************************
*/
.globl _phys_to_virt
_phys_to_virt:
/* Preserve registers and flags */
pushfl
pushl %eax
pushl %ebp
/* Switch to virtual code segment */
cli
ljmp $VIRTUAL_CS, $1f
1:
/* Reload data segment registers */
movl $VIRTUAL_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
/* Reload stack segment and adjust %esp */
movl virt_offset, %ebp
movl %eax, %ss
subl %ebp, %esp
/* Change the return address to a virtual address */
subl %ebp, 12(%esp)
/* Restore registers and flags, and return */
popl %ebp
popl %eax
popfl
ret
/****************************************************************************
* _intr_to_virt (virtual code segment, virtual or physical stack segment)
*
* Switch from virtual code segment with either a virtual or physical
* stack segment to using virtual addressing. %esp is adjusted if
* necessary to a virtual value. Segment registers are set to virtual
* selectors. All other registers are preserved. Flags are
* preserved.
*
* Parameters: none
* Returns: none
****************************************************************************
*/
.globl _intr_to_virt
_intr_to_virt:
/* Preserve registers and flags */
pushfl
pushl %eax
pushl %ebp
/* Check whether stack segment is physical or virtual */
movl %ss, %eax
cmpw $VIRTUAL_DS, %ax
movl $VIRTUAL_DS, %eax
/* Reload data segment registers */
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
/* Reload stack segment and adjust %esp if necessary */
je 1f
movl virt_offset, %ebp
movl %eax, %ss
subl %ebp, %esp
1:
/* Restore registers and flags, and return */
popl %ebp
popl %eax
popfl
ret

View File

@@ -1,146 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undirom.h>
#include <undiload.h>
#include <undinet.h>
#include <undipreload.h>
/** @file
*
* UNDI PCI driver
*
*/
/**
* Find UNDI ROM for PCI device
*
* @v pci PCI device
* @ret undirom UNDI ROM, or NULL
*
* Try to find a driver for this device. Try an exact match on the
* ROM address first, then fall back to a vendor/device ID match only
*/
static struct undi_rom * undipci_find_rom ( struct pci_device *pci ) {
struct undi_rom *undirom;
unsigned long rombase;
rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS );
undirom = undirom_find_pci ( pci->vendor, pci->device, rombase );
if ( ! undirom )
undirom = undirom_find_pci ( pci->vendor, pci->device, 0 );
return undirom;
}
/**
* Probe PCI device
*
* @v pci PCI device
* @v id PCI ID
* @ret rc Return status code
*/
static int undipci_probe ( struct pci_device *pci ) {
struct undi_device *undi;
struct undi_rom *undirom;
int rc;
/* Allocate UNDI device structure */
undi = zalloc ( sizeof ( *undi ) );
if ( ! undi )
return -ENOMEM;
pci_set_drvdata ( pci, undi );
/* Find/create our pixie */
if ( preloaded_undi.pci_busdevfn == pci->busdevfn ) {
/* Claim preloaded UNDI device */
DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi );
memcpy ( undi, &preloaded_undi, sizeof ( *undi ) );
memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) );
} else {
/* Find UNDI ROM for PCI device */
if ( ! ( undirom = undipci_find_rom ( pci ) ) ) {
rc = -ENODEV;
goto err_find_rom;
}
/* Call UNDI ROM loader to create pixie */
if ( ( rc = undi_load_pci ( undi, undirom,
pci->busdevfn ) ) != 0 ) {
goto err_load_pci;
}
}
/* Add to device hierarchy */
snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
"UNDI-%s", pci->dev.name );
memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) );
undi->dev.parent = &pci->dev;
INIT_LIST_HEAD ( &undi->dev.children );
list_add ( &undi->dev.siblings, &pci->dev.children );
/* Create network device */
if ( ( rc = undinet_probe ( undi ) ) != 0 )
goto err_undinet_probe;
return 0;
err_undinet_probe:
undi_unload ( undi );
list_del ( &undi->dev.siblings );
err_find_rom:
err_load_pci:
free ( undi );
pci_set_drvdata ( pci, NULL );
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void undipci_remove ( struct pci_device *pci ) {
struct undi_device *undi = pci_get_drvdata ( pci );
undinet_remove ( undi );
undi_unload ( undi );
list_del ( &undi->dev.siblings );
free ( undi );
pci_set_drvdata ( pci, NULL );
}
static struct pci_device_id undipci_nics[] = {
PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ),
};
struct pci_driver undipci_driver __pci_driver_fallback = {
.ids = undipci_nics,
.id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ),
.class = PCI_CLASS_ID ( PCI_CLASS_NETWORK, PCI_ANY_ID, PCI_ANY_ID ),
.probe = undipci_probe,
.remove = undipci_remove,
};

View File

@@ -1,87 +0,0 @@
FILE_LICENCE ( GPL2_OR_LATER )
#define PXENV_UNDI_ISR 0x0014
#define PXENV_UNDI_ISR_IN_START 1
#define PXENV_UNDI_ISR_OUT_OURS 0
#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
#define IRQ_PIC_CUTOFF 8
#define ICR_EOI_NON_SPECIFIC 0x20
#define PIC1_ICR 0x20
#define PIC2_ICR 0xa0
.text
.arch i386
.code16
.section ".text16", "ax", @progbits
.globl undiisr
undiisr:
/* Preserve registers */
pushw %ds
pushw %es
pushw %fs
pushw %gs
pushfl
pushal
/* Set up our segment registers */
movw %cs:rm_ds, %ax
movw %ax, %ds
/* Check that we have an UNDI entry point */
cmpw $0, pxeparent_entry_point
je chain
/* Issue UNDI API call */
movw %ax, %es
movw $undinet_params, %di
movw $PXENV_UNDI_ISR, %bx
movw $PXENV_UNDI_ISR_IN_START, funcflag
pushw %es
pushw %di
pushw %bx
lcall *pxeparent_entry_point
cli /* Just in case */
addw $6, %sp
cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag
jne eoi
trig: /* Record interrupt occurence */
incb undiisr_trigger_count
eoi: /* Send EOI */
movb $ICR_EOI_NON_SPECIFIC, %al
cmpb $IRQ_PIC_CUTOFF, undiisr_irq
jb 1f
outb %al, $PIC2_ICR
1: outb %al, $PIC1_ICR
jmp exit
chain: /* Chain to next handler */
pushfw
lcall *undiisr_next_handler
exit: /* Restore registers and return */
cli
popal
movzwl %sp, %esp
addr32 movl -20(%esp), %esp /* %esp isn't restored by popal */
popfl
popw %gs
popw %fs
popw %es
popw %ds
iret
.section ".data16", "aw", @progbits
undinet_params:
status: .word 0
funcflag: .word 0
bufferlength: .word 0
framelength: .word 0
frameheaderlength: .word 0
frame: .word 0, 0
prottype: .byte 0
pkttype: .byte 0

View File

@@ -1,184 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pxe.h>
#include <realmode.h>
#include <bios.h>
#include <pnpbios.h>
#include <basemem.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undirom.h>
#include <undiload.h>
/** @file
*
* UNDI load/unload
*
*/
/* Disambiguate the various error causes */
#define EINFO_EUNDILOAD \
__einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
"UNDI loader error" )
#define EUNDILOAD( status ) EPLATFORM ( EINFO_EUNDILOAD, status )
/** Parameter block for calling UNDI loader */
static struct s_UNDI_LOADER __bss16 ( undi_loader );
#define undi_loader __use_data16 ( undi_loader )
/** UNDI loader entry point */
static SEGOFF16_t __bss16 ( undi_loader_entry );
#define undi_loader_entry __use_data16 ( undi_loader_entry )
/**
* Call UNDI loader to create a pixie
*
* @v undi UNDI device
* @v undirom UNDI ROM
* @ret rc Return status code
*/
int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
struct s_PXE ppxe;
unsigned int fbms_seg;
uint16_t exit;
int rc;
/* Only one UNDI instance may be loaded at any given time */
if ( undi_loader_entry.segment ) {
DBG ( "UNDI %p cannot load multiple instances\n", undi );
return -EBUSY;
}
/* Set up START_UNDI parameters */
memset ( &undi_loader, 0, sizeof ( undi_loader ) );
undi_loader.AX = undi->pci_busdevfn;
undi_loader.BX = undi->isapnp_csn;
undi_loader.DX = undi->isapnp_read_port;
undi_loader.ES = BIOS_SEG;
undi_loader.DI = find_pnp_bios();
/* Allocate base memory for PXE stack */
undi->restore_fbms = get_fbms();
fbms_seg = ( undi->restore_fbms << 6 );
fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 );
undi_loader.UNDI_CS = fbms_seg;
fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 );
undi_loader.UNDI_DS = fbms_seg;
/* Debug info */
DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ",
undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS );
if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
unsigned int bus = ( undi->pci_busdevfn >> 8 );
unsigned int devfn = ( undi->pci_busdevfn & 0xff );
DBGC ( undi, "PCI %02x:%02x.%x\n",
bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) );
}
if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
DBGC ( undi, "ISAPnP(%04x) CSN %04x\n",
undi->isapnp_read_port, undi->isapnp_csn );
}
/* Call loader */
undi_loader_entry = undirom->loader_entry;
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%ds\n\t"
"pushw %%ax\n\t"
"lcall *undi_loader_entry\n\t"
"popl %%ebp\n\t" /* discard */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( exit )
: "a" ( __from_data16 ( &undi_loader ) )
: "ebx", "ecx", "edx", "esi", "edi" );
if ( exit != PXENV_EXIT_SUCCESS ) {
/* Clear entry point */
memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
rc = -EUNDILOAD ( undi_loader.Status );
DBGC ( undi, "UNDI %p loader failed: %s\n",
undi, strerror ( rc ) );
return rc;
}
/* Populate PXE device structure */
undi->pxenv = undi_loader.PXENVptr;
undi->ppxe = undi_loader.PXEptr;
copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset,
sizeof ( ppxe ) );
undi->entry = ppxe.EntryPointSP;
DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x "
"entry %04x:%04x\n", undi, undi->pxenv.segment,
undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset,
undi->entry.segment, undi->entry.offset );
/* Update free base memory counter */
undi->fbms = ( fbms_seg >> 6 );
set_fbms ( undi->fbms );
DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
return 0;
}
/**
* Unload a pixie
*
* @v undi UNDI device
* @ret rc Return status code
*
* Erases the PXENV+ and !PXE signatures, and frees the used base
* memory (if possible).
*/
int undi_unload ( struct undi_device *undi ) {
static uint32_t dead = 0xdeaddead;
DBGC ( undi, "UNDI %p unloading\n", undi );
/* Clear entry point */
memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
/* Erase signatures */
if ( undi->pxenv.segment )
put_real ( dead, undi->pxenv.segment, undi->pxenv.offset );
if ( undi->ppxe.segment )
put_real ( dead, undi->ppxe.segment, undi->ppxe.offset );
/* Free base memory, if possible */
if ( undi->fbms == get_fbms() ) {
DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
set_fbms ( undi->restore_fbms );
return 0;
} else {
DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
return -EBUSY;
}
}

View File

@@ -1,820 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <unistd.h>
#include <byteswap.h>
#include <pxe.h>
#include <realmode.h>
#include <pic8259.h>
#include <biosint.h>
#include <pnpbios.h>
#include <basemem_packet.h>
#include <ipxe/io.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/if_ether.h>
#include <ipxe/ethernet.h>
#include <ipxe/profile.h>
#include <undi.h>
#include <undinet.h>
#include <pxeparent.h>
/** @file
*
* UNDI network device driver
*
*/
/** An UNDI NIC */
struct undi_nic {
/** Device supports IRQs */
int irq_supported;
/** Assigned IRQ number */
unsigned int irq;
/** Currently processing ISR */
int isr_processing;
/** Bug workarounds */
int hacks;
};
/**
* @defgroup undi_hacks UNDI workarounds
* @{
*/
/** Work around Etherboot 5.4 bugs */
#define UNDI_HACK_EB54 0x0001
/** @} */
/** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
#define UNDI_INITIALIZE_RETRY_MAX 10
/** Delay between retries of PXENV_UNDI_INITIALIZE */
#define UNDI_INITIALIZE_RETRY_DELAY_MS 200
/** Maximum number of received packets per poll */
#define UNDI_RX_QUOTA 4
/** Alignment of received frame payload */
#define UNDI_RX_ALIGN 16
static void undinet_close ( struct net_device *netdev );
/** Address of UNDI entry point */
static SEGOFF16_t undinet_entry;
/** Transmit profiler */
static struct profiler undinet_tx_profiler __profiler =
{ .name = "undinet.tx" };
/** Transmit call profiler */
static struct profiler undinet_tx_call_profiler __profiler =
{ .name = "undinet.tx_call" };
/** IRQ profiler */
static struct profiler undinet_irq_profiler __profiler =
{ .name = "undinet.irq" };
/** ISR call profiler */
static struct profiler undinet_isr_call_profiler __profiler =
{ .name = "undinet.isr_call" };
/** Receive profiler */
static struct profiler undinet_rx_profiler __profiler =
{ .name = "undinet.rx" };
/*****************************************************************************
*
* UNDI interrupt service routine
*
*****************************************************************************
*/
/**
* UNDI interrupt service routine
*
* The UNDI ISR increments a counter (@c trigger_count) and exits.
*/
extern void undiisr ( void );
/** IRQ number */
uint8_t __data16 ( undiisr_irq );
#define undiisr_irq __use_data16 ( undiisr_irq )
/** IRQ chain vector */
struct segoff __data16 ( undiisr_next_handler );
#define undiisr_next_handler __use_data16 ( undiisr_next_handler )
/** IRQ trigger count */
volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
#define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
/** Last observed trigger count */
static unsigned int last_trigger_count = 0;
/**
* Hook UNDI interrupt service routine
*
* @v irq IRQ number
*/
static void undinet_hook_isr ( unsigned int irq ) {
assert ( irq <= IRQ_MAX );
assert ( undiisr_irq == 0 );
undiisr_irq = irq;
hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
&undiisr_next_handler );
}
/**
* Unhook UNDI interrupt service routine
*
* @v irq IRQ number
*/
static void undinet_unhook_isr ( unsigned int irq ) {
assert ( irq <= IRQ_MAX );
unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
&undiisr_next_handler );
undiisr_irq = 0;
}
/**
* Test to see if UNDI ISR has been triggered
*
* @ret triggered ISR has been triggered since last check
*/
static int undinet_isr_triggered ( void ) {
unsigned int this_trigger_count;
/* Read trigger_count. Do this only once; it is volatile */
this_trigger_count = undiisr_trigger_count;
if ( this_trigger_count == last_trigger_count ) {
/* Not triggered */
return 0;
} else {
/* Triggered */
last_trigger_count = this_trigger_count;
return 1;
}
}
/*****************************************************************************
*
* UNDI network device interface
*
*****************************************************************************
*/
/** UNDI transmit buffer descriptor */
static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
#define undinet_tbd __use_data16 ( undinet_tbd )
/** UNDI transmit destination address */
static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
#define undinet_destaddr __use_data16 ( undinet_destaddr )
/**
* Transmit packet
*
* @v netdev Network device
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int undinet_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) {
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_TRANSMIT undi_transmit;
const void *ll_dest;
const void *ll_source;
uint16_t net_proto;
unsigned int flags;
uint8_t protocol;
size_t len;
int rc;
/* Start profiling */
profile_start ( &undinet_tx_profiler );
/* Technically, we ought to make sure that the previous
* transmission has completed before we re-use the buffer.
* However, many PXE stacks (including at least some Intel PXE
* stacks and Etherboot 5.4) fail to generate TX completions.
* In practice this won't be a problem, since our TX datapath
* has a very low packet volume and we can get away with
* assuming that a TX will be complete by the time we want to
* transmit the next packet.
*/
/* Some PXE stacks are unable to cope with P_UNKNOWN, and will
* always try to prepend a link-layer header. Work around
* these stacks by stripping the existing link-layer header
* and allowing the PXE stack to (re)construct the link-layer
* header itself.
*/
if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
&net_proto, &flags ) ) != 0 ) {
DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
"%s\n", undinic, strerror ( rc ) );
return rc;
}
memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
switch ( net_proto ) {
case htons ( ETH_P_IP ) :
protocol = P_IP;
break;
case htons ( ETH_P_ARP ) :
protocol = P_ARP;
break;
case htons ( ETH_P_RARP ) :
protocol = P_RARP;
break;
default:
/* Unknown protocol; restore the original link-layer header */
iob_push ( iobuf, sizeof ( struct ethhdr ) );
protocol = P_UNKNOWN;
break;
}
/* Copy packet to UNDI I/O buffer */
len = iob_len ( iobuf );
if ( len > sizeof ( basemem_packet ) )
len = sizeof ( basemem_packet );
memcpy ( &basemem_packet, iobuf->data, len );
/* Create PXENV_UNDI_TRANSMIT data structure */
memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
undi_transmit.Protocol = protocol;
undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
XMT_BROADCAST : XMT_DESTADDR );
undi_transmit.DestAddr.segment = rm_ds;
undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
undi_transmit.TBD.segment = rm_ds;
undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
/* Create PXENV_UNDI_TBD data structure */
undinet_tbd.ImmedLength = len;
undinet_tbd.Xmit.segment = rm_ds;
undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
/* Issue PXE API call */
profile_start ( &undinet_tx_call_profiler );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
&undi_transmit,
sizeof ( undi_transmit ) ) ) != 0 )
goto done;
profile_stop ( &undinet_tx_call_profiler );
/* Free I/O buffer */
netdev_tx_complete ( netdev, iobuf );
profile_stop ( &undinet_tx_profiler );
done:
return rc;
}
/**
* Poll for received packets
*
* @v netdev Network device
*
* Fun, fun, fun. UNDI drivers don't use polling; they use
* interrupts. We therefore cheat and pretend that an interrupt has
* occurred every time undinet_poll() is called. This isn't too much
* of a hack; PCI devices share IRQs and so the first thing that a
* proper ISR should do is call PXENV_UNDI_ISR to determine whether or
* not the UNDI NIC generated the interrupt; there is no harm done by
* spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
* handling them any more rapidly than the usual rate of
* undinet_poll() being called even if we did implement a full ISR.
* So it should work. Ha!
*
* Addendum (21/10/03). Some cards don't play nicely with this trick,
* so instead of doing it the easy way we have to go to all the hassle
* of installing a genuine interrupt service routine and dealing with
* the wonderful 8259 Programmable Interrupt Controller. Joy.
*
* Addendum (10/07/07). When doing things such as iSCSI boot, in
* which we have to co-operate with a running OS, we can't get away
* with the "ISR-just-increments-a-counter-and-returns" trick at all,
* because it involves tying up the PIC for far too long, and other
* interrupt-dependent components (e.g. local disks) start breaking.
* We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
* from within interrupt context in order to deassert the device
* interrupt, and sends EOI if applicable.
*/
static void undinet_poll ( struct net_device *netdev ) {
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_ISR undi_isr;
struct io_buffer *iobuf = NULL;
unsigned int quota = UNDI_RX_QUOTA;
size_t len;
size_t reserve_len;
size_t frag_len;
size_t max_frag_len;
int rc;
if ( ! undinic->isr_processing ) {
/* Allow interrupt to occur. Do this even if
* interrupts are not known to be supported, since
* some cards erroneously report that they do not
* support interrupts.
*/
if ( ! undinet_isr_triggered() ) {
/* Allow interrupt to occur */
profile_start ( &undinet_irq_profiler );
__asm__ __volatile__ ( "sti\n\t"
"nop\n\t"
"nop\n\t"
"cli\n\t" );
profile_stop ( &undinet_irq_profiler );
/* If interrupts are known to be supported,
* then do nothing on this poll; wait for the
* interrupt to be triggered.
*/
if ( undinic->irq_supported )
return;
}
/* Start ISR processing */
undinic->isr_processing = 1;
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
} else {
/* Continue ISR processing */
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
}
/* Run through the ISR loop */
while ( quota ) {
profile_start ( &undinet_isr_call_profiler );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
&undi_isr,
sizeof ( undi_isr ) ) ) != 0 ) {
netdev_rx_err ( netdev, NULL, rc );
break;
}
profile_stop ( &undinet_isr_call_profiler );
switch ( undi_isr.FuncFlag ) {
case PXENV_UNDI_ISR_OUT_TRANSMIT:
/* We don't care about transmit completions */
break;
case PXENV_UNDI_ISR_OUT_RECEIVE:
/* Packet fragment received */
profile_start ( &undinet_rx_profiler );
len = undi_isr.FrameLength;
frag_len = undi_isr.BufferLength;
reserve_len = ( -undi_isr.FrameHeaderLength &
( UNDI_RX_ALIGN - 1 ) );
if ( ( len == 0 ) || ( len < frag_len ) ) {
/* Don't laugh. VMWare does it. */
DBGC ( undinic, "UNDINIC %p reported insane "
"fragment (%zd of %zd bytes)\n",
undinic, frag_len, len );
netdev_rx_err ( netdev, NULL, -EINVAL );
break;
}
if ( ! iobuf ) {
iobuf = alloc_iob ( reserve_len + len );
if ( ! iobuf ) {
DBGC ( undinic, "UNDINIC %p could not "
"allocate %zd bytes for RX "
"buffer\n", undinic, len );
/* Fragment will be dropped */
netdev_rx_err ( netdev, NULL, -ENOMEM );
goto done;
}
iob_reserve ( iobuf, reserve_len );
}
max_frag_len = iob_tailroom ( iobuf );
if ( frag_len > max_frag_len ) {
DBGC ( undinic, "UNDINIC %p fragment too big "
"(%zd+%zd does not fit into %zd)\n",
undinic, iob_len ( iobuf ), frag_len,
( iob_len ( iobuf ) + max_frag_len ) );
frag_len = max_frag_len;
}
copy_from_real ( iob_put ( iobuf, frag_len ),
undi_isr.Frame.segment,
undi_isr.Frame.offset, frag_len );
if ( iob_len ( iobuf ) == len ) {
/* Whole packet received; deliver it */
netdev_rx ( netdev, iob_disown ( iobuf ) );
quota--;
/* Etherboot 5.4 fails to return all packets
* under mild load; pretend it retriggered.
*/
if ( undinic->hacks & UNDI_HACK_EB54 )
--last_trigger_count;
}
profile_stop ( &undinet_rx_profiler );
break;
case PXENV_UNDI_ISR_OUT_DONE:
/* Processing complete */
undinic->isr_processing = 0;
goto done;
default:
/* Should never happen. VMWare does it routinely. */
DBGC ( undinic, "UNDINIC %p ISR returned invalid "
"FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
undinic->isr_processing = 0;
goto done;
}
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
}
done:
if ( iobuf ) {
DBGC ( undinic, "UNDINIC %p returned incomplete packet "
"(%zd of %zd)\n", undinic, iob_len ( iobuf ),
( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
netdev_rx_err ( netdev, iobuf, -EINVAL );
}
}
/**
* Open NIC
*
* @v netdev Net device
* @ret rc Return status code
*/
static int undinet_open ( struct net_device *netdev ) {
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
struct s_PXENV_UNDI_OPEN undi_open;
int rc;
/* Hook interrupt service routine and enable interrupt if applicable */
if ( undinic->irq ) {
undinet_hook_isr ( undinic->irq );
enable_irq ( undinic->irq );
send_eoi ( undinic->irq );
}
/* Set station address. Required for some PXE stacks; will
* spuriously fail on others. Ignore failures. We only ever
* use it to set the MAC address to the card's permanent value
* anyway.
*/
memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
sizeof ( undi_set_address.StationAddress ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
&undi_set_address, sizeof ( undi_set_address ) );
/* Open NIC. We ask for promiscuous operation, since it's the
* only way to ask for all multicast addresses. On any
* switched network, it shouldn't really make a difference to
* performance.
*/
memset ( &undi_open, 0, sizeof ( undi_open ) );
undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
&undi_open, sizeof ( undi_open ) ) ) != 0 )
goto err;
DBGC ( undinic, "UNDINIC %p opened\n", undinic );
return 0;
err:
undinet_close ( netdev );
return rc;
}
/**
* Close NIC
*
* @v netdev Net device
*/
static void undinet_close ( struct net_device *netdev ) {
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_ISR undi_isr;
struct s_PXENV_UNDI_CLOSE undi_close;
int rc;
/* Ensure ISR has exited cleanly */
while ( undinic->isr_processing ) {
undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
&undi_isr,
sizeof ( undi_isr ) ) ) != 0 )
break;
switch ( undi_isr.FuncFlag ) {
case PXENV_UNDI_ISR_OUT_TRANSMIT:
case PXENV_UNDI_ISR_OUT_RECEIVE:
/* Continue draining */
break;
default:
/* Stop processing */
undinic->isr_processing = 0;
break;
}
}
/* Close NIC */
pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
&undi_close, sizeof ( undi_close ) );
/* Disable interrupt and unhook ISR if applicable */
if ( undinic->irq ) {
disable_irq ( undinic->irq );
undinet_unhook_isr ( undinic->irq );
}
DBGC ( undinic, "UNDINIC %p closed\n", undinic );
}
/**
* Enable/disable interrupts
*
* @v netdev Net device
* @v enable Interrupts should be enabled
*/
static void undinet_irq ( struct net_device *netdev, int enable ) {
struct undi_nic *undinic = netdev->priv;
/* Cannot support interrupts yet */
DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
undinic, ( enable ? "enable" : "disable" ) );
}
/** UNDI network device operations */
static struct net_device_operations undinet_operations = {
.open = undinet_open,
.close = undinet_close,
.transmit = undinet_transmit,
.poll = undinet_poll,
.irq = undinet_irq,
};
/** A device with broken support for generating interrupts */
struct undinet_irq_broken {
/** PCI vendor ID */
uint16_t pci_vendor;
/** PCI device ID */
uint16_t pci_device;
};
/**
* List of devices with broken support for generating interrupts
*
* Some PXE stacks are known to claim that IRQs are supported, but
* then never generate interrupts. No satisfactory solution has been
* found to this problem; the workaround is to add the PCI vendor and
* device IDs to this list. This is something of a hack, since it
* will generate false positives for identical devices with a working
* PXE stack (e.g. those that have been reflashed with iPXE), but it's
* an improvement on the current situation.
*/
static const struct undinet_irq_broken undinet_irq_broken_list[] = {
/* HP XX70x laptops */
{ .pci_vendor = 0x8086, .pci_device = 0x1502 },
{ .pci_vendor = 0x8086, .pci_device = 0x1503 },
};
/**
* Check for devices with broken support for generating interrupts
*
* @v undi UNDI device
* @ret irq_is_broken Interrupt support is broken; no interrupts are generated
*/
static int undinet_irq_is_broken ( struct undi_device *undi ) {
const struct undinet_irq_broken *broken;
unsigned int i;
for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
broken = &undinet_irq_broken_list[i];
if ( ( undi->dev.desc.bus_type == BUS_TYPE_PCI ) &&
( undi->dev.desc.vendor == broken->pci_vendor ) &&
( undi->dev.desc.device == broken->pci_device ) ) {
return 1;
}
}
return 0;
}
/**
* Probe UNDI device
*
* @v undi UNDI device
* @ret rc Return status code
*/
int undinet_probe ( struct undi_device *undi ) {
struct net_device *netdev;
struct undi_nic *undinic;
struct s_PXENV_START_UNDI start_undi;
struct s_PXENV_UNDI_STARTUP undi_startup;
struct s_PXENV_UNDI_INITIALIZE undi_init;
struct s_PXENV_UNDI_GET_INFORMATION undi_info;
struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
struct s_PXENV_UNDI_CLEANUP undi_cleanup;
struct s_PXENV_STOP_UNDI stop_undi;
unsigned int retry;
int rc;
/* Allocate net device */
netdev = alloc_etherdev ( sizeof ( *undinic ) );
if ( ! netdev )
return -ENOMEM;
netdev_init ( netdev, &undinet_operations );
undinic = netdev->priv;
undi_set_drvdata ( undi, netdev );
netdev->dev = &undi->dev;
memset ( undinic, 0, sizeof ( *undinic ) );
undinet_entry = undi->entry;
DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
/* Hook in UNDI stack */
if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
memset ( &start_undi, 0, sizeof ( start_undi ) );
start_undi.AX = undi->pci_busdevfn;
start_undi.BX = undi->isapnp_csn;
start_undi.DX = undi->isapnp_read_port;
start_undi.ES = BIOS_SEG;
start_undi.DI = find_pnp_bios();
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
&start_undi,
sizeof ( start_undi ) ) ) != 0 )
goto err_start_undi;
}
undi->flags |= UNDI_FL_STARTED;
/* Bring up UNDI stack */
if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
memset ( &undi_startup, 0, sizeof ( undi_startup ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
&undi_startup,
sizeof ( undi_startup ) ) ) != 0 )
goto err_undi_startup;
/* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
* due to a transient condition (e.g. media test
* failing because the link has only just come out of
* reset). We may therefore need to retry this call
* several times.
*/
for ( retry = 0 ; ; ) {
memset ( &undi_init, 0, sizeof ( undi_init ) );
if ( ( rc = pxeparent_call ( undinet_entry,
PXENV_UNDI_INITIALIZE,
&undi_init,
sizeof ( undi_init ))) ==0)
break;
if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
goto err_undi_initialize;
DBGC ( undinic, "UNDINIC %p retrying "
"PXENV_UNDI_INITIALIZE (retry %d)\n",
undinic, retry );
/* Delay to allow link to settle if necessary */
mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
}
}
undi->flags |= UNDI_FL_INITIALIZED;
/* Get device information */
memset ( &undi_info, 0, sizeof ( undi_info ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
&undi_info, sizeof ( undi_info ) ) ) != 0 )
goto err_undi_get_information;
memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
undinic->irq = undi_info.IntNumber;
if ( undinic->irq > IRQ_MAX ) {
DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
undinic, undinic->irq );
rc = -EINVAL;
goto err_bad_irq;
}
DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
/* Get interface information */
memset ( &undi_iface, 0, sizeof ( undi_iface ) );
if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
&undi_iface,
sizeof ( undi_iface ) ) ) != 0 )
goto err_undi_get_iface_info;
DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
undi_iface.ServiceFlags );
if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
( undinic->irq != 0 ) ) {
undinic->irq_supported = 1;
}
DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
( undinic->irq_supported ? "interrupt" : "polling" ) );
if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
sizeof ( undi_iface.IfaceType ) ) == 0 ) {
DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
undinic );
undinic->hacks |= UNDI_HACK_EB54;
}
if ( undinet_irq_is_broken ( undi ) ) {
DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
"broken interrupts\n", undinic );
undinic->irq_supported = 0;
}
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register;
/* Mark as link up; we don't handle link state */
netdev_link_up ( netdev );
DBGC ( undinic, "UNDINIC %p added\n", undinic );
return 0;
err_register:
err_undi_get_iface_info:
err_bad_irq:
err_undi_get_information:
err_undi_initialize:
/* Shut down UNDI stack */
memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
sizeof ( undi_shutdown ) );
memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
sizeof ( undi_cleanup ) );
undi->flags &= ~UNDI_FL_INITIALIZED;
err_undi_startup:
/* Unhook UNDI stack */
memset ( &stop_undi, 0, sizeof ( stop_undi ) );
pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undi->flags &= ~UNDI_FL_STARTED;
err_start_undi:
netdev_nullify ( netdev );
netdev_put ( netdev );
undi_set_drvdata ( undi, NULL );
return rc;
}
/**
* Remove UNDI device
*
* @v undi UNDI device
*/
void undinet_remove ( struct undi_device *undi ) {
struct net_device *netdev = undi_get_drvdata ( undi );
struct undi_nic *undinic = netdev->priv;
struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
struct s_PXENV_UNDI_CLEANUP undi_cleanup;
struct s_PXENV_STOP_UNDI stop_undi;
/* Unregister net device */
unregister_netdev ( netdev );
/* If we are preparing for an OS boot, or if we cannot exit
* via the PXE stack, then shut down the PXE stack.
*/
if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
/* Shut down UNDI stack */
memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
&undi_shutdown, sizeof ( undi_shutdown ) );
memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
&undi_cleanup, sizeof ( undi_cleanup ) );
undi->flags &= ~UNDI_FL_INITIALIZED;
/* Unhook UNDI stack */
memset ( &stop_undi, 0, sizeof ( stop_undi ) );
pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
sizeof ( stop_undi ) );
undi->flags &= ~UNDI_FL_STARTED;
}
/* Clear entry point */
memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
/* Free network device */
netdev_nullify ( netdev );
netdev_put ( netdev );
DBGC ( undinic, "UNDINIC %p removed\n", undinic );
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ipxe/device.h>
#include <ipxe/init.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undinet.h>
#include <undipreload.h>
/** @file
*
* "Pure" UNDI driver
*
* This is the UNDI driver without explicit support for PCI or any
* other bus type. It is capable only of using the preloaded UNDI
* device. It must not be combined in an image with any other
* drivers.
*
* If you want a PXE-loadable image that contains only the UNDI
* driver, build "bin/undionly.kpxe".
*
* If you want any other image format, or any other drivers in
* addition to the UNDI driver, build e.g. "bin/undi.dsk".
*/
/**
* Probe UNDI root bus
*
* @v rootdev UNDI bus root device
*
* Scans the UNDI bus for devices and registers all devices it can
* find.
*/
static int undibus_probe ( struct root_device *rootdev ) {
struct undi_device *undi = &preloaded_undi;
int rc;
/* Check for a valie preloaded UNDI device */
if ( ! undi->entry.segment ) {
DBG ( "No preloaded UNDI device found!\n" );
return -ENODEV;
}
/* Add to device hierarchy */
undi->dev.driver_name = "undionly";
if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
undi->dev.desc.bus_type = BUS_TYPE_PCI;
undi->dev.desc.location = undi->pci_busdevfn;
undi->dev.desc.vendor = undi->pci_vendor;
undi->dev.desc.device = undi->pci_device;
snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
"UNDI-PCI%02x:%02x.%x",
PCI_BUS ( undi->pci_busdevfn ),
PCI_SLOT ( undi->pci_busdevfn ),
PCI_FUNC ( undi->pci_busdevfn ) );
} else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
undi->dev.desc.bus_type = BUS_TYPE_ISAPNP;
snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
"UNDI-ISAPNP" );
}
undi->dev.parent = &rootdev->dev;
list_add ( &undi->dev.siblings, &rootdev->dev.children);
INIT_LIST_HEAD ( &undi->dev.children );
/* Create network device */
if ( ( rc = undinet_probe ( undi ) ) != 0 )
goto err;
return 0;
err:
list_del ( &undi->dev.siblings );
return rc;
}
/**
* Remove UNDI root bus
*
* @v rootdev UNDI bus root device
*/
static void undibus_remove ( struct root_device *rootdev __unused ) {
struct undi_device *undi = &preloaded_undi;
undinet_remove ( undi );
list_del ( &undi->dev.siblings );
}
/** UNDI bus root device driver */
static struct root_driver undi_root_driver = {
.probe = undibus_probe,
.remove = undibus_remove,
};
/** UNDI bus root device */
struct root_device undi_root_device __root_device = {
.dev = { .name = "UNDI" },
.driver = &undi_root_driver,
};
/**
* Prepare for exit
*
* @v booting System is shutting down for OS boot
*/
static void undionly_shutdown ( int booting ) {
/* If we are shutting down to boot an OS, clear the "keep PXE
* stack" flag.
*/
if ( booting )
preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL;
}
struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = {
.shutdown = undionly_shutdown,
};

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
#include <undipreload.h>
/** @file
*
* Preloaded UNDI stack
*
*/
/**
* Preloaded UNDI device
*
* This is the UNDI device that was present when Etherboot started
* execution (i.e. when loading a .kpxe image). The first driver to
* claim this device must zero out this data structure.
*/
struct undi_device __data16 ( preloaded_undi );

View File

@@ -1,235 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pxe.h>
#include <realmode.h>
#include <undirom.h>
/** @file
*
* UNDI expansion ROMs
*
*/
/** List of all UNDI ROMs */
static LIST_HEAD ( undiroms );
/**
* Parse PXE ROM ID structure
*
* @v undirom UNDI ROM
* @v pxeromid Offset within ROM to PXE ROM ID structure
* @ret rc Return status code
*/
static int undirom_parse_pxeromid ( struct undi_rom *undirom,
unsigned int pxeromid ) {
struct undi_rom_id undi_rom_id;
unsigned int undiloader;
DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom,
undirom->rom_segment, pxeromid );
/* Read PXE ROM ID structure and verify */
copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid,
sizeof ( undi_rom_id ) );
if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature "
"%08x\n", undirom, undi_rom_id.Signature );
return -EINVAL;
}
/* Check for UNDI loader */
undiloader = undi_rom_id.UNDILoader;
if ( ! undiloader ) {
DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom );
return -EINVAL;
}
/* Fill in UNDI ROM loader fields */
undirom->loader_entry.segment = undirom->rom_segment;
undirom->loader_entry.offset = undiloader;
undirom->code_size = undi_rom_id.CodeSize;
undirom->data_size = undi_rom_id.DataSize;
DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x "
"(code %04zx data %04zx)\n", undirom,
undirom->loader_entry.segment, undirom->loader_entry.offset,
undirom->code_size, undirom->data_size );
return 0;
}
/**
* Parse PCI expansion header
*
* @v undirom UNDI ROM
* @v pcirheader Offset within ROM to PCI expansion header
*/
static int undirom_parse_pcirheader ( struct undi_rom *undirom,
unsigned int pcirheader ) {
struct pcir_header pcir_header;
DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n",
undirom, undirom->rom_segment, pcirheader );
/* Read PCI expansion header and verify */
copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader,
sizeof ( pcir_header ) );
if ( pcir_header.signature != PCIR_SIGNATURE ) {
DBGC ( undirom, "UNDIROM %p has bad PCI expansion header "
"signature %08x\n", undirom, pcir_header.signature );
return -EINVAL;
}
/* Fill in UNDI ROM PCI device fields */
undirom->bus_type = PCI_NIC;
undirom->bus_id.pci.vendor_id = pcir_header.vendor_id;
undirom->bus_id.pci.device_id = pcir_header.device_id;
DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom,
undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id );
return 0;
}
/**
* Probe UNDI ROM
*
* @v rom_segment ROM segment address
* @ret rc Return status code
*/
static int undirom_probe ( unsigned int rom_segment ) {
struct undi_rom *undirom = NULL;
struct undi_rom_header romheader;
size_t rom_len;
unsigned int pxeromid;
unsigned int pcirheader;
int rc;
/* Read expansion ROM header and verify */
copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) );
if ( romheader.Signature != ROM_SIGNATURE ) {
rc = -EINVAL;
goto err;
}
rom_len = ( romheader.ROMLength * 512 );
/* Allocate memory for UNDI ROM */
undirom = zalloc ( sizeof ( *undirom ) );
if ( ! undirom ) {
DBG ( "Could not allocate UNDI ROM structure\n" );
rc = -ENOMEM;
goto err;
}
DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 "
"(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) );
undirom->rom_segment = rom_segment;
/* Check for and parse PXE ROM ID */
pxeromid = romheader.PXEROMID;
if ( ! pxeromid ) {
DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom );
rc = -EINVAL;
goto err;
}
if ( pxeromid > rom_len ) {
DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n",
undirom );
rc = -EINVAL;
goto err;
}
if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 )
goto err;
/* Parse PCIR header, if present */
pcirheader = romheader.PCIRHeader;
if ( pcirheader )
undirom_parse_pcirheader ( undirom, pcirheader );
/* Add to UNDI ROM list and return */
DBGC ( undirom, "UNDIROM %p registered\n", undirom );
list_add ( &undirom->list, &undiroms );
return 0;
err:
free ( undirom );
return rc;
}
/**
* Create UNDI ROMs for all possible expansion ROMs
*
* @ret
*/
static void undirom_probe_all_roms ( void ) {
static int probed = 0;
unsigned int rom_segment;
/* Perform probe only once */
if ( probed )
return;
DBG ( "Scanning for PXE expansion ROMs\n" );
/* Scan through expansion ROM region at 512 byte intervals */
for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
rom_segment += 0x20 ) {
undirom_probe ( rom_segment );
}
probed = 1;
}
/**
* Find UNDI ROM for PCI device
*
* @v vendor_id PCI vendor ID
* @v device_id PCI device ID
* @v rombase ROM base address, or 0 for any
* @ret undirom UNDI ROM, or NULL
*/
struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
unsigned int device_id,
unsigned int rombase ) {
struct undi_rom *undirom;
undirom_probe_all_roms();
list_for_each_entry ( undirom, &undiroms, list ) {
if ( undirom->bus_type != PCI_NIC )
continue;
if ( undirom->bus_id.pci.vendor_id != vendor_id )
continue;
if ( undirom->bus_id.pci.device_id != device_id )
continue;
if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) )
continue;
DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n",
undirom, vendor_id, device_id, rombase );
return undirom;
}
DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n",
vendor_id, device_id, rombase );
return NULL;
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <realmode.h>
#include <bios.h>
#include <basemem.h>
#include <ipxe/hidemem.h>
/** @file
*
* Base memory allocation
*
*/
/**
* Set the BIOS free base memory counter
*
* @v new_fbms New free base memory counter (in kB)
*/
void set_fbms ( unsigned int new_fbms ) {
uint16_t fbms = new_fbms;
/* Update the BIOS memory counter */
put_real ( fbms, BDA_SEG, BDA_FBMS );
/* Update our hidden memory region map */
hide_basemem();
}

View File

@@ -1,566 +0,0 @@
/*
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <assert.h>
#include <realmode.h>
#include <bios.h>
#include <biosint.h>
#include <ipxe/console.h>
#include <ipxe/ansiesc.h>
#include <ipxe/keys.h>
#include <ipxe/keymap.h>
#include <ipxe/init.h>
#include <config/console.h>
#define ATTR_BOLD 0x08
#define ATTR_FCOL_MASK 0x07
#define ATTR_FCOL_BLACK 0x00
#define ATTR_FCOL_BLUE 0x01
#define ATTR_FCOL_GREEN 0x02
#define ATTR_FCOL_CYAN 0x03
#define ATTR_FCOL_RED 0x04
#define ATTR_FCOL_MAGENTA 0x05
#define ATTR_FCOL_YELLOW 0x06
#define ATTR_FCOL_WHITE 0x07
#define ATTR_BLINK 0x80
#define ATTR_BCOL_MASK 0x70
#define ATTR_BCOL_BLACK 0x00
#define ATTR_BCOL_BLUE 0x10
#define ATTR_BCOL_GREEN 0x20
#define ATTR_BCOL_CYAN 0x30
#define ATTR_BCOL_RED 0x40
#define ATTR_BCOL_MAGENTA 0x50
#define ATTR_BCOL_YELLOW 0x60
#define ATTR_BCOL_WHITE 0x70
#define ATTR_DEFAULT ATTR_FCOL_WHITE
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
#undef CONSOLE_PCBIOS
#define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
/** Current character attribute */
static unsigned int bios_attr = ATTR_DEFAULT;
/** Keypress injection lock */
static uint8_t __text16 ( bios_inject_lock );
#define bios_inject_lock __use_text16 ( bios_inject_lock )
/** Vector for chaining to other INT 16 handlers */
static struct segoff __text16 ( int16_vector );
#define int16_vector __use_text16 ( int16_vector )
/** Assembly wrapper */
extern void int16_wrapper ( void );
/**
* Handle ANSI CUP (cursor position)
*
* @v ctx ANSI escape sequence context
* @v count Parameter count
* @v params[0] Row (1 is top)
* @v params[1] Column (1 is left)
*/
static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
unsigned int count __unused, int params[] ) {
int cx = ( params[1] - 1 );
int cy = ( params[0] - 1 );
if ( cx < 0 )
cx = 0;
if ( cy < 0 )
cy = 0;
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x10\n\t"
"cli\n\t" )
: : "a" ( 0x0200 ), "b" ( 1 ),
"d" ( ( cy << 8 ) | cx ) );
}
/**
* Handle ANSI ED (erase in page)
*
* @v ctx ANSI escape sequence context
* @v count Parameter count
* @v params[0] Region to erase
*/
static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
unsigned int count __unused,
int params[] __unused ) {
/* We assume that we always clear the whole screen */
assert ( params[0] == ANSIESC_ED_ALL );
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x10\n\t"
"cli\n\t" )
: : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
"c" ( 0 ),
"d" ( ( ( console_height - 1 ) << 8 ) |
( console_width - 1 ) ) );
}
/**
* Handle ANSI SGR (set graphics rendition)
*
* @v ctx ANSI escape sequence context
* @v count Parameter count
* @v params List of graphic rendition aspects
*/
static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
unsigned int count, int params[] ) {
static const uint8_t bios_attr_fcols[10] = {
ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
};
static const uint8_t bios_attr_bcols[10] = {
ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
};
unsigned int i;
int aspect;
for ( i = 0 ; i < count ; i++ ) {
aspect = params[i];
if ( aspect == 0 ) {
bios_attr = ATTR_DEFAULT;
} else if ( aspect == 1 ) {
bios_attr |= ATTR_BOLD;
} else if ( aspect == 5 ) {
bios_attr |= ATTR_BLINK;
} else if ( aspect == 22 ) {
bios_attr &= ~ATTR_BOLD;
} else if ( aspect == 25 ) {
bios_attr &= ~ATTR_BLINK;
} else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
bios_attr &= ~ATTR_FCOL_MASK;
bios_attr |= bios_attr_fcols[ aspect - 30 ];
} else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
bios_attr &= ~ATTR_BCOL_MASK;
bios_attr |= bios_attr_bcols[ aspect - 40 ];
}
}
}
/**
* Handle ANSI DECTCEM set (show cursor)
*
* @v ctx ANSI escape sequence context
* @v count Parameter count
* @v params List of graphic rendition aspects
*/
static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
unsigned int count __unused,
int params[] __unused ) {
uint8_t height;
/* Get character height */
get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x10\n\t"
"cli\n\t" )
: : "a" ( 0x0100 ),
"c" ( ( ( height - 2 ) << 8 ) |
( height - 1 ) ) );
}
/**
* Handle ANSI DECTCEM reset (hide cursor)
*
* @v ctx ANSI escape sequence context
* @v count Parameter count
* @v params List of graphic rendition aspects
*/
static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
unsigned int count __unused,
int params[] __unused ) {
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x10\n\t"
"cli\n\t" )
: : "a" ( 0x0100 ), "c" ( 0x2000 ) );
}
/** BIOS console ANSI escape sequence handlers */
static struct ansiesc_handler bios_ansiesc_handlers[] = {
{ ANSIESC_CUP, bios_handle_cup },
{ ANSIESC_ED, bios_handle_ed },
{ ANSIESC_SGR, bios_handle_sgr },
{ ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
{ ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
{ 0, NULL }
};
/** BIOS console ANSI escape sequence context */
static struct ansiesc_context bios_ansiesc_ctx = {
.handlers = bios_ansiesc_handlers,
};
/**
* Print a character to BIOS console
*
* @v character Character to be printed
*/
static void bios_putchar ( int character ) {
int discard_a, discard_b, discard_c;
/* Intercept ANSI escape sequences */
character = ansiesc_process ( &bios_ansiesc_ctx, character );
if ( character < 0 )
return;
/* Print character with attribute */
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"sti\n\t"
/* Skip non-printable characters */
"cmpb $0x20, %%al\n\t"
"jb 1f\n\t"
/* Read attribute */
"movb %%al, %%cl\n\t"
"movb $0x08, %%ah\n\t"
"int $0x10\n\t"
"xchgb %%al, %%cl\n\t"
/* Skip if attribute matches */
"cmpb %%ah, %%bl\n\t"
"je 1f\n\t"
/* Set attribute */
"movw $0x0001, %%cx\n\t"
"movb $0x09, %%ah\n\t"
"int $0x10\n\t"
"\n1:\n\t"
/* Print character */
"xorw %%bx, %%bx\n\t"
"movb $0x0e, %%ah\n\t"
"int $0x10\n\t"
"cli\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( discard_a ), "=b" ( discard_b ),
"=c" ( discard_c )
: "a" ( character ), "b" ( bios_attr ) );
}
/**
* Pointer to current ANSI output sequence
*
* While we are in the middle of returning an ANSI sequence for a
* special key, this will point to the next character to return. When
* not in the middle of such a sequence, this will point to a NUL
* (note: not "will be NULL").
*/
static const char *bios_ansi_input = "";
/** A BIOS key */
struct bios_key {
/** Scancode */
uint8_t scancode;
/** Key code */
uint16_t key;
} __attribute__ (( packed ));
/** Mapping from BIOS scan codes to iPXE key codes */
static const struct bios_key bios_keys[] = {
{ 0x53, KEY_DC },
{ 0x48, KEY_UP },
{ 0x50, KEY_DOWN },
{ 0x4b, KEY_LEFT },
{ 0x4d, KEY_RIGHT },
{ 0x47, KEY_HOME },
{ 0x4f, KEY_END },
{ 0x49, KEY_PPAGE },
{ 0x51, KEY_NPAGE },
{ 0x3f, KEY_F5 },
{ 0x40, KEY_F6 },
{ 0x41, KEY_F7 },
{ 0x42, KEY_F8 },
{ 0x43, KEY_F9 },
{ 0x44, KEY_F10 },
{ 0x85, KEY_F11 },
{ 0x86, KEY_F12 },
};
/**
* Get ANSI escape sequence corresponding to BIOS scancode
*
* @v scancode BIOS scancode
* @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
*/
static const char * bios_ansi_seq ( unsigned int scancode ) {
static char buf[ 5 /* "[" + two digits + terminator + NUL */ ];
unsigned int key;
unsigned int terminator;
unsigned int n;
unsigned int i;
char *tmp = buf;
/* Construct ANSI escape sequence for scancode, if known */
for ( i = 0 ; i < ( sizeof ( bios_keys ) /
sizeof ( bios_keys[0] ) ) ; i++ ) {
/* Look for matching scancode */
if ( bios_keys[i].scancode != scancode )
continue;
/* Construct escape sequence */
key = bios_keys[i].key;
n = KEY_ANSI_N ( key );
terminator = KEY_ANSI_TERMINATOR ( key );
*(tmp++) = '[';
if ( n )
tmp += sprintf ( tmp, "%d", n );
*(tmp++) = terminator;
*(tmp++) = '\0';
assert ( tmp <= &buf[ sizeof ( buf ) ] );
return buf;
}
DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
return NULL;
}
/**
* Map a key
*
* @v character Character read from console
* @ret character Mapped character
*/
static int bios_keymap ( unsigned int character ) {
struct key_mapping *mapping;
for_each_table_entry ( mapping, KEYMAP ) {
if ( mapping->from == character )
return mapping->to;
}
return character;
}
/**
* Get character from BIOS console
*
* @ret character Character read from console
*/
static int bios_getchar ( void ) {
uint16_t keypress;
unsigned int character;
const char *ansi_seq;
/* If we are mid-sequence, pass out the next byte */
if ( ( character = *bios_ansi_input ) ) {
bios_ansi_input++;
return character;
}
/* Do nothing if injection is in progress */
if ( bios_inject_lock )
return 0;
/* Read character from real BIOS console */
bios_inject_lock++;
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x16\n\t"
"cli\n\t" )
: "=a" ( keypress )
: "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
bios_inject_lock--;
character = ( keypress & 0xff );
/* If it's a normal character, just map and return it */
if ( character && ( character < 0x80 ) )
return bios_keymap ( character );
/* Otherwise, check for a special key that we know about */
if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) {
/* Start of escape sequence: return ESC (0x1b) */
bios_ansi_input = ansi_seq;
return 0x1b;
}
return 0;
}
/**
* Check for character ready to read from BIOS console
*
* @ret True Character available to read
* @ret False No character available to read
*/
static int bios_iskey ( void ) {
unsigned int discard_a;
unsigned int flags;
/* If we are mid-sequence, we are always ready */
if ( *bios_ansi_input )
return 1;
/* Do nothing if injection is in progress */
if ( bios_inject_lock )
return 0;
/* Otherwise check the real BIOS console */
bios_inject_lock++;
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"int $0x16\n\t"
"pushfw\n\t"
"popw %w0\n\t"
"cli\n\t" )
: "=R" ( flags ), "=a" ( discard_a )
: "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
bios_inject_lock--;
return ( ! ( flags & ZF ) );
}
/** BIOS console */
struct console_driver bios_console __console_driver = {
.putchar = bios_putchar,
.getchar = bios_getchar,
.iskey = bios_iskey,
.usage = CONSOLE_PCBIOS,
};
/**
* Inject keypresses
*
* @v ix86 Registers as passed to INT 16
*/
static __asmcall void bios_inject ( struct i386_all_regs *ix86 ) {
unsigned int discard_a;
unsigned int scancode;
unsigned int i;
uint16_t keypress;
int key;
/* If this is a blocking call, then loop until the
* non-blocking variant of the call indicates that a keypress
* is available. Do this without acquiring the injection
* lock, so that injection may take place.
*/
if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) {
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"\n1:\n\t"
"pushw %%ax\n\t"
"int $0x16\n\t"
"popw %%ax\n\t"
"jc 2f\n\t"
"jz 1b\n\t"
"\n2:\n\t"
"cli\n\t" )
: "=a" ( discard_a )
: "a" ( ix86->regs.eax | 0x0100 ),
"m" ( bios_inject_lock ) );
}
/* Acquire injection lock */
bios_inject_lock++;
/* Check for keypresses */
if ( iskey() ) {
/* Get key */
key = getkey ( 0 );
/* Reverse internal CR->LF mapping */
if ( key == '\n' )
key = '\r';
/* Convert to keypress */
keypress = ( ( key << 8 ) | key );
/* Handle special keys */
if ( key >= KEY_MIN ) {
for ( i = 0 ; i < ( sizeof ( bios_keys ) /
sizeof ( bios_keys[0] ) ) ; i++ ) {
if ( bios_keys[i].key == key ) {
scancode = bios_keys[i].scancode;
keypress = ( scancode << 8 );
break;
}
}
}
/* Inject keypress */
DBGC ( &bios_console, "BIOS injecting keypress %04x\n",
keypress );
__asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" )
: "=a" ( discard_a )
: "a" ( 0x0500 ), "c" ( keypress ),
"m" ( bios_inject_lock ) );
}
/* Release injection lock */
bios_inject_lock--;
}
/**
* Start up keypress injection
*
*/
static void bios_inject_startup ( void ) {
/* Assembly wrapper to call bios_inject() */
__asm__ __volatile__ (
TEXT16_CODE ( "\nint16_wrapper:\n\t"
"pushfw\n\t"
"cmpb $0, %%cs:bios_inject_lock\n\t"
"jnz 1f\n\t"
"pushl %0\n\t"
"pushw %%cs\n\t"
"call prot_call\n\t"
"addw $4, %%sp\n\t"
"\n1:\n\t"
"popfw\n\t"
"ljmp *%%cs:int16_vector\n\t" )
: : "i" ( bios_inject ) );
/* Hook INT 16 */
hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
&int16_vector );
}
/**
* Shut down keypress injection
*
* @v booting System is shutting down for OS boot
*/
static void bios_inject_shutdown ( int booting __unused ) {
/* Unhook INT 16 */
unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
&int16_vector );
}
/** Keypress injection startup function */
struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.startup = bios_inject_startup,
.shutdown = bios_inject_shutdown,
};

View File

@@ -1,589 +0,0 @@
/*
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arch i386
.code16
#define SMAP 0x534d4150
/* Most documentation refers to the E820 buffer as being 20 bytes, and
* the API makes it perfectly legitimate to pass only a 20-byte buffer
* and expect to get valid data. However, some morons at ACPI decided
* to extend the data structure by adding an extra "extended
* attributes" field and by including critical information within this
* field, such as whether or not the region is enabled. A caller who
* passes in only a 20-byte buffer therefore risks getting very, very
* misleading information.
*
* I have personally witnessed an HP BIOS that returns a value of
* 0x0009 in the extended attributes field. If we don't pass this
* value through to the caller, 32-bit WinPE will die, usually with a
* PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
*
* Allow a ridiculously large maximum value (64 bytes) for the E820
* buffer as a guard against insufficiently creative idiots in the
* future.
*/
#define E820MAXSIZE 64
/****************************************************************************
*
* Allowed memory windows
*
* There are two ways to view this list. The first is as a list of
* (non-overlapping) allowed memory regions, sorted by increasing
* address. The second is as a list of (non-overlapping) hidden
* memory regions, again sorted by increasing address. The second
* view is offset by half an entry from the first: think about this
* for a moment and it should make sense.
*
* xxx_memory_window is used to indicate an "allowed region"
* structure, hidden_xxx_memory is used to indicate a "hidden region"
* structure. Each structure is 16 bytes in length.
*
****************************************************************************
*/
.section ".data16", "aw", @progbits
.align 16
.globl hidemem_base
.globl hidemem_umalloc
.globl hidemem_textdata
memory_windows:
base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
.long 0xffffffff, 0xffffffff /* Changes at runtime */
hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
.long 0xffffffff, 0xffffffff /* Changes at runtime */
.long 0xffffffff, 0xffffffff /* End of memory */
memory_windows_end:
/****************************************************************************
* Truncate region to memory window
*
* Parameters:
* %edx:%eax Start of region
* %ecx:%ebx Length of region
* %si Memory window
* Returns:
* %edx:%eax Start of windowed region
* %ecx:%ebx Length of windowed region
****************************************************************************
*/
.section ".text16", "ax", @progbits
window_region:
/* Convert (start,len) to (start, end) */
addl %eax, %ebx
adcl %edx, %ecx
/* Truncate to window start */
cmpl 4(%si), %edx
jne 1f
cmpl 0(%si), %eax
1: jae 2f
movl 4(%si), %edx
movl 0(%si), %eax
2: /* Truncate to window end */
cmpl 12(%si), %ecx
jne 1f
cmpl 8(%si), %ebx
1: jbe 2f
movl 12(%si), %ecx
movl 8(%si), %ebx
2: /* Convert (start, end) back to (start, len) */
subl %eax, %ebx
sbbl %edx, %ecx
/* If length is <0, set length to 0 */
jae 1f
xorl %ebx, %ebx
xorl %ecx, %ecx
ret
.size window_region, . - window_region
/****************************************************************************
* Patch "memory above 1MB" figure
*
* Parameters:
* %ax Memory above 1MB, in 1kB blocks
* Returns:
* %ax Modified memory above 1M in 1kB blocks
****************************************************************************
*/
.section ".text16", "ax", @progbits
patch_1m:
pushal
/* Convert to (start,len) format and call truncate */
xorl %ecx, %ecx
movzwl %ax, %ebx
shll $10, %ebx
xorl %edx, %edx
movl $0x100000, %eax
movw $ext_memory_window, %si
call window_region
/* Convert back to "memory above 1MB" format and return via %ax */
pushfw
shrl $10, %ebx
popfw
movw %sp, %bp
movw %bx, 28(%bp)
popal
ret
.size patch_1m, . - patch_1m
/****************************************************************************
* Patch "memory above 16MB" figure
*
* Parameters:
* %bx Memory above 16MB, in 64kB blocks
* Returns:
* %bx Modified memory above 16M in 64kB blocks
****************************************************************************
*/
.section ".text16", "ax", @progbits
patch_16m:
pushal
/* Convert to (start,len) format and call truncate */
xorl %ecx, %ecx
shll $16, %ebx
xorl %edx, %edx
movl $0x1000000, %eax
movw $ext_memory_window, %si
call window_region
/* Convert back to "memory above 16MB" format and return via %bx */
pushfw
shrl $16, %ebx
popfw
movw %sp, %bp
movw %bx, 16(%bp)
popal
ret
.size patch_16m, . - patch_16m
/****************************************************************************
* Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
*
* Parameters:
* %ax Memory between 1MB and 16MB, in 1kB blocks
* %bx Memory above 16MB, in 64kB blocks
* Returns:
* %ax Modified memory between 1MB and 16MB, in 1kB blocks
* %bx Modified memory above 16MB, in 64kB blocks
****************************************************************************
*/
.section ".text16", "ax", @progbits
patch_1m_16m:
call patch_1m
call patch_16m
/* If 1M region is no longer full-length, kill off the 16M region */
cmpw $( 15 * 1024 ), %ax
je 1f
xorw %bx, %bx
1: ret
.size patch_1m_16m, . - patch_1m_16m
/****************************************************************************
* Get underlying e820 memory region to underlying_e820 buffer
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that the continuation
* value (%ebx) is a 16-bit simple sequence counter (with the high 16
* bits ignored), and termination is always via CF=1 rather than
* %ebx=0.
*
****************************************************************************
*/
.section ".text16", "ax", @progbits
get_underlying_e820:
/* If the requested region is in the cache, return it */
cmpw %bx, underlying_e820_index
jne 2f
pushw %di
pushw %si
movw $underlying_e820_cache, %si
cmpl underlying_e820_cache_size, %ecx
jbe 1f
movl underlying_e820_cache_size, %ecx
1: pushl %ecx
rep movsb
popl %ecx
popw %si
popw %di
incw %bx
movl %edx, %eax
clc
ret
2:
/* If the requested region is earlier than the cached region,
* invalidate the cache.
*/
cmpw %bx, underlying_e820_index
jbe 1f
movw $0xffff, underlying_e820_index
1:
/* If the cache is invalid, reset the underlying %ebx */
cmpw $0xffff, underlying_e820_index
jne 1f
andl $0, underlying_e820_ebx
1:
/* If the cache is valid but the continuation value is zero,
* this means that the previous underlying call returned with
* %ebx=0. Return with CF=1 in this case.
*/
cmpw $0xffff, underlying_e820_index
je 1f
cmpl $0, underlying_e820_ebx
jne 1f
stc
ret
1:
/* Get the next region into the cache */
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi /* Some implementations corrupt %esi, so we */
pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
pushl %ebp
pushw %es
pushw %ds
popw %es
movw $underlying_e820_cache, %di
cmpl $E820MAXSIZE, %ecx
jbe 1f
movl $E820MAXSIZE, %ecx
1: movl underlying_e820_ebx, %ebx
stc
pushfw
lcall *%cs:int15_vector
popw %es
popl %ebp
popl %edi
popl %esi
/* Check for error return from underlying e820 call */
jc 2f /* CF set: error */
cmpl $SMAP, %eax
je 3f /* 'SMAP' missing: error */
2: /* An error occurred: return values returned by underlying e820 call */
stc /* Force CF set if SMAP was missing */
addr32 leal 16(%esp), %esp /* avoid changing other flags */
ret
3: /* No error occurred */
movl %ebx, underlying_e820_ebx
movl %ecx, underlying_e820_cache_size
popl %edx
popl %ecx
popl %ebx
popl %eax
/* Mark cache as containing this result */
incw underlying_e820_index
/* Loop until found */
jmp get_underlying_e820
.size get_underlying_e820, . - get_underlying_e820
.section ".data16", "aw", @progbits
underlying_e820_index:
.word 0xffff /* Initialise to an invalid value */
.size underlying_e820_index, . - underlying_e820_index
.section ".bss16", "aw", @nobits
underlying_e820_ebx:
.long 0
.size underlying_e820_ebx, . - underlying_e820_ebx
.section ".bss16", "aw", @nobits
underlying_e820_cache:
.space E820MAXSIZE
.size underlying_e820_cache, . - underlying_e820_cache
.section ".bss16", "aw", @nobits
underlying_e820_cache_size:
.long 0
.size underlying_e820_cache_size, . - underlying_e820_cache_size
/****************************************************************************
* Get windowed e820 region, without empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that each underlying
* region is returned N times, windowed to fit within N visible-memory
* windows. Termination is always via CF=1.
*
****************************************************************************
*/
.section ".text16", "ax", @progbits
get_windowed_e820:
/* Preserve registers */
pushl %esi
pushw %bp
/* Split %ebx into %si:%bx, store original %bx in %bp */
pushl %ebx
popw %bp
popw %si
/* %si == 0 => start of memory_windows list */
testw %si, %si
jne 1f
movw $memory_windows, %si
1:
/* Get (cached) underlying e820 region to buffer */
call get_underlying_e820
jc 99f /* Abort on error */
/* Preserve registers */
pushal
/* start => %edx:%eax, len => %ecx:%ebx */
movl %es:0(%di), %eax
movl %es:4(%di), %edx
movl %es:8(%di), %ebx
movl %es:12(%di), %ecx
/* Truncate region to current window */
call window_region
1: /* Store modified values in e820 map entry */
movl %eax, %es:0(%di)
movl %edx, %es:4(%di)
movl %ebx, %es:8(%di)
movl %ecx, %es:12(%di)
/* Restore registers */
popal
/* Derive continuation value for next call */
addw $16, %si
cmpw $memory_windows_end, %si
jne 1f
/* End of memory windows: reset %si and allow %bx to continue */
xorw %si, %si
jmp 2f
1: /* More memory windows to go: restore original %bx */
movw %bp, %bx
2: /* Construct %ebx from %si:%bx */
pushw %si
pushw %bx
popl %ebx
98: /* Clear CF */
clc
99: /* Restore registers and return */
popw %bp
popl %esi
ret
.size get_windowed_e820, . - get_windowed_e820
/****************************************************************************
* Get windowed e820 region, with empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that each underlying
* region is returned up to N times, windowed to fit within N
* visible-memory windows. Empty windows are never returned.
* Termination is always via CF=1.
*
****************************************************************************
*/
.section ".text16", "ax", @progbits
get_nonempty_e820:
/* Record entry parameters */
pushl %eax
pushl %ecx
pushl %edx
/* Get next windowed region */
call get_windowed_e820
jc 99f /* abort on error */
/* If region is non-empty, finish here */
cmpl $0, %es:8(%di)
jne 98f
cmpl $0, %es:12(%di)
jne 98f
/* Region was empty: restore entry parameters and go to next region */
popl %edx
popl %ecx
popl %eax
jmp get_nonempty_e820
98: /* Clear CF */
clc
99: /* Return values from underlying call */
addr32 leal 12(%esp), %esp /* avoid changing flags */
ret
.size get_nonempty_e820, . - get_nonempty_e820
/****************************************************************************
* Get mangled e820 region, with empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that underlying regions
* are windowed to the allowed memory regions. Empty regions are
* stripped from the map. Termination is always via %ebx=0.
*
****************************************************************************
*/
.section ".text16", "ax", @progbits
get_mangled_e820:
/* Get a nonempty region */
call get_nonempty_e820
jc 99f /* Abort on error */
/* Peek ahead to see if there are any further nonempty regions */
pushal
pushw %es
movw %sp, %bp
subw %cx, %sp
movl $0xe820, %eax
movl $SMAP, %edx
pushw %ss
popw %es
movw %sp, %di
call get_nonempty_e820
movw %bp, %sp
popw %es
popal
jnc 99f /* There are further nonempty regions */
/* No futher nonempty regions: zero %ebx and clear CF */
xorl %ebx, %ebx
99: /* Return */
ret
.size get_mangled_e820, . - get_mangled_e820
/****************************************************************************
* INT 15,e820 handler
****************************************************************************
*/
.section ".text16", "ax", @progbits
int15_e820:
pushw %ds
pushw %cs:rm_ds
popw %ds
call get_mangled_e820
popw %ds
call patch_cf
iret
.size int15_e820, . - int15_e820
/****************************************************************************
* INT 15,e801 handler
****************************************************************************
*/
.section ".text16", "ax", @progbits
int15_e801:
/* Call previous handler */
pushfw
lcall *%cs:int15_vector
call patch_cf
/* Edit result */
pushw %ds
pushw %cs:rm_ds
popw %ds
call patch_1m_16m
xchgw %ax, %cx
xchgw %bx, %dx
call patch_1m_16m
xchgw %ax, %cx
xchgw %bx, %dx
popw %ds
iret
.size int15_e801, . - int15_e801
/****************************************************************************
* INT 15,88 handler
****************************************************************************
*/
.section ".text16", "ax", @progbits
int15_88:
/* Call previous handler */
pushfw
lcall *%cs:int15_vector
call patch_cf
/* Edit result */
pushw %ds
pushw %cs:rm_ds
popw %ds
call patch_1m
popw %ds
iret
.size int15_88, . - int15_88
/****************************************************************************
* INT 15 handler
****************************************************************************
*/
.section ".text16", "ax", @progbits
.globl int15
int15:
/* See if we want to intercept this call */
pushfw
cmpw $0xe820, %ax
jne 1f
cmpl $SMAP, %edx
jne 1f
popfw
jmp int15_e820
1: cmpw $0xe801, %ax
jne 2f
popfw
jmp int15_e801
2: cmpb $0x88, %ah
jne 3f
popfw
jmp int15_88
3: popfw
ljmp *%cs:int15_vector
.size int15, . - int15
.section ".text16.data", "aw", @progbits
.globl int15_vector
int15_vector:
.long 0
.size int15_vector, . - int15_vector

View File

@@ -1,98 +0,0 @@
/* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
#include <biosint.h>
/** Assembly routine in inline asm */
extern void int15_fakee820();
/** Original INT 15 handler */
static struct segoff __text16 ( real_int15_vector );
#define real_int15_vector __use_text16 ( real_int15_vector )
/** An INT 15,e820 memory map entry */
struct e820_entry {
/** Start of region */
uint64_t start;
/** Length of region */
uint64_t len;
/** Type of region */
uint32_t type;
} __attribute__ (( packed ));
#define E820_TYPE_RAM 1 /**< Normal memory */
#define E820_TYPE_RSVD 2 /**< Reserved and unavailable */
#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
/** Fake e820 map */
static struct e820_entry __text16_array ( e820map, [] ) __used = {
{ 0x00000000ULL, ( 0x000a0000ULL - 0x00000000ULL ), E820_TYPE_RAM },
{ 0x00100000ULL, ( 0xcfb50000ULL - 0x00100000ULL ), E820_TYPE_RAM },
{ 0xcfb50000ULL, ( 0xcfb64000ULL - 0xcfb50000ULL ), E820_TYPE_RSVD },
{ 0xcfb64000ULL, ( 0xcfb66000ULL - 0xcfb64000ULL ), E820_TYPE_RSVD },
{ 0xcfb66000ULL, ( 0xcfb85c00ULL - 0xcfb66000ULL ), E820_TYPE_ACPI },
{ 0xcfb85c00ULL, ( 0xd0000000ULL - 0xcfb85c00ULL ), E820_TYPE_RSVD },
{ 0xe0000000ULL, ( 0xf0000000ULL - 0xe0000000ULL ), E820_TYPE_RSVD },
{ 0xfe000000ULL, (0x100000000ULL - 0xfe000000ULL ), E820_TYPE_RSVD },
{0x100000000ULL, (0x230000000ULL -0x100000000ULL ), E820_TYPE_RAM },
};
#define e820map __use_text16 ( e820map )
void fake_e820 ( void ) {
__asm__ __volatile__ (
TEXT16_CODE ( "\nint15_fakee820:\n\t"
"pushfw\n\t"
"cmpl $0xe820, %%eax\n\t"
"jne 99f\n\t"
"cmpl $0x534d4150, %%edx\n\t"
"jne 99f\n\t"
"pushaw\n\t"
"movw %%sp, %%bp\n\t"
"andb $~0x01, 22(%%bp)\n\t" /* Clear return CF */
"leaw e820map(%%bx), %%si\n\t"
"cs rep movsb\n\t"
"popaw\n\t"
"movl %%edx, %%eax\n\t"
"addl $20, %%ebx\n\t"
"cmpl %0, %%ebx\n\t"
"jne 1f\n\t"
"xorl %%ebx,%%ebx\n\t"
"\n1:\n\t"
"popfw\n\t"
"iret\n\t"
"\n99:\n\t"
"popfw\n\t"
"ljmp *%%cs:real_int15_vector\n\t" )
: : "i" ( sizeof ( e820map ) ) );
hook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820,
&real_int15_vector );
}
void unfake_e820 ( void ) {
unhook_bios_interrupt ( 0x15, ( intptr_t ) int15_fakee820,
&real_int15_vector );
}

View File

@@ -1,234 +0,0 @@
/* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <assert.h>
#include <realmode.h>
#include <biosint.h>
#include <basemem.h>
#include <fakee820.h>
#include <ipxe/init.h>
#include <ipxe/io.h>
#include <ipxe/hidemem.h>
/** Set to true if you want to test a fake E820 map */
#define FAKE_E820 0
/** Alignment for hidden memory regions */
#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */
/**
* A hidden region of iPXE
*
* This represents a region that will be edited out of the system's
* memory map.
*
* This structure is accessed by assembly code, so must not be
* changed.
*/
struct hidden_region {
/** Physical start address */
uint64_t start;
/** Physical end address */
uint64_t end;
};
/** Hidden base memory */
extern struct hidden_region __data16 ( hidemem_base );
#define hidemem_base __use_data16 ( hidemem_base )
/** Hidden umalloc memory */
extern struct hidden_region __data16 ( hidemem_umalloc );
#define hidemem_umalloc __use_data16 ( hidemem_umalloc )
/** Hidden text memory */
extern struct hidden_region __data16 ( hidemem_textdata );
#define hidemem_textdata __use_data16 ( hidemem_textdata )
/** Assembly routine in e820mangler.S */
extern void int15();
/** Vector for storing original INT 15 handler */
extern struct segoff __text16 ( int15_vector );
#define int15_vector __use_text16 ( int15_vector )
/* The linker defines these symbols for us */
extern char _textdata[];
extern char _etextdata[];
extern char _text16_memsz[];
#define _text16_memsz ( ( size_t ) _text16_memsz )
extern char _data16_memsz[];
#define _data16_memsz ( ( size_t ) _data16_memsz )
/**
* Hide region of memory from system memory map
*
* @v region Hidden memory region
* @v start Start of region
* @v end End of region
*/
static void hide_region ( struct hidden_region *region,
physaddr_t start, physaddr_t end ) {
/* Some operating systems get a nasty shock if a region of the
* E820 map seems to start on a non-page boundary. Make life
* safer by rounding out our edited region.
*/
region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
}
/**
* Hide used base memory
*
*/
void hide_basemem ( void ) {
/* Hide from the top of free base memory to 640kB. Don't use
* hide_region(), because we don't want this rounded to the
* nearest page boundary.
*/
hidemem_base.start = ( get_fbms() * 1024 );
}
/**
* Hide umalloc() region
*
*/
void hide_umalloc ( physaddr_t start, physaddr_t end ) {
assert ( end <= virt_to_phys ( _textdata ) );
hide_region ( &hidemem_umalloc, start, end );
}
/**
* Hide .text and .data
*
*/
void hide_textdata ( void ) {
hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
virt_to_phys ( _etextdata ) );
}
/**
* Hide Etherboot
*
* Installs an INT 15 handler to edit Etherboot out of the memory map
* returned by the BIOS.
*/
static void hide_etherboot ( void ) {
struct memory_map memmap;
unsigned int rm_ds_top;
unsigned int rm_cs_top;
unsigned int fbms;
/* Dump memory map before mangling */
DBG ( "Hiding iPXE from system memory map\n" );
get_memmap ( &memmap );
/* Hook in fake E820 map, if we're testing one */
if ( FAKE_E820 ) {
DBG ( "Hooking in fake E820 map\n" );
fake_e820();
get_memmap ( &memmap );
}
/* Initialise the hidden regions */
hide_basemem();
hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
hide_textdata();
/* Some really moronic BIOSes bring up the PXE stack via the
* UNDI loader entry point and then don't bother to unload it
* before overwriting the code and data segments. If this
* happens, we really don't want to leave INT 15 hooked,
* because that will cause any loaded OS to die horribly as
* soon as it attempts to fetch the system memory map.
*
* We use a heuristic to guess whether or not we are being
* loaded sensibly.
*/
rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
fbms = get_fbms();
if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
"DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
DBG ( "Disabling INT 15 memory hiding\n" );
return;
}
/* Hook INT 15 */
hook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector );
/* Dump memory map after mangling */
DBG ( "Hidden iPXE from system memory map\n" );
get_memmap ( &memmap );
}
/**
* Unhide Etherboot
*
* Uninstalls the INT 15 handler installed by hide_etherboot(), if
* possible.
*/
static void unhide_etherboot ( int flags __unused ) {
struct memory_map memmap;
int rc;
/* If we have more than one hooked interrupt at this point, it
* means that some other vector is still hooked, in which case
* we can't safely unhook INT 15 because we need to keep our
* memory protected. (We expect there to be at least one
* hooked interrupt, because INT 15 itself is still hooked).
*/
if ( hooked_bios_interrupts > 1 ) {
DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
hooked_bios_interrupts );
return;
}
/* Try to unhook INT 15 */
if ( ( rc = unhook_bios_interrupt ( 0x15, ( intptr_t ) int15,
&int15_vector ) ) != 0 ) {
DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) );
/* Leave it hooked; there's nothing else we can do,
* and it should be intrinsically safe (though
* wasteful of RAM).
*/
}
/* Unhook fake E820 map, if used */
if ( FAKE_E820 )
unfake_e820();
/* Dump memory map after unhiding */
DBG ( "Unhidden iPXE from system memory map\n" );
get_memmap ( &memmap );
}
/** Hide Etherboot startup function */
struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.startup = hide_etherboot,
.shutdown = unhide_etherboot,
};

View File

@@ -1,343 +0,0 @@
/*
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <errno.h>
#include <realmode.h>
#include <bios.h>
#include <memsizes.h>
#include <ipxe/io.h>
/**
* @file
*
* Memory mapping
*
*/
/** Magic value for INT 15,e820 calls */
#define SMAP ( 0x534d4150 )
/** An INT 15,e820 memory map entry */
struct e820_entry {
/** Start of region */
uint64_t start;
/** Length of region */
uint64_t len;
/** Type of region */
uint32_t type;
/** Extended attributes (optional) */
uint32_t attrs;
} __attribute__ (( packed ));
#define E820_TYPE_RAM 1 /**< Normal memory */
#define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */
#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
#define E820_ATTR_ENABLED 0x00000001UL
#define E820_ATTR_NONVOLATILE 0x00000002UL
#define E820_ATTR_UNKNOWN 0xfffffffcUL
#define E820_MIN_SIZE 20
/** Buffer for INT 15,e820 calls */
static struct e820_entry __bss16 ( e820buf );
#define e820buf __use_data16 ( e820buf )
/** We are running during POST; inhibit INT 15,e820 and INT 15,e801 */
uint8_t __bss16 ( memmap_post );
#define memmap_post __use_data16 ( memmap_post )
/**
* Get size of extended memory via INT 15,e801
*
* @ret extmem Extended memory size, in kB, or 0
*/
static unsigned int extmemsize_e801 ( void ) {
uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
unsigned int flags;
unsigned int extmem;
/* Inhibit INT 15,e801 during POST */
if ( memmap_post ) {
DBG ( "INT 15,e801 not available during POST\n" );
return 0;
}
__asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
"int $0x15\n\t"
"pushfw\n\t"
"popw %w0\n\t" )
: "=R" ( flags ),
"=a" ( extmem_1m_to_16m_k ),
"=b" ( extmem_16m_plus_64k ),
"=c" ( confmem_1m_to_16m_k ),
"=d" ( confmem_16m_plus_64k )
: "a" ( 0xe801 ) );
if ( flags & CF ) {
DBG ( "INT 15,e801 failed with CF set\n" );
return 0;
}
if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) {
DBG ( "INT 15,e801 extmem=0, using confmem\n" );
extmem_1m_to_16m_k = confmem_1m_to_16m_k;
extmem_16m_plus_64k = confmem_16m_plus_64k;
}
extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) );
DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB "
"[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k,
extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) );
/* Sanity check. Some BIOSes report the entire 4GB address
* space as available, which cannot be correct (since that
* would leave no address space available for 32-bit PCI
* BARs).
*/
if ( extmem == ( 0x400000 - 0x400 ) ) {
DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" );
return 0;
}
return extmem;
}
/**
* Get size of extended memory via INT 15,88
*
* @ret extmem Extended memory size, in kB
*/
static unsigned int extmemsize_88 ( void ) {
uint16_t extmem;
/* Ignore CF; it is not reliable for this call */
__asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
: "=a" ( extmem ) : "a" ( 0x8800 ) );
DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n",
extmem, ( 0x100000 + ( extmem * 1024 ) ) );
return extmem;
}
/**
* Get size of extended memory
*
* @ret extmem Extended memory size, in kB
*
* Note that this is only an approximation; for an accurate picture,
* use the E820 memory map obtained via get_memmap();
*/
unsigned int extmemsize ( void ) {
unsigned int extmem_e801;
unsigned int extmem_88;
/* Try INT 15,e801 first, then fall back to INT 15,88 */
extmem_88 = extmemsize_88();
extmem_e801 = extmemsize_e801();
return ( extmem_e801 ? extmem_e801 : extmem_88 );
}
/**
* Get e820 memory map
*
* @v memmap Memory map to fill in
* @ret rc Return status code
*/
static int meme820 ( struct memory_map *memmap ) {
struct memory_region *region = memmap->regions;
struct memory_region *prev_region = NULL;
uint32_t next = 0;
uint32_t smap;
size_t size;
unsigned int flags;
unsigned int discard_D;
/* Inhibit INT 15,e820 during POST */
if ( memmap_post ) {
DBG ( "INT 15,e820 not available during POST\n" );
return -ENOTTY;
}
/* Clear the E820 buffer. Do this once before starting,
* rather than on each call; some BIOSes rely on the contents
* being preserved between calls.
*/
memset ( &e820buf, 0, sizeof ( e820buf ) );
do {
/* Some BIOSes corrupt %esi for fun. Guard against
* this by telling gcc that all non-output registers
* may be corrupted.
*/
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t"
"stc\n\t"
"int $0x15\n\t"
"pushfw\n\t"
"popw %%dx\n\t"
"popl %%ebp\n\t" )
: "=a" ( smap ), "=b" ( next ),
"=c" ( size ), "=d" ( flags ),
"=D" ( discard_D )
: "a" ( 0xe820 ), "b" ( next ),
"D" ( __from_data16 ( &e820buf ) ),
"c" ( sizeof ( e820buf ) ),
"d" ( SMAP )
: "esi", "memory" );
if ( smap != SMAP ) {
DBG ( "INT 15,e820 failed SMAP signature check\n" );
return -ENOTSUP;
}
if ( size < E820_MIN_SIZE ) {
DBG ( "INT 15,e820 returned only %zd bytes\n", size );
return -EINVAL;
}
if ( flags & CF ) {
DBG ( "INT 15,e820 terminated on CF set\n" );
break;
}
/* If first region is not RAM, assume map is invalid */
if ( ( memmap->count == 0 ) &&
( e820buf.type != E820_TYPE_RAM ) ) {
DBG ( "INT 15,e820 failed, first entry not RAM\n" );
return -EINVAL;
}
DBG ( "INT 15,e820 region [%llx,%llx) type %d",
e820buf.start, ( e820buf.start + e820buf.len ),
( int ) e820buf.type );
if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED )
? "enabled" : "disabled" ) );
if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
DBG ( ", non-volatile" );
if ( e820buf.attrs & E820_ATTR_UNKNOWN )
DBG ( ", other [%08x]", e820buf.attrs );
DBG ( ")" );
}
DBG ( "\n" );
/* Discard non-RAM regions */
if ( e820buf.type != E820_TYPE_RAM )
continue;
/* Check extended attributes, if present */
if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) )
continue;
if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
continue;
}
region->start = e820buf.start;
region->end = e820buf.start + e820buf.len;
/* Check for adjacent regions and merge them */
if ( prev_region && ( region->start == prev_region->end ) ) {
prev_region->end = region->end;
} else {
prev_region = region;
region++;
memmap->count++;
}
if ( memmap->count >= ( sizeof ( memmap->regions ) /
sizeof ( memmap->regions[0] ) ) ) {
DBG ( "INT 15,e820 too many regions returned\n" );
/* Not a fatal error; what we've got so far at
* least represents valid regions of memory,
* even if we couldn't get them all.
*/
break;
}
} while ( next != 0 );
/* Sanity checks. Some BIOSes report complete garbage via INT
* 15,e820 (especially at POST time), despite passing the
* signature checks. We currently check for a base memory
* region (starting at 0) and at least one high memory region
* (starting at 0x100000).
*/
if ( memmap->count < 2 ) {
DBG ( "INT 15,e820 returned only %d regions; assuming "
"insane\n", memmap->count );
return -EINVAL;
}
if ( memmap->regions[0].start != 0 ) {
DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); "
"assuming insane\n", memmap->regions[0].start );
return -EINVAL;
}
if ( memmap->regions[1].start != 0x100000 ) {
DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); "
"assuming insane\n", memmap->regions[0].start );
return -EINVAL;
}
return 0;
}
/**
* Get memory map
*
* @v memmap Memory map to fill in
*/
void x86_get_memmap ( struct memory_map *memmap ) {
unsigned int basemem, extmem;
int rc;
DBG ( "Fetching system memory map\n" );
/* Clear memory map */
memset ( memmap, 0, sizeof ( *memmap ) );
/* Get base and extended memory sizes */
basemem = basememsize();
DBG ( "FBMS base memory size %d kB [0,%x)\n",
basemem, ( basemem * 1024 ) );
extmem = extmemsize();
/* Try INT 15,e820 first */
if ( ( rc = meme820 ( memmap ) ) == 0 ) {
DBG ( "Obtained system memory map via INT 15,e820\n" );
return;
}
/* Fall back to constructing a map from basemem and extmem sizes */
DBG ( "INT 15,e820 failed; constructing map\n" );
memmap->regions[0].end = ( basemem * 1024 );
memmap->regions[1].start = 0x100000;
memmap->regions[1].end = 0x100000 + ( extmem * 1024 );
memmap->count = 2;
}
PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap );

View File

@@ -1,114 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <realmode.h>
#include <pnpbios.h>
/** @file
*
* PnP BIOS
*
*/
/** PnP BIOS structure */
struct pnp_bios {
/** Signature
*
* Must be equal to @c PNP_BIOS_SIGNATURE
*/
uint32_t signature;
/** Version as BCD (e.g. 1.0 is 0x10) */
uint8_t version;
/** Length of this structure */
uint8_t length;
/** System capabilities */
uint16_t control;
/** Checksum */
uint8_t checksum;
} __attribute__ (( packed ));
/** Signature for a PnP BIOS structure */
#define PNP_BIOS_SIGNATURE \
( ( '$' << 0 ) + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
/**
* Test address for PnP BIOS structure
*
* @v offset Offset within BIOS segment to test
* @ret rc Return status code
*/
static int is_pnp_bios ( unsigned int offset ) {
union {
struct pnp_bios pnp_bios;
uint8_t bytes[256]; /* 256 is maximum length possible */
} u;
size_t len;
unsigned int i;
uint8_t sum = 0;
/* Read start of header and verify signature */
copy_from_real ( &u.pnp_bios, BIOS_SEG, offset, sizeof ( u.pnp_bios ));
if ( u.pnp_bios.signature != PNP_BIOS_SIGNATURE )
return -EINVAL;
/* Read whole header and verify checksum */
len = u.pnp_bios.length;
copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
for ( i = 0 ; i < len ; i++ ) {
sum += u.bytes[i];
}
if ( sum != 0 )
return -EINVAL;
DBG ( "Found PnP BIOS at %04x:%04x\n", BIOS_SEG, offset );
return 0;
}
/**
* Locate Plug-and-Play BIOS
*
* @ret pnp_offset Offset of PnP BIOS structure within BIOS segment
*
* The PnP BIOS structure will be at BIOS_SEG:pnp_offset. If no PnP
* BIOS is found, -1 is returned.
*/
int find_pnp_bios ( void ) {
static int pnp_offset = 0;
if ( pnp_offset )
return pnp_offset;
for ( pnp_offset = 0 ; pnp_offset < 0x10000 ; pnp_offset += 0x10 ) {
if ( is_pnp_bios ( pnp_offset ) == 0 )
return pnp_offset;
}
pnp_offset = -1;
return pnp_offset;
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
#include <ipxe/netdevice.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
#include <hci/ifmgmt_cmd.h>
#include <pxe_call.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* PXE commands
*
*/
/** "startpxe" options */
struct startpxe_options {};
/** "startpxe" option list */
static struct option_descriptor startpxe_opts[] = {};
/**
* "startpxe" payload
*
* @v netdev Network device
* @v opts Command options
* @ret rc Return status code
*/
static int startpxe_payload ( struct net_device *netdev,
struct startpxe_options *opts __unused ) {
if ( netdev_is_open ( netdev ) )
pxe_activate ( netdev );
return 0;
}
/** "startpxe" command descriptor */
static struct ifcommon_command_descriptor startpxe_cmd =
IFCOMMON_COMMAND_DESC ( struct startpxe_options, startpxe_opts,
0, MAX_ARGUMENTS, "[<interface>]",
startpxe_payload, 0 );
/**
* The "startpxe" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int startpxe_exec ( int argc, char **argv ) {
return ifcommon_exec ( argc, argv, &startpxe_cmd );
}
/** "stoppxe" options */
struct stoppxe_options {};
/** "stoppxe" option list */
static struct option_descriptor stoppxe_opts[] = {};
/** "stoppxe" command descriptor */
static struct command_descriptor stoppxe_cmd =
COMMAND_DESC ( struct stoppxe_options, stoppxe_opts, 0, 0, NULL );
/**
* The "stoppxe" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int stoppxe_exec ( int argc __unused, char **argv __unused ) {
struct stoppxe_options opts;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &stoppxe_cmd, &opts ) ) != 0 )
return rc;
pxe_deactivate();
return 0;
}
/** PXE commands */
struct command pxe_commands[] __command = {
{
.name = "startpxe",
.exec = startpxe_exec,
},
{
.name = "stoppxe",
.exec = stoppxe_exec,
},
};

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* x86 bootsector image format
*
*/
#include <errno.h>
#include <realmode.h>
#include <biosint.h>
#include <bootsector.h>
#include <ipxe/console.h>
/** Vector for storing original INT 18 handler
*
* We do not chain to this vector, so there is no need to place it in
* .text16.
*/
static struct segoff int18_vector;
/** Vector for storing original INT 19 handler
*
* We do not chain to this vector, so there is no need to place it in
* .text16.
*/
static struct segoff int19_vector;
/** Restart point for INT 18 or 19 */
extern void bootsector_exec_fail ( void );
/**
* Jump to preloaded bootsector
*
* @v segment Real-mode segment
* @v offset Real-mode offset
* @v drive Drive number to pass to boot sector
* @ret rc Return status code
*/
int call_bootsector ( unsigned int segment, unsigned int offset,
unsigned int drive ) {
int discard_b, discard_D, discard_d;
/* Reset console, since boot sector will probably use it */
console_reset();
DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset );
/* Hook INTs 18 and 19 to capture failure paths */
hook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
hook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail,
&int19_vector );
/* Boot the loaded sector
*
* We assume that the boot sector may completely destroy our
* real-mode stack, so we preserve everything we need in
* static storage.
*/
__asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
"popw %%cs:saved_retaddr\n\t"
/* Save stack pointer */
"movw %%ss, %%ax\n\t"
"movw %%ax, %%cs:saved_ss\n\t"
"movw %%sp, %%cs:saved_sp\n\t"
/* Save frame pointer (gcc bug) */
"movl %%ebp, %%cs:saved_ebp\n\t"
/* Prepare jump to boot sector */
"pushw %%bx\n\t"
"pushw %%di\n\t"
/* Clear all registers */
"xorl %%eax, %%eax\n\t"
"xorl %%ebx, %%ebx\n\t"
"xorl %%ecx, %%ecx\n\t"
/* %edx contains drive number */
"xorl %%esi, %%esi\n\t"
"xorl %%edi, %%edi\n\t"
"xorl %%ebp, %%ebp\n\t"
"movw %%ax, %%ds\n\t"
"movw %%ax, %%es\n\t"
"movw %%ax, %%fs\n\t"
"movw %%ax, %%gs\n\t"
/* Jump to boot sector */
"sti\n\t"
"lret\n\t"
/* Preserved variables */
"\nsaved_ebp: .long 0\n\t"
"\nsaved_ss: .word 0\n\t"
"\nsaved_sp: .word 0\n\t"
"\nsaved_retaddr: .word 0\n\t"
/* Boot failure return point */
"\nbootsector_exec_fail:\n\t"
/* Restore frame pointer (gcc bug) */
"movl %%cs:saved_ebp, %%ebp\n\t"
/* Restore stack pointer */
"movw %%cs:saved_ss, %%ax\n\t"
"movw %%ax, %%ss\n\t"
"movw %%cs:saved_sp, %%sp\n\t"
/* Return via saved address */
"jmp *%%cs:saved_retaddr\n\t" )
: "=b" ( discard_b ), "=D" ( discard_D ),
"=d" ( discard_d )
: "b" ( segment ), "D" ( offset ),
"d" ( drive )
: "eax", "ecx", "esi" );
DBG ( "Booted disk returned via INT 18 or 19\n" );
/* Unhook INTs 18 and 19 */
unhook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
unhook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail,
&int19_vector );
return -ECANCELED;
}

View File

@@ -1,669 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Linux bzImage image format
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <bzimage.h>
#include <initrd.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/init.h>
#include <ipxe/cpio.h>
#include <ipxe/features.h>
FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
/**
* bzImage context
*/
struct bzimage_context {
/** Boot protocol version */
unsigned int version;
/** Real-mode kernel portion load segment address */
unsigned int rm_kernel_seg;
/** Real-mode kernel portion load address */
userptr_t rm_kernel;
/** Real-mode kernel portion file size */
size_t rm_filesz;
/** Real-mode heap top (offset from rm_kernel) */
size_t rm_heap;
/** Command line (offset from rm_kernel) */
size_t rm_cmdline;
/** Command line maximum length */
size_t cmdline_size;
/** Real-mode kernel portion total memory size */
size_t rm_memsz;
/** Non-real-mode kernel portion load address */
userptr_t pm_kernel;
/** Non-real-mode kernel portion file and memory size */
size_t pm_sz;
/** Video mode */
unsigned int vid_mode;
/** Memory limit */
uint64_t mem_limit;
/** Initrd address */
physaddr_t ramdisk_image;
/** Initrd size */
physaddr_t ramdisk_size;
/** Command line magic block */
struct bzimage_cmdline cmdline_magic;
/** bzImage header */
struct bzimage_header bzhdr;
};
/**
* Parse bzImage header
*
* @v image bzImage file
* @v bzimg bzImage context
* @v src bzImage to parse
* @ret rc Return status code
*/
static int bzimage_parse_header ( struct image *image,
struct bzimage_context *bzimg,
userptr_t src ) {
unsigned int syssize;
int is_bzimage;
/* Sanity check */
if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) {
DBGC ( image, "bzImage %p too short for kernel header\n",
image );
return -ENOEXEC;
}
/* Read in header structures */
memset ( bzimg, 0, sizeof ( *bzimg ) );
copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET,
sizeof ( bzimg->cmdline_magic ) );
copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET,
sizeof ( bzimg->bzhdr ) );
/* Calculate size of real-mode portion */
bzimg->rm_filesz = ( ( ( bzimg->bzhdr.setup_sects ?
bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9 );
if ( bzimg->rm_filesz > image->len ) {
DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
image, bzimg->rm_filesz );
return -ENOEXEC;
}
bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE;
/* Calculate size of protected-mode portion */
bzimg->pm_sz = ( image->len - bzimg->rm_filesz );
syssize = ( ( bzimg->pm_sz + 15 ) / 16 );
/* Check for signatures and determine version */
if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) {
DBGC ( image, "bzImage %p missing 55AA signature\n", image );
return -ENOEXEC;
}
if ( bzimg->bzhdr.header == BZI_SIGNATURE ) {
/* 2.00+ */
bzimg->version = bzimg->bzhdr.version;
} else {
/* Pre-2.00. Check that the syssize field is correct,
* as a guard against accepting arbitrary binary data,
* since the 55AA check is pretty lax. Note that the
* syssize field is unreliable for protocols between
* 2.00 and 2.03 inclusive, so we should not always
* check this field.
*/
bzimg->version = 0x0100;
if ( bzimg->bzhdr.syssize != syssize ) {
DBGC ( image, "bzImage %p bad syssize %x (expected "
"%x)\n", image, bzimg->bzhdr.syssize, syssize );
return -ENOEXEC;
}
}
/* Determine image type */
is_bzimage = ( ( bzimg->version >= 0x0200 ) ?
( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 );
/* Calculate load address of real-mode portion */
bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 );
bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 );
/* Allow space for the stack and heap */
bzimg->rm_memsz += BZI_STACK_SIZE;
bzimg->rm_heap = bzimg->rm_memsz;
/* Allow space for the command line */
bzimg->rm_cmdline = bzimg->rm_memsz;
bzimg->rm_memsz += BZI_CMDLINE_SIZE;
/* Calculate load address of protected-mode portion */
bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR
: BZI_LOAD_LOW_ADDR );
/* Extract video mode */
bzimg->vid_mode = bzimg->bzhdr.vid_mode;
/* Extract memory limit */
bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ?
bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX );
/* Extract command line size */
bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ?
bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE );
DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx "
"cmdlen %zd\n", image, bzimg->version,
user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz,
user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz,
bzimg->cmdline_size );
return 0;
}
/**
* Update bzImage header in loaded kernel
*
* @v image bzImage file
* @v bzimg bzImage context
* @v dst bzImage to update
*/
static void bzimage_update_header ( struct image *image,
struct bzimage_context *bzimg,
userptr_t dst ) {
/* Set loader type */
if ( bzimg->version >= 0x0200 )
bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_IPXE;
/* Set heap end pointer */
if ( bzimg->version >= 0x0201 ) {
bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 );
bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP;
}
/* Set command line */
if ( bzimg->version >= 0x0202 ) {
bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel,
bzimg->rm_cmdline );
} else {
bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC;
bzimg->cmdline_magic.offset = bzimg->rm_cmdline;
if ( bzimg->version >= 0x0200 )
bzimg->bzhdr.setup_move_size = bzimg->rm_memsz;
}
/* Set video mode */
bzimg->bzhdr.vid_mode = bzimg->vid_mode;
/* Set initrd address */
if ( bzimg->version >= 0x0200 ) {
bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image;
bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size;
}
/* Write out header structures */
copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic,
sizeof ( bzimg->cmdline_magic ) );
copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr,
sizeof ( bzimg->bzhdr ) );
DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode );
}
/**
* Parse kernel command line for bootloader parameters
*
* @v image bzImage file
* @v bzimg bzImage context
* @v cmdline Kernel command line
* @ret rc Return status code
*/
static int bzimage_parse_cmdline ( struct image *image,
struct bzimage_context *bzimg,
const char *cmdline ) {
char *vga;
char *mem;
/* Look for "vga=" */
if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
vga += 4;
if ( strcmp ( vga, "normal" ) == 0 ) {
bzimg->vid_mode = BZI_VID_MODE_NORMAL;
} else if ( strcmp ( vga, "ext" ) == 0 ) {
bzimg->vid_mode = BZI_VID_MODE_EXT;
} else if ( strcmp ( vga, "ask" ) == 0 ) {
bzimg->vid_mode = BZI_VID_MODE_ASK;
} else {
bzimg->vid_mode = strtoul ( vga, &vga, 0 );
if ( *vga && ( *vga != ' ' ) ) {
DBGC ( image, "bzImage %p strange \"vga=\""
"terminator '%c'\n", image, *vga );
}
}
}
/* Look for "mem=" */
if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
mem += 4;
bzimg->mem_limit = strtoul ( mem, &mem, 0 );
switch ( *mem ) {
case 'G':
case 'g':
bzimg->mem_limit <<= 10;
case 'M':
case 'm':
bzimg->mem_limit <<= 10;
case 'K':
case 'k':
bzimg->mem_limit <<= 10;
break;
case '\0':
case ' ':
break;
default:
DBGC ( image, "bzImage %p strange \"mem=\" "
"terminator '%c'\n", image, *mem );
break;
}
bzimg->mem_limit -= 1;
}
return 0;
}
/**
* Set command line
*
* @v image bzImage image
* @v bzimg bzImage context
* @v cmdline Kernel command line
*/
static void bzimage_set_cmdline ( struct image *image,
struct bzimage_context *bzimg,
const char *cmdline ) {
size_t cmdline_len;
/* Copy command line down to real-mode portion */
cmdline_len = ( strlen ( cmdline ) + 1 );
if ( cmdline_len > bzimg->cmdline_size )
cmdline_len = bzimg->cmdline_size;
copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline,
cmdline, cmdline_len );
DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
}
/**
* Parse standalone image command line for cpio parameters
*
* @v image bzImage file
* @v cpio CPIO header
* @v cmdline Command line
*/
static void bzimage_parse_cpio_cmdline ( struct image *image,
struct cpio_header *cpio,
const char *cmdline ) {
char *arg;
char *end;
unsigned int mode;
/* Look for "mode=" */
if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
arg += 5;
mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
if ( *end && ( *end != ' ' ) ) {
DBGC ( image, "bzImage %p strange \"mode=\""
"terminator '%c'\n", image, *end );
}
cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) );
}
}
/**
* Align initrd length
*
* @v len Length
* @ret len Length rounded up to INITRD_ALIGN
*/
static inline size_t bzimage_align ( size_t len ) {
return ( ( len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
}
/**
* Load initrd
*
* @v image bzImage image
* @v initrd initrd image
* @v address Address at which to load, or UNULL
* @ret len Length of loaded image, excluding zero-padding
*/
static size_t bzimage_load_initrd ( struct image *image,
struct image *initrd,
userptr_t address ) {
char *filename = initrd->cmdline;
char *cmdline;
struct cpio_header cpio;
size_t offset;
size_t name_len;
size_t pad_len;
/* Do not include kernel image itself as an initrd */
if ( initrd == image )
return 0;
/* Create cpio header for non-prebuilt images */
if ( filename && filename[0] ) {
cmdline = strchr ( filename, ' ' );
name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) )
: strlen ( filename ) ) + 1 /* NUL */ );
memset ( &cpio, '0', sizeof ( cpio ) );
memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) );
cpio_set_field ( cpio.c_mode, 0100644 );
cpio_set_field ( cpio.c_nlink, 1 );
cpio_set_field ( cpio.c_filesize, initrd->len );
cpio_set_field ( cpio.c_namesize, name_len );
if ( cmdline ) {
bzimage_parse_cpio_cmdline ( image, &cpio,
( cmdline + 1 /* ' ' */ ));
}
offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 );
} else {
offset = 0;
name_len = 0;
}
/* Copy in initrd image body (and cpio header if applicable) */
if ( address ) {
memmove_user ( address, offset, initrd->data, 0, initrd->len );
if ( offset ) {
memset_user ( address, 0, 0, offset );
copy_to_user ( address, 0, &cpio, sizeof ( cpio ) );
copy_to_user ( address, sizeof ( cpio ), filename,
( name_len - 1 /* NUL (or space) */ ) );
}
DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
"%s%s\n", image, initrd, user_to_phys ( address, 0 ),
user_to_phys ( address, offset ),
user_to_phys ( address, ( offset + initrd->len ) ),
( filename ? " " : "" ), ( filename ? filename : "" ) );
DBGC2_MD5A ( image, user_to_phys ( address, offset ),
user_to_virt ( address, offset ), initrd->len );
}
offset += initrd->len;
/* Zero-pad to next INITRD_ALIGN boundary */
pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
if ( address )
memset_user ( address, offset, 0, pad_len );
return offset;
}
/**
* Check that initrds can be loaded
*
* @v image bzImage image
* @v bzimg bzImage context
* @ret rc Return status code
*/
static int bzimage_check_initrds ( struct image *image,
struct bzimage_context *bzimg ) {
struct image *initrd;
userptr_t bottom;
size_t len = 0;
int rc;
/* Calculate total loaded length of initrds */
for_each_image ( initrd ) {
/* Skip kernel */
if ( initrd == image )
continue;
/* Calculate length */
len += bzimage_load_initrd ( image, initrd, UNULL );
len = bzimage_align ( len );
DBGC ( image, "bzImage %p initrd %p from [%#08lx,%#08lx)%s%s\n",
image, initrd, user_to_phys ( initrd->data, 0 ),
user_to_phys ( initrd->data, initrd->len ),
( initrd->cmdline ? " " : "" ),
( initrd->cmdline ? initrd->cmdline : "" ) );
DBGC2_MD5A ( image, user_to_phys ( initrd->data, 0 ),
user_to_virt ( initrd->data, 0 ), initrd->len );
}
/* Calculate lowest usable address */
bottom = userptr_add ( bzimg->pm_kernel, bzimg->pm_sz );
/* Check that total length fits within space available for
* reshuffling. This is a conservative check, since CPIO
* headers are not present during reshuffling, but this
* doesn't hurt and keeps the code simple.
*/
if ( ( rc = initrd_reshuffle_check ( len, bottom ) ) != 0 ) {
DBGC ( image, "bzImage %p failed reshuffle check: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Check that total length fits within kernel's memory limit */
if ( user_to_phys ( bottom, len ) > bzimg->mem_limit ) {
DBGC ( image, "bzImage %p not enough space for initrds\n",
image );
return -ENOBUFS;
}
return 0;
}
/**
* Load initrds, if any
*
* @v image bzImage image
* @v bzimg bzImage context
*/
static void bzimage_load_initrds ( struct image *image,
struct bzimage_context *bzimg ) {
struct image *initrd;
struct image *highest = NULL;
struct image *other;
userptr_t top;
userptr_t dest;
size_t offset;
size_t len;
/* Reshuffle initrds into desired order */
initrd_reshuffle ( userptr_add ( bzimg->pm_kernel, bzimg->pm_sz ) );
/* Find highest initrd */
for_each_image ( initrd ) {
if ( ( highest == NULL ) ||
( userptr_sub ( initrd->data, highest->data ) > 0 ) ) {
highest = initrd;
}
}
/* Do nothing if there are no initrds */
if ( ! highest )
return;
/* Find highest usable address */
top = userptr_add ( highest->data, bzimage_align ( highest->len ) );
if ( user_to_phys ( top, 0 ) > bzimg->mem_limit )
top = phys_to_user ( bzimg->mem_limit );
DBGC ( image, "bzImage %p loading initrds from %#08lx downwards\n",
image, user_to_phys ( top, 0 ) );
/* Load initrds in order */
for_each_image ( initrd ) {
/* Calculate cumulative length of following
* initrds (including padding).
*/
offset = 0;
for_each_image ( other ) {
if ( other == initrd )
offset = 0;
offset += bzimage_load_initrd ( image, other, UNULL );
offset = bzimage_align ( offset );
}
/* Load initrd at this address */
dest = userptr_add ( top, -offset );
len = bzimage_load_initrd ( image, initrd, dest );
/* Record initrd location */
if ( ! bzimg->ramdisk_image )
bzimg->ramdisk_image = user_to_phys ( dest, 0 );
bzimg->ramdisk_size = ( user_to_phys ( dest, len ) -
bzimg->ramdisk_image );
}
DBGC ( image, "bzImage %p initrds at [%#08lx,%#08lx)\n",
image, bzimg->ramdisk_image,
( bzimg->ramdisk_image + bzimg->ramdisk_size ) );
}
/**
* Execute bzImage image
*
* @v image bzImage image
* @ret rc Return status code
*/
static int bzimage_exec ( struct image *image ) {
struct bzimage_context bzimg;
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
int rc;
/* Read and parse header from image */
if ( ( rc = bzimage_parse_header ( image, &bzimg,
image->data ) ) != 0 )
return rc;
/* Prepare segments */
if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz,
bzimg.rm_memsz ) ) != 0 ) {
DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
image, strerror ( rc ) );
return rc;
}
if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz,
bzimg.pm_sz ) ) != 0 ) {
DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Parse command line for bootloader parameters */
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
return rc;
/* Check that initrds can be loaded */
if ( ( rc = bzimage_check_initrds ( image, &bzimg ) ) != 0 )
return rc;
/* Remove kernel from image list (without invalidating image pointer) */
unregister_image ( image_get ( image ) );
/* Load segments */
memcpy_user ( bzimg.rm_kernel, 0, image->data,
0, bzimg.rm_filesz );
memcpy_user ( bzimg.pm_kernel, 0, image->data,
bzimg.rm_filesz, bzimg.pm_sz );
/* Store command line */
bzimage_set_cmdline ( image, &bzimg, cmdline );
/* Prepare for exiting. Must do this before loading initrds,
* since loading the initrds will corrupt the external heap.
*/
shutdown_boot();
/* Load any initrds */
bzimage_load_initrds ( image, &bzimg );
/* Update kernel header */
bzimage_update_header ( image, &bzimg, bzimg.rm_kernel );
DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
"(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ),
bzimg.rm_kernel_seg, bzimg.rm_heap );
/* Jump to the kernel */
__asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
"movw %w0, %%es\n\t"
"movw %w0, %%fs\n\t"
"movw %w0, %%gs\n\t"
"movw %w0, %%ss\n\t"
"movw %w1, %%sp\n\t"
"pushw %w2\n\t"
"pushw $0\n\t"
"lret\n\t" )
: : "R" ( bzimg.rm_kernel_seg ),
"R" ( bzimg.rm_heap ),
"R" ( bzimg.rm_kernel_seg + 0x20 ) );
/* There is no way for the image to return, since we provide
* no return address.
*/
assert ( 0 );
return -ECANCELED; /* -EIMPOSSIBLE */
}
/**
* Probe bzImage image
*
* @v image bzImage file
* @ret rc Return status code
*/
int bzimage_probe ( struct image *image ) {
struct bzimage_context bzimg;
int rc;
/* Read and parse header from image */
if ( ( rc = bzimage_parse_header ( image, &bzimg,
image->data ) ) != 0 )
return rc;
return 0;
}
/** Linux bzImage image type */
struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = {
.name = "bzImage",
.probe = bzimage_probe,
.exec = bzimage_exec,
};

View File

@@ -1,145 +0,0 @@
/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <elf.h>
#include <ipxe/image.h>
#include <ipxe/elf.h>
#include <ipxe/features.h>
#include <ipxe/init.h>
/**
* @file
*
* ELF bootable image
*
*/
FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 );
/**
* Execute ELF image
*
* @v image ELF image
* @ret rc Return status code
*/
static int elfboot_exec ( struct image *image ) {
physaddr_t entry;
physaddr_t max;
int rc;
/* Load the image using core ELF support */
if ( ( rc = elf_load ( image, &entry, &max ) ) != 0 ) {
DBGC ( image, "ELF %p could not load: %s\n",
image, strerror ( rc ) );
return rc;
}
/* An ELF image has no callback interface, so we need to shut
* down before invoking it.
*/
shutdown_boot();
/* Jump to OS with flat physical addressing */
DBGC ( image, "ELF %p starting execution at %lx\n", image, entry );
__asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"call *%%edi\n\t"
"popl %%ebp\n\t" /* gcc bug */ )
: : "D" ( entry )
: "eax", "ebx", "ecx", "edx", "esi", "memory" );
DBGC ( image, "ELF %p returned\n", image );
/* It isn't safe to continue after calling shutdown() */
while ( 1 ) {}
return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
}
/**
* Check that ELF segment uses flat physical addressing
*
* @v image ELF file
* @v phdr ELF program header
* @v dest Destination address
* @ret rc Return status code
*/
static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr,
physaddr_t dest ) {
/* Check that ELF segment uses flat physical addressing */
if ( phdr->p_vaddr != dest ) {
DBGC ( image, "ELF %p uses virtual addressing (phys %x, "
"virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr );
return -ENOEXEC;
}
return 0;
}
/**
* Probe ELF image
*
* @v image ELF file
* @ret rc Return status code
*/
static int elfboot_probe ( struct image *image ) {
Elf32_Ehdr ehdr;
static const uint8_t e_ident[] = {
[EI_MAG0] = ELFMAG0,
[EI_MAG1] = ELFMAG1,
[EI_MAG2] = ELFMAG2,
[EI_MAG3] = ELFMAG3,
[EI_CLASS] = ELFCLASS32,
[EI_DATA] = ELFDATA2LSB,
[EI_VERSION] = EV_CURRENT,
};
physaddr_t entry;
physaddr_t max;
int rc;
/* Read ELF header */
copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) {
DBGC ( image, "Invalid ELF identifier\n" );
return -ENOEXEC;
}
/* Check that this image uses flat physical addressing */
if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment,
&entry, &max ) ) != 0 ) {
DBGC ( image, "Unloadable ELF image\n" );
return rc;
}
return 0;
}
/** ELF image type */
struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = {
.name = "ELF",
.probe = elfboot_probe,
.exec = elfboot_exec,
};

View File

@@ -1,304 +0,0 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <initrd.h>
#include <ipxe/image.h>
#include <ipxe/uaccess.h>
#include <ipxe/init.h>
#include <ipxe/memblock.h>
/** @file
*
* Initial ramdisk (initrd) reshuffling
*
*/
/** Maximum address available for initrd */
userptr_t initrd_top;
/** Minimum address available for initrd */
userptr_t initrd_bottom;
/**
* Squash initrds as high as possible in memory
*
* @v top Highest possible address
* @ret used Lowest address used by initrds
*/
static userptr_t initrd_squash_high ( userptr_t top ) {
userptr_t current = top;
struct image *initrd;
struct image *highest;
size_t len;
/* Squash up any initrds already within or below the region */
while ( 1 ) {
/* Find the highest image not yet in its final position */
highest = NULL;
for_each_image ( initrd ) {
if ( ( userptr_sub ( initrd->data, current ) < 0 ) &&
( ( highest == NULL ) ||
( userptr_sub ( initrd->data,
highest->data ) > 0 ) ) ) {
highest = initrd;
}
}
if ( ! highest )
break;
/* Move this image to its final position */
len = ( ( highest->len + INITRD_ALIGN - 1 ) &
~( INITRD_ALIGN - 1 ) );
current = userptr_sub ( current, len );
DBGC ( &images, "INITRD squashing %s [%#08lx,%#08lx)->"
"[%#08lx,%#08lx)\n", highest->name,
user_to_phys ( highest->data, 0 ),
user_to_phys ( highest->data, highest->len ),
user_to_phys ( current, 0 ),
user_to_phys ( current, highest->len ) );
memmove_user ( current, 0, highest->data, 0, highest->len );
highest->data = current;
}
/* Copy any remaining initrds (e.g. embedded images) to the region */
for_each_image ( initrd ) {
if ( userptr_sub ( initrd->data, top ) >= 0 ) {
len = ( ( initrd->len + INITRD_ALIGN - 1 ) &
~( INITRD_ALIGN - 1 ) );
current = userptr_sub ( current, len );
DBGC ( &images, "INITRD copying %s [%#08lx,%#08lx)->"
"[%#08lx,%#08lx)\n", initrd->name,
user_to_phys ( initrd->data, 0 ),
user_to_phys ( initrd->data, initrd->len ),
user_to_phys ( current, 0 ),
user_to_phys ( current, initrd->len ) );
memcpy_user ( current, 0, initrd->data, 0,
initrd->len );
initrd->data = current;
}
}
return current;
}
/**
* Swap position of two adjacent initrds
*
* @v low Lower initrd
* @v high Higher initrd
* @v free Free space
* @v free_len Length of free space
*/
static void initrd_swap ( struct image *low, struct image *high,
userptr_t free, size_t free_len ) {
size_t len = 0;
size_t frag_len;
size_t new_len;
DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
"%s\n", low->name, user_to_phys ( low->data, 0 ),
user_to_phys ( low->data, low->len ),
user_to_phys ( high->data, 0 ),
user_to_phys ( high->data, high->len ), high->name );
/* Round down length of free space */
free_len &= ~( INITRD_ALIGN - 1 );
assert ( free_len > 0 );
/* Swap image data */
while ( len < high->len ) {
/* Calculate maximum fragment length */
frag_len = ( high->len - len );
if ( frag_len > free_len )
frag_len = free_len;
new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
~( INITRD_ALIGN - 1 ) );
/* Swap fragments */
memcpy_user ( free, 0, high->data, len, frag_len );
memmove_user ( low->data, new_len, low->data, len, low->len );
memcpy_user ( low->data, len, free, 0, frag_len );
len = new_len;
}
/* Adjust data pointers */
high->data = low->data;
low->data = userptr_add ( low->data, len );
}
/**
* Swap position of any two adjacent initrds not currently in the correct order
*
* @v free Free space
* @v free_len Length of free space
* @ret swapped A pair of initrds was swapped
*/
static int initrd_swap_any ( userptr_t free, size_t free_len ) {
struct image *low;
struct image *high;
size_t padded_len;
userptr_t adjacent;
/* Find any pair of initrds that can be swapped */
for_each_image ( low ) {
/* Calculate location of adjacent image (if any) */
padded_len = ( ( low->len + INITRD_ALIGN - 1 ) &
~( INITRD_ALIGN - 1 ) );
adjacent = userptr_add ( low->data, padded_len );
/* Search for adjacent image */
for_each_image ( high ) {
/* If we have found the adjacent image, swap and exit */
if ( high->data == adjacent ) {
initrd_swap ( low, high, free, free_len );
return 1;
}
/* Stop search if all remaining potential
* adjacent images are already in the correct
* order.
*/
if ( high == low )
break;
}
}
/* Nothing swapped */
return 0;
}
/**
* Dump initrd locations (for debug)
*
*/
static void initrd_dump ( void ) {
struct image *initrd;
/* Do nothing unless debugging is enabled */
if ( ! DBG_LOG )
return;
/* Dump initrd locations */
for_each_image ( initrd ) {
DBGC ( &images, "INITRD %s at [%#08lx,%#08lx)\n",
initrd->name, user_to_phys ( initrd->data, 0 ),
user_to_phys ( initrd->data, initrd->len ) );
DBGC2_MD5A ( &images, user_to_phys ( initrd->data, 0 ),
user_to_virt ( initrd->data, 0 ), initrd->len );
}
}
/**
* Reshuffle initrds into desired order at top of memory
*
* @v bottom Lowest address available for initrds
*
* After this function returns, the initrds have been rearranged in
* memory and the external heap structures will have been corrupted.
* Reshuffling must therefore take place immediately prior to jumping
* to the loaded OS kernel; no further execution within iPXE is
* permitted.
*/
void initrd_reshuffle ( userptr_t bottom ) {
userptr_t top;
userptr_t used;
userptr_t free;
size_t free_len;
/* Calculate limits of available space for initrds */
top = initrd_top;
if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
bottom = initrd_bottom;
/* Debug */
DBGC ( &images, "INITRD region [%#08lx,%#08lx)\n",
user_to_phys ( bottom, 0 ), user_to_phys ( top, 0 ) );
initrd_dump();
/* Squash initrds as high as possible in memory */
used = initrd_squash_high ( top );
/* Calculate available free space */
free = bottom;
free_len = userptr_sub ( used, free );
/* Bubble-sort initrds into desired order */
while ( initrd_swap_any ( free, free_len ) ) {}
/* Debug */
initrd_dump();
}
/**
* Check that there is enough space to reshuffle initrds
*
* @v len Total length of initrds (including padding)
* @v bottom Lowest address available for initrds
* @ret rc Return status code
*/
int initrd_reshuffle_check ( size_t len, userptr_t bottom ) {
userptr_t top;
size_t available;
/* Calculate limits of available space for initrds */
top = initrd_top;
if ( userptr_sub ( initrd_bottom, bottom ) > 0 )
bottom = initrd_bottom;
available = userptr_sub ( top, bottom );
/* Allow for a sensible minimum amount of free space */
len += INITRD_MIN_FREE_LEN;
/* Check for available space */
return ( ( len < available ) ? 0 : -ENOBUFS );
}
/**
* initrd startup function
*
*/
static void initrd_startup ( void ) {
size_t len;
/* Record largest memory block available. Do this after any
* allocations made during driver startup (e.g. large host
* memory blocks for Infiniband devices, which may still be in
* use at the time of rearranging if a SAN device is hooked)
* but before any allocations for downloaded images (which we
* can safely reuse when rearranging).
*/
len = largest_memblock ( &initrd_bottom );
initrd_top = userptr_add ( initrd_bottom, len );
}
/** initrd startup function */
struct startup_fn startup_initrd __startup_fn ( STARTUP_LATE ) = {
.startup = initrd_startup,
};

View File

@@ -1,492 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Multiboot image format
*
*/
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <multiboot.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/io.h>
#include <ipxe/elf.h>
#include <ipxe/init.h>
#include <ipxe/features.h>
#include <ipxe/uri.h>
#include <ipxe/version.h>
FEATURE ( FEATURE_IMAGE, "MBOOT", DHCP_EB_FEATURE_MULTIBOOT, 1 );
/**
* Maximum number of modules we will allow for
*
* If this has bitten you: sorry. I did have a perfect scheme with a
* dynamically allocated list of modules on the protected-mode stack,
* but it was incompatible with some broken OSes that can only access
* low memory at boot time (even though we kindly set up 4GB flat
* physical addressing as per the multiboot specification.
*
*/
#define MAX_MODULES 8
/**
* Maximum combined length of command lines
*
* Again; sorry. Some broken OSes zero out any non-base memory that
* isn't part of the loaded module set, so we can't just use
* virt_to_phys(cmdline) to point to the command lines, even though
* this would comply with the Multiboot spec.
*/
#define MB_MAX_CMDLINE 512
/** Multiboot flags that we support */
#define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
MB_FLAG_VIDMODE | MB_FLAG_RAW )
/** Compulsory feature multiboot flags */
#define MB_COMPULSORY_FLAGS 0x0000ffff
/** Optional feature multiboot flags */
#define MB_OPTIONAL_FLAGS 0xffff0000
/**
* Multiboot flags that we don't support
*
* We only care about the compulsory feature flags (bits 0-15); we are
* allowed to ignore the optional feature flags.
*/
#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
/** A multiboot header descriptor */
struct multiboot_header_info {
/** The actual multiboot header */
struct multiboot_header mb;
/** Offset of header within the multiboot image */
size_t offset;
};
/** Multiboot module command lines */
static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
#define mb_cmdlines __use_data16 ( mb_cmdlines )
/** Offset within module command lines */
static unsigned int mb_cmdline_offset;
/**
* Build multiboot memory map
*
* @v image Multiboot image
* @v mbinfo Multiboot information structure
* @v mbmemmap Multiboot memory map
* @v limit Maxmimum number of memory map entries
*/
static void multiboot_build_memmap ( struct image *image,
struct multiboot_info *mbinfo,
struct multiboot_memory_map *mbmemmap,
unsigned int limit ) {
struct memory_map memmap;
unsigned int i;
/* Get memory map */
get_memmap ( &memmap );
/* Translate into multiboot format */
memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
for ( i = 0 ; i < memmap.count ; i++ ) {
if ( i >= limit ) {
DBGC ( image, "MULTIBOOT %p limit of %d memmap "
"entries reached\n", image, limit );
break;
}
mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
sizeof ( mbmemmap[i].size ) );
mbmemmap[i].base_addr = memmap.regions[i].start;
mbmemmap[i].length = ( memmap.regions[i].end -
memmap.regions[i].start );
mbmemmap[i].type = MBMEM_RAM;
mbinfo->mmap_length += sizeof ( mbmemmap[i] );
if ( memmap.regions[i].start == 0 )
mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
if ( memmap.regions[i].start == 0x100000 )
mbinfo->mem_upper = ( ( memmap.regions[i].end -
0x100000 ) / 1024 );
}
}
/**
* Add command line in base memory
*
* @v image Image
* @ret physaddr Physical address of command line
*/
static physaddr_t multiboot_add_cmdline ( struct image *image ) {
char *mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
size_t remaining = ( sizeof ( mb_cmdlines ) - mb_cmdline_offset );
char *buf = mb_cmdline;
size_t len;
/* Copy image URI to base memory buffer as start of command line */
len = ( format_uri ( image->uri, buf, remaining ) + 1 /* NUL */ );
if ( len > remaining )
len = remaining;
mb_cmdline_offset += len;
buf += len;
remaining -= len;
/* Copy command line to base memory buffer, if present */
if ( image->cmdline ) {
mb_cmdline_offset--; /* Strip NUL */
buf--;
remaining++;
len = ( snprintf ( buf, remaining, " %s",
image->cmdline ) + 1 /* NUL */ );
if ( len > remaining )
len = remaining;
mb_cmdline_offset += len;
}
return virt_to_phys ( mb_cmdline );
}
/**
* Add multiboot modules
*
* @v image Multiboot image
* @v start Start address for modules
* @v mbinfo Multiboot information structure
* @v modules Multiboot module list
* @ret rc Return status code
*/
static int multiboot_add_modules ( struct image *image, physaddr_t start,
struct multiboot_info *mbinfo,
struct multiboot_module *modules,
unsigned int limit ) {
struct image *module_image;
struct multiboot_module *module;
int rc;
/* Add each image as a multiboot module */
for_each_image ( module_image ) {
if ( mbinfo->mods_count >= limit ) {
DBGC ( image, "MULTIBOOT %p limit of %d modules "
"reached\n", image, limit );
break;
}
/* Do not include kernel image itself as a module */
if ( module_image == image )
continue;
/* Page-align the module */
start = ( ( start + 0xfff ) & ~0xfff );
/* Prepare segment */
if ( ( rc = prep_segment ( phys_to_user ( start ),
module_image->len,
module_image->len ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p could not prepare module "
"%s: %s\n", image, module_image->name,
strerror ( rc ) );
return rc;
}
/* Copy module */
memcpy_user ( phys_to_user ( start ), 0,
module_image->data, 0, module_image->len );
/* Add module to list */
module = &modules[mbinfo->mods_count++];
module->mod_start = start;
module->mod_end = ( start + module_image->len );
module->string = multiboot_add_cmdline ( module_image );
module->reserved = 0;
DBGC ( image, "MULTIBOOT %p module %s is [%x,%x)\n",
image, module_image->name, module->mod_start,
module->mod_end );
start += module_image->len;
}
return 0;
}
/**
* The multiboot information structure
*
* Kept in base memory because some OSes won't find it elsewhere,
* along with the other structures belonging to the Multiboot
* information table.
*/
static struct multiboot_info __bss16 ( mbinfo );
#define mbinfo __use_data16 ( mbinfo )
/** The multiboot bootloader name */
static char __bss16_array ( mb_bootloader_name, [32] );
#define mb_bootloader_name __use_data16 ( mb_bootloader_name )
/** The multiboot memory map */
static struct multiboot_memory_map
__bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
#define mbmemmap __use_data16 ( mbmemmap )
/** The multiboot module list */
static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
#define mbmodules __use_data16 ( mbmodules )
/**
* Find multiboot header
*
* @v image Multiboot file
* @v hdr Multiboot header descriptor to fill in
* @ret rc Return status code
*/
static int multiboot_find_header ( struct image *image,
struct multiboot_header_info *hdr ) {
uint32_t buf[64];
size_t offset;
unsigned int buf_idx;
uint32_t checksum;
/* Scan through first 8kB of image file 256 bytes at a time.
* (Use the buffering to avoid the overhead of a
* copy_from_user() for every dword.)
*/
for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
/* Check for end of image */
if ( offset > image->len )
break;
/* Refill buffer if applicable */
buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
if ( buf_idx == 0 ) {
copy_from_user ( buf, image->data, offset,
sizeof ( buf ) );
}
/* Check signature */
if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
continue;
/* Copy header and verify checksum */
copy_from_user ( &hdr->mb, image->data, offset,
sizeof ( hdr->mb ) );
checksum = ( hdr->mb.magic + hdr->mb.flags +
hdr->mb.checksum );
if ( checksum != 0 )
continue;
/* Record offset of multiboot header and return */
hdr->offset = offset;
return 0;
}
/* No multiboot header found */
return -ENOEXEC;
}
/**
* Load raw multiboot image into memory
*
* @v image Multiboot file
* @v hdr Multiboot header descriptor
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
static int multiboot_load_raw ( struct image *image,
struct multiboot_header_info *hdr,
physaddr_t *entry, physaddr_t *max ) {
size_t offset;
size_t filesz;
size_t memsz;
userptr_t buffer;
int rc;
/* Sanity check */
if ( ! ( hdr->mb.flags & MB_FLAG_RAW ) ) {
DBGC ( image, "MULTIBOOT %p is not flagged as a raw image\n",
image );
return -EINVAL;
}
/* Verify and prepare segment */
offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
filesz = ( hdr->mb.load_end_addr ?
( hdr->mb.load_end_addr - hdr->mb.load_addr ) :
( image->len - offset ) );
memsz = ( hdr->mb.bss_end_addr ?
( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz );
buffer = phys_to_user ( hdr->mb.load_addr );
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, offset, filesz );
/* Record execution entry point and maximum used address */
*entry = hdr->mb.entry_addr;
*max = ( hdr->mb.load_addr + memsz );
return 0;
}
/**
* Load ELF multiboot image into memory
*
* @v image Multiboot file
* @ret entry Entry point
* @ret max Maximum used address
* @ret rc Return status code
*/
static int multiboot_load_elf ( struct image *image, physaddr_t *entry,
physaddr_t *max ) {
int rc;
/* Load ELF image*/
if ( ( rc = elf_load ( image, entry, max ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
image, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Execute multiboot image
*
* @v image Multiboot image
* @ret rc Return status code
*/
static int multiboot_exec ( struct image *image ) {
struct multiboot_header_info hdr;
physaddr_t entry;
physaddr_t max;
int rc;
/* Locate multiboot header, if present */
if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
image );
return rc;
}
/* Abort if we detect flags that we cannot support */
if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
DBGC ( image, "MULTIBOOT %p flags %08x not supported\n",
image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
return -ENOTSUP;
}
/* There is technically a bit MB_FLAG_RAW to indicate whether
* this is an ELF or a raw image. In practice, grub will use
* the ELF header if present, and Solaris relies on this
* behaviour.
*/
if ( ( ( rc = multiboot_load_elf ( image, &entry, &max ) ) != 0 ) &&
( ( rc = multiboot_load_raw ( image, &hdr, &entry, &max ) ) != 0 ))
return rc;
/* Populate multiboot information structure */
memset ( &mbinfo, 0, sizeof ( mbinfo ) );
mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
mb_cmdline_offset = 0;
mbinfo.cmdline = multiboot_add_cmdline ( image );
mbinfo.mods_addr = virt_to_phys ( mbmodules );
mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
snprintf ( mb_bootloader_name, sizeof ( mb_bootloader_name ),
"iPXE %s", product_version );
mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
if ( ( rc = multiboot_add_modules ( image, max, &mbinfo, mbmodules,
( sizeof ( mbmodules ) /
sizeof ( mbmodules[0] ) ) ) ) !=0)
return rc;
/* Multiboot images may not return and have no callback
* interface, so shut everything down prior to booting the OS.
*/
shutdown_boot();
/* Build memory map after unhiding bootloader memory regions as part of
* shutting everything down.
*/
multiboot_build_memmap ( image, &mbinfo, mbmemmap,
( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
/* Jump to OS with flat physical addressing */
DBGC ( image, "MULTIBOOT %p starting execution at %lx\n",
image, entry );
__asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t"
"call *%%edi\n\t"
"popl %%ebp\n\t" )
: : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
"b" ( virt_to_phys ( &mbinfo ) ),
"D" ( entry )
: "ecx", "edx", "esi", "memory" );
DBGC ( image, "MULTIBOOT %p returned\n", image );
/* It isn't safe to continue after calling shutdown() */
while ( 1 ) {}
return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
}
/**
* Probe multiboot image
*
* @v image Multiboot file
* @ret rc Return status code
*/
static int multiboot_probe ( struct image *image ) {
struct multiboot_header_info hdr;
int rc;
/* Locate multiboot header, if present */
if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
image );
return rc;
}
DBGC ( image, "MULTIBOOT %p found header with flags %08x\n",
image, hdr.mb.flags );
return 0;
}
/** Multiboot image type */
struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = {
.name = "Multiboot",
.probe = multiboot_probe,
.exec = multiboot_exec,
};

View File

@@ -1,427 +0,0 @@
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <memsizes.h>
#include <basemem_packet.h>
#include <ipxe/uaccess.h>
#include <ipxe/segment.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <ipxe/fakedhcp.h>
#include <ipxe/image.h>
#include <ipxe/features.h>
#include <ipxe/version.h>
/** @file
*
* NBI image format.
*
* The Net Boot Image format is defined by the "Draft Net Boot Image
* Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now
* considered to be a legacy format, but it still included because a
* large amount of software (e.g. nymph, LTSP) makes use of NBI files.
*
* Etherboot does not implement the INT 78 callback interface
* described by the NBI specification. For a callback interface on
* x86 architecture, use PXE.
*
*/
FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 );
/**
* An NBI image header
*
* Note that the length field uses a peculiar encoding; use the
* NBI_LENGTH() macro to decode the actual header length.
*
*/
struct imgheader {
unsigned long magic; /**< Magic number (NBI_MAGIC) */
union {
unsigned char length; /**< Nibble-coded header length */
unsigned long flags; /**< Image flags */
};
segoff_t location; /**< 16-bit seg:off header location */
union {
segoff_t segoff; /**< 16-bit seg:off entry point */
unsigned long linear; /**< 32-bit entry point */
} execaddr;
} __attribute__ (( packed ));
/** NBI magic number */
#define NBI_MAGIC 0x1B031336UL
/* Interpretation of the "length" fields */
#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 )
#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 )
#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) )
/* Interpretation of the "flags" fields */
#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) )
#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) )
/** NBI header length */
#define NBI_HEADER_LENGTH 512
/**
* An NBI segment header
*
* Note that the length field uses a peculiar encoding; use the
* NBI_LENGTH() macro to decode the actual header length.
*
*/
struct segheader {
unsigned char length; /**< Nibble-coded header length */
unsigned char vendortag; /**< Vendor-defined private tag */
unsigned char reserved;
unsigned char flags; /**< Segment flags */
unsigned long loadaddr; /**< Load address */
unsigned long imglength; /**< Segment length in NBI file */
unsigned long memlength; /**< Segment length in memory */
};
/* Interpretation of the "flags" fields */
#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 )
#define NBI_LOADADDR_ABS 0x00
#define NBI_LOADADDR_AFTER 0x01
#define NBI_LOADADDR_END 0x02
#define NBI_LOADADDR_BEFORE 0x03
#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) )
/* Define a type for passing info to a loaded program */
struct ebinfo {
uint8_t major, minor; /* Version */
uint16_t flags; /* Bit flags */
};
/**
* Prepare a segment for an NBI image
*
* @v image NBI image
* @v offset Offset within NBI image
* @v filesz Length of initialised-data portion of the segment
* @v memsz Total length of the segment
* @v src Source for initialised data
* @ret rc Return status code
*/
static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
userptr_t dest, size_t filesz, size_t memsz ){
int rc;
if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) {
DBGC ( image, "NBI %p could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Load a segment for an NBI image
*
* @v image NBI image
* @v offset Offset within NBI image
* @v filesz Length of initialised-data portion of the segment
* @v memsz Total length of the segment
* @v src Source for initialised data
* @ret rc Return status code
*/
static int nbi_load_segment ( struct image *image, size_t offset,
userptr_t dest, size_t filesz,
size_t memsz __unused ) {
memcpy_user ( dest, 0, image->data, offset, filesz );
return 0;
}
/**
* Process segments of an NBI image
*
* @v image NBI image
* @v imgheader Image header information
* @v process Function to call for each segment
* @ret rc Return status code
*/
static int nbi_process_segments ( struct image *image,
struct imgheader *imgheader,
int ( * process ) ( struct image *image,
size_t offset,
userptr_t dest,
size_t filesz,
size_t memsz ) ) {
struct segheader sh;
size_t offset = 0;
size_t sh_off;
userptr_t dest;
size_t filesz;
size_t memsz;
int rc;
/* Copy image header to target location */
dest = real_to_user ( imgheader->location.segment,
imgheader->location.offset );
filesz = memsz = NBI_HEADER_LENGTH;
if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 )
return rc;
offset += filesz;
/* Process segments in turn */
sh_off = NBI_LENGTH ( imgheader->length );
do {
/* Read segment header */
copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) );
if ( sh.length == 0 ) {
/* Avoid infinite loop? */
DBGC ( image, "NBI %p invalid segheader length 0\n",
image );
return -ENOEXEC;
}
/* Calculate segment load address */
switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) {
case NBI_LOADADDR_ABS:
dest = phys_to_user ( sh.loadaddr );
break;
case NBI_LOADADDR_AFTER:
dest = userptr_add ( dest, memsz + sh.loadaddr );
break;
case NBI_LOADADDR_BEFORE:
dest = userptr_add ( dest, -sh.loadaddr );
break;
case NBI_LOADADDR_END:
/* Not correct according to the spec, but
* maintains backwards compatibility with
* previous versions of Etherboot.
*/
dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024
- sh.loadaddr );
break;
default:
/* Cannot be reached */
assert ( 0 );
}
/* Process this segment */
filesz = sh.imglength;
memsz = sh.memlength;
if ( ( offset + filesz ) > image->len ) {
DBGC ( image, "NBI %p segment outside file\n", image );
return -ENOEXEC;
}
if ( ( rc = process ( image, offset, dest,
filesz, memsz ) ) != 0 ) {
return rc;
}
offset += filesz;
/* Next segheader */
sh_off += NBI_LENGTH ( sh.length );
if ( sh_off >= NBI_HEADER_LENGTH ) {
DBGC ( image, "NBI %p header overflow\n", image );
return -ENOEXEC;
}
} while ( ! NBI_LAST_SEGHEADER ( sh.flags ) );
if ( offset != image->len ) {
DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n",
image, image->len, offset );
return -ENOEXEC;
}
return 0;
}
/**
* Boot a 16-bit NBI image
*
* @v imgheader Image header information
* @ret rc Return status code, if image returns
*/
static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
int discard_D, discard_S, discard_b;
int rc;
DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image,
imgheader->execaddr.segoff.segment,
imgheader->execaddr.segoff.offset );
__asm__ __volatile__ (
REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%ds\n\t" /* far pointer to bootp data */
"pushw %%bx\n\t"
"pushl %%esi\n\t" /* location */
"pushw %%cs\n\t" /* lcall execaddr */
"call 1f\n\t"
"jmp 2f\n\t"
"\n1:\n\t"
"pushl %%edi\n\t"
"lret\n\t"
"\n2:\n\t"
"addw $8,%%sp\n\t" /* clean up stack */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
"=b" ( discard_b )
: "D" ( imgheader->execaddr.segoff ),
"S" ( imgheader->location ),
"b" ( __from_data16 ( basemem_packet ) )
: "ecx", "edx" );
return rc;
}
/**
* Boot a 32-bit NBI image
*
* @v imgheader Image header information
* @ret rc Return status code, if image returns
*/
static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
struct ebinfo loaderinfo = {
product_major_version, product_minor_version,
0
};
int discard_D, discard_S, discard_b;
int rc;
DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
image, imgheader->execaddr.linear );
/* Jump to OS with flat physical addressing */
__asm__ __volatile__ (
PHYS_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushl %%ebx\n\t" /* bootp data */
"pushl %%esi\n\t" /* imgheader */
"pushl %%eax\n\t" /* loaderinfo */
"call *%%edi\n\t"
"addl $12, %%esp\n\t" /* clean up stack */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
"=b" ( discard_b )
: "D" ( imgheader->execaddr.linear ),
"S" ( ( imgheader->location.segment << 4 ) +
imgheader->location.offset ),
"b" ( virt_to_phys ( basemem_packet ) ),
"a" ( virt_to_phys ( &loaderinfo ) )
: "ecx", "edx", "memory" );
return rc;
}
/**
* Prepare DHCP parameter block for NBI image
*
* @v image NBI image
* @ret rc Return status code
*/
static int nbi_prepare_dhcp ( struct image *image ) {
struct net_device *boot_netdev;
int rc;
boot_netdev = last_opened_netdev();
if ( ! boot_netdev ) {
DBGC ( image, "NBI %p could not identify a network device\n",
image );
return -ENODEV;
}
if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet,
sizeof ( basemem_packet ) ) ) != 0 ) {
DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
return rc;
}
return 0;
}
/**
* Execute a loaded NBI image
*
* @v image NBI image
* @ret rc Return status code
*/
static int nbi_exec ( struct image *image ) {
struct imgheader imgheader;
int may_return;
int rc;
/* Retrieve image header */
copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
DBGC ( image, "NBI %p placing header at %hx:%hx\n", image,
imgheader.location.segment, imgheader.location.offset );
/* NBI files can have overlaps between segments; the bss of
* one segment may overlap the initialised data of another. I
* assume this is a design flaw, but there are images out
* there that we need to work with. We therefore do two
* passes: first to initialise the segments, then to copy the
* data. This avoids zeroing out already-copied data.
*/
if ( ( rc = nbi_process_segments ( image, &imgheader,
nbi_prepare_segment ) ) != 0 )
return rc;
if ( ( rc = nbi_process_segments ( image, &imgheader,
nbi_load_segment ) ) != 0 )
return rc;
/* Prepare DHCP option block */
if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 )
return rc;
/* Shut down now if NBI image will not return */
may_return = NBI_PROGRAM_RETURNS ( imgheader.flags );
if ( ! may_return )
shutdown_boot();
/* Execute NBI image */
if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) {
rc = nbi_boot32 ( image, &imgheader );
} else {
rc = nbi_boot16 ( image, &imgheader );
}
if ( ! may_return ) {
/* Cannot continue after shutdown() called */
DBGC ( image, "NBI %p returned %d from non-returnable image\n",
image, rc );
while ( 1 ) {}
}
DBGC ( image, "NBI %p returned %d\n", image, rc );
return rc;
}
/**
* Probe NBI image
*
* @v image NBI image
* @ret rc Return status code
*/
static int nbi_probe ( struct image *image ) {
struct imgheader imgheader;
/* If we don't have enough data give up */
if ( image->len < NBI_HEADER_LENGTH ) {
DBGC ( image, "NBI %p too short for an NBI image\n", image );
return -ENOEXEC;
}
/* Check image header */
copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
if ( imgheader.magic != NBI_MAGIC ) {
DBGC ( image, "NBI %p has no NBI signature\n", image );
return -ENOEXEC;
}
return 0;
}
/** NBI image type */
struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = {
.name = "NBI",
.probe = nbi_probe,
.exec = nbi_exec,
};

View File

@@ -1,174 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* PXE image format
*
*/
#include <pxe.h>
#include <pxe_call.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/netdevice.h>
#include <ipxe/features.h>
#include <ipxe/console.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/IndustryStandard/PeImage.h>
FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 );
/** PXE command line */
const char *pxe_cmdline;
/**
* Execute PXE image
*
* @v image PXE image
* @ret rc Return status code
*/
static int pxe_exec ( struct image *image ) {
userptr_t buffer = real_to_user ( 0, 0x7c00 );
struct net_device *netdev;
int rc;
/* Verify and prepare segment */
if ( ( rc = prep_segment ( buffer, image->len, image->len ) ) != 0 ) {
DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, 0, image->len );
/* Arbitrarily pick the most recently opened network device */
if ( ( netdev = last_opened_netdev() ) == NULL ) {
DBGC ( image, "IMAGE %p could not locate PXE net device\n",
image );
return -ENODEV;
}
netdev_get ( netdev );
/* Activate PXE */
pxe_activate ( netdev );
/* Construct fake DHCP packets */
pxe_fake_cached_info();
/* Set PXE command line */
pxe_cmdline = image->cmdline;
/* Reset console since PXE NBP will probably use it */
console_reset();
/* Start PXE NBP */
rc = pxe_start_nbp();
/* Clear PXE command line */
pxe_cmdline = NULL;
/* Deactivate PXE */
pxe_deactivate();
/* Try to reopen network device. Ignore errors, since the NBP
* may have called PXENV_STOP_UNDI.
*/
netdev_open ( netdev );
netdev_put ( netdev );
return rc;
}
/**
* Probe PXE image
*
* @v image PXE file
* @ret rc Return status code
*/
int pxe_probe ( struct image *image ) {
/* Images too large to fit in base memory cannot be PXE
* images. We include this check to help prevent unrecognised
* images from being marked as PXE images, since PXE images
* have no signature we can check against.
*/
if ( image->len > ( 0xa0000 - 0x7c00 ) )
return -ENOEXEC;
/* Rejecting zero-length images is also useful, since these
* end up looking to the user like bugs in iPXE.
*/
if ( ! image->len )
return -ENOEXEC;
return 0;
}
/**
* Probe PXE image (with rejection of potential EFI images)
*
* @v image PXE file
* @ret rc Return status code
*/
int pxe_probe_no_mz ( struct image *image ) {
uint16_t magic;
int rc;
/* Probe PXE image */
if ( ( rc = pxe_probe ( image ) ) != 0 )
return rc;
/* Reject image with an "MZ" signature which may indicate an
* EFI image incorrectly handed out to a BIOS system.
*/
if ( image->len >= sizeof ( magic ) ) {
copy_from_user ( &magic, image->data, 0, sizeof ( magic ) );
if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) {
DBGC ( image, "IMAGE %p may be an EFI image\n",
image );
return -ENOTTY;
}
}
return 0;
}
/** PXE image type */
struct image_type pxe_image_type[] __image_type ( PROBE_PXE ) = {
{
.name = "PXE-NBP",
.probe = pxe_probe_no_mz,
.exec = pxe_exec,
},
{
.name = "PXE-NBP (may be EFI?)",
.probe = pxe_probe,
.exec = pxe_exec,
},
};

View File

@@ -1,140 +0,0 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <realmode.h>
#include <sdi.h>
#include <ipxe/image.h>
#include <ipxe/features.h>
/** @file
*
* System Deployment Image (SDI)
*
* Based on the MSDN article "RAM boot using SDI in Windows XP
* Embedded with Service Pack 1", available at the time of writing
* from:
*
* http://msdn.microsoft.com/en-us/library/ms838543.aspx
*/
FEATURE ( FEATURE_IMAGE, "SDI", DHCP_EB_FEATURE_SDI, 1 );
/**
* Parse SDI image header
*
* @v image SDI file
* @v sdi SDI header to fill in
* @ret rc Return status code
*/
static int sdi_parse_header ( struct image *image, struct sdi_header *sdi ) {
/* Sanity check */
if ( image->len < sizeof ( *sdi ) ) {
DBGC ( image, "SDI %p too short for SDI header\n", image );
return -ENOEXEC;
}
/* Read in header */
copy_from_user ( sdi, image->data, 0, sizeof ( *sdi ) );
/* Check signature */
if ( sdi->magic != SDI_MAGIC ) {
DBGC ( image, "SDI %p is not an SDI image\n", image );
return -ENOEXEC;
}
return 0;
}
/**
* Execute SDI image
*
* @v image SDI file
* @ret rc Return status code
*/
static int sdi_exec ( struct image *image ) {
struct sdi_header sdi;
uint32_t sdiptr;
int rc;
/* Parse image header */
if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
return rc;
/* Check that image is bootable */
if ( sdi.boot_size == 0 ) {
DBGC ( image, "SDI %p is not bootable\n", image );
return -ENOTTY;
}
DBGC ( image, "SDI %p image at %08lx+%08zx\n",
image, user_to_phys ( image->data, 0 ), image->len );
DBGC ( image, "SDI %p boot code at %08lx+%llx\n", image,
user_to_phys ( image->data, sdi.boot_offset ), sdi.boot_size );
/* Copy boot code */
memcpy_user ( real_to_user ( SDI_BOOT_SEG, SDI_BOOT_OFF ), 0,
image->data, sdi.boot_offset, sdi.boot_size );
/* Jump to boot code */
sdiptr = ( user_to_phys ( image->data, 0 ) | SDI_WTF );
__asm__ __volatile__ ( REAL_CODE ( "ljmp %0, %1\n\t" )
: : "i" ( SDI_BOOT_SEG ),
"i" ( SDI_BOOT_OFF ),
"d" ( sdiptr ) );
/* There is no way for the image to return, since we provide
* no return address.
*/
assert ( 0 );
return -ECANCELED; /* -EIMPOSSIBLE */
}
/**
* Probe SDI image
*
* @v image SDI file
* @ret rc Return status code
*/
static int sdi_probe ( struct image *image ) {
struct sdi_header sdi;
int rc;
/* Parse image */
if ( ( rc = sdi_parse_header ( image, &sdi ) ) != 0 )
return rc;
return 0;
}
/** SDI image type */
struct image_type sdi_image_type __image_type ( PROBE_NORMAL ) = {
.name = "SDI",
.probe = sdi_probe,
.exec = sdi_exec,
};

View File

@@ -1,35 +0,0 @@
#ifndef _BASEMEM_H
#define _BASEMEM_H
/** @file
*
* Base memory allocation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <realmode.h>
#include <bios.h>
/**
* Read the BIOS free base memory counter
*
* @ret fbms Free base memory counter (in kB)
*/
static inline unsigned int get_fbms ( void ) {
uint16_t fbms;
get_real ( fbms, BDA_SEG, BDA_FBMS );
return fbms;
}
extern void set_fbms ( unsigned int new_fbms );
/* Actually in hidemem.c, but putting it here avoids polluting the
* architecture-independent include/hidemem.h.
*/
extern void hide_basemem ( void );
#endif /* _BASEMEM_H */

View File

@@ -1,15 +0,0 @@
#ifndef BASEMEM_PACKET_H
#define BASEMEM_PACKET_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
/** Maximum length of base memory packet buffer */
#define BASEMEM_PACKET_LEN 1514
/** Base memory packet buffer */
extern char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );
#define basemem_packet __use_data16 ( basemem_packet )
#endif /* BASEMEM_PACKET_H */

View File

@@ -1,14 +0,0 @@
#ifndef BIOS_H
#define BIOS_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define BDA_SEG 0x0040
#define BDA_EQUIPMENT_WORD 0x0010
#define BDA_FBMS 0x0013
#define BDA_REBOOT 0x0072
#define BDA_REBOOT_WARM 0x1234
#define BDA_NUM_DRIVES 0x0075
#define BDA_CHAR_HEIGHT 0x0085
#endif /* BIOS_H */

View File

@@ -1,69 +0,0 @@
#ifndef BIOS_DISKS_H
#define BIOS_DISKS_H
#include "dev.h"
/*
* Constants
*
*/
#define BIOS_DISK_MAX_NAME_LEN 6
struct bios_disk_sector {
char data[512];
};
/*
* The location of a BIOS disk
*
*/
struct bios_disk_loc {
uint8_t drive;
};
/*
* A physical BIOS disk device
*
*/
struct bios_disk_device {
char name[BIOS_DISK_MAX_NAME_LEN];
uint8_t drive;
uint8_t type;
};
/*
* A BIOS disk driver, with a valid device ID range and naming
* function.
*
*/
struct bios_disk_driver {
void ( *fill_drive_name ) ( char *buf, uint8_t drive );
uint8_t min_drive;
uint8_t max_drive;
};
/*
* Define a BIOS disk driver
*
*/
#define BIOS_DISK_DRIVER( _name, _fill_drive_name, _min_drive, _max_drive ) \
static struct bios_disk_driver _name = { \
.fill_drive_name = _fill_drive_name, \
.min_drive = _min_drive, \
.max_drive = _max_drive, \
}
/*
* Functions in bios_disks.c
*
*/
/*
* bios_disk bus global definition
*
*/
extern struct bus_driver bios_disk_driver;
#endif /* BIOS_DISKS_H */

View File

@@ -1,34 +0,0 @@
#ifndef BIOSINT_H
#define BIOSINT_H
/**
* @file BIOS interrupts
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
struct segoff;
/**
* Hooked interrupt count
*
* At exit, after unhooking all possible interrupts, this counter
* should be examined. If it is non-zero, it means that we failed to
* unhook at least one interrupt vector, and so must not free up the
* memory we are using. (Note that this also implies that we should
* re-hook INT 15 in order to hide ourselves from the memory map).
*/
extern uint16_t __text16 ( hooked_bios_interrupts );
#define hooked_bios_interrupts __use_text16 ( hooked_bios_interrupts )
extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
struct segoff *chain_vector );
extern int unhook_bios_interrupt ( unsigned int interrupt,
unsigned int handler,
struct segoff *chain_vector );
extern void check_bios_interrupts ( void );
#endif /* BIOSINT_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* i386-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rtc_entropy.h>
#endif /* _BITS_ENTROPY_H */

View File

@@ -1,15 +0,0 @@
#ifndef _BITS_NAP_H
#define _BITS_NAP_H
/** @file
*
* i386-specific CPU sleeping API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_nap.h>
#include <ipxe/efi/efix86_nap.h>
#endif /* _BITS_MAP_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_REBOOT_H
#define _BITS_REBOOT_H
/** @file
*
* i386-specific reboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_reboot.h>
#endif /* _BITS_REBOOT_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_SANBOOT_H
#define _BITS_SANBOOT_H
/** @file
*
* i386-specific sanboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_sanboot.h>
#endif /* _BITS_SANBOOT_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_SMBIOS_H
#define _BITS_SMBIOS_H
/** @file
*
* i386-specific SMBIOS API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_smbios.h>
#endif /* _BITS_SMBIOS_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_TIME_H
#define _BITS_TIME_H
/** @file
*
* i386-specific time API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rtc_time.h>
#endif /* _BITS_TIME_H */

View File

@@ -1,15 +0,0 @@
#ifndef _BITS_TIMER_H
#define _BITS_TIMER_H
/** @file
*
* i386-specific timer API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_timer.h>
#include <ipxe/rdtsc_timer.h>
#endif /* _BITS_TIMER_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_UACCESS_H
#define _BITS_UACCESS_H
/** @file
*
* i386-specific user access API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <librm.h>
#endif /* _BITS_UACCESS_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BITS_UMALLOC_H
#define _BITS_UMALLOC_H
/** @file
*
* i386-specific user memory allocation API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/memtop_umalloc.h>
#endif /* _BITS_UMALLOC_H */

View File

@@ -1,34 +0,0 @@
#ifndef BOCHS_H
#define BOCHS_H
/** @file
*
* bochs breakpoints
*
* This file defines @c bochsbp, the magic breakpoint instruction that
* is incredibly useful when debugging under bochs. This file should
* never be included in production code.
*
* Use the pseudo-instruction @c bochsbp in assembly code, or the
* bochsbp() function in C code.
*
*/
#ifdef ASSEMBLY
/* Breakpoint for when debugging under bochs */
#define bochsbp xchgw %bx, %bx
#define BOCHSBP bochsbp
#else /* ASSEMBLY */
/** Breakpoint for when debugging under bochs */
static inline void bochsbp ( void ) {
__asm__ __volatile__ ( "xchgw %bx, %bx" );
}
#endif /* ASSEMBLY */
#warning "bochs.h should not be included into production code"
#endif /* BOCHS_H */

View File

@@ -1,14 +0,0 @@
#ifndef _BOOTSECTOR_H
#define _BOOTSECTOR_H
/** @file
*
* x86 bootsector image format
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern int call_bootsector ( unsigned int segment, unsigned int offset,
unsigned int drive );
#endif /* _BOOTSECTOR_H */

View File

@@ -1,142 +0,0 @@
#ifndef _BZIMAGE_H
#define _BZIMAGE_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* A bzImage header
*
* As documented in Documentation/i386/boot.txt
*/
struct bzimage_header {
/** The size of the setup in sectors
*
* If this field contains 0, assume it contains 4.
*/
uint8_t setup_sects;
/** If set, the root is mounted readonly */
uint16_t root_flags;
/** DO NOT USE - for bootsect.S use only */
uint16_t syssize;
/** DO NOT USE - obsolete */
uint16_t swap_dev;
/** DO NOT USE - for bootsect.S use only */
uint16_t ram_size;
/** Video mode control */
uint16_t vid_mode;
/** Default root device number */
uint16_t root_dev;
/** 0xAA55 magic number */
uint16_t boot_flag;
/** Jump instruction */
uint16_t jump;
/** Magic signature "HdrS" */
uint32_t header;
/** Boot protocol version supported */
uint16_t version;
/** Boot loader hook (see below) */
uint32_t realmode_swtch;
/** The load-low segment (0x1000) (obsolete) */
uint16_t start_sys;
/** Pointer to kernel version string */
uint16_t kernel_version;
/** Boot loader identifier */
uint8_t type_of_loader;
/** Boot protocol option flags */
uint8_t loadflags;
/** Move to high memory size (used with hooks) */
uint16_t setup_move_size;
/** Boot loader hook (see below) */
uint32_t code32_start;
/** initrd load address (set by boot loader) */
uint32_t ramdisk_image;
/** initrd size (set by boot loader) */
uint32_t ramdisk_size;
/** DO NOT USE - for bootsect.S use only */
uint32_t bootsect_kludge;
/** Free memory after setup end */
uint16_t heap_end_ptr;
/** Unused */
uint16_t pad1;
/** 32-bit pointer to the kernel command line */
uint32_t cmd_line_ptr;
/** Highest legal initrd address */
uint32_t initrd_addr_max;
/** Physical addr alignment required for kernel */
uint32_t kernel_alignment;
/** Whether kernel is relocatable or not */
uint8_t relocatable_kernel;
/** Unused */
uint8_t pad2[3];
/** Maximum size of the kernel command line */
uint32_t cmdline_size;
} __attribute__ (( packed ));
/** Offset of bzImage header within kernel image */
#define BZI_HDR_OFFSET 0x1f1
/** bzImage boot flag value */
#define BZI_BOOT_FLAG 0xaa55
/** bzImage magic signature value */
#define BZI_SIGNATURE 0x53726448
/** bzImage boot loader identifier for Etherboot */
#define BZI_LOADER_TYPE_ETHERBOOT 0x40
/** bzImage boot loader identifier for iPXE
*
* We advertise ourselves as Etherboot version 6.
*/
#define BZI_LOADER_TYPE_IPXE ( BZI_LOADER_TYPE_ETHERBOOT | 0x06 )
/** bzImage "load high" flag */
#define BZI_LOAD_HIGH 0x01
/** Load address for high-loaded kernels */
#define BZI_LOAD_HIGH_ADDR 0x100000
/** Load address for low-loaded kernels */
#define BZI_LOAD_LOW_ADDR 0x10000
/** bzImage "kernel can use heap" flag */
#define BZI_CAN_USE_HEAP 0x80
/** bzImage special video mode "normal" */
#define BZI_VID_MODE_NORMAL 0xffff
/** bzImage special video mode "ext" */
#define BZI_VID_MODE_EXT 0xfffe
/** bzImage special video mode "ask" */
#define BZI_VID_MODE_ASK 0xfffd
/** bzImage maximum initrd address for versions < 2.03 */
#define BZI_INITRD_MAX 0x37ffffff
/** bzImage command-line structure used by older kernels */
struct bzimage_cmdline {
/** Magic signature */
uint16_t magic;
/** Offset to command line */
uint16_t offset;
} __attribute__ (( packed ));
/** Offset of bzImage command-line structure within kernel image */
#define BZI_CMDLINE_OFFSET 0x20
/** bzImage command line present magic marker value */
#define BZI_CMDLINE_MAGIC 0xa33f
/** Assumed size of real-mode portion (including .bss) */
#define BZI_ASSUMED_RM_SIZE 0x8000
/** Amount of stack space to provide */
#define BZI_STACK_SIZE 0x1000
/** Maximum size of command line */
#define BZI_CMDLINE_SIZE 0x7ff
#endif /* _BZIMAGE_H */

View File

@@ -1,9 +0,0 @@
#ifndef _FAKEE820_H
#define _FAKEE820_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern void fake_e820 ( void );
extern void unfake_e820 ( void );
#endif /* _FAKEE820_H */

View File

@@ -1,30 +0,0 @@
#ifndef _INITRD_H
#define _INITRD_H
/** @file
*
* Initial ramdisk (initrd) reshuffling
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/uaccess.h>
/** Minimum alignment for initrds
*
* Some versions of Linux complain about initrds that are not
* page-aligned.
*/
#define INITRD_ALIGN 4096
/** Minimum free space required to reshuffle initrds
*
* Chosen to avoid absurdly long reshuffling times
*/
#define INITRD_MIN_FREE_LEN ( 512 * 1024 )
extern void initrd_reshuffle ( userptr_t bottom );
extern int initrd_reshuffle_check ( size_t len, userptr_t bottom );
#endif /* _INITRD_H */

View File

@@ -1,333 +0,0 @@
#ifndef INT13_H
#define INT13_H
/** @file
*
* INT 13 emulation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/list.h>
#include <ipxe/edd.h>
#include <realmode.h>
/**
* @defgroup int13ops INT 13 operation codes
* @{
*/
/** Reset disk system */
#define INT13_RESET 0x00
/** Get status of last operation */
#define INT13_GET_LAST_STATUS 0x01
/** Read sectors */
#define INT13_READ_SECTORS 0x02
/** Write sectors */
#define INT13_WRITE_SECTORS 0x03
/** Get drive parameters */
#define INT13_GET_PARAMETERS 0x08
/** Get disk type */
#define INT13_GET_DISK_TYPE 0x15
/** Extensions installation check */
#define INT13_EXTENSION_CHECK 0x41
/** Extended read */
#define INT13_EXTENDED_READ 0x42
/** Extended write */
#define INT13_EXTENDED_WRITE 0x43
/** Verify sectors */
#define INT13_EXTENDED_VERIFY 0x44
/** Extended seek */
#define INT13_EXTENDED_SEEK 0x47
/** Get extended drive parameters */
#define INT13_GET_EXTENDED_PARAMETERS 0x48
/** Get CD-ROM status / terminate emulation */
#define INT13_CDROM_STATUS_TERMINATE 0x4b
/** Read CD-ROM boot catalog */
#define INT13_CDROM_READ_BOOT_CATALOG 0x4d
/** @} */
/**
* @defgroup int13status INT 13 status codes
* @{
*/
/** Operation completed successfully */
#define INT13_STATUS_SUCCESS 0x00
/** Invalid function or parameter */
#define INT13_STATUS_INVALID 0x01
/** Read error */
#define INT13_STATUS_READ_ERROR 0x04
/** Reset failed */
#define INT13_STATUS_RESET_FAILED 0x05
/** Write error */
#define INT13_STATUS_WRITE_ERROR 0xcc
/** @} */
/** Block size for non-extended INT 13 calls */
#define INT13_BLKSIZE 512
/** @defgroup int13fddtype INT 13 floppy disk drive types
* @{
*/
/** 360K */
#define INT13_FDD_TYPE_360K 0x01
/** 1.2M */
#define INT13_FDD_TYPE_1M2 0x02
/** 720K */
#define INT13_FDD_TYPE_720K 0x03
/** 1.44M */
#define INT13_FDD_TYPE_1M44 0x04
/** An INT 13 disk address packet */
struct int13_disk_address {
/** Size of the packet, in bytes */
uint8_t bufsize;
/** Reserved */
uint8_t reserved_a;
/** Block count */
uint8_t count;
/** Reserved */
uint8_t reserved_b;
/** Data buffer */
struct segoff buffer;
/** Starting block number */
uint64_t lba;
/** Data buffer (EDD 3.0+ only) */
uint64_t buffer_phys;
/** Block count (EDD 4.0+ only) */
uint32_t long_count;
/** Reserved */
uint32_t reserved_c;
} __attribute__ (( packed ));
/** INT 13 disk parameters */
struct int13_disk_parameters {
/** Size of this structure */
uint16_t bufsize;
/** Flags */
uint16_t flags;
/** Number of cylinders */
uint32_t cylinders;
/** Number of heads */
uint32_t heads;
/** Number of sectors per track */
uint32_t sectors_per_track;
/** Total number of sectors on drive */
uint64_t sectors;
/** Bytes per sector */
uint16_t sector_size;
/** Device parameter table extension */
struct segoff dpte;
/** Device path information */
struct edd_device_path_information dpi;
} __attribute__ (( packed ));
/**
* @defgroup int13types INT 13 disk types
* @{
*/
/** No such drive */
#define INT13_DISK_TYPE_NONE 0x00
/** Floppy without change-line support */
#define INT13_DISK_TYPE_FDD 0x01
/** Floppy with change-line support */
#define INT13_DISK_TYPE_FDD_CL 0x02
/** Hard disk */
#define INT13_DISK_TYPE_HDD 0x03
/** @} */
/**
* @defgroup int13flags INT 13 disk parameter flags
* @{
*/
/** DMA boundary errors handled transparently */
#define INT13_FL_DMA_TRANSPARENT 0x01
/** CHS information is valid */
#define INT13_FL_CHS_VALID 0x02
/** Removable drive */
#define INT13_FL_REMOVABLE 0x04
/** Write with verify supported */
#define INT13_FL_VERIFIABLE 0x08
/** Has change-line supported (valid only for removable drives) */
#define INT13_FL_CHANGE_LINE 0x10
/** Drive can be locked (valid only for removable drives) */
#define INT13_FL_LOCKABLE 0x20
/** CHS is max possible, not current media (valid only for removable drives) */
#define INT13_FL_CHS_MAX 0x40
/** @} */
/**
* @defgroup int13exts INT 13 extension flags
* @{
*/
/** Extended disk access functions supported */
#define INT13_EXTENSION_LINEAR 0x01
/** Removable drive functions supported */
#define INT13_EXTENSION_REMOVABLE 0x02
/** EDD functions supported */
#define INT13_EXTENSION_EDD 0x04
/** 64-bit extensions are present */
#define INT13_EXTENSION_64BIT 0x08
/** @} */
/**
* @defgroup int13vers INT 13 extension versions
* @{
*/
/** INT13 extensions version 1.x */
#define INT13_EXTENSION_VER_1_X 0x01
/** INT13 extensions version 2.0 (EDD-1.0) */
#define INT13_EXTENSION_VER_2_0 0x20
/** INT13 extensions version 2.1 (EDD-1.1) */
#define INT13_EXTENSION_VER_2_1 0x21
/** INT13 extensions version 3.0 (EDD-3.0) */
#define INT13_EXTENSION_VER_3_0 0x30
/** @} */
/** Maximum number of sectors for which CHS geometry is allowed to be valid
*
* This number is taken from the EDD specification.
*/
#define INT13_MAX_CHS_SECTORS 15482880
/** Bootable CD-ROM specification packet */
struct int13_cdrom_specification {
/** Size of packet in bytes */
uint8_t size;
/** Boot media type */
uint8_t media_type;
/** Drive number */
uint8_t drive;
/** CD-ROM controller number */
uint8_t controller;
/** LBA of disk image to emulate */
uint32_t lba;
/** Device specification */
uint16_t device;
/** Segment of 3K buffer for caching CD-ROM reads */
uint16_t cache_segment;
/** Load segment for initial boot image */
uint16_t load_segment;
/** Number of 512-byte sectors to load */
uint16_t load_sectors;
/** Low 8 bits of cylinder number */
uint8_t cyl;
/** Sector number, plus high 2 bits of cylinder number */
uint8_t cyl_sector;
/** Head number */
uint8_t head;
} __attribute__ (( packed ));
/** Bootable CD-ROM boot catalog command packet */
struct int13_cdrom_boot_catalog_command {
/** Size of packet in bytes */
uint8_t size;
/** Number of sectors of boot catalog to read */
uint8_t count;
/** Buffer for boot catalog */
uint32_t buffer;
/** First sector in boot catalog to transfer */
uint16_t start;
} __attribute__ (( packed ));
/** A C/H/S address within a partition table entry */
struct partition_chs {
/** Head number */
uint8_t head;
/** Sector number, plus high 2 bits of cylinder number */
uint8_t cyl_sector;
/** Low 8 bits of cylinder number */
uint8_t cyl;
} __attribute__ (( packed ));
#define PART_HEAD(chs) ( (chs).head )
#define PART_SECTOR(chs) ( (chs).cyl_sector & 0x3f )
#define PART_CYLINDER(chs) ( (chs).cyl | ( ( (chs).cyl_sector & 0xc0 ) << 2 ) )
/** A partition table entry within the MBR */
struct partition_table_entry {
/** Bootable flag */
uint8_t bootable;
/** C/H/S start address */
struct partition_chs chs_start;
/** System indicator (partition type) */
uint8_t type;
/** C/H/S end address */
struct partition_chs chs_end;
/** Linear start address */
uint32_t start;
/** Linear length */
uint32_t length;
} __attribute__ (( packed ));
/** A Master Boot Record */
struct master_boot_record {
/** Code area */
uint8_t code[440];
/** Disk signature */
uint32_t signature;
/** Padding */
uint8_t pad[2];
/** Partition table */
struct partition_table_entry partitions[4];
/** 0x55aa MBR signature */
uint16_t magic;
} __attribute__ (( packed ));
/** MBR magic signature */
#define INT13_MBR_MAGIC 0xaa55
/** A floppy disk geometry */
struct int13_fdd_geometry {
/** Number of tracks */
uint8_t tracks;
/** Number of heads and sectors per track */
uint8_t heads_spt;
};
/** Define a floppy disk geometry */
#define INT13_FDD_GEOMETRY( cylinders, heads, sectors ) \
{ \
.tracks = (cylinders), \
.heads_spt = ( ( (heads) << 6 ) | (sectors) ), \
}
/** Get floppy disk number of cylinders */
#define INT13_FDD_CYLINDERS( geometry ) ( (geometry)->tracks )
/** Get floppy disk number of heads */
#define INT13_FDD_HEADS( geometry ) ( (geometry)->heads_spt >> 6 )
/** Get floppy disk number of sectors per track */
#define INT13_FDD_SECTORS( geometry ) ( (geometry)->heads_spt & 0x3f )
/** A floppy drive parameter table */
struct int13_fdd_parameters {
uint8_t step_rate__head_unload;
uint8_t head_load__ndma;
uint8_t motor_off_delay;
uint8_t bytes_per_sector;
uint8_t sectors_per_track;
uint8_t gap_length;
uint8_t data_length;
uint8_t format_gap_length;
uint8_t format_filler;
uint8_t head_settle_time;
uint8_t motor_start_time;
} __attribute__ (( packed ));
#endif /* INT13_H */

View File

@@ -1,18 +0,0 @@
#ifndef _IPXE_BIOS_NAP_H
#define _IPXE_BIOS_NAP_H
/** @file
*
* BIOS CPU sleeping
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef NAP_PCBIOS
#define NAP_PREFIX_pcbios
#else
#define NAP_PREFIX_pcbios __pcbios_
#endif
#endif /* _IPXE_BIOS_NAP_H */

View File

@@ -1,18 +0,0 @@
#ifndef _IPXE_BIOS_REBOOT_H
#define _IPXE_BIOS_REBOOT_H
/** @file
*
* Standard PC-BIOS reboot mechanism
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef REBOOT_PCBIOS
#define REBOOT_PREFIX_pcbios
#else
#define REBOOT_PREFIX_pcbios __pcbios_
#endif
#endif /* _IPXE_BIOS_REBOOT_H */

View File

@@ -1,29 +0,0 @@
#ifndef _IPXE_BIOS_SANBOOT_H
#define _IPXE_BIOS_SANBOOT_H
/** @file
*
* Standard PC-BIOS sanboot interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef SANBOOT_PCBIOS
#define SANBOOT_PREFIX_pcbios
#else
#define SANBOOT_PREFIX_pcbios __pcbios_
#endif
/**
* Get default SAN drive number
*
* @ret drive Default drive number
*/
static inline __always_inline unsigned int
SANBOOT_INLINE ( pcbios, san_default_drive ) ( void ) {
/* Default to booting from first hard disk */
return 0x80;
}
#endif /* _IPXE_BIOS_SANBOOT_H */

View File

@@ -1,18 +0,0 @@
#ifndef _IPXE_BIOS_SMBIOS_H
#define _IPXE_BIOS_SMBIOS_H
/** @file
*
* Standard PC-BIOS SMBIOS interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef SMBIOS_PCBIOS
#define SMBIOS_PREFIX_pcbios
#else
#define SMBIOS_PREFIX_pcbios __pcbios_
#endif
#endif /* _IPXE_BIOS_SMBIOS_H */

View File

@@ -1,44 +0,0 @@
#ifndef _IPXE_BIOS_TIMER_H
#define _IPXE_BIOS_TIMER_H
/** @file
*
* BIOS timer
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef TIMER_PCBIOS
#define TIMER_PREFIX_pcbios
#else
#define TIMER_PREFIX_pcbios __pcbios_
#endif
#include <ipxe/pit8254.h>
/**
* Delay for a fixed number of microseconds
*
* @v usecs Number of microseconds for which to delay
*/
static inline __always_inline void
TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) {
/* BIOS timer is not high-resolution enough for udelay(), so
* we use the 8254 Programmable Interval Timer.
*/
pit8254_udelay ( usecs );
}
/**
* Get number of ticks per second
*
* @ret ticks_per_sec Number of ticks per second
*/
static inline __always_inline unsigned long
TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) {
/* BIOS timer ticks over at 18.2 ticks per second */
return 18;
}
#endif /* _IPXE_BIOS_TIMER_H */

View File

@@ -1,115 +0,0 @@
#ifndef _IPXE_ERRNO_PCBIOS_H
#define _IPXE_ERRNO_PCBIOS_H
/**
* @file
*
* PC-BIOS platform error codes
*
* We use the PXE-specified error codes as the platform error codes
* for the PC-BIOS platform.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <pxe_error.h>
/**
* Convert platform error code to platform component of iPXE error code
*
* @v platform Platform error code
* @ret errno Platform component of iPXE error code
*/
#define PLATFORM_TO_ERRNO( platform ) ( (platform) & 0xff )
/**
* Convert iPXE error code to platform error code
*
* @v errno iPXE error code
* @ret platform Platform error code
*/
#define ERRNO_TO_PLATFORM( errno ) ( (errno) & 0xff )
/* Platform-specific error codes */
#define PLATFORM_ENOERR PXENV_STATUS_SUCCESS
#define PLATFORM_E2BIG PXENV_STATUS_BAD_FUNC
#define PLATFORM_EACCES PXENV_STATUS_TFTP_ACCESS_VIOLATION
#define PLATFORM_EADDRINUSE PXENV_STATUS_UDP_OPEN
#define PLATFORM_EADDRNOTAVAIL PXENV_STATUS_UDP_OPEN
#define PLATFORM_EAFNOSUPPORT PXENV_STATUS_UNSUPPORTED
#define PLATFORM_EAGAIN PXENV_STATUS_FAILURE
#define PLATFORM_EALREADY PXENV_STATUS_UDP_OPEN
#define PLATFORM_EBADF PXENV_STATUS_TFTP_CLOSED
#define PLATFORM_EBADMSG PXENV_STATUS_FAILURE
#define PLATFORM_EBUSY PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ECANCELED PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE
#define PLATFORM_ECHILD PXENV_STATUS_TFTP_FILE_NOT_FOUND
#define PLATFORM_ECONNABORTED PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
#define PLATFORM_ECONNREFUSED PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION
#define PLATFORM_ECONNRESET PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
#define PLATFORM_EDEADLK PXENV_STATUS_FAILURE
#define PLATFORM_EDESTADDRREQ PXENV_STATUS_BAD_FUNC
#define PLATFORM_EDOM PXENV_STATUS_FAILURE
#define PLATFORM_EDQUOT PXENV_STATUS_FAILURE
#define PLATFORM_EEXIST PXENV_STATUS_FAILURE
#define PLATFORM_EFAULT PXENV_STATUS_MCOPY_PROBLEM
#define PLATFORM_EFBIG PXENV_STATUS_MCOPY_PROBLEM
#define PLATFORM_EHOSTUNREACH PXENV_STATUS_ARP_TIMEOUT
#define PLATFORM_EIDRM PXENV_STATUS_FAILURE
#define PLATFORM_EILSEQ PXENV_STATUS_FAILURE
#define PLATFORM_EINPROGRESS PXENV_STATUS_FAILURE
#define PLATFORM_EINTR PXENV_STATUS_FAILURE
#define PLATFORM_EINVAL PXENV_STATUS_BAD_FUNC
#define PLATFORM_EIO PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION
#define PLATFORM_EISCONN PXENV_STATUS_UDP_OPEN
#define PLATFORM_EISDIR PXENV_STATUS_FAILURE
#define PLATFORM_ELOOP PXENV_STATUS_FAILURE
#define PLATFORM_EMFILE PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_EMLINK PXENV_STATUS_FAILURE
#define PLATFORM_EMSGSIZE PXENV_STATUS_BAD_FUNC
#define PLATFORM_EMULTIHOP PXENV_STATUS_FAILURE
#define PLATFORM_ENAMETOOLONG PXENV_STATUS_FAILURE
#define PLATFORM_ENETDOWN PXENV_STATUS_ARP_TIMEOUT
#define PLATFORM_ENETRESET PXENV_STATUS_FAILURE
#define PLATFORM_ENETUNREACH PXENV_STATUS_ARP_TIMEOUT
#define PLATFORM_ENFILE PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ENOBUFS PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ENODATA PXENV_STATUS_FAILURE
#define PLATFORM_ENODEV PXENV_STATUS_TFTP_FILE_NOT_FOUND
#define PLATFORM_ENOENT PXENV_STATUS_TFTP_FILE_NOT_FOUND
#define PLATFORM_ENOEXEC PXENV_STATUS_FAILURE
#define PLATFORM_ENOLCK PXENV_STATUS_FAILURE
#define PLATFORM_ENOLINK PXENV_STATUS_FAILURE
#define PLATFORM_ENOMEM PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ENOMSG PXENV_STATUS_FAILURE
#define PLATFORM_ENOPROTOOPT PXENV_STATUS_UNSUPPORTED
#define PLATFORM_ENOSPC PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ENOSR PXENV_STATUS_OUT_OF_RESOURCES
#define PLATFORM_ENOSTR PXENV_STATUS_FAILURE
#define PLATFORM_ENOSYS PXENV_STATUS_UNSUPPORTED
#define PLATFORM_ENOTCONN PXENV_STATUS_FAILURE
#define PLATFORM_ENOTDIR PXENV_STATUS_FAILURE
#define PLATFORM_ENOTEMPTY PXENV_STATUS_FAILURE
#define PLATFORM_ENOTSOCK PXENV_STATUS_FAILURE
#define PLATFORM_ENOTSUP PXENV_STATUS_UNSUPPORTED
#define PLATFORM_ENOTTY PXENV_STATUS_FAILURE
#define PLATFORM_ENXIO PXENV_STATUS_TFTP_FILE_NOT_FOUND
#define PLATFORM_EOPNOTSUPP PXENV_STATUS_UNSUPPORTED
#define PLATFORM_EOVERFLOW PXENV_STATUS_FAILURE
#define PLATFORM_EPERM PXENV_STATUS_TFTP_ACCESS_VIOLATION
#define PLATFORM_EPIPE PXENV_STATUS_FAILURE
#define PLATFORM_EPROTO PXENV_STATUS_FAILURE
#define PLATFORM_EPROTONOSUPPORT PXENV_STATUS_UNSUPPORTED
#define PLATFORM_EPROTOTYPE PXENV_STATUS_FAILURE
#define PLATFORM_ERANGE PXENV_STATUS_FAILURE
#define PLATFORM_EROFS PXENV_STATUS_FAILURE
#define PLATFORM_ESPIPE PXENV_STATUS_FAILURE
#define PLATFORM_ESRCH PXENV_STATUS_TFTP_FILE_NOT_FOUND
#define PLATFORM_ESTALE PXENV_STATUS_FAILURE
#define PLATFORM_ETIME PXENV_STATUS_FAILURE
#define PLATFORM_ETIMEDOUT PXENV_STATUS_TFTP_READ_TIMEOUT
#define PLATFORM_ETXTBSY PXENV_STATUS_FAILURE
#define PLATFORM_EWOULDBLOCK PXENV_STATUS_TFTP_OPEN
#define PLATFORM_EXDEV PXENV_STATUS_FAILURE
#endif /* _IPXE_ERRNO_PCBIOS_H */

View File

@@ -1,18 +0,0 @@
#ifndef _IPXE_MEMTOP_UMALLOC_H
#define _IPXE_MEMTOP_UMALLOC_H
/** @file
*
* External memory allocation
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef UMALLOC_MEMTOP
#define UMALLOC_PREFIX_memtop
#else
#define UMALLOC_PREFIX_memtop __memtop_
#endif
#endif /* _IPXE_MEMTOP_UMALLOC_H */

View File

@@ -1,39 +0,0 @@
#ifndef _IPXE_RDTSC_TIMER_H
#define _IPXE_RDTSC_TIMER_H
/** @file
*
* RDTSC timer
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef TIMER_RDTSC
#define TIMER_PREFIX_rdtsc
#else
#define TIMER_PREFIX_rdtsc __rdtsc_
#endif
/**
* RDTSC values can easily overflow an unsigned long. We discard the
* low-order bits in order to obtain sensibly-scaled values.
*/
#define TSC_SHIFT 8
/**
* Get current system time in ticks
*
* @ret ticks Current time, in ticks
*/
static inline __always_inline unsigned long
TIMER_INLINE ( rdtsc, currticks ) ( void ) {
unsigned long ticks;
__asm__ __volatile__ ( "rdtsc\n\t"
"shrdl %1, %%edx, %%eax\n\t"
: "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" );
return ticks;
}
#endif /* _IPXE_RDTSC_TIMER_H */

View File

@@ -1,62 +0,0 @@
#ifndef _IPXE_RTC_ENTROPY_H
#define _IPXE_RTC_ENTROPY_H
/** @file
*
* RTC-based entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_RTC
#define ENTROPY_PREFIX_rtc
#else
#define ENTROPY_PREFIX_rtc __rtc_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline double
ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) {
/* The min-entropy has been measured on several platforms
* using the entropy_sample test code. Modelling the samples
* as independent, and using a confidence level of 99.99%, the
* measurements were as follows:
*
* qemu-kvm : 7.38 bits
* VMware : 7.46 bits
* Physical hardware : 2.67 bits
*
* We choose the lowest of these (2.67 bits) and apply a 50%
* safety margin to allow for some potential non-independence
* of samples.
*/
return 1.3;
}
extern uint8_t rtc_sample ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static inline __always_inline int
ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) {
/* Get sample */
*noise = rtc_sample();
/* Always successful */
return 0;
}
#endif /* _IPXE_RTC_ENTROPY_H */

View File

@@ -1,18 +0,0 @@
#ifndef _IPXE_RTC_TIME_H
#define _IPXE_RTC_TIME_H
/** @file
*
* RTC-based time source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef TIME_RTC
#define TIME_PREFIX_rtc
#else
#define TIME_PREFIX_rtc __rtc_
#endif
#endif /* _IPXE_RTC_TIME_H */

View File

@@ -1,210 +0,0 @@
#ifndef _IPXE_VESAFB_H
#define _IPXE_VESAFB_H
/** @file
*
* VESA frame buffer console
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <realmode.h>
/** INT 10,4f00: return controller information */
#define VBE_CONTROLLER_INFO 0x4f00
/** VBE controller information */
struct vbe_controller_info {
/** VBE signature */
uint32_t vbe_signature;
/** VBE minor version */
uint8_t vbe_minor_version;
/** VBE major version */
uint8_t vbe_major_version;
/** Pointer to OEM string */
struct segoff oem_string_ptr;
/** Capabilities of graphics controller */
uint32_t capabilities;
/** Pointer to video mode list */
struct segoff video_mode_ptr;
/** Number of 64kB memory blocks */
uint16_t total_memory;
/** VBE implementation software revision */
uint16_t oem_software_rev;
/** Pointer to vendor name string */
struct segoff oem_vendor_name_ptr;
/** Pointer to product name string */
struct segoff oem_product_name_ptr;
/** Pointer to product revision string */
struct segoff oem_product_rev_ptr;
/** Reserved for VBE implementation scratch area */
uint8_t reserved[222];
/* VBE2.0 defines an additional 256-byte data area for
* including the OEM strings inline within the VBE information
* block; we omit this to reduce the amount of base memory
* required for VBE calls.
*/
} __attribute__ (( packed ));
/** VBE controller information signature */
#define VBE_CONTROLLER_SIGNATURE \
( ( 'V' << 0 ) | ( 'E' << 8 ) | ( 'S' << 16 ) | ( 'A' << 24 ) )
/** VBE mode list end marker */
#define VBE_MODE_END 0xffff
/** INT 10,4f01: return VBE mode information */
#define VBE_MODE_INFO 0x4f01
/** VBE mode information */
struct vbe_mode_info {
/** Mode attributes */
uint16_t mode_attributes;
/** Window A attributes */
uint8_t win_a_attributes;
/** Window B attributes */
uint8_t win_b_attributes;
/** Window granularity */
uint16_t win_granularity;
/** Window size */
uint16_t win_size;
/** Window A start segment */
uint16_t win_a_segment;
/** Window B start segment */
uint16_t win_b_segment;
/** Pointer to window function */
struct segoff win_func_ptr;
/** Bytes per scan line */
uint16_t bytes_per_scan_line;
/** Horizontal resolution in pixels or characters */
uint16_t x_resolution;
/** Vertical resolution in pixels or characters */
uint16_t y_resolution;
/** Character cell width in pixels */
uint8_t x_char_size;
/** Character cell height in pixels */
uint8_t y_char_size;
/** Number of memory planes */
uint8_t number_of_planes;
/** Bits per pixel */
uint8_t bits_per_pixel;
/** Number of banks */
uint8_t number_of_banks;
/** Memory model type */
uint8_t memory_model;
/** Bank size in kB */
uint8_t bank_size;
/** Number of images */
uint8_t number_of_image_pages;
/** Reserved for page function */
uint8_t reserved_1;
/** Size of direct colour red mask in bits */
uint8_t red_mask_size;
/** Bit position of LSB of red mask */
uint8_t red_field_position;
/** Size of direct colour green mask in bits */
uint8_t green_mask_size;
/** Bit position of LSB of green mask */
uint8_t green_field_position;
/** Size of direct colour blue mask in bits */
uint8_t blue_mask_size;
/** Bit position of LSB of blue mask */
uint8_t blue_field_position;
/** Size of direct colour reserved mask in bits */
uint8_t rsvd_mask_size;
/** Bit position of LSB of reserved mask */
uint8_t rsvd_field_position;
/** Direct colour mode attributes */
uint8_t direct_colour_mode_info;
/** Physical address for flat memory frame buffer */
uint32_t phys_base_ptr;
/** Pointer to start of off-screen memory */
uint32_t off_screen_mem_offset;
/** Amount of off-screen memory in 1kB units */
uint16_t off_screen_mem_size;
/** Reserved */
uint8_t reserved_2[206];
} __attribute__ (( packed ));
/** VBE mode attributes */
enum vbe_mode_attributes {
/** Mode supported in hardware */
VBE_MODE_ATTR_SUPPORTED = 0x0001,
/** TTY output functions supported by BIOS */
VBE_MODE_ATTR_TTY = 0x0004,
/** Colour mode */
VBE_MODE_ATTR_COLOUR = 0x0008,
/** Graphics mode */
VBE_MODE_ATTR_GRAPHICS = 0x0010,
/** Not a VGA compatible mode */
VBE_MODE_ATTR_NOT_VGA = 0x0020,
/** VGA compatible windowed memory mode is not available */
VBE_MODE_ATTR_NOT_WINDOWED = 0x0040,
/** Linear frame buffer mode is available */
VBE_MODE_ATTR_LINEAR = 0x0080,
/** Double scan mode is available */
VBE_MODE_ATTR_DOUBLE = 0x0100,
/** Interlaced mode is available */
VBE_MODE_ATTR_INTERLACED = 0x0200,
/** Hardware triple buffering support */
VBE_MODE_ATTR_TRIPLE_BUF = 0x0400,
/** Hardware stereoscopic display support */
VBE_MODE_ATTR_STEREO = 0x0800,
/** Dual display start address support */
VBE_MODE_ATTR_DUAL = 0x1000,
};
/** VBE mode memory models */
enum vbe_mode_memory_model {
/** Text mode */
VBE_MODE_MODEL_TEXT = 0x00,
/** CGA graphics mode */
VBE_MODE_MODEL_CGA = 0x01,
/** Hercules graphics mode */
VBE_MODE_MODEL_HERCULES = 0x02,
/** Planar mode */
VBE_MODE_MODEL_PLANAR = 0x03,
/** Packed pixel mode */
VBE_MODE_MODEL_PACKED_PIXEL = 0x04,
/** Non-chain 4, 256 colour mode */
VBE_MODE_MODEL_NON_CHAIN_4 = 0x05,
/** Direct colour mode */
VBE_MODE_MODEL_DIRECT_COLOUR = 0x06,
/** YUV mode */
VBE_MODE_MODEL_YUV = 0x07,
};
/** INT 10,4f02: set VBE mode */
#define VBE_SET_MODE 0x4f02
/** VBE linear frame buffer mode bit */
#define VBE_MODE_LINEAR 0x4000
/** INT 10,1130: get font information */
#define VBE_GET_FONT 0x1130
/** Font sets */
enum vbe_font_set {
/** 8x14 character font */
VBE_FONT_8x14 = 0x0200,
/** 8x8 double dot font */
VBE_FONT_8x8_DOUBLE = 0x0300,
/** 8x8 double dot font (high 128 characters) */
VBE_FONT_8x8_DOUBLE_HIGH = 0x0400,
/** 9x14 alpha alternate font */
VBE_FONT_9x14_ALPHA_ALT = 0x0500,
/** 8x16 font */
VBE_FONT_8x16 = 0x0600,
/** 9x16 alternate font */
VBE_FONT_9x16_ALT = 0x0700,
};
/** INT 10,00: set VGA mode */
#define VBE_SET_VGA_MODE 0x0000
/** INT 10,0f: get VGA mode */
#define VBE_GET_VGA_MODE 0x0f00
#endif /* _IPXE_VESAFB_H */

View File

@@ -1,18 +0,0 @@
#ifndef KIR_H
#define KIR_H
#ifndef KEEP_IT_REAL
#error "kir.h can be used only with -DKEEP_IT_REAL"
#endif
#ifdef ASSEMBLY
#define code32 code16gcc
#else /* ASSEMBLY */
__asm__ ( ".code16gcc" );
#endif /* ASSEMBLY */
#endif /* KIR_H */

View File

@@ -1,233 +0,0 @@
#ifndef LIBKIR_H
#define LIBKIR_H
#include "realmode.h"
#ifndef ASSEMBLY
/*
* Full API documentation for these functions is in realmode.h.
*
*/
/* Access to variables in .data16 and .text16 in a way compatible with librm */
#define __data16( variable ) variable
#define __data16_array( variable, array ) variable array
#define __bss16( variable ) variable
#define __bss16_array( variable, array ) variable array
#define __text16( variable ) variable
#define __text16_array( variable,array ) variable array
#define __use_data16( variable ) variable
#define __use_text16( variable ) variable
#define __from_data16( pointer ) pointer
#define __from_text16( pointer ) pointer
/* Real-mode data and code segments */
static inline __attribute__ (( always_inline )) unsigned int _rm_cs ( void ) {
uint16_t cs;
__asm__ __volatile__ ( "movw %%cs, %w0" : "=r" ( cs ) );
return cs;
}
static inline __attribute__ (( always_inline )) unsigned int _rm_ds ( void ) {
uint16_t ds;
__asm__ __volatile__ ( "movw %%ds, %w0" : "=r" ( ds ) );
return ds;
}
#define rm_cs ( _rm_cs() )
#define rm_ds ( _rm_ds() )
/* Copy to/from base memory */
static inline void copy_to_real_libkir ( unsigned int dest_seg,
unsigned int dest_off,
const void *src, size_t n ) {
unsigned int discard_D, discard_S, discard_c;
__asm__ __volatile__ ( "pushw %%es\n\t"
"movw %3, %%es\n\t"
"rep movsb\n\t"
"popw %%es\n\t"
: "=D" ( discard_D ), "=S" ( discard_S ),
"=c" ( discard_c )
: "r" ( dest_seg ), "D" ( dest_off ),
"S" ( src ),
"c" ( n )
: "memory" );
}
static inline void copy_from_real_libkir ( void *dest,
unsigned int src_seg,
unsigned int src_off,
size_t n ) {
unsigned int discard_D, discard_S, discard_c;
__asm__ __volatile__ ( "pushw %%ds\n\t"
"movw %4, %%ds\n\t"
"rep movsb\n\t"
"popw %%ds\n\t"
: "=D" ( discard_D ), "=S" ( discard_S ),
"=c" ( discard_c )
: "D" ( dest ),
"r" ( src_seg ), "S" ( src_off ),
"c" ( n )
: "memory" );
}
#define copy_to_real copy_to_real_libkir
#define copy_from_real copy_from_real_libkir
/*
* Transfer individual values to/from base memory. There may well be
* a neater way to do this. We have two versions: one for constant
* offsets (where the mov instruction must be of the form "mov
* %es:123, %xx") and one for non-constant offsets (where the mov
* instruction must be of the form "mov %es:(%xx), %yx". If it's
* possible to incorporate both forms into one __asm__ instruction, I
* don't know how to do it.
*
* Ideally, the mov instruction should be "mov%z0"; the "%z0" is meant
* to expand to either "b", "w" or "l" depending on the size of
* operand 0. This would remove the (minor) ambiguity in the mov
* instruction. However, gcc on at least my system barfs with an
* "internal compiler error" when confronted with %z0.
*
*/
#define put_real_kir_const_off( var, seg, off ) \
__asm__ ( "movw %w1, %%es\n\t" \
"mov %0, %%es:%c2\n\t" \
"pushw %%ds\n\t" /* restore %es */ \
"popw %%es\n\t" \
: \
: "r,r" ( var ), "rm,rm" ( seg ), "i,!r" ( off ) \
)
#define put_real_kir_nonconst_off( var, seg, off ) \
__asm__ ( "movw %w1, %%es\n\t" \
"mov %0, %%es:(%2)\n\t" \
"pushw %%ds\n\t" /* restore %es */ \
"popw %%es\n\t" \
: \
: "r" ( var ), "rm" ( seg ), "r" ( off ) \
)
#define put_real_kir( var, seg, off ) \
do { \
if ( __builtin_constant_p ( off ) ) \
put_real_kir_const_off ( var, seg, off ); \
else \
put_real_kir_nonconst_off ( var, seg, off ); \
} while ( 0 )
#define get_real_kir_const_off( var, seg, off ) \
__asm__ ( "movw %w1, %%es\n\t" \
"mov %%es:%c2, %0\n\t" \
"pushw %%ds\n\t" /* restore %es */ \
"popw %%es\n\t" \
: "=r,r" ( var ) \
: "rm,rm" ( seg ), "i,!r" ( off ) \
)
#define get_real_kir_nonconst_off( var, seg, off ) \
__asm__ ( "movw %w1, %%es\n\t" \
"mov %%es:(%2), %0\n\t" \
"pushw %%ds\n\t" /* restore %es */ \
"popw %%es\n\t" \
: "=r" ( var ) \
: "rm" ( seg ), "r" ( off ) \
)
#define get_real_kir( var, seg, off ) \
do { \
if ( __builtin_constant_p ( off ) ) \
get_real_kir_const_off ( var, seg, off ); \
else \
get_real_kir_nonconst_off ( var, seg, off ); \
} while ( 0 )
#define put_real put_real_kir
#define get_real get_real_kir
/**
* A pointer to a user buffer
*
* This is actually a struct segoff, but encoded as a uint32_t to
* ensure that gcc passes it around efficiently.
*/
typedef uint32_t userptr_t;
/**
* Copy data to user buffer
*
* @v buffer User buffer
* @v offset Offset within user buffer
* @v src Source
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) {
copy_to_real ( ( buffer >> 16 ), ( ( buffer & 0xffff ) + offset ),
src, len );
}
/**
* Copy data from user buffer
*
* @v dest Destination
* @v buffer User buffer
* @v offset Offset within user buffer
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
copy_from_real ( dest, ( buffer >> 16 ),
( ( buffer & 0xffff ) + offset ), len );
}
/**
* Convert segment:offset address to user buffer
*
* @v segment Real-mode segment
* @v offset Real-mode offset
* @ret buffer User buffer
*/
static inline __attribute__ (( always_inline )) userptr_t
real_to_user ( unsigned int segment, unsigned int offset ) {
return ( ( segment << 16 ) | offset );
}
/**
* Convert virtual address to user buffer
*
* @v virtual Virtual address
* @ret buffer User buffer
*
* This constructs a user buffer from an ordinary pointer. Use it
* when you need to pass a pointer to an internal buffer to a function
* that expects a @c userptr_t.
*/
static inline __attribute__ (( always_inline )) userptr_t
virt_to_user ( void * virtual ) {
return real_to_user ( rm_ds, ( intptr_t ) virtual );
}
/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
#define TEXT16_CODE( asm_code_str ) \
".section \".text16\", \"ax\", @progbits\n\t" \
".code16\n\t" \
".arch i386\n\t" \
asm_code_str "\n\t" \
".code16gcc\n\t" \
".previous\n\t"
/* REAL_CODE: declare a fragment of code that executes in real mode */
#define REAL_CODE( asm_code_str ) \
".code16\n\t" \
asm_code_str "\n\t" \
".code16gcc\n\t"
#endif /* ASSEMBLY */
#endif /* LIBKIR_H */

View File

@@ -1,276 +0,0 @@
#ifndef LIBRM_H
#define LIBRM_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Segment selectors as used in our protected-mode GDTs.
*
* Don't change these unless you really know what you're doing.
*/
#define VIRTUAL_CS 0x08
#define VIRTUAL_DS 0x10
#define PHYSICAL_CS 0x18
#define PHYSICAL_DS 0x20
#define REAL_CS 0x28
#define REAL_DS 0x30
#if 0
#define LONG_CS 0x38
#define LONG_DS 0x40
#endif
#ifndef ASSEMBLY
#ifdef UACCESS_LIBRM
#define UACCESS_PREFIX_librm
#else
#define UACCESS_PREFIX_librm __librm_
#endif
/* Variables in librm.S */
extern unsigned long virt_offset;
/**
* Convert physical address to user pointer
*
* @v phys_addr Physical address
* @ret userptr User pointer
*/
static inline __always_inline userptr_t
UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) {
return ( phys_addr - virt_offset );
}
/**
* Convert user buffer to physical address
*
* @v userptr User pointer
* @v offset Offset from user pointer
* @ret phys_addr Physical address
*/
static inline __always_inline unsigned long
UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) {
return ( userptr + offset + virt_offset );
}
static inline __always_inline userptr_t
UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) {
return trivial_virt_to_user ( addr );
}
static inline __always_inline void *
UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) {
return trivial_user_to_virt ( userptr, offset );
}
static inline __always_inline userptr_t
UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) {
return trivial_userptr_add ( userptr, offset );
}
static inline __always_inline off_t
UACCESS_INLINE ( librm, userptr_sub ) ( userptr_t userptr,
userptr_t subtrahend ) {
return trivial_userptr_sub ( userptr, subtrahend );
}
static inline __always_inline void
UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off,
size_t len ) {
trivial_memcpy_user ( dest, dest_off, src, src_off, len );
}
static inline __always_inline void
UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off,
size_t len ) {
trivial_memmove_user ( dest, dest_off, src, src_off, len );
}
static inline __always_inline int
UACCESS_INLINE ( librm, memcmp_user ) ( userptr_t first, off_t first_off,
userptr_t second, off_t second_off,
size_t len ) {
return trivial_memcmp_user ( first, first_off, second, second_off, len);
}
static inline __always_inline void
UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset,
int c, size_t len ) {
trivial_memset_user ( buffer, offset, c, len );
}
static inline __always_inline size_t
UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) {
return trivial_strlen_user ( buffer, offset );
}
static inline __always_inline off_t
UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset,
int c, size_t len ) {
return trivial_memchr_user ( buffer, offset, c, len );
}
/******************************************************************************
*
* Access to variables in .data16 and .text16
*
*/
extern char *data16;
extern char *text16;
#define __data16( variable ) \
__attribute__ (( section ( ".data16" ) )) \
_data16_ ## variable __asm__ ( #variable )
#define __data16_array( variable, array ) \
__attribute__ (( section ( ".data16" ) )) \
_data16_ ## variable array __asm__ ( #variable )
#define __bss16( variable ) \
__attribute__ (( section ( ".bss16" ) )) \
_data16_ ## variable __asm__ ( #variable )
#define __bss16_array( variable, array ) \
__attribute__ (( section ( ".bss16" ) )) \
_data16_ ## variable array __asm__ ( #variable )
#define __text16( variable ) \
__attribute__ (( section ( ".text16.data" ) )) \
_text16_ ## variable __asm__ ( #variable )
#define __text16_array( variable, array ) \
__attribute__ (( section ( ".text16.data" ) )) \
_text16_ ## variable array __asm__ ( #variable )
#define __use_data16( variable ) \
( * ( ( typeof ( _data16_ ## variable ) * ) \
& ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) )
#define __use_text16( variable ) \
( * ( ( typeof ( _text16_ ## variable ) * ) \
& ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) )
#define __from_data16( pointer ) \
( ( unsigned int ) \
( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) )
#define __from_text16( pointer ) \
( ( unsigned int ) \
( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) )
/* Variables in librm.S, present in the normal data segment */
extern uint16_t rm_sp;
extern uint16_t rm_ss;
extern uint16_t __text16 ( rm_cs );
#define rm_cs __use_text16 ( rm_cs )
extern uint16_t __text16 ( rm_ds );
#define rm_ds __use_text16 ( rm_ds )
extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );
extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
/* CODE_DEFAULT: restore default .code32/.code64 directive */
#ifdef __x86_64__
#define CODE_DEFAULT ".code64"
#else
#define CODE_DEFAULT ".code32"
#endif
/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
#define TEXT16_CODE( asm_code_str ) \
".section \".text16\", \"ax\", @progbits\n\t" \
".code16\n\t" \
asm_code_str "\n\t" \
CODE_DEFAULT "\n\t" \
".previous\n\t"
/* REAL_CODE: declare a fragment of code that executes in real mode */
#define REAL_CODE( asm_code_str ) \
"push $1f\n\t" \
"call real_call\n\t" \
"addl $4, %%esp\n\t" \
TEXT16_CODE ( "\n1:\n\t" \
asm_code_str \
"\n\t" \
"ret\n\t" )
/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
#define PHYS_CODE( asm_code_str ) \
"call _virt_to_phys\n\t" \
".code32\n\t" \
asm_code_str \
"call _phys_to_virt\n\t" \
CODE_DEFAULT "\n\t"
/** Number of interrupts */
#define NUM_INT 256
/** An interrupt descriptor table register */
struct idtr {
/** Limit */
uint16_t limit;
/** Base */
uint32_t base;
} __attribute__ (( packed ));
/** An interrupt descriptor table entry */
struct interrupt_descriptor {
/** Low 16 bits of address */
uint16_t low;
/** Code segment */
uint16_t segment;
/** Unused */
uint8_t unused;
/** Type and attributes */
uint8_t attr;
/** High 16 bits of address */
uint16_t high;
} __attribute__ (( packed ));
/** Interrupt descriptor is present */
#define IDTE_PRESENT 0x80
/** Interrupt descriptor 32-bit interrupt gate type */
#define IDTE_TYPE_IRQ32 0x0e
/** An interrupt vector
*
* Each interrupt vector comprises an eight-byte fragment of code:
*
* 60 pushal
* b0 xx movb $INT, %al
* e9 xx xx xx xx jmp interrupt_wrapper
*/
struct interrupt_vector {
/** "pushal" instruction */
uint8_t pushal;
/** "movb" instruction */
uint8_t movb;
/** Interrupt number */
uint8_t intr;
/** "jmp" instruction */
uint8_t jmp;
/** Interrupt wrapper address offset */
uint32_t offset;
/** Next instruction after jump */
uint8_t next[0];
} __attribute__ (( packed ));
/** "pushal" instruction */
#define PUSHAL_INSN 0x60
/** "movb" instruction */
#define MOVB_INSN 0xb0
/** "jmp" instruction */
#define JMP_INSN 0xe9
extern void set_interrupt_vector ( unsigned int intr, void *vector );
#endif /* ASSEMBLY */
#endif /* LIBRM_H */

View File

@@ -1,19 +0,0 @@
#ifndef _MEMSIZES_H
#define _MEMSIZES_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <basemem.h>
/**
* Get size of base memory from BIOS free base memory counter
*
* @ret basemem Base memory size, in kB
*/
static inline unsigned int basememsize ( void ) {
return get_fbms();
}
extern unsigned int extmemsize ( void );
#endif /* _MEMSIZES_H */

View File

@@ -1,149 +0,0 @@
#ifndef _MULTIBOOT_H
#define _MULTIBOOT_H
/**
* @file
*
* Multiboot operating systems
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** The magic number for the Multiboot header */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/** Boot modules must be page aligned */
#define MB_FLAG_PGALIGN 0x00000001
/** Memory map must be provided */
#define MB_FLAG_MEMMAP 0x00000002
/** Video mode information must be provided */
#define MB_FLAG_VIDMODE 0x00000004
/** Image is a raw multiboot image (not ELF) */
#define MB_FLAG_RAW 0x00010000
/**
* The magic number passed by a Multiboot-compliant boot loader
*
* Must be passed in register %eax when jumping to the Multiboot OS
* image.
*/
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/** Multiboot information structure mem_* fields are valid */
#define MBI_FLAG_MEM 0x00000001
/** Multiboot information structure boot_device field is valid */
#define MBI_FLAG_BOOTDEV 0x00000002
/** Multiboot information structure cmdline field is valid */
#define MBI_FLAG_CMDLINE 0x00000004
/** Multiboot information structure module fields are valid */
#define MBI_FLAG_MODS 0x00000008
/** Multiboot information structure a.out symbol table is valid */
#define MBI_FLAG_AOUT 0x00000010
/** Multiboot information struture ELF section header table is valid */
#define MBI_FLAG_ELF 0x00000020
/** Multiboot information structure memory map is valid */
#define MBI_FLAG_MMAP 0x00000040
/** Multiboot information structure drive list is valid */
#define MBI_FLAG_DRIVES 0x00000080
/** Multiboot information structure ROM configuration field is valid */
#define MBI_FLAG_CFGTBL 0x00000100
/** Multiboot information structure boot loader name field is valid */
#define MBI_FLAG_LOADER 0x00000200
/** Multiboot information structure APM table is valid */
#define MBI_FLAG_APM 0x00000400
/** Multiboot information structure video information is valid */
#define MBI_FLAG_VBE 0x00000800
/** A multiboot header */
struct multiboot_header {
uint32_t magic;
uint32_t flags;
uint32_t checksum;
uint32_t header_addr;
uint32_t load_addr;
uint32_t load_end_addr;
uint32_t bss_end_addr;
uint32_t entry_addr;
} __attribute__ (( packed, may_alias ));
/** A multiboot a.out symbol table */
struct multiboot_aout_symbol_table {
uint32_t tabsize;
uint32_t strsize;
uint32_t addr;
uint32_t reserved;
} __attribute__ (( packed, may_alias ));
/** A multiboot ELF section header table */
struct multiboot_elf_section_header_table {
uint32_t num;
uint32_t size;
uint32_t addr;
uint32_t shndx;
} __attribute__ (( packed, may_alias ));
/** A multiboot information structure */
struct multiboot_info {
uint32_t flags;
uint32_t mem_lower;
uint32_t mem_upper;
uint32_t boot_device;
uint32_t cmdline;
uint32_t mods_count;
uint32_t mods_addr;
union {
struct multiboot_aout_symbol_table aout_syms;
struct multiboot_elf_section_header_table elf_sections;
} syms;
uint32_t mmap_length;
uint32_t mmap_addr;
uint32_t drives_length;
uint32_t drives_addr;
uint32_t config_table;
uint32_t boot_loader_name;
uint32_t apm_table;
uint32_t vbe_control_info;
uint32_t vbe_mode_info;
uint16_t vbe_mode;
uint16_t vbe_interface_seg;
uint16_t vbe_interface_off;
uint16_t vbe_interface_len;
} __attribute__ (( packed, may_alias ));
/** A multiboot module structure */
struct multiboot_module {
uint32_t mod_start;
uint32_t mod_end;
uint32_t string;
uint32_t reserved;
} __attribute__ (( packed, may_alias ));
/** A multiboot memory map entry */
struct multiboot_memory_map {
uint32_t size;
uint64_t base_addr;
uint64_t length;
uint32_t type;
} __attribute__ (( packed, may_alias ));
/** Usable RAM */
#define MBMEM_RAM 1
#endif /* _MULTIBOOT_H */

View File

@@ -1,17 +0,0 @@
#ifndef _PNPBIOS_H
#define _PNPBIOS_H
/** @file
*
* PnP BIOS
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* BIOS segment address */
#define BIOS_SEG 0xf000
extern int find_pnp_bios ( void );
#endif /* _PNPBIOS_H */

View File

@@ -1,200 +0,0 @@
#ifndef PXE_H
#define PXE_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include "pxe_types.h"
#include "pxe_error.h"
#include "pxe_api.h"
#include <ipxe/device.h>
#include <ipxe/tables.h>
/** PXE API invalid function code */
#define PXENV_UNKNOWN 0xffff
/** Parameter block for pxenv_unknown() */
struct s_PXENV_UNKNOWN {
PXENV_STATUS_t Status; /**< PXE status code */
} __attribute__ (( packed ));
typedef struct s_PXENV_UNKNOWN PXENV_UNKNOWN_t;
/* Union used for PXE API calls; we don't know the type of the
* structure until we interpret the opcode. Also, Status is available
* in the same location for any opcode, and it's convenient to have
* non-specific access to it.
*/
union u_PXENV_ANY {
/* Make it easy to read status for any operation */
PXENV_STATUS_t Status;
struct s_PXENV_UNKNOWN unknown;
struct s_PXENV_UNLOAD_STACK unload_stack;
struct s_PXENV_GET_CACHED_INFO get_cached_info;
struct s_PXENV_TFTP_READ_FILE restart_tftp;
struct s_PXENV_START_UNDI start_undi;
struct s_PXENV_STOP_UNDI stop_undi;
struct s_PXENV_START_BASE start_base;
struct s_PXENV_STOP_BASE stop_base;
struct s_PXENV_TFTP_OPEN tftp_open;
struct s_PXENV_TFTP_CLOSE tftp_close;
struct s_PXENV_TFTP_READ tftp_read;
struct s_PXENV_TFTP_READ_FILE tftp_read_file;
struct s_PXENV_TFTP_GET_FSIZE tftp_get_fsize;
struct s_PXENV_UDP_OPEN udp_open;
struct s_PXENV_UDP_CLOSE udp_close;
struct s_PXENV_UDP_WRITE udp_write;
struct s_PXENV_UDP_READ udp_read;
struct s_PXENV_UNDI_STARTUP undi_startup;
struct s_PXENV_UNDI_CLEANUP undi_cleanup;
struct s_PXENV_UNDI_INITIALIZE undi_initialize;
struct s_PXENV_UNDI_RESET undi_reset_adapter;
struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
struct s_PXENV_UNDI_OPEN undi_open;
struct s_PXENV_UNDI_CLOSE undi_close;
struct s_PXENV_UNDI_TRANSMIT undi_transmit;
struct s_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address;
struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address;
struct s_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter;
struct s_PXENV_UNDI_GET_INFORMATION undi_get_information;
struct s_PXENV_UNDI_GET_STATISTICS undi_get_statistics;
struct s_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics;
struct s_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags;
struct s_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt;
struct s_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address;
struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type;
struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info;
struct s_PXENV_UNDI_GET_STATE undi_get_state;
struct s_PXENV_UNDI_ISR undi_isr;
struct s_PXENV_FILE_OPEN file_open;
struct s_PXENV_FILE_CLOSE file_close;
struct s_PXENV_FILE_SELECT file_select;
struct s_PXENV_FILE_READ file_read;
struct s_PXENV_GET_FILE_SIZE get_file_size;
struct s_PXENV_FILE_EXEC file_exec;
struct s_PXENV_FILE_API_CHECK file_api_check;
struct s_PXENV_FILE_EXIT_HOOK file_exit_hook;
};
typedef union u_PXENV_ANY PXENV_ANY_t;
/** A PXE API call */
struct pxe_api_call {
/** Entry point
*
* @v params PXE API call parameters
* @ret exit PXE API call exit code
*/
PXENV_EXIT_t ( * entry ) ( union u_PXENV_ANY *params );
/** Length of parameters */
uint16_t params_len;
/** Opcode */
uint16_t opcode;
};
/** PXE API call table */
#define PXE_API_CALLS __table ( struct pxe_api_call, "pxe_api_calls" )
/** Declare a PXE API call */
#define __pxe_api_call __table_entry ( PXE_API_CALLS, 01 )
/**
* Define a PXE API call
*
* @v _opcode Opcode
* @v _entry Entry point
* @v _params_type Type of parameter structure
* @ret call PXE API call
*/
#define PXE_API_CALL( _opcode, _entry, _params_type ) { \
.entry = ( ( ( ( PXENV_EXIT_t ( * ) ( _params_type *params ) ) NULL ) \
== ( ( typeof ( _entry ) * ) NULL ) ) \
? ( ( PXENV_EXIT_t ( * ) \
( union u_PXENV_ANY *params ) ) _entry ) \
: ( ( PXENV_EXIT_t ( * ) \
( union u_PXENV_ANY *params ) ) _entry ) ), \
.params_len = sizeof ( _params_type ), \
.opcode = _opcode, \
}
/** An UNDI expansion ROM header */
struct undi_rom_header {
/** Signature
*
* Must be equal to @c ROM_SIGNATURE
*/
UINT16_t Signature;
/** ROM length in 512-byte blocks */
UINT8_t ROMLength;
/** Unused */
UINT8_t unused[0x13];
/** Offset of the PXE ROM ID structure */
UINT16_t PXEROMID;
/** Offset of the PCI ROM structure */
UINT16_t PCIRHeader;
} __attribute__ (( packed ));
/** Signature for an expansion ROM */
#define ROM_SIGNATURE 0xaa55
/** An UNDI ROM ID structure */
struct undi_rom_id {
/** Signature
*
* Must be equal to @c UNDI_ROM_ID_SIGNATURE
*/
UINT32_t Signature;
/** Length of structure */
UINT8_t StructLength;
/** Checksum */
UINT8_t StructCksum;
/** Structure revision
*
* Must be zero.
*/
UINT8_t StructRev;
/** UNDI revision
*
* Version 2.1.0 is encoded as the byte sequence 0x00, 0x01, 0x02.
*/
UINT8_t UNDIRev[3];
/** Offset to UNDI loader */
UINT16_t UNDILoader;
/** Minimum required stack segment size */
UINT16_t StackSize;
/** Minimum required data segment size */
UINT16_t DataSize;
/** Minimum required code segment size */
UINT16_t CodeSize;
} __attribute__ (( packed ));
/** Signature for an UNDI ROM ID structure */
#define UNDI_ROM_ID_SIGNATURE \
( ( 'U' << 0 ) + ( 'N' << 8 ) + ( 'D' << 16 ) + ( 'I' << 24 ) )
/** A PCI expansion header */
struct pcir_header {
/** Signature
*
* Must be equal to @c PCIR_SIGNATURE
*/
uint32_t signature;
/** PCI vendor ID */
uint16_t vendor_id;
/** PCI device ID */
uint16_t device_id;
} __attribute__ (( packed ));
/** Signature for an UNDI ROM ID structure */
#define PCIR_SIGNATURE \
( ( 'P' << 0 ) + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
extern struct net_device *pxe_netdev;
extern const char *pxe_cmdline;
extern void pxe_set_netdev ( struct net_device *netdev );
extern void pxe_fake_cached_info ( void );
extern PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
*tftp_read_file );
extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
#endif /* PXE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +0,0 @@
#ifndef _PXE_CALL_H
#define _PXE_CALL_H
/** @file
*
* PXE API entry point
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <pxe_api.h>
#include <realmode.h>
#include <rmsetjmp.h>
struct net_device;
/** PXE load address segment */
#define PXE_LOAD_SEGMENT 0
/** PXE load address offset */
#define PXE_LOAD_OFFSET 0x7c00
/** PXE physical load address */
#define PXE_LOAD_PHYS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET )
/** !PXE structure */
extern struct s_PXE __text16 ( ppxe );
#define ppxe __use_text16 ( ppxe )
/** PXENV+ structure */
extern struct s_PXENV __text16 ( pxenv );
#define pxenv __use_text16 ( pxenv )
/** PXENV_RESTART_TFTP jump buffer */
extern rmjmp_buf pxe_restart_nbp;
extern void pxe_activate ( struct net_device *netdev );
extern int pxe_deactivate ( void );
extern int pxe_start_nbp ( void );
extern __asmcall void pxe_api_call ( struct i386_all_regs *ix86 );
extern int pxe_api_call_weak ( struct i386_all_regs *ix86 );
#endif /* _PXE_CALL_H */

View File

@@ -1,123 +0,0 @@
#ifndef PXE_ERROR_H
#define PXE_ERROR_H
/** @file
*
* Preboot eXecution Environment (PXE) error definitions
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @defgroup pxeerrors PXE error codes
*
* @{
*/
/* Generic errors */
#define PXENV_STATUS_SUCCESS 0x0000
#define PXENV_STATUS_FAILURE 0x0001
#define PXENV_STATUS_BAD_FUNC 0x0002
#define PXENV_STATUS_UNSUPPORTED 0x0003
#define PXENV_STATUS_KEEP_UNDI 0x0004
#define PXENV_STATUS_KEEP_ALL 0x0005
#define PXENV_STATUS_OUT_OF_RESOURCES 0x0006
/* ARP errors (0x0010 to 0x001f) */
#define PXENV_STATUS_ARP_TIMEOUT 0x0011
/* Base-Code state errors */
#define PXENV_STATUS_UDP_CLOSED 0x0018
#define PXENV_STATUS_UDP_OPEN 0x0019
#define PXENV_STATUS_TFTP_CLOSED 0x001a
#define PXENV_STATUS_TFTP_OPEN 0x001b
/* BIOS/system errors (0x0020 to 0x002f) */
#define PXENV_STATUS_MCOPY_PROBLEM 0x0020
#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x0021
#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x0022
#define PXENV_STATUS_BIS_INIT_FAILURE 0x0023
#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x0024
#define PXENV_STATUS_BIS_GBOA_FAILURE 0x0025
#define PXENV_STATUS_BIS_FREE_FAILURE 0x0026
#define PXENV_STATUS_BIS_GSI_FAILURE 0x0027
#define PXENV_STATUS_BIS_BAD_CKSUM 0x0028
/* TFTP/MTFTP errors (0x0030 to 0x003f) */
#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x0030
#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x0032
#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x0033
#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x0035
#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x0036
#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x0038
#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x0039
#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x003a
#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x003b
#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x003c
#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x003d
#define PXENV_STATUS_TFTP_NO_FILESIZE 0x003e
#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x003f
/* Reserved errors 0x0040 to 0x004f) */
/* DHCP/BOOTP errors (0x0050 to 0x005f) */
#define PXENV_STATUS_DHCP_TIMEOUT 0x0051
#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x0052
#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x0053
#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x0054
/* Driver errors (0x0060 to 0x006f) */
#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x0060
#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x0061
#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x0062
#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x0063
#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x0064
#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x0065
#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x0066
#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x0067
#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x0068
#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x0069
#define PXENV_STATUS_UNDI_INVALID_STATE 0x006a
#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x006b
#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x006c
/* ROM and NBP bootstrap errors (0x0070 to 0x007f) */
#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x0074
#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x0076
#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x0077
#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x0078
#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x0079
/* Environment NBP errors (0x0080 to 0x008f) */
/* Reserved errors (0x0090 to 0x009f) */
/* Miscellaneous errors (0x00a0 to 0x00af) */
#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0x00a0
#define PXENV_STATUS_BINL_NO_PXE_SERVER 0x00a1
#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0x00a2
#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0x00a3
/* BUSD errors (0x00b0 to 0x00bf) */
#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0x00b0
/* Loader errors (0x00c0 to 0x00cf) */
#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0x00c0
#define PXENV_STATUS_LOADER_NO_BC_ROMID 0x00c1
#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0x00c2
#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0x00c3
#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0x00c4
#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0x00c5
#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0x00c6
#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0x00c8
#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0x00c9
#define PXENV_STATUS_LOADER_UNDI_START 0x00ca
#define PXENV_STATUS_LOADER_BC_START 0x00cb
/** @} */
/** Derive PXENV_STATUS code from iPXE error number */
#define PXENV_STATUS( rc ) ( (-(rc)) & 0x00ff )
#endif /* PXE_ERROR_H */

View File

@@ -1,127 +0,0 @@
#ifndef PXE_TYPES_H
#define PXE_TYPES_H
/** @file
*
* PXE data types
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <errno.h> /* PXE status codes */
/** @addtogroup pxe Preboot eXecution Environment (PXE) API
* @{
*/
/** @defgroup pxe_types PXE data types
*
* Basic PXE data types such as #UINT16_t, #ADDR32_t, #SEGSEL_t etc.
*
* These definitions are based on Table 1-1 ("Data Type Definitions")
* in the Intel PXE specification version 2.1. They have been
* generalised to non-x86 architectures where possible.
*
* @{
*/
/** An 8-bit unsigned integer */
typedef uint8_t UINT8_t;
/** A 16-bit unsigned integer */
typedef uint16_t UINT16_t;
/** A 32-bit unsigned integer */
typedef uint32_t UINT32_t;
/** A PXE exit code.
*
* Permitted values are #PXENV_EXIT_SUCCESS and #PXENV_EXIT_FAILURE.
*
*/
typedef UINT16_t PXENV_EXIT_t;
#define PXENV_EXIT_SUCCESS 0x0000 /**< No error occurred */
#define PXENV_EXIT_FAILURE 0x0001 /**< An error occurred */
/** A PXE status code.
*
* Status codes are defined in errno.h.
*
*/
typedef UINT16_t PXENV_STATUS_t;
/** An IPv4 address.
*
* @note This data type is in network (big-endian) byte order.
*
*/
typedef UINT32_t IP4_t;
/** A UDP port.
*
* @note This data type is in network (big-endian) byte order.
*
*/
typedef UINT16_t UDP_PORT_t;
/** Maximum length of a MAC address */
#define MAC_ADDR_LEN 16
/** A MAC address */
typedef UINT8_t MAC_ADDR_t[MAC_ADDR_LEN];
#ifndef HAVE_ARCH_ADDR32
/** A physical address.
*
* For x86, this is a 32-bit physical address, and is therefore
* limited to the low 4GB.
*
*/
typedef UINT32_t ADDR32_t;
#endif
#ifndef HAVE_ARCH_SEGSEL
/** A segment selector.
*
* For x86, this is a real mode segment (0x0000-0xffff), or a
* protected-mode segment selector, such as could be loaded into a
* segment register.
*
*/
typedef UINT16_t SEGSEL_t;
#endif
#ifndef HAVE_ARCH_OFF16
/** An offset within a segment identified by #SEGSEL
*
* For x86, this is a 16-bit offset.
*
*/
typedef UINT16_t OFF16_t;
#endif
/** A segment:offset address
*
* For x86, this is a 16-bit real-mode or protected-mode
* segment:offset address.
*
*/
typedef struct s_SEGOFF16 {
OFF16_t offset; /**< Offset within the segment */
SEGSEL_t segment; /**< Segment selector */
} __attribute__ (( packed )) SEGOFF16_t;
/** A segment descriptor */
typedef struct s_SEGDESC {
SEGSEL_t segment_address; /**< Segment selector */
ADDR32_t Physical_address; /**< Segment base address */
OFF16_t Seg_size; /**< Size of the segment */
} __attribute__ (( packed )) SEGDESC_t;
/** @} */ /* pxe_types */
/** @} */ /* pxe */
#endif /* PXE_TYPES_H */

View File

@@ -1,11 +0,0 @@
#ifndef PXEPARENT_H
#define PXEPARENT_H
FILE_LICENCE ( GPL2_OR_LATER );
#include <pxe_types.h>
extern int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
void *params, size_t params_len );
#endif

View File

@@ -1,139 +0,0 @@
#ifndef REALMODE_H
#define REALMODE_H
#include <stdint.h>
#include <registers.h>
#include <ipxe/uaccess.h>
/*
* Data structures and type definitions
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/*
* Declaration of variables in .data16
*
* To place a variable in the .data16 segment, declare it using the
* pattern:
*
* int __data16 ( foo );
* #define foo __use_data16 ( foo );
*
* extern uint32_t __data16 ( bar );
* #define bar __use_data16 ( bar );
*
* static long __data16 ( baz ) = 0xff000000UL;
* #define baz __use_data16 ( baz );
*
* i.e. take a normal declaration, add __data16() around the variable
* name, and add a line saying "#define <name> __use_data16 ( <name> )
*
* You can then access them just like any other variable, for example
*
* int x = foo + bar;
*
* This magic is achieved at a cost of only around 7 extra bytes per
* group of accesses to .data16 variables. When using KEEP_IT_REAL,
* there is no extra cost.
*
* You should place variables in .data16 when they need to be accessed
* by real-mode code. Real-mode assembly (e.g. as created by
* REAL_CODE()) can access these variables via the usual data segment.
* You can therefore write something like
*
* static uint16_t __data16 ( foo );
* #define foo __use_data16 ( foo )
*
* int bar ( void ) {
* __asm__ __volatile__ ( REAL_CODE ( "int $0xff\n\t"
* "movw %ax, foo" )
* : : );
* return foo;
* }
*
* Variables may also be placed in .text16 using __text16 and
* __use_text16. Some variables (e.g. chained interrupt vectors) fit
* most naturally in .text16; most should be in .data16.
*
* If you have only a pointer to a magic symbol within .data16 or
* .text16, rather than the symbol itself, you can attempt to extract
* the underlying symbol name using __from_data16() or
* __from_text16(). This is not for the faint-hearted; check the
* assembler output to make sure that it's doing the right thing.
*/
/**
* Convert segment:offset address to user buffer
*
* @v segment Real-mode segment
* @v offset Real-mode offset
* @ret buffer User buffer
*/
static inline __always_inline userptr_t
real_to_user ( unsigned int segment, unsigned int offset ) {
return ( phys_to_user ( ( segment << 4 ) + offset ) );
}
/**
* Copy data to base memory
*
* @v dest_seg Destination segment
* @v dest_off Destination offset
* @v src Source
* @v len Length
*/
static inline __always_inline void
copy_to_real ( unsigned int dest_seg, unsigned int dest_off,
void *src, size_t n ) {
copy_to_user ( real_to_user ( dest_seg, dest_off ), 0, src, n );
}
/**
* Copy data to base memory
*
* @v dest Destination
* @v src_seg Source segment
* @v src_off Source offset
* @v len Length
*/
static inline __always_inline void
copy_from_real ( void *dest, unsigned int src_seg,
unsigned int src_off, size_t n ) {
copy_from_user ( dest, real_to_user ( src_seg, src_off ), 0, n );
}
/**
* Write a single variable to base memory
*
* @v var Variable to write
* @v dest_seg Destination segment
* @v dest_off Destination offset
*/
#define put_real( var, dest_seg, dest_off ) \
copy_to_real ( (dest_seg), (dest_off), &(var), sizeof (var) )
/**
* Read a single variable from base memory
*
* @v var Variable to read
* @v src_seg Source segment
* @v src_off Source offset
*/
#define get_real( var, src_seg, src_off ) \
copy_from_real ( &(var), (src_seg), (src_off), sizeof (var) )
/*
* REAL_CODE ( asm_code_str )
*
* This can be used in inline assembly to create a fragment of code
* that will execute in real mode. For example: to write a character
* to the BIOS console using INT 10, you would do something like:
*
* __asm__ __volatile__ ( REAL_CODE ( "int $0x16" )
* : "=a" ( character ) : "a" ( 0x0000 ) );
*
*/
#endif /* REALMODE_H */

View File

@@ -1,198 +0,0 @@
#ifndef REGISTERS_H
#define REGISTERS_H
/** @file
*
* i386 registers.
*
* This file defines data structures that allow easy access to i386
* register dumps.
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* A 16-bit general register.
*
* This type encapsulates a 16-bit register such as %ax, %bx, %cx,
* %dx, %si, %di, %bp or %sp.
*
*/
typedef union {
struct {
union {
uint8_t l;
uint8_t byte;
};
uint8_t h;
} __attribute__ (( packed ));
uint16_t word;
} __attribute__ (( packed )) reg16_t;
/**
* A 32-bit general register.
*
* This type encapsulates a 32-bit register such as %eax, %ebx, %ecx,
* %edx, %esi, %edi, %ebp or %esp.
*
*/
typedef union {
struct {
union {
uint8_t l;
uint8_t byte;
};
uint8_t h;
} __attribute__ (( packed ));
uint16_t word;
uint32_t dword;
} __attribute__ (( packed )) reg32_t;
/**
* A 32-bit general register dump.
*
* This is the data structure that is created on the stack by the @c
* pushal instruction, and can be read back using the @c popal
* instruction.
*
*/
struct i386_regs {
union {
uint16_t di;
uint32_t edi;
};
union {
uint16_t si;
uint32_t esi;
};
union {
uint16_t bp;
uint32_t ebp;
};
union {
uint16_t sp;
uint32_t esp;
};
union {
struct {
uint8_t bl;
uint8_t bh;
} __attribute__ (( packed ));
uint16_t bx;
uint32_t ebx;
};
union {
struct {
uint8_t dl;
uint8_t dh;
} __attribute__ (( packed ));
uint16_t dx;
uint32_t edx;
};
union {
struct {
uint8_t cl;
uint8_t ch;
} __attribute__ (( packed ));
uint16_t cx;
uint32_t ecx;
};
union {
struct {
uint8_t al;
uint8_t ah;
} __attribute__ (( packed ));
uint16_t ax;
uint32_t eax;
};
} __attribute__ (( packed ));
/**
* A segment register dump.
*
* The i386 has no equivalent of the @c pushal or @c popal
* instructions for the segment registers. We adopt the convention of
* always using the sequences
*
* @code
*
* pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
*
* @endcode
*
* and
*
* @code
*
* addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
*
* @endcode
*
* This is the data structure that is created and read back by these
* instruction sequences.
*
*/
struct i386_seg_regs {
uint16_t cs;
uint16_t ss;
uint16_t ds;
uint16_t es;
uint16_t fs;
uint16_t gs;
} __attribute__ (( packed ));
/**
* A full register dump.
*
* This data structure is created by the instructions
*
* @code
*
* pushfl
* pushal
* pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
*
* @endcode
*
* and can be read back using the instructions
*
* @code
*
* addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
* popal
* popfl
*
* @endcode
*
* prot_call() and kir_call() create this data structure on the stack
* and pass in a pointer to this structure.
*
*/
struct i386_all_regs {
struct i386_seg_regs segs;
struct i386_regs regs;
uint32_t flags;
} __attribute__ (( packed ));
/* Flags */
#define CF ( 1 << 0 )
#define PF ( 1 << 2 )
#define AF ( 1 << 4 )
#define ZF ( 1 << 6 )
#define SF ( 1 << 7 )
#define OF ( 1 << 11 )
/* Segment:offset structure. Note that the order within the structure
* is offset:segment.
*/
struct segoff {
uint16_t offset;
uint16_t segment;
} __attribute__ (( packed ));
typedef struct segoff segoff_t;
#endif /* REGISTERS_H */

View File

@@ -1,28 +0,0 @@
#ifndef _RMSETJMP_H
#define _RMSETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <setjmp.h>
#include <realmode.h>
/** A real-mode-extended jump buffer */
typedef struct {
/** Jump buffer */
jmp_buf env;
/** Real-mode stack pointer */
segoff_t rm_stack;
} rmjmp_buf[1];
#define rmsetjmp( _env ) ( { \
(_env)->rm_stack.segment = rm_ss; \
(_env)->rm_stack.offset = rm_sp; \
setjmp ( (_env)->env ); } ) \
#define rmlongjmp( _env, _val ) do { \
rm_ss = (_env)->rm_stack.segment; \
rm_sp = (_env)->rm_stack.offset; \
longjmp ( (_env)->env, (_val) ); \
} while ( 0 )
#endif /* _RMSETJMP_H */

View File

@@ -1,83 +0,0 @@
#ifndef _RTC_H
#define _RTC_H
/** @file
*
* CMOS Real-Time Clock (RTC)
*
* The CMOS/RTC registers are documented (with varying degrees of
* accuracy and consistency) at
*
* http://www.nondot.org/sabre/os/files/MiscHW/RealtimeClockFAQ.txt
* http://wiki.osdev.org/RTC
* http://wiki.osdev.org/CMOS
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <pic8259.h>
/** RTC IRQ */
#define RTC_IRQ 8
/** RTC interrupt vector */
#define RTC_INT IRQ_INT ( RTC_IRQ )
/** CMOS/RTC address (and NMI) register */
#define CMOS_ADDRESS 0x70
/** NMI disable bit */
#define CMOS_DISABLE_NMI 0x80
/** CMOS/RTC data register */
#define CMOS_DATA 0x71
/** RTC seconds */
#define RTC_SEC 0x00
/** RTC minutes */
#define RTC_MIN 0x02
/** RTC hours */
#define RTC_HOUR 0x04
/** RTC weekday */
#define RTC_WDAY 0x06
/** RTC day of month */
#define RTC_MDAY 0x07
/** RTC month */
#define RTC_MON 0x08
/** RTC year */
#define RTC_YEAR 0x09
/** RTC status register A */
#define RTC_STATUS_A 0x0a
/** RTC update in progress bit */
#define RTC_STATUS_A_UPDATE_IN_PROGRESS 0x80
/** RTC status register B */
#define RTC_STATUS_B 0x0b
/** RTC 24 hour format bit */
#define RTC_STATUS_B_24_HOUR 0x02
/** RTC binary mode bit */
#define RTC_STATUS_B_BINARY 0x04
/** RTC Periodic Interrupt Enabled bit */
#define RTC_STATUS_B_PIE 0x40
/** RTC status register C */
#define RTC_STATUS_C 0x0c
/** RTC status register D */
#define RTC_STATUS_D 0x0d
/** CMOS default address */
#define CMOS_DEFAULT_ADDRESS RTC_STATUS_D
#endif /* _RTC_H */

View File

@@ -1,39 +0,0 @@
#ifndef _SDI_H
#define _SDI_H
/** @file
*
* System Deployment Image (SDI)
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** SDI image header */
struct sdi_header {
/** Signature */
uint32_t magic;
/** Version (as an ASCII string) */
uint32_t version;
/** Reserved */
uint8_t reserved[8];
/** Boot code offset */
uint64_t boot_offset;
/** Boot code size */
uint64_t boot_size;
} __attribute__ (( packed ));
/** SDI image signature */
#define SDI_MAGIC \
( ( '$' << 0 ) | ( 'S' << 8 ) | ( 'D' << 16 ) | ( 'I' << 24 ) )
/** SDI boot segment */
#define SDI_BOOT_SEG 0x0000
/** SDI boot offset */
#define SDI_BOOT_OFF 0x7c00
/** Constant to binary-OR with physical address of SDI image */
#define SDI_WTF 0x41
#endif /* _SDI_H */

View File

@@ -1,106 +0,0 @@
#ifndef _UNDI_H
#define _UNDI_H
/** @file
*
* UNDI driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
#include <ipxe/device.h>
#include <pxe_types.h>
/** An UNDI device
*
* This structure is used by assembly code as well as C; do not alter
* this structure without editing pxeprefix.S to match.
*/
struct undi_device {
/** PXENV+ structure address */
SEGOFF16_t pxenv;
/** !PXE structure address */
SEGOFF16_t ppxe;
/** Entry point */
SEGOFF16_t entry;
/** Free base memory after load */
UINT16_t fbms;
/** Free base memory prior to load */
UINT16_t restore_fbms;
/** PCI bus:dev.fn, or @c UNDI_NO_PCI_BUSDEVFN */
UINT16_t pci_busdevfn;
/** ISAPnP card select number, or @c UNDI_NO_ISAPNP_CSN */
UINT16_t isapnp_csn;
/** ISAPnP read port, or @c UNDI_NO_ISAPNP_READ_PORT */
UINT16_t isapnp_read_port;
/** PCI vendor ID
*
* Filled in only for the preloaded UNDI device by pxeprefix.S
*/
UINT16_t pci_vendor;
/** PCI device ID
*
* Filled in only for the preloaded UNDI device by pxeprefix.S
*/
UINT16_t pci_device;
/** Flags
*
* This is the bitwise OR of zero or more UNDI_FL_XXX
* constants.
*/
UINT16_t flags;
/** Generic device */
struct device dev;
/** Driver-private data
*
* Use undi_set_drvdata() and undi_get_drvdata() to access this
* field.
*/
void *priv;
} __attribute__ (( packed ));
/**
* Set UNDI driver-private data
*
* @v undi UNDI device
* @v priv Private data
*/
static inline void undi_set_drvdata ( struct undi_device *undi, void *priv ) {
undi->priv = priv;
}
/**
* Get UNDI driver-private data
*
* @v undi UNDI device
* @ret priv Private data
*/
static inline void * undi_get_drvdata ( struct undi_device *undi ) {
return undi->priv;
}
#endif /* ASSEMBLY */
/** PCI bus:dev.fn field is invalid */
#define UNDI_NO_PCI_BUSDEVFN 0xffff
/** ISAPnP card select number field is invalid */
#define UNDI_NO_ISAPNP_CSN 0xffff
/** ISAPnP read port field is invalid */
#define UNDI_NO_ISAPNP_READ_PORT 0xffff
/** UNDI flag: START_UNDI has been called */
#define UNDI_FL_STARTED 0x0001
/** UNDI flag: UNDI_STARTUP and UNDI_INITIALIZE have been called */
#define UNDI_FL_INITIALIZED 0x0002
/** UNDI flag: keep stack resident */
#define UNDI_FL_KEEP_ALL 0x0004
#endif /* _UNDI_H */

View File

@@ -1,35 +0,0 @@
#ifndef _UNDILOAD_H
#define _UNDILOAD_H
/** @file
*
* UNDI load/unload
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
struct undi_device;
struct undi_rom;
extern int undi_load ( struct undi_device *undi, struct undi_rom *undirom );
extern int undi_unload ( struct undi_device *undi );
/**
* Call UNDI loader to create a pixie
*
* @v undi UNDI device
* @v undirom UNDI ROM
* @v pci_busdevfn PCI bus:dev.fn
* @ret rc Return status code
*/
static inline int undi_load_pci ( struct undi_device *undi,
struct undi_rom *undirom,
unsigned int pci_busdevfn ) {
undi->pci_busdevfn = pci_busdevfn;
undi->isapnp_csn = UNDI_NO_ISAPNP_CSN;
undi->isapnp_read_port = UNDI_NO_ISAPNP_READ_PORT;
return undi_load ( undi, undirom );
}
#endif /* _UNDILOAD_H */

View File

@@ -1,17 +0,0 @@
#ifndef _UNDINET_H
#define _UNDINET_H
/** @file
*
* UNDI network device driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
struct undi_device;
extern int undinet_probe ( struct undi_device *undi );
extern void undinet_remove ( struct undi_device *undi );
#endif /* _UNDINET_H */

View File

@@ -1,18 +0,0 @@
#ifndef _UNDIPRELOAD_H
#define _UNDIPRELOAD_H
/** @file
*
* Preloaded UNDI stack
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
#include <undi.h>
extern struct undi_device __data16 ( preloaded_undi );
#define preloaded_undi __use_data16 ( preloaded_undi )
#endif /* _UNDIPRELOAD_H */

View File

@@ -1,53 +0,0 @@
#ifndef _UNDIROM_H
#define _UNDIROM_H
/** @file
*
* UNDI expansion ROMs
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <pxe_types.h>
/** An UNDI PCI device ID */
struct undi_pci_device_id {
/** PCI vendor ID */
unsigned int vendor_id;
/** PCI device ID */
unsigned int device_id;
};
/** An UNDI device ID */
union undi_device_id {
/** PCI device ID */
struct undi_pci_device_id pci;
};
/** An UNDI ROM */
struct undi_rom {
/** List of UNDI ROMs */
struct list_head list;
/** ROM segment address */
unsigned int rom_segment;
/** UNDI loader entry point */
SEGOFF16_t loader_entry;
/** Code segment size */
size_t code_size;
/** Data segment size */
size_t data_size;
/** Bus type
*
* Values are as used by @c PXENV_UNDI_GET_NIC_TYPE
*/
unsigned int bus_type;
/** Device ID */
union undi_device_id bus_id;
};
extern struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
unsigned int device_id,
unsigned int rombase );
#endif /* _UNDIROM_H */

View File

@@ -1,228 +0,0 @@
/*
*
* modified
* by Steve M. Gehlbach <steve@kesa.com>
*
* Originally from linux/drivers/video/vga16.c by
* Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
* Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
* Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
* Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
*
*/
#ifndef VGA_H_INCL
#define VGA_H_INCL 1
//#include <cpu/p5/io.h>
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int
#define __u32 u32
#define VERROR -1
#define CHAR_HEIGHT 16
#define LINES 25
#define COLS 80
// macros for writing to vga regs
#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
u8 read_seq_b(u16 addr);
u8 read_gra_b(u16 addr);
u8 read_crtc_b(u16 addr);
u8 read_att_b(u16 addr);
#ifdef VGA_HARDWARE_FIXUP
void vga_hardware_fixup(void);
#else
#define vga_hardware_fixup() do{} while(0)
#endif
#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
#define SYNC_EXT 4 /* external sync */
#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
#define SYNC_BROADCAST 16 /* broadcast video timings */
/* vtotal = 144d/288n/576i => PAL */
/* vtotal = 121d/242n/484i => NTSC */
#define SYNC_ON_GREEN 32 /* sync on green */
#define VMODE_NONINTERLACED 0 /* non interlaced */
#define VMODE_INTERLACED 1 /* interlaced */
#define VMODE_DOUBLE 2 /* double scan */
#define VMODE_MASK 255
#define VMODE_YWRAP 256 /* ywrap instead of panning */
#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
/* VGA data register ports */
#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
#define GRA_D 0x3CF /* Graphics Controller Data Register */
#define SEQ_D 0x3C5 /* Sequencer Data Register */
#define MIS_R 0x3CC // Misc Output Read Register
#define MIS_W 0x3C2 // Misc Output Write Register
#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
#define PEL_D 0x3C9 /* PEL Data Register */
#define PEL_MSK 0x3C6 /* PEL mask register */
/* EGA-specific registers */
#define GRA_E0 0x3CC /* Graphics enable processor 0 */
#define GRA_E1 0x3CA /* Graphics enable processor 1 */
/* VGA index register ports */
#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
#define GRA_I 0x3CE /* Graphics Controller Index */
#define SEQ_I 0x3C4 /* Sequencer Index */
#define PEL_IW 0x3C8 /* PEL Write Index */
#define PEL_IR 0x3C7 /* PEL Read Index */
/* standard VGA indexes max counts */
#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
// the remainder are not in the par array
#define ATT_C 21 /* 21 Attribute Controller Registers */
#define GRA_C 9 /* 9 Graphics Controller Registers */
#define SEQ_C 5 /* 5 Sequencer Registers */
#define MIS_C 1 /* 1 Misc Output Register */
#define CRTC_H_TOTAL 0
#define CRTC_H_DISP 1
#define CRTC_H_BLANK_START 2
#define CRTC_H_BLANK_END 3
#define CRTC_H_SYNC_START 4
#define CRTC_H_SYNC_END 5
#define CRTC_V_TOTAL 6
#define CRTC_OVERFLOW 7
#define CRTC_PRESET_ROW 8
#define CRTC_MAX_SCAN 9
#define CRTC_CURSOR_START 0x0A
#define CRTC_CURSOR_END 0x0B
#define CRTC_START_HI 0x0C
#define CRTC_START_LO 0x0D
#define CRTC_CURSOR_HI 0x0E
#define CRTC_CURSOR_LO 0x0F
#define CRTC_V_SYNC_START 0x10
#define CRTC_V_SYNC_END 0x11
#define CRTC_V_DISP_END 0x12
#define CRTC_OFFSET 0x13
#define CRTC_UNDERLINE 0x14
#define CRTC_V_BLANK_START 0x15
#define CRTC_V_BLANK_END 0x16
#define CRTC_MODE 0x17
#define CRTC_LINE_COMPARE 0x18
#define ATC_MODE 0x10
#define ATC_OVERSCAN 0x11
#define ATC_PLANE_ENABLE 0x12
#define ATC_PEL 0x13
#define ATC_COLOR_PAGE 0x14
#define SEQ_CLOCK_MODE 0x01
#define SEQ_PLANE_WRITE 0x02
#define SEQ_CHARACTER_MAP 0x03
#define SEQ_MEMORY_MODE 0x04
#define GDC_SR_VALUE 0x00
#define GDC_SR_ENABLE 0x01
#define GDC_COMPARE_VALUE 0x02
#define GDC_DATA_ROTATE 0x03
#define GDC_PLANE_READ 0x04
#define GDC_MODE 0x05
#define GDC_MISC 0x06
#define GDC_COMPARE_MASK 0x07
#define GDC_BIT_MASK 0x08
// text attributes
#define VGA_ATTR_CLR_RED 0x4
#define VGA_ATTR_CLR_GRN 0x2
#define VGA_ATTR_CLR_BLU 0x1
#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
#define VGA_ATTR_CLR_BLK 0
#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
#define VGA_ATTR_BNK 0x80
#define VGA_ATTR_ITN 0x08
/*
* vga register parameters
* these are copied to the
* registers.
*
*/
struct vga_par {
u8 crtc[CRTC_C];
u8 atc[ATT_C];
u8 gdc[GRA_C];
u8 seq[SEQ_C];
u8 misc; // the misc register, MIS_W
u8 vss;
};
/* Interpretation of offset for color fields: All offsets are from the right,
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
* can use the offset as right argument to <<). A pixel afterwards is a bit
* stream and is written to video memory as that unmodified. This implies
* big-endian byte order if bits_per_pixel is greater than 8.
*/
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is */
/* right */
};
struct screeninfo {
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* != 0 Graylevels instead of colors */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* acceleration flags (hints) */
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* sync polarity */
__u32 vmode; /* interlaced etc */
__u32 reserved[6]; /* Reserved for future compatibility */
};
#endif

View File

@@ -1,112 +0,0 @@
/*
* Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Advanced Power Management
*
*/
#include <errno.h>
#include <realmode.h>
#include <ipxe/reboot.h>
/**
* Power off the computer using APM
*
* @ret rc Return status code
*/
static int apm_poweroff ( void ) {
uint16_t apm_version;
uint16_t apm_signature;
uint16_t apm_flags;
uint16_t carry;
/* APM check */
__asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
"adc %%edx,0\n\t" )
: "=a" ( apm_version ), "=b" ( apm_signature ),
"=c" ( apm_flags ), "=d" ( carry )
: "a" ( 0x5300 ), "b" ( 0x0000 ),
"d" ( 0x0000 ) );
if ( carry ) {
DBG ( "APM not present\n" );
return -ENOTSUP;
}
if ( apm_signature != 0x504d ) { /* signature 'PM' */
DBG ( "APM not present\n" );
return -ENOTSUP;
}
if ( apm_version < 0x0101 ) { /* Need version 1.1+ */
DBG ( "APM 1.1+ not supported\n" );
return -ENOTSUP;
}
if ( ( apm_flags & 0x8 ) == 0x8 ) {
DBG ( "APM power management disabled\n" );
return -EPERM;
}
DBG2 ( "APM check completed\n" );
/* APM initialisation */
__asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
"adc %%edx,0\n\t" )
: "=d" ( carry )
: "a" ( 0x5301 ), "b" ( 0x0000 ),
"d" ( 0x0000 ) );
if ( carry ) {
DBG ( "APM initialisation failed\n" );
return -EIO;
}
DBG2 ( "APM initialisation completed\n" );
/* Set APM driver version */
__asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
"adc %%edx,0\n\t" )
: "=d" ( carry )
: "a" ( 0x530e ), "b" ( 0x0000 ),
"c" ( 0x0101 ), "d" ( 0x0000 ) );
if ( carry ) {
DBG ( "APM setting driver version failed\n" );
return -EIO;
}
DBG2 ( "APM driver version set\n" );
/* Setting power state to off */
__asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t"
"adc %%edx,0\n\t" )
: "=d" ( carry )
: "a" ( 0x5307 ), "b" ( 0x0001 ),
"c" ( 0x0003 ), "d" ( 0x0000) );
if ( carry ) {
DBG ( "APM setting power state failed\n" );
return -ENOTTY;
}
/* Should never happen */
return -ECANCELED;
}
PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff );

View File

@@ -1,16 +0,0 @@
#include <ipxe/nap.h>
#include <realmode.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Save power by halting the CPU until the next interrupt
*
*/
static void bios_cpu_nap ( void ) {
__asm__ __volatile__ ( "sti\n\t"
"hlt\n\t"
"cli\n\t" );
}
PROVIDE_NAP ( pcbios, cpu_nap, bios_cpu_nap );

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Standard PC-BIOS reboot mechanism
*
*/
#include <ipxe/reboot.h>
#include <realmode.h>
#include <bios.h>
/**
* Reboot system
*
* @v warm Perform a warm reboot
*/
static void bios_reboot ( int warm ) {
uint16_t flag;
/* Configure BIOS for cold/warm reboot */
flag = ( warm ? BDA_REBOOT_WARM : 0 );
put_real ( flag, BDA_SEG, BDA_REBOOT );
/* Jump to system reset vector */
__asm__ __volatile__ ( REAL_CODE ( "ljmp $0xf000, $0xfff0" ) : : );
}
PROVIDE_REBOOT ( pcbios, reboot, bios_reboot );

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/uaccess.h>
#include <ipxe/smbios.h>
#include <realmode.h>
#include <pnpbios.h>
/** @file
*
* System Management BIOS
*
*/
/**
* Find SMBIOS
*
* @v smbios SMBIOS entry point descriptor structure to fill in
* @ret rc Return status code
*/
static int bios_find_smbios ( struct smbios *smbios ) {
struct smbios_entry entry;
int rc;
/* Scan through BIOS segment to find SMBIOS entry point */
if ( ( rc = find_smbios_entry ( real_to_user ( BIOS_SEG, 0 ), 0x10000,
&entry ) ) != 0 )
return rc;
/* Fill in entry point descriptor structure */
smbios->address = phys_to_user ( entry.smbios_address );
smbios->len = entry.smbios_len;
smbios->count = entry.smbios_count;
smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
return 0;
}
PROVIDE_SMBIOS ( pcbios, find_smbios, bios_find_smbios );

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* BIOS timer
*
*/
#include <ipxe/timer.h>
#include <realmode.h>
#include <bios.h>
/**
* Get current system time in ticks
*
* @ret ticks Current time, in ticks
*
* Use direct memory access to BIOS variables, longword 0040:006C
* (ticks today) and byte 0040:0070 (midnight crossover flag) instead
* of calling timeofday BIOS interrupt.
*/
static unsigned long bios_currticks ( void ) {
static int days = 0;
uint32_t ticks;
uint8_t midnight;
/* Re-enable interrupts so that the timer interrupt can occur */
__asm__ __volatile__ ( "sti\n\t"
"nop\n\t"
"nop\n\t"
"cli\n\t" );
get_real ( ticks, BDA_SEG, 0x006c );
get_real ( midnight, BDA_SEG, 0x0070 );
if ( midnight ) {
midnight = 0;
put_real ( midnight, BDA_SEG, 0x0070 );
days += 0x1800b0;
}
return ( days + ticks );
}
PROVIDE_TIMER_INLINE ( pcbios, udelay );
PROVIDE_TIMER ( pcbios, currticks, bios_currticks );
PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec );

View File

@@ -1,119 +0,0 @@
#include <errno.h>
#include <realmode.h>
#include <biosint.h>
/**
* @file BIOS interrupts
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Hook INT vector
*
* @v interrupt INT number
* @v handler Offset within .text16 to interrupt handler
* @v chain_vector Vector for chaining to previous handler
*
* Hooks in an i386 INT handler. The handler itself must reside
* within the .text16 segment. @c chain_vector will be filled in with
* the address of the previously-installed handler for this interrupt;
* the handler should probably exit by ljmping via this vector.
*/
void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
struct segoff *chain_vector ) {
struct segoff vector = {
.segment = rm_cs,
.offset = handler,
};
DBG ( "Hooking INT %#02x to %04x:%04x\n",
interrupt, rm_cs, handler );
if ( ( chain_vector->segment != 0 ) ||
( chain_vector->offset != 0 ) ) {
/* Already hooked; do nothing */
DBG ( "...already hooked\n" );
return;
}
copy_from_real ( chain_vector, 0, ( interrupt * 4 ),
sizeof ( *chain_vector ) );
DBG ( "...chaining to %04x:%04x\n",
chain_vector->segment, chain_vector->offset );
if ( DBG_LOG ) {
char code[64];
copy_from_real ( code, chain_vector->segment,
chain_vector->offset, sizeof ( code ) );
DBG_HDA ( *chain_vector, code, sizeof ( code ) );
}
copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) );
hooked_bios_interrupts++;
}
/**
* Unhook INT vector
*
* @v interrupt INT number
* @v handler Offset within .text16 to interrupt handler
* @v chain_vector Vector containing address of previous handler
*
* Unhooks an i386 interrupt handler hooked by hook_i386_vector().
* Note that this operation may fail, if some external code has hooked
* the vector since we hooked in our handler. If it fails, it means
* that it is not possible to unhook our handler, and we must leave it
* (and its chaining vector) resident in memory.
*/
int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
struct segoff *chain_vector ) {
struct segoff vector;
DBG ( "Unhooking INT %#02x from %04x:%04x\n",
interrupt, rm_cs, handler );
copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) );
if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) {
DBG ( "...cannot unhook; vector points to %04x:%04x\n",
vector.segment, vector.offset );
return -EBUSY;
}
DBG ( "...restoring to %04x:%04x\n",
chain_vector->segment, chain_vector->offset );
copy_to_real ( 0, ( interrupt * 4 ), chain_vector,
sizeof ( *chain_vector ) );
chain_vector->segment = 0;
chain_vector->offset = 0;
hooked_bios_interrupts--;
return 0;
}
/**
* Dump changes to interrupt vector table (for debugging)
*
*/
void check_bios_interrupts ( void ) {
static struct segoff vectors[256];
static uint8_t initialised;
struct segoff vector;
unsigned int i;
/* Print any changed interrupt vectors */
for ( i = 0; i < ( sizeof ( vectors ) / sizeof ( vectors[0] ) ); i++ ) {
copy_from_real ( &vector, 0, ( i * sizeof ( vector ) ),
sizeof ( vector ) );
if ( memcmp ( &vector, &vectors[i], sizeof ( vector ) ) == 0 )
continue;
if ( initialised ) {
dbg_printf ( "INT %02x changed %04x:%04x => "
"%04x:%04x\n", i, vectors[i].segment,
vectors[i].offset, vector.segment,
vector.offset );
}
memcpy ( &vectors[i], &vector, sizeof ( vectors[i] ) );
}
initialised = 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,284 +0,0 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <ipxe/console.h>
#include <ipxe/init.h>
#include <realmode.h>
#include <int13.h>
#include <config/console.h>
/** @file
*
* INT13 disk log console
*
*/
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) )
#undef CONSOLE_INT13
#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
/** Disk drive number */
#define INT13CON_DRIVE 0x80
/** Log partition type */
#define INT13CON_PARTITION_TYPE 0xe0
/** Maximum number of outstanding unwritten characters */
#define INT13CON_MAX_UNWRITTEN 64
/** Log partition header */
struct int13con_header {
/** Magic signature */
char magic[10];
} __attribute__ (( packed ));
/** Log partition magic signature */
#define INT13CON_MAGIC "iPXE LOG\n\n"
/** Sector buffer */
static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] );
#define int13con_buffer __use_data16 ( int13con_buffer )
/** Disk address packet */
static struct int13_disk_address __bss16 ( int13con_address );
#define int13con_address __use_data16 ( int13con_address )
/** Current LBA */
static uint64_t int13con_lba;
/** Maximum LBA */
static uint64_t int13con_max_lba;
/** Current offset within sector */
static size_t int13con_offset;
/** Number of unwritten characters */
static size_t int13con_unwritten;
struct console_driver int13con __console_driver;
/**
* Read/write disk sector
*
* @v op Operation
* @v lba Logical block address
* @ret rc Return status code
*/
static int int13con_rw ( unsigned int op, uint64_t lba ) {
uint8_t error;
/* Construct disk address packet */
int13con_address.bufsize = sizeof ( int13con_address );
int13con_address.count = 1;
int13con_address.buffer.segment = rm_ds;
int13con_address.buffer.offset = __from_data16 ( int13con_buffer );
int13con_address.lba = lba;
/* Issue INT13 */
__asm__ ( REAL_CODE ( "int $0x13\n\t" )
: "=a" ( error )
: "0" ( op << 8 ), "d" ( INT13CON_DRIVE ),
"S" ( __from_data16 ( &int13con_address ) ) );
if ( error ) {
DBG ( "INT13CON operation %04x failed: %02x\n",
op, error );
return -EIO;
}
return 0;
}
/**
* Write character to console
*
* @v character Character
*/
static void int13con_putchar ( int character ) {
static int busy;
int rc;
/* Ignore if we are already mid-logging */
if ( busy )
return;
busy = 1;
/* Write character to buffer */
int13con_buffer[int13con_offset++] = character;
int13con_unwritten++;
/* Write sector to disk, if applicable */
if ( ( int13con_offset == INT13_BLKSIZE ) ||
( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) ||
( character == '\n' ) ) {
/* Write sector to disk */
if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE,
int13con_lba ) ) != 0 ) {
DBG ( "INT13CON could not write log\n" );
/* Ignore and continue; there's nothing we can do */
}
/* Reset count of unwritten characters */
int13con_unwritten = 0;
}
/* Move to next sector, if applicable */
if ( int13con_offset == INT13_BLKSIZE ) {
/* Disable console if we have run out of space */
if ( int13con_lba >= int13con_max_lba )
int13con.disabled = 1;
/* Clear log buffer */
memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) );
int13con_offset = 0;
/* Move to next sector */
int13con_lba++;
}
/* Clear busy flag */
busy = 0;
}
/**
* Find log partition
*
* @ret rc Return status code
*/
static int int13con_find ( void ) {
struct master_boot_record *mbr =
( ( struct master_boot_record * ) int13con_buffer );
struct int13con_header *hdr =
( ( struct int13con_header * ) int13con_buffer );
struct partition_table_entry part[4];
unsigned int i;
int rc;
/* Read MBR */
if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) {
DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) );
return rc;
}
/* Check MBR magic */
if ( mbr->magic != INT13_MBR_MAGIC ) {
DBG ( "INT13CON incorrect MBR magic\n" );
DBG2_HDA ( 0, mbr, sizeof ( *mbr ) );
return -EINVAL;
}
/* Look for magic partition */
memcpy ( part, mbr->partitions, sizeof ( part ) );
for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) {
/* Skip partitions of the wrong type */
if ( part[i].type != INT13CON_PARTITION_TYPE )
continue;
/* Read partition header */
if ( ( rc = int13con_rw ( INT13_EXTENDED_READ,
part[i].start ) ) != 0 ) {
DBG ( "INT13CON partition %d could not read header: "
"%s\n", ( i + 1 ), strerror ( rc ) );
continue;
}
/* Check partition header */
if ( memcmp ( hdr->magic, INT13CON_MAGIC,
sizeof ( hdr->magic ) ) != 0 ) {
DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) );
DBG2_HDA ( 0, hdr, sizeof ( *hdr ) );
continue;
}
/* Found log partition */
DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ),
part[i].start, ( part[i].start + part[i].length ) );
int13con_lba = part[i].start;
int13con_max_lba = ( part[i].start + part[i].length - 1 );
/* Initialise log buffer */
memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0,
( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) );
int13con_offset = sizeof ( hdr->magic );
return 0;
}
DBG ( "INT13CON found no log partition\n" );
return -ENOENT;
}
/**
* Initialise INT13 console
*
*/
static void int13con_init ( void ) {
uint8_t error;
uint16_t check;
unsigned int discard_c;
unsigned int discard_d;
int rc;
/* Check for INT13 extensions */
__asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t"
"setc %%al\n\t" )
: "=a" ( error ), "=b" ( check ),
"=c" ( discard_c ), "=d" ( discard_d )
: "0" ( INT13_EXTENSION_CHECK << 8 ),
"1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) );
if ( error || ( check != 0xaa55 ) ) {
DBG ( "INT13CON missing extensions (%02x,%04x)\n",
error, check );
return;
}
/* Locate log partition */
if ( ( rc = int13con_find() ) != 0)
return;
/* Enable console */
int13con.disabled = 0;
}
/**
* INT13 console initialisation function
*/
struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = {
.initialise = int13con_init,
};
/** INT13 console driver */
struct console_driver int13con __console_driver = {
.putchar = int13con_putchar,
.disabled = CONSOLE_DISABLED,
.usage = CONSOLE_INT13,
};

View File

@@ -1,182 +0,0 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* External memory allocation
*
*/
#include <limits.h>
#include <errno.h>
#include <ipxe/uaccess.h>
#include <ipxe/hidemem.h>
#include <ipxe/io.h>
#include <ipxe/memblock.h>
#include <ipxe/umalloc.h>
/** Alignment of external allocated memory */
#define EM_ALIGN ( 4 * 1024 )
/** Equivalent of NOWHERE for user pointers */
#define UNOWHERE ( ~UNULL )
/** An external memory block */
struct external_memory {
/** Size of this memory block (excluding this header) */
size_t size;
/** Block is currently in use */
int used;
};
/** Top of heap */
static userptr_t top = UNULL;
/** Bottom of heap (current lowest allocated block) */
static userptr_t bottom = UNULL;
/** Remaining space on heap */
static size_t heap_size;
/**
* Initialise external heap
*
*/
static void init_eheap ( void ) {
userptr_t base;
heap_size = largest_memblock ( &base );
bottom = top = userptr_add ( base, heap_size );
DBG ( "External heap grows downwards from %lx (size %zx)\n",
user_to_phys ( top, 0 ), heap_size );
}
/**
* Collect free blocks
*
*/
static void ecollect_free ( void ) {
struct external_memory extmem;
size_t len;
/* Walk the free list and collect empty blocks */
while ( bottom != top ) {
copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
sizeof ( extmem ) );
if ( extmem.used )
break;
DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
user_to_phys ( bottom, extmem.size ) );
len = ( extmem.size + sizeof ( extmem ) );
bottom = userptr_add ( bottom, len );
heap_size += len;
}
}
/**
* Reallocate external memory
*
* @v old_ptr Memory previously allocated by umalloc(), or UNULL
* @v new_size Requested size
* @ret new_ptr Allocated memory, or UNULL
*
* Calling realloc() with a new size of zero is a valid way to free a
* memory block.
*/
static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
struct external_memory extmem;
userptr_t new = ptr;
size_t align;
/* (Re)initialise external memory allocator if necessary */
if ( bottom == top )
init_eheap();
/* Get block properties into extmem */
if ( ptr && ( ptr != UNOWHERE ) ) {
/* Determine old size */
copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
sizeof ( extmem ) );
} else {
/* Create a zero-length block */
if ( heap_size < sizeof ( extmem ) ) {
DBG ( "EXTMEM out of space\n" );
return UNULL;
}
ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
heap_size -= sizeof ( extmem );
DBG ( "EXTMEM allocating [%lx,%lx)\n",
user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
extmem.size = 0;
}
extmem.used = ( new_size > 0 );
/* Expand/shrink block if possible */
if ( ptr == bottom ) {
/* Update block */
if ( new_size > ( heap_size - extmem.size ) ) {
DBG ( "EXTMEM out of space\n" );
return UNULL;
}
new = userptr_add ( ptr, - ( new_size - extmem.size ) );
align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
new_size += align;
new = userptr_add ( new, -align );
DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
user_to_phys ( ptr, 0 ),
user_to_phys ( ptr, extmem.size ),
user_to_phys ( new, 0 ),
user_to_phys ( new, new_size ));
memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
extmem.size : new_size ) );
bottom = new;
heap_size -= ( new_size - extmem.size );
extmem.size = new_size;
} else {
/* Cannot expand; can only pretend to shrink */
if ( new_size > extmem.size ) {
/* Refuse to expand */
DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
user_to_phys ( ptr, 0 ),
user_to_phys ( ptr, extmem.size ) );
return UNULL;
}
}
/* Write back block properties */
copy_to_user ( new, -sizeof ( extmem ), &extmem,
sizeof ( extmem ) );
/* Collect any free blocks and update hidden memory region */
ecollect_free();
hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
0 : -sizeof ( extmem ) ) ),
user_to_phys ( top, 0 ) );
return ( new_size ? new : UNOWHERE );
}
PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );

Some files were not shown because too many files have changed in this diff Show More