mirror of
https://github.com/ipxe/ipxe
synced 2026-01-14 01:49:16 +03:00
[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:
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] );
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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
@@ -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,
|
||||
};
|
||||
@@ -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
Reference in New Issue
Block a user