aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/interface
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
committerH. Peter Anvin <hpa@zytor.com>2009-02-17 20:17:17 -0800
commitd0c6656a62113b913948361779d6298fe76f6e61 (patch)
treeefa2541a1abae4760717c6db421ea818114ab6f7 /gpxe/src/interface
parent85b92a462dab7ce36c48614ea18314f8fc83ca9c (diff)
downloadsyslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.gz
syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.tar.xz
syslinux.git-d0c6656a62113b913948361779d6298fe76f6e61.zip
Update gPXE to version 0.9.6+ 277b84c6e7d49f3cf01c855007f591de8c7cb75f
Update gPXE to version 0.9.6+, from commit 277b84c6e7d49f3cf01c855007f591de8c7cb75f in the main gPXE repository. The only differences is src/config/general.h which has a few protocols added, and src/arch/i386/prefix/boot1a.S which was called boot1a.s in the upstream repository.
Diffstat (limited to 'gpxe/src/interface')
-rw-r--r--gpxe/src/interface/efi/efi_console.c274
-rw-r--r--gpxe/src/interface/efi/efi_init.c122
-rw-r--r--gpxe/src/interface/efi/efi_io.c203
-rw-r--r--gpxe/src/interface/efi/efi_pci.c81
-rw-r--r--gpxe/src/interface/efi/efi_smbios.c62
-rw-r--r--gpxe/src/interface/efi/efi_snp.c1145
-rw-r--r--gpxe/src/interface/efi/efi_strerror.c43
-rw-r--r--gpxe/src/interface/efi/efi_timer.c116
-rw-r--r--gpxe/src/interface/efi/efi_uaccess.c37
-rw-r--r--gpxe/src/interface/efi/efi_umalloc.c96
-rw-r--r--gpxe/src/interface/pxe/pxe_errors.c103
-rw-r--r--gpxe/src/interface/pxe/pxe_file.c264
-rw-r--r--gpxe/src/interface/pxe/pxe_loader.c51
-rw-r--r--gpxe/src/interface/pxe/pxe_preboot.c352
-rw-r--r--gpxe/src/interface/pxe/pxe_tftp.c584
-rw-r--r--gpxe/src/interface/pxe/pxe_udp.c403
-rw-r--r--gpxe/src/interface/pxe/pxe_undi.c682
-rw-r--r--gpxe/src/interface/smbios/smbios.c178
-rw-r--r--gpxe/src/interface/smbios/smbios_settings.c201
19 files changed, 2558 insertions, 2439 deletions
diff --git a/gpxe/src/interface/efi/efi_console.c b/gpxe/src/interface/efi/efi_console.c
new file mode 100644
index 00000000..b78de618
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_console.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/ansiesc.h>
+#include <console.h>
+
+#define ATTR_BOLD 0x08
+
+#define ATTR_FCOL_MASK 0x07
+#define ATTR_FCOL_BLACK 0x00
+#define ATTR_FCOL_BLUE 0x01
+#define ATTR_FCOL_GREEN 0x02
+#define ATTR_FCOL_CYAN 0x03
+#define ATTR_FCOL_RED 0x04
+#define ATTR_FCOL_MAGENTA 0x05
+#define ATTR_FCOL_YELLOW 0x06
+#define ATTR_FCOL_WHITE 0x07
+
+#define ATTR_BCOL_MASK 0x70
+#define ATTR_BCOL_BLACK 0x00
+#define ATTR_BCOL_BLUE 0x10
+#define ATTR_BCOL_GREEN 0x20
+#define ATTR_BCOL_CYAN 0x30
+#define ATTR_BCOL_RED 0x40
+#define ATTR_BCOL_MAGENTA 0x50
+#define ATTR_BCOL_YELLOW 0x60
+#define ATTR_BCOL_WHITE 0x70
+
+#define ATTR_DEFAULT ATTR_FCOL_WHITE
+
+/** Current character attribute */
+static unsigned int efi_attr = ATTR_DEFAULT;
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v count Parameter count
+ * @v params[0] Row (1 is top)
+ * @v params[1] Column (1 is left)
+ */
+static void efi_handle_cup ( unsigned int count __unused, int params[] ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ int cx = ( params[1] - 1 );
+ int cy = ( params[0] - 1 );
+
+ if ( cx < 0 )
+ cx = 0;
+ if ( cy < 0 )
+ cy = 0;
+
+ conout->SetCursorPosition ( conout, cx, cy );
+}
+
+/**
+ * Handle ANSI ED (erase in page)
+ *
+ * @v count Parameter count
+ * @v params[0] Region to erase
+ */
+static void efi_handle_ed ( unsigned int count __unused,
+ int params[] __unused ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+
+ /* We assume that we always clear the whole screen */
+ assert ( params[0] == ANSIESC_ED_ALL );
+
+ conout->ClearScreen ( conout );
+}
+
+/**
+ * Handle ANSI SGR (set graphics rendition)
+ *
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void efi_handle_sgr ( unsigned int count, int params[] ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ static const uint8_t efi_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 efi_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 ) {
+ efi_attr = ATTR_DEFAULT;
+ } else if ( aspect == 1 ) {
+ efi_attr |= ATTR_BOLD;
+ } else if ( aspect == 22 ) {
+ efi_attr &= ~ATTR_BOLD;
+ } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
+ efi_attr &= ~ATTR_FCOL_MASK;
+ efi_attr |= efi_attr_fcols[ aspect - 30 ];
+ } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
+ efi_attr &= ~ATTR_BCOL_MASK;
+ efi_attr |= efi_attr_bcols[ aspect - 40 ];
+ }
+ }
+
+ conout->SetAttribute ( conout, efi_attr );
+}
+
+/** EFI console ANSI escape sequence handlers */
+static struct ansiesc_handler efi_ansiesc_handlers[] = {
+ { ANSIESC_CUP, efi_handle_cup },
+ { ANSIESC_ED, efi_handle_ed },
+ { ANSIESC_SGR, efi_handle_sgr },
+ { 0, NULL }
+};
+
+/** EFI console ANSI escape sequence context */
+static struct ansiesc_context efi_ansiesc_ctx = {
+ .handlers = efi_ansiesc_handlers,
+};
+
+/**
+ * Print a character to EFI console
+ *
+ * @v character Character to be printed
+ */
+static void efi_putchar ( int character ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ wchar_t wstr[] = { character, 0 };
+
+ /* Intercept ANSI escape sequences */
+ character = ansiesc_process ( &efi_ansiesc_ctx, character );
+ if ( character < 0 )
+ return;
+
+ conout->OutputString ( conout, wstr );
+}
+
+/**
+ * Pointer to current ANSI output sequence
+ *
+ * While we are in the middle of returning an ANSI sequence for a
+ * special key, this will point to the next character to return. When
+ * not in the middle of such a sequence, this will point to a NUL
+ * (note: not "will be NULL").
+ */
+static const char *ansi_input = "";
+
+/** Mapping from EFI scan codes to ANSI escape sequences */
+static const char *ansi_sequences[] = {
+ [SCAN_UP] = "[A",
+ [SCAN_DOWN] = "[B",
+ [SCAN_RIGHT] = "[C",
+ [SCAN_LEFT] = "[D",
+ [SCAN_HOME] = "[H",
+ [SCAN_END] = "[F",
+ [SCAN_INSERT] = "[2~",
+ /* EFI translates an incoming backspace via the serial console
+ * into a SCAN_DELETE. There's not much we can do about this.
+ */
+ [SCAN_DELETE] = "[3~",
+ [SCAN_PAGE_UP] = "[5~",
+ [SCAN_PAGE_DOWN] = "[6~",
+ /* EFI translates some (but not all) incoming escape sequences
+ * via the serial console into equivalent scancodes. When it
+ * doesn't recognise a sequence, it helpfully(!) translates
+ * the initial ESC and passes the remainder through verbatim.
+ * Treating SCAN_ESC as equivalent to an empty escape sequence
+ * works around this bug.
+ */
+ [SCAN_ESC] = "",
+};
+
+/**
+ * Get ANSI escape sequence corresponding to EFI scancode
+ *
+ * @v scancode EFI scancode
+ * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
+ */
+static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
+ if ( scancode < ( sizeof ( ansi_sequences ) /
+ sizeof ( ansi_sequences[0] ) ) ) {
+ return ansi_sequences[scancode];
+ }
+ return NULL;
+}
+
+/**
+ * Get character from EFI console
+ *
+ * @ret character Character read from console
+ */
+static int efi_getchar ( void ) {
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+ const char *ansi_seq;
+ EFI_INPUT_KEY key;
+ EFI_STATUS efirc;
+
+ /* If we are mid-sequence, pass out the next byte */
+ if ( *ansi_input )
+ return *(ansi_input++);
+
+ /* Read key from real EFI console */
+ if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) {
+ DBG ( "EFI could not read keystroke: %s\n",
+ efi_strerror ( efirc ) );
+ return 0;
+ }
+ DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
+ key.UnicodeChar, key.ScanCode );
+
+ /* If key has a Unicode representation, return it */
+ if ( key.UnicodeChar )
+ return key.UnicodeChar;
+
+ /* Otherwise, check for a special key that we know about */
+ if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) {
+ /* Start of escape sequence: return ESC (0x1b) */
+ ansi_input = ansi_seq;
+ return 0x1b;
+ }
+
+ return 0;
+}
+
+/**
+ * Check for character ready to read from EFI console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int efi_iskey ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+ EFI_STATUS efirc;
+
+ /* If we are mid-sequence, we are always ready */
+ if ( *ansi_input )
+ return 1;
+
+ /* Check to see if the WaitForKey event has fired */
+ if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
+ return 1;
+
+ return 0;
+}
+
+struct console_driver efi_console __console_driver = {
+ .putchar = efi_putchar,
+ .getchar = efi_getchar,
+ .iskey = efi_iskey,
+};
diff --git a/gpxe/src/interface/efi/efi_init.c b/gpxe/src/interface/efi/efi_init.c
new file mode 100644
index 00000000..6e54cf7e
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_init.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/uuid.h>
+
+/** Image handle passed to entry point */
+EFI_HANDLE efi_image_handle;
+
+/** System table passed to entry point */
+EFI_SYSTEM_TABLE *efi_systab;
+
+/** Declared used EFI protocols */
+static struct efi_protocol efi_protocols[0] \
+ __table_start ( struct efi_protocol, efi_protocols );
+static struct efi_protocol efi_protocols_end[0] \
+ __table_end ( struct efi_protocol, efi_protocols );
+
+/** Declared used EFI configuration tables */
+static struct efi_config_table efi_config_tables[0] \
+ __table_start ( struct efi_config_table, efi_config_tables );
+static struct efi_config_table efi_config_tables_end[0] \
+ __table_end ( struct efi_config_table, efi_config_tables );
+
+/**
+ * Look up EFI configuration table
+ *
+ * @v guid Configuration table GUID
+ * @ret table Configuration table, or NULL
+ */
+static void * efi_find_table ( EFI_GUID *guid ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) {
+ if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid,
+ guid, sizeof ( *guid ) ) == 0 )
+ return efi_systab->ConfigurationTable[i].VendorTable;
+ }
+
+ return NULL;
+}
+
+/**
+ * Initialise EFI environment
+ *
+ * @v image_handle Image handle
+ * @v systab System table
+ * @ret efirc EFI return status code
+ */
+EFI_STATUS efi_init ( EFI_HANDLE image_handle,
+ EFI_SYSTEM_TABLE *systab ) {
+ EFI_BOOT_SERVICES *bs;
+ struct efi_protocol *prot;
+ struct efi_config_table *tab;
+ EFI_STATUS efirc;
+
+ /* Store image handle and system table pointer for future use */
+ efi_image_handle = image_handle;
+ efi_systab = systab;
+
+ /* Sanity checks */
+ if ( ! systab )
+ return EFI_NOT_AVAILABLE_YET;
+ if ( ! systab->ConOut )
+ return EFI_NOT_AVAILABLE_YET;
+ if ( ! systab->BootServices ) {
+ DBGC ( systab, "EFI provided no BootServices entry point\n" );
+ return EFI_NOT_AVAILABLE_YET;
+ }
+ if ( ! systab->RuntimeServices ) {
+ DBGC ( systab, "EFI provided no RuntimeServices entry "
+ "point\n" );
+ return EFI_NOT_AVAILABLE_YET;
+ }
+ DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
+
+ /* Look up used protocols */
+ bs = systab->BootServices;
+ for ( prot = efi_protocols ; prot < efi_protocols_end ; prot++ ) {
+ if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL,
+ prot->protocol ) ) == 0 ) {
+ DBGC ( systab, "EFI protocol %s is at %p\n",
+ uuid_ntoa ( &prot->u.uuid ), *(prot->protocol));
+ } else {
+ DBGC ( systab, "EFI does not provide protocol %s\n",
+ uuid_ntoa ( &prot->u.uuid ) );
+ /* All protocols are required */
+ return efirc;
+ }
+ }
+
+ /* Look up used configuration tables */
+ for ( tab = efi_config_tables ; tab < efi_config_tables_end ; tab++ ) {
+ if ( ( *(tab->table) = efi_find_table ( &tab->u.guid ) ) ) {
+ DBGC ( systab, "EFI configuration table %s is at %p\n",
+ uuid_ntoa ( &tab->u.uuid ), *(tab->table) );
+ } else {
+ DBGC ( systab, "EFI does not provide configuration "
+ "table %s\n", uuid_ntoa ( &tab->u.uuid ) );
+ if ( tab->required )
+ return EFI_NOT_AVAILABLE_YET;
+ }
+ }
+
+ return 0;
+}
diff --git a/gpxe/src/interface/efi/efi_io.c b/gpxe/src/interface/efi/efi_io.c
new file mode 100644
index 00000000..e11f9bfd
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_io.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/io.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/CpuIo.h>
+#include <gpxe/efi/efi_io.h>
+
+/** @file
+ *
+ * gPXE I/O API for EFI
+ *
+ */
+
+/** CPU I/O protocol */
+static EFI_CPU_IO_PROTOCOL *cpu_io;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io );
+
+/** Maximum address that can be used for port I/O */
+#define MAX_PORT_ADDRESS 0xffff
+
+/**
+ * Determine whether or not address is a port I/O address
+ *
+ * @v io_addr I/O address
+ * @v is_port I/O address is a port I/O address
+ */
+#define IS_PORT_ADDRESS(io_addr) \
+ ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS )
+
+/**
+ * Determine EFI CPU I/O width code
+ *
+ * @v size Size of value
+ * @ret width EFI width code
+ *
+ * Someone at Intel clearly gets paid by the number of lines of code
+ * they write. No-one should ever be able to make I/O this
+ * convoluted. The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite
+ * idiocy.
+ */
+static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) {
+ switch ( size ) {
+ case 1 : return EfiCpuIoWidthFifoUint8;
+ case 2 : return EfiCpuIoWidthFifoUint16;
+ case 4 : return EfiCpuIoWidthFifoUint32;
+ case 8 : return EfiCpuIoWidthFifoUint64;
+ default :
+ assert ( 0 );
+ /* I wonder what this will actually do... */
+ return EfiCpuIoWidthMaximum;
+ }
+}
+
+/**
+ * Read from device
+ *
+ * @v io_addr I/O address
+ * @v size Size of value
+ * @ret data Value read
+ */
+unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM read;
+ unsigned long long data = 0;
+ EFI_STATUS efirc;
+
+ read = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Read : cpu_io->Mem.Read );
+
+ if ( ( efirc = read ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, 1,
+ ( void * ) &data ) ) != 0 ) {
+ DBG ( "EFI I/O read at %p failed: %s\n",
+ io_addr, efi_strerror ( efirc ) );
+ return -1ULL;
+ }
+
+ return data;
+}
+
+/**
+ * Write to device
+ *
+ * @v data Value to write
+ * @v io_addr I/O address
+ * @v size Size of value
+ */
+void efi_iowrite ( unsigned long long data, volatile void *io_addr,
+ size_t size ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM write;
+ EFI_STATUS efirc;
+
+ write = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Write : cpu_io->Mem.Write );
+
+ if ( ( efirc = write ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, 1,
+ ( void * ) &data ) ) != 0 ) {
+ DBG ( "EFI I/O write at %p failed: %s\n",
+ io_addr, efi_strerror ( efirc ) );
+ }
+}
+
+/**
+ * String read from device
+ *
+ * @v io_addr I/O address
+ * @v data Data buffer
+ * @v size Size of values
+ * @v count Number of values to read
+ */
+void efi_ioreads ( volatile void *io_addr, void *data,
+ size_t size, unsigned int count ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM read;
+ EFI_STATUS efirc;
+
+ read = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Read : cpu_io->Mem.Read );
+
+ if ( ( efirc = read ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, count,
+ ( void * ) data ) ) != 0 ) {
+ DBG ( "EFI I/O string read at %p failed: %s\n",
+ io_addr, efi_strerror ( efirc ) );
+ }
+}
+
+/**
+ * String write to device
+ *
+ * @v io_addr I/O address
+ * @v data Data buffer
+ * @v size Size of values
+ * @v count Number of values to write
+ */
+void efi_iowrites ( volatile void *io_addr, const void *data,
+ size_t size, unsigned int count ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM write;
+ EFI_STATUS efirc;
+
+ write = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Write : cpu_io->Mem.Write );
+
+ if ( ( efirc = write ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, count,
+ ( void * ) data ) ) != 0 ) {
+ DBG ( "EFI I/O write at %p failed: %s\n",
+ io_addr, efi_strerror ( efirc ) );
+ }
+}
+
+/**
+ * Wait for I/O-mapped operation to complete
+ *
+ */
+static void efi_iodelay ( void ) {
+ /* Write to non-existent port. Probably x86-only. */
+ outb ( 0, 0x80 );
+}
+
+PROVIDE_IOAPI_INLINE ( efi, phys_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, bus_to_phys );
+PROVIDE_IOAPI_INLINE ( efi, ioremap );
+PROVIDE_IOAPI_INLINE ( efi, iounmap );
+PROVIDE_IOAPI_INLINE ( efi, io_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, readb );
+PROVIDE_IOAPI_INLINE ( efi, readw );
+PROVIDE_IOAPI_INLINE ( efi, readl );
+PROVIDE_IOAPI_INLINE ( efi, readq );
+PROVIDE_IOAPI_INLINE ( efi, writeb );
+PROVIDE_IOAPI_INLINE ( efi, writew );
+PROVIDE_IOAPI_INLINE ( efi, writel );
+PROVIDE_IOAPI_INLINE ( efi, writeq );
+PROVIDE_IOAPI_INLINE ( efi, inb );
+PROVIDE_IOAPI_INLINE ( efi, inw );
+PROVIDE_IOAPI_INLINE ( efi, inl );
+PROVIDE_IOAPI_INLINE ( efi, outb );
+PROVIDE_IOAPI_INLINE ( efi, outw );
+PROVIDE_IOAPI_INLINE ( efi, outl );
+PROVIDE_IOAPI_INLINE ( efi, insb );
+PROVIDE_IOAPI_INLINE ( efi, insw );
+PROVIDE_IOAPI_INLINE ( efi, insl );
+PROVIDE_IOAPI_INLINE ( efi, outsb );
+PROVIDE_IOAPI_INLINE ( efi, outsw );
+PROVIDE_IOAPI_INLINE ( efi, outsl );
+PROVIDE_IOAPI ( efi, iodelay, efi_iodelay );
+PROVIDE_IOAPI_INLINE ( efi, mb );
diff --git a/gpxe/src/interface/efi/efi_pci.c b/gpxe/src/interface/efi/efi_pci.c
new file mode 100644
index 00000000..f87b5407
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_pci.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/PciRootBridgeIo.h>
+
+/** @file
+ *
+ * gPXE PCI I/O API for EFI
+ *
+ */
+
+/** PCI root bridge I/O protocol */
+static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci;
+EFI_REQUIRE_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci );
+
+static unsigned long efipci_address ( struct pci_device *pci,
+ unsigned long location ) {
+ return EFI_PCI_ADDRESS ( pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ),
+ EFIPCI_OFFSET ( location ) );
+}
+
+int efipci_read ( struct pci_device *pci, unsigned long location,
+ void *value ) {
+ EFI_STATUS efirc;
+
+ if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ value ) ) != 0 ) {
+ DBG ( "EFIPCI config read from %02x:%02x.%x offset %02lx "
+ "failed: %s\n", pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+ efi_strerror ( efirc ) );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int efipci_write ( struct pci_device *pci, unsigned long location,
+ unsigned long value ) {
+ EFI_STATUS efirc;
+
+ if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ &value ) ) != 0 ) {
+ DBG ( "EFIPCI config write to %02x:%02x.%x offset %02lx "
+ "failed: %s\n", pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+ efi_strerror ( efirc ) );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+PROVIDE_PCIAPI_INLINE ( efi, pci_max_bus );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
diff --git a/gpxe/src/interface/efi/efi_smbios.c b/gpxe/src/interface/efi/efi_smbios.c
new file mode 100644
index 00000000..5888f2f9
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_smbios.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/smbios.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Guid/SmBios.h>
+
+/** @file
+ *
+ * gPXE SMBIOS API for EFI
+ *
+ */
+
+/** SMBIOS configuration table */
+static struct smbios_entry *smbios_entry;
+EFI_USE_TABLE ( EFI_SMBIOS_TABLE, &smbios_entry, 0 );
+
+/**
+ * Find SMBIOS
+ *
+ * @v smbios SMBIOS entry point descriptor structure to fill in
+ * @ret rc Return status code
+ */
+static int efi_find_smbios ( struct smbios *smbios ) {
+
+ if ( ! smbios_entry ) {
+ DBG ( "No SMBIOS table provided\n" );
+ return -ENODEV;
+ }
+
+ if ( smbios_entry->signature != SMBIOS_SIGNATURE ) {
+ DBG ( "Invalid SMBIOS signature\n" );
+ return -ENODEV;
+ }
+
+ smbios->address = phys_to_user ( smbios_entry->smbios_address );
+ smbios->len = smbios_entry->smbios_len;
+ smbios->count = smbios_entry->smbios_count;
+ DBG ( "Found SMBIOS v%d.%d entry point at %p (%x+%zx)\n",
+ smbios_entry->major, smbios_entry->minor, smbios_entry,
+ smbios_entry->smbios_address, smbios->len );
+
+ return 0;
+}
+
+PROVIDE_SMBIOS ( efi, find_smbios, efi_find_smbios );
diff --git a/gpxe/src/interface/efi/efi_snp.c b/gpxe/src/interface/efi/efi_snp.c
new file mode 100644
index 00000000..771b9174
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_snp.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/in.h>
+#include <gpxe/pci.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/DriverBinding.h>
+#include <gpxe/efi/Protocol/PciIo.h>
+#include <gpxe/efi/Protocol/SimpleNetwork.h>
+#include <gpxe/efi/Protocol/ComponentName2.h>
+#include <gpxe/efi/Protocol/NetworkInterfaceIdentifier.h>
+#include <config/general.h>
+
+/** @file
+ *
+ * gPXE EFI SNP interface
+ *
+ */
+
+/** An SNP device */
+struct efi_snp_device {
+ /** The underlying gPXE network device */
+ struct net_device *netdev;
+ /** EFI device handle */
+ EFI_HANDLE handle;
+ /** The SNP structure itself */
+ EFI_SIMPLE_NETWORK_PROTOCOL snp;
+ /** The SNP "mode" (parameters) */
+ EFI_SIMPLE_NETWORK_MODE mode;
+ /** Outstanding TX packet count (via "interrupt status")
+ *
+ * Used in order to generate TX completions.
+ */
+ unsigned int tx_count_interrupts;
+ /** Outstanding TX packet count (via "recycled tx buffers")
+ *
+ * Used in order to generate TX completions.
+ */
+ unsigned int tx_count_txbufs;
+ /** Outstanding RX packet count (via "interrupt status") */
+ unsigned int rx_count_interrupts;
+ /** Outstanding RX packet count (via WaitForPacket event) */
+ unsigned int rx_count_events;
+ /** The network interface identifier */
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
+ /** Device name */
+ wchar_t name[ sizeof ( ( ( struct net_device * ) NULL )->name ) ];
+ /** The device path
+ *
+ * This field is variable in size and must appear at the end
+ * of the structure.
+ */
+ EFI_DEVICE_PATH_PROTOCOL path;
+};
+
+/** EFI simple network protocol GUID */
+static EFI_GUID efi_simple_network_protocol_guid
+ = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
+
+/** EFI driver binding protocol GUID */
+static EFI_GUID efi_driver_binding_protocol_guid
+ = EFI_DRIVER_BINDING_PROTOCOL_GUID;
+
+/** EFI component name protocol GUID */
+static EFI_GUID efi_component_name2_protocol_guid
+ = EFI_COMPONENT_NAME2_PROTOCOL_GUID;
+
+/** EFI device path protocol GUID */
+static EFI_GUID efi_device_path_protocol_guid
+ = EFI_DEVICE_PATH_PROTOCOL_GUID;
+
+/** EFI network interface identifier GUID */
+static EFI_GUID efi_nii_protocol_guid
+ = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID;
+
+/** EFI network interface identifier GUID (extra special version) */
+static EFI_GUID efi_nii31_protocol_guid = {
+ /* At some point, it seems that someone decided to change the
+ * GUID. Current EFI builds ignore the older GUID, older EFI
+ * builds ignore the newer GUID, so we have to expose both.
+ */
+ 0x1ACED566, 0x76ED, 0x4218,
+ { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 }
+};
+
+/** EFI PCI I/O protocol GUID */
+static EFI_GUID efi_pci_io_protocol_guid
+ = EFI_PCI_IO_PROTOCOL_GUID;
+
+/**
+ * Set EFI SNP mode based on gPXE net device parameters
+ *
+ * @v snp SNP interface
+ */
+static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) {
+ struct net_device *netdev = snpdev->netdev;
+ EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
+ unsigned int ll_addr_len = netdev->ll_protocol->ll_addr_len;
+
+ mode->HwAddressSize = ll_addr_len;
+ mode->MediaHeaderSize = netdev->ll_protocol->ll_header_len;
+ mode->MaxPacketSize = netdev->max_pkt_len;
+ mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST );
+ assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) );
+ memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len );
+ memcpy ( &mode->BroadcastAddress, netdev->ll_protocol->ll_broadcast,
+ ll_addr_len );
+ memcpy ( &mode->PermanentAddress, netdev->ll_addr, ll_addr_len );
+ mode->IfType = ntohs ( netdev->ll_protocol->ll_proto );
+ mode->MacAddressChangeable = TRUE;
+ mode->MediaPresentSupported = TRUE;
+ mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
+}
+
+/**
+ * Poll net device and count received packets
+ *
+ * @v snpdev SNP device
+ */
+static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
+ struct io_buffer *iobuf;
+ unsigned int before = 0;
+ unsigned int after = 0;
+ unsigned int arrived;
+
+ /* We have to report packet arrivals, and this is the easiest
+ * way to fake it.
+ */
+ list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
+ before++;
+ netdev_poll ( snpdev->netdev );
+ list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
+ after++;
+ arrived = ( after - before );
+
+ snpdev->rx_count_interrupts += arrived;
+ snpdev->rx_count_events += arrived;
+}
+
+/**
+ * Change SNP state from "stopped" to "started"
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev );
+
+ snpdev->mode.State = EfiSimpleNetworkStarted;
+ return 0;
+}
+
+/**
+ * Change SNP state from "started" to "stopped"
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev );
+
+ snpdev->mode.State = EfiSimpleNetworkStopped;
+ return 0;
+}
+
+/**
+ * Open the network device
+ *
+ * @v snp SNP interface
+ * @v extra_rx_bufsize Extra RX buffer size, in bytes
+ * @v extra_tx_bufsize Extra TX buffer size, in bytes
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
+ snpdev, ( ( unsigned long ) extra_rx_bufsize ),
+ ( ( unsigned long ) extra_tx_bufsize ) );
+
+ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n",
+ snpdev, snpdev->netdev->name, strerror ( rc ) );
+ return RC_TO_EFIRC ( rc );
+ }
+
+ snpdev->mode.State = EfiSimpleNetworkInitialized;
+ return 0;
+}
+
+/**
+ * Reset the network device
+ *
+ * @v snp SNP interface
+ * @v ext_verify Extended verification required
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
+ snpdev, ( ext_verify ? "with" : "without" ) );
+
+ netdev_close ( snpdev->netdev );
+ snpdev->mode.State = EfiSimpleNetworkStarted;
+
+ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
+ snpdev, snpdev->netdev->name, strerror ( rc ) );
+ return RC_TO_EFIRC ( rc );
+ }
+
+ snpdev->mode.State = EfiSimpleNetworkInitialized;
+ return 0;
+}
+
+/**
+ * Shut down the network device
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
+
+ netdev_close ( snpdev->netdev );
+ snpdev->mode.State = EfiSimpleNetworkStarted;
+ return 0;
+}
+
+/**
+ * Manage receive filters
+ *
+ * @v snp SNP interface
+ * @v enable Receive filters to enable
+ * @v disable Receive filters to disable
+ * @v mcast_reset Reset multicast filters
+ * @v mcast_count Number of multicast filters
+ * @v mcast Multicast filters
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
+ UINT32 disable, BOOLEAN mcast_reset,
+ UINTN mcast_count, EFI_MAC_ADDRESS *mcast ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ unsigned int i;
+
+ DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n",
+ snpdev, enable, disable, ( mcast_reset ? " reset" : "" ),
+ ( ( unsigned long ) mcast_count ) );
+ for ( i = 0 ; i < mcast_count ; i++ ) {
+ DBGC2_HDA ( snpdev, i, &mcast[i],
+ snpdev->netdev->ll_protocol->ll_addr_len );
+ }
+
+ /* Lie through our teeth, otherwise MNP refuses to accept us */
+ return 0;
+}
+
+/**
+ * Set station address
+ *
+ * @v snp SNP interface
+ * @v reset Reset to permanent address
+ * @v new New station address
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
+ EFI_MAC_ADDRESS *new ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+
+ DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
+ ( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
+
+ /* Set the MAC address */
+ if ( reset )
+ new = &snpdev->mode.PermanentAddress;
+ memcpy ( snpdev->netdev->ll_addr, new, ll_protocol->ll_addr_len );
+
+ /* MAC address changes take effect only on netdev_open() */
+ if ( snpdev->netdev->state & NETDEV_OPEN ) {
+ DBGC ( snpdev, "SNPDEV %p MAC address changed while net "
+ "devive open\n", snpdev );
+ }
+
+ return 0;
+}
+
+/**
+ * Get (or reset) statistics
+ *
+ * @v snp SNP interface
+ * @v reset Reset statistics
+ * @v stats_len Size of statistics table
+ * @v stats Statistics table
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
+ UINTN *stats_len, EFI_NETWORK_STATISTICS *stats ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ EFI_NETWORK_STATISTICS stats_buf;
+
+ DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
+ ( reset ? " reset" : "" ) );
+
+ /* Gather statistics */
+ memset ( &stats_buf, 0, sizeof ( stats_buf ) );
+ stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
+ stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad;
+ stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good +
+ snpdev->netdev->tx_stats.bad );
+ stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good;
+ stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad;
+ stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good +
+ snpdev->netdev->rx_stats.bad );
+ if ( *stats_len > sizeof ( stats_buf ) )
+ *stats_len = sizeof ( stats_buf );
+ if ( stats )
+ memcpy ( stats, &stats_buf, *stats_len );
+
+ /* Reset statistics if requested to do so */
+ if ( reset ) {
+ memset ( &snpdev->netdev->tx_stats, 0,
+ sizeof ( snpdev->netdev->tx_stats ) );
+ memset ( &snpdev->netdev->rx_stats, 0,
+ sizeof ( snpdev->netdev->rx_stats ) );
+ }
+
+ return 0;
+}
+
+/**
+ * Convert multicast IP address to MAC address
+ *
+ * @v snp SNP interface
+ * @v ipv6 Address is IPv6
+ * @v ip IP address
+ * @v mac MAC address
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6,
+ EFI_IP_ADDRESS *ip, EFI_MAC_ADDRESS *mac ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ const char *ip_str;
+ int rc;
+
+ ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ :
+ inet_ntoa ( *( ( struct in_addr * ) ip ) ) );
+ DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
+
+ /* Try to hash the address */
+ if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ),
+ ip, mac ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not hash %s: %s\n",
+ snpdev, ip_str, strerror ( rc ) );
+ return RC_TO_EFIRC ( rc );
+ }
+
+ return 0;
+}
+
+/**
+ * Read or write non-volatile storage
+ *
+ * @v snp SNP interface
+ * @v read Operation is a read
+ * @v offset Starting offset within NVRAM
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
+ UINTN offset, UINTN len, VOID *data ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev,
+ ( read ? "read" : "write" ), ( ( unsigned long ) offset ),
+ ( ( unsigned long ) len ) );
+ if ( ! read )
+ DBGC2_HDA ( snpdev, offset, data, len );
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * Read interrupt status and TX recycled buffer status
+ *
+ * @v snp SNP interface
+ * @v interrupts Interrupt status, or NULL
+ * @v txbufs Recycled transmit buffer address, or NULL
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINT32 *interrupts, VOID **txbufs ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Interrupt status. In practice, this seems to be used only
+ * to detect TX completions.
+ */
+ if ( interrupts ) {
+ *interrupts = 0;
+ /* Report TX completions once queue is empty; this
+ * avoids having to add hooks in the net device layer.
+ */
+ if ( snpdev->tx_count_interrupts &&
+ list_empty ( &snpdev->netdev->tx_queue ) ) {
+ *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ snpdev->tx_count_interrupts--;
+ }
+ /* Report RX */
+ if ( snpdev->rx_count_interrupts ) {
+ *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ snpdev->rx_count_interrupts--;
+ }
+ DBGC2 ( snpdev, " INTS:%02x", *interrupts );
+ }
+
+ /* TX completions. It would be possible to design a more
+ * idiotic scheme for this, but it would be a challenge.
+ * According to the UEFI header file, txbufs will be filled in
+ * with a list of "recycled transmit buffers" (i.e. completed
+ * TX buffers). Observant readers may care to note that
+ * *txbufs is a void pointer. Precisely how a list of
+ * completed transmit buffers is meant to be represented as an
+ * array of voids is left as an exercise for the reader.
+ *
+ * The only users of this interface (MnpDxe/MnpIo.c and
+ * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until
+ * seeing a non-NULL result return in txbufs. This is valid
+ * provided that they do not ever attempt to transmit more
+ * than one packet concurrently (and that TX never times out).
+ */
+ if ( txbufs ) {
+ if ( snpdev->tx_count_txbufs &&
+ list_empty ( &snpdev->netdev->tx_queue ) ) {
+ *txbufs = "Which idiot designed this API?";
+ snpdev->tx_count_txbufs--;
+ } else {
+ *txbufs = NULL;
+ }
+ DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) );
+ }
+
+ DBGC2 ( snpdev, "\n" );
+ return 0;
+}
+
+/**
+ * Start packet transmission
+ *
+ * @v snp SNP interface
+ * @v ll_header_len Link-layer header length, if to be filled in
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @v ll_src Link-layer source address, if specified
+ * @v ll_dest Link-layer destination address, if specified
+ * @v net_proto Network-layer protocol (in host order)
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN ll_header_len, UINTN len, VOID *data,
+ EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
+ UINT16 *net_proto ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct io_buffer *iobuf;
+ int rc;
+ EFI_STATUS efirc;
+
+ DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
+ ( ( unsigned long ) len ) );
+ if ( ll_header_len ) {
+ if ( ll_src ) {
+ DBGC2 ( snpdev, " src %s",
+ ll_protocol->ntoa ( ll_src ) );
+ }
+ if ( ll_dest ) {
+ DBGC2 ( snpdev, " dest %s",
+ ll_protocol->ntoa ( ll_dest ) );
+ }
+ if ( net_proto ) {
+ DBGC2 ( snpdev, " proto %04x", *net_proto );
+ }
+ }
+ DBGC2 ( snpdev, "\n" );
+
+ /* Sanity checks */
+ if ( ll_header_len ) {
+ if ( ll_header_len != ll_protocol->ll_header_len ) {
+ DBGC ( snpdev, "SNPDEV %p TX invalid header length "
+ "%ld\n", snpdev,
+ ( ( unsigned long ) ll_header_len ) );
+ efirc = EFI_INVALID_PARAMETER;
+ goto err_sanity;
+ }
+ if ( len < ll_header_len ) {
+ DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n",
+ snpdev, ( ( unsigned long ) len ) );
+ efirc = EFI_BUFFER_TOO_SMALL;
+ goto err_sanity;
+ }
+ if ( ! ll_dest ) {
+ DBGC ( snpdev, "SNPDEV %p TX missing destination "
+ "address\n", snpdev );
+ efirc = EFI_INVALID_PARAMETER;
+ goto err_sanity;
+ }
+ if ( ! net_proto ) {
+ DBGC ( snpdev, "SNPDEV %p TX missing network "
+ "protocol\n", snpdev );
+ efirc = EFI_INVALID_PARAMETER;
+ goto err_sanity;
+ }
+ if ( ! ll_src )
+ ll_src = &snpdev->mode.CurrentAddress;
+ }
+
+ /* Allocate buffer */
+ iobuf = alloc_iob ( len );
+ if ( ! iobuf ) {
+ DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte "
+ "buffer\n", snpdev, ( ( unsigned long ) len ) );
+ efirc = EFI_DEVICE_ERROR;
+ goto err_alloc_iob;
+ }
+ memcpy ( iob_put ( iobuf, len ), data, len );
+
+ /* Create link-layer header, if specified */
+ if ( ll_header_len ) {
+ iob_pull ( iobuf, ll_header_len );
+ if ( ( rc = ll_protocol->push ( iobuf, ll_dest, ll_src,
+ htons ( *net_proto ) )) != 0 ){
+ DBGC ( snpdev, "SNPDEV %p TX could not construct "
+ "header: %s\n", snpdev, strerror ( rc ) );
+ efirc = RC_TO_EFIRC ( rc );
+ goto err_ll_push;
+ }
+ }
+
+ /* Transmit packet */
+ if ( ( rc = netdev_tx ( snpdev->netdev, iob_disown ( iobuf ) ) ) != 0){
+ DBGC ( snpdev, "SNPDEV %p TX could not transmit: %s\n",
+ snpdev, strerror ( rc ) );
+ efirc = RC_TO_EFIRC ( rc );
+ goto err_tx;
+ }
+
+ /* Record transmission as outstanding */
+ snpdev->tx_count_interrupts++;
+ snpdev->tx_count_txbufs++;
+
+ return 0;
+
+ err_tx:
+ err_ll_push:
+ free_iob ( iobuf );
+ err_alloc_iob:
+ err_sanity:
+ return efirc;
+}
+
+/**
+ * Receive packet
+ *
+ * @v snp SNP interface
+ * @v ll_header_len Link-layer header length, if to be filled in
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @v ll_src Link-layer source address, if specified
+ * @v ll_dest Link-layer destination address, if specified
+ * @v net_proto Network-layer protocol (in host order)
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN *ll_header_len, UINTN *len, VOID *data,
+ EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
+ UINT16 *net_proto ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct io_buffer *iobuf;
+ const void *iob_ll_dest;
+ const void *iob_ll_src;
+ uint16_t iob_net_proto;
+ int rc;
+ EFI_STATUS efirc;
+
+ DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
+ ( ( unsigned long ) *len ) );
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Dequeue a packet, if one is available */
+ iobuf = netdev_rx_dequeue ( snpdev->netdev );
+ if ( ! iobuf ) {
+ DBGC2 ( snpdev, "\n" );
+ efirc = EFI_NOT_READY;
+ goto out_no_packet;
+ }
+ DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
+
+ /* Return packet to caller */
+ memcpy ( data, iobuf->data, iob_len ( iobuf ) );
+ *len = iob_len ( iobuf );
+
+ /* Attempt to decode link-layer header */
+ if ( ( rc = ll_protocol->pull ( iobuf, &iob_ll_dest, &iob_ll_src,
+ &iob_net_proto ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n",
+ snpdev, strerror ( rc ) );
+ efirc = RC_TO_EFIRC ( rc );
+ goto out_bad_ll_header;
+ }
+
+ /* Return link-layer header parameters to caller, if required */
+ if ( ll_header_len )
+ *ll_header_len = ll_protocol->ll_header_len;
+ if ( ll_src )
+ memcpy ( ll_src, iob_ll_src, ll_protocol->ll_addr_len );
+ if ( ll_dest )
+ memcpy ( ll_dest, iob_ll_dest, ll_protocol->ll_addr_len );
+ if ( net_proto )
+ *net_proto = ntohs ( iob_net_proto );
+
+ efirc = 0;
+
+ out_bad_ll_header:
+ free_iob ( iobuf );
+out_no_packet:
+ return efirc;
+}
+
+/**
+ * Poll event
+ *
+ * @v event Event
+ * @v context Event context
+ */
+static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
+ VOID *context ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_snp_device *snpdev = context;
+
+ DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
+
+ /* Do nothing unless the net device is open */
+ if ( ! ( snpdev->netdev->state & NETDEV_OPEN ) )
+ return;
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Fire event if packets have been received */
+ if ( snpdev->rx_count_events != 0 ) {
+ DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n",
+ snpdev );
+ bs->SignalEvent ( event );
+ snpdev->rx_count_events--;
+ }
+}
+
+/** SNP interface */
+static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = {
+ .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION,
+ .Start = efi_snp_start,
+ .Stop = efi_snp_stop,
+ .Initialize = efi_snp_initialize,
+ .Reset = efi_snp_reset,
+ .Shutdown = efi_snp_shutdown,
+ .ReceiveFilters = efi_snp_receive_filters,
+ .StationAddress = efi_snp_station_address,
+ .Statistics = efi_snp_statistics,
+ .MCastIpToMac = efi_snp_mcast_ip_to_mac,
+ .NvData = efi_snp_nvdata,
+ .GetStatus = efi_snp_get_status,
+ .Transmit = efi_snp_transmit,
+ .Receive = efi_snp_receive,
+};
+
+/**
+ * Locate net device corresponding to EFI device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @ret netdev Net device, or NULL if not found
+ */
+static struct net_device *
+efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_PCI_IO_PROTOCOL *pci;
+ void *interface;
+ } u;
+ UINTN pci_segment, pci_bus, pci_dev, pci_fn;
+ unsigned int pci_busdevfn;
+ struct net_device *netdev = NULL;
+ EFI_STATUS efirc;
+
+ /* See if device is a PCI device */
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_pci_io_protocol_guid,
+ &u.interface,
+ driver->DriverBindingHandle,
+ device,
+ EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){
+ DBGCP ( driver, "SNPDRV %p device %p is not a PCI device\n",
+ driver, device );
+ goto out_no_pci_io;
+ }
+
+ /* Get PCI bus:dev.fn address */
+ if ( ( efirc = u.pci->GetLocation ( u.pci, &pci_segment, &pci_bus,
+ &pci_dev, &pci_fn ) ) != 0 ) {
+ DBGC ( driver, "SNPDRV %p device %p could not get PCI "
+ "location: %s\n",
+ driver, device, efi_strerror ( efirc ) );
+ goto out_no_pci_location;
+ }
+ DBGCP ( driver, "SNPDRV %p device %p is PCI %04lx:%02lx:%02lx.%lx\n",
+ driver, device, ( ( unsigned long ) pci_segment ),
+ ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ),
+ ( ( unsigned long ) pci_fn ) );
+
+ /* Look up corresponding network device */
+ pci_busdevfn = PCI_BUSDEVFN ( pci_bus, PCI_DEVFN ( pci_dev, pci_fn ) );
+ if ( ( netdev = find_netdev_by_location ( BUS_TYPE_PCI,
+ pci_busdevfn ) ) == NULL ) {
+ DBGCP ( driver, "SNPDRV %p device %p is not a gPXE network "
+ "device\n", driver, device );
+ goto out_no_netdev;
+ }
+ DBGC ( driver, "SNPDRV %p device %p is %s\n",
+ driver, device, netdev->name );
+
+ out_no_netdev:
+ out_no_pci_location:
+ bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
+ driver->DriverBindingHandle, device );
+ out_no_pci_io:
+ return netdev;
+}
+
+/**
+ * Locate SNP corresponding to EFI device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @ret snp EFI SNP, or NULL if not found
+ */
+static struct efi_snp_device *
+efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_SIMPLE_NETWORK_PROTOCOL *snp;
+ void *interface;
+ } u;
+ struct efi_snp_device *snpdev = NULL;
+ EFI_STATUS efirc;
+
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_simple_network_protocol_guid,
+ &u.interface,
+ driver->DriverBindingHandle,
+ device,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){
+ DBGC ( driver, "SNPDRV %p device %p could not locate SNP: "
+ "%s\n", driver, device, efi_strerror ( efirc ) );
+ goto err_no_snp;
+ }
+
+ snpdev = container_of ( u.snp, struct efi_snp_device, snp );
+ DBGCP ( driver, "SNPDRV %p device %p is SNPDEV %p\n",
+ driver, device, snpdev );
+
+ bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,
+ driver->DriverBindingHandle, device );
+ err_no_snp:
+ return snpdev;
+}
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v child Path to child device, if any
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver,
+ EFI_HANDLE device,
+ EFI_DEVICE_PATH_PROTOCOL *child ) {
+ struct net_device *netdev;
+
+ DBGCP ( driver, "SNPDRV %p DRIVER_SUPPORTED %p (%p)\n",
+ driver, device, child );
+
+ netdev = efi_snp_netdev ( driver, device );
+ return ( netdev ? 0 : EFI_UNSUPPORTED );
+}
+
+/**
+ * Attach driver to device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v child Path to child device, if any
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver,
+ EFI_HANDLE device,
+ EFI_DEVICE_PATH_PROTOCOL *child ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ EFI_DEVICE_PATH_PROTOCOL *subpath;
+ MAC_ADDR_DEVICE_PATH *macpath;
+ struct efi_snp_device *snpdev;
+ struct net_device *netdev;
+ size_t subpath_len;
+ size_t path_prefix_len = 0;
+ unsigned int i;
+ EFI_STATUS efirc;
+
+ DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n",
+ driver, device, child );
+
+ /* Determine device path prefix length */
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_device_path_protocol_guid,
+ ( void * ) &path,
+ driver->DriverBindingHandle,
+ device,
+ EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){
+ DBGCP ( driver, "SNPDRV %p device %p has no device path\n",
+ driver, device );
+ goto err_no_device_path;
+ }
+ subpath = path;
+ while ( subpath->Type != END_DEVICE_PATH_TYPE ) {
+ subpath_len = ( ( subpath->Length[1] << 8 ) |
+ subpath->Length[0] );
+ path_prefix_len += subpath_len;
+ subpath = ( ( ( void * ) subpath ) + subpath_len );
+ }
+
+ /* Allocate the SNP device */
+ snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len +
+ sizeof ( *macpath ) );
+ if ( ! snpdev ) {
+ efirc = EFI_OUT_OF_RESOURCES;
+ goto err_alloc_snp;
+ }
+
+ /* Identify the net device */
+ netdev = efi_snp_netdev ( driver, device );
+ if ( ! netdev ) {
+ DBGC ( snpdev, "SNPDEV %p cannot find netdev for device %p\n",
+ snpdev, device );
+ efirc = EFI_UNSUPPORTED;
+ goto err_no_netdev;
+ }
+ snpdev->netdev = netdev_get ( netdev );
+
+ /* Sanity check */
+ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
+ DBGC ( snpdev, "SNPDEV %p cannot support link-layer address "
+ "length %d for %s\n", snpdev,
+ netdev->ll_protocol->ll_addr_len, netdev->name );
+ efirc = EFI_INVALID_PARAMETER;
+ goto err_ll_addr_len;
+ }
+
+ /* Populate the SNP structure */
+ memcpy ( &snpdev->snp, &efi_snp_device_snp, sizeof ( snpdev->snp ) );
+ snpdev->snp.Mode = &snpdev->mode;
+ if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY,
+ efi_snp_wait_for_packet, snpdev,
+ &snpdev->snp.WaitForPacket ) ) != 0 ){
+ DBGC ( snpdev, "SNPDEV %p could not create event: %s\n",
+ snpdev, efi_strerror ( efirc ) );
+ goto err_create_event;
+ }
+
+ /* Populate the SNP mode structure */
+ snpdev->mode.State = EfiSimpleNetworkStopped;
+ efi_snp_set_mode ( snpdev );
+
+ /* Populate the NII structure */
+ snpdev->nii.Revision =
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;
+ strncpy ( snpdev->nii.StringId, "gPXE",
+ sizeof ( snpdev->nii.StringId ) );
+
+ /* Populate the device name */
+ for ( i = 0 ; i < sizeof ( netdev->name ) ; i++ ) {
+ /* Damn Unicode names */
+ assert ( i < ( sizeof ( snpdev->name ) /
+ sizeof ( snpdev->name[0] ) ) );
+ snpdev->name[i] = netdev->name[i];
+ }
+
+ /* Populate the device path */
+ memcpy ( &snpdev->path, path, path_prefix_len );
+ macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len );
+ subpath = ( ( void * ) ( macpath + 1 ) );
+ memset ( macpath, 0, sizeof ( *macpath ) );
+ macpath->Header.Type = MESSAGING_DEVICE_PATH;
+ macpath->Header.SubType = MSG_MAC_ADDR_DP;
+ macpath->Header.Length[0] = sizeof ( *macpath );
+ memcpy ( &macpath->MacAddress, netdev->ll_addr,
+ sizeof ( macpath->MacAddress ) );
+ macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto );
+ memset ( subpath, 0, sizeof ( *subpath ) );
+ subpath->Type = END_DEVICE_PATH_TYPE;
+ subpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ subpath->Length[0] = sizeof ( *subpath );
+
+ /* Install the SNP */
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, &snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ NULL ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not install protocols: "
+ "%s\n", snpdev, efi_strerror ( efirc ) );
+ goto err_install_protocol_interface;
+ }
+
+ DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n",
+ snpdev, netdev->name, snpdev->handle );
+ return 0;
+
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, &snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ NULL );
+ err_install_protocol_interface:
+ bs->CloseEvent ( snpdev->snp.WaitForPacket );
+ err_create_event:
+ err_ll_addr_len:
+ netdev_put ( netdev );
+ err_no_netdev:
+ free ( snpdev );
+ err_alloc_snp:
+ bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
+ driver->DriverBindingHandle, device );
+ err_no_device_path:
+ return efirc;
+}
+
+/**
+ * Detach driver from device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v num_children Number of child devices
+ * @v children List of child devices
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver,
+ EFI_HANDLE device,
+ UINTN num_children,
+ EFI_HANDLE *children ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_snp_device *snpdev;
+
+ DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n",
+ driver, device, ( ( unsigned long ) num_children ), children );
+
+ /* Locate SNP device */
+ snpdev = efi_snp_snpdev ( driver, device );
+ if ( ! snpdev ) {
+ DBGC ( driver, "SNPDRV %p device %p could not find SNPDEV\n",
+ driver, device );
+ return EFI_DEVICE_ERROR;
+ }
+
+ /* Uninstall the SNP */
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, &snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ NULL );
+ bs->CloseEvent ( snpdev->snp.WaitForPacket );
+ netdev_put ( snpdev->netdev );
+ free ( snpdev );
+ bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
+ driver->DriverBindingHandle, device );
+ return 0;
+}
+
+/** EFI SNP driver binding */
+static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = {
+ efi_snp_driver_supported,
+ efi_snp_driver_start,
+ efi_snp_driver_stop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+/**
+ * Look up driver name
+ *
+ * @v wtf Component name protocol
+ * @v language Language to use
+ * @v driver_name Driver name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
+ CHAR8 *language __unused, CHAR16 **driver_name ) {
+
+ *driver_name = L"" PRODUCT_SHORT_NAME " Driver";
+ return 0;
+}
+
+/**
+ * Look up controller name
+ *
+ * @v wtf Component name protocol
+ * @v device Device
+ * @v child Child device, or NULL
+ * @v language Language to use
+ * @v driver_name Device name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
+ EFI_HANDLE device __unused,
+ EFI_HANDLE child __unused,
+ CHAR8 *language __unused,
+ CHAR16 **controller_name __unused ) {
+
+ /* Just let EFI use the default Device Path Name */
+ return EFI_UNSUPPORTED;
+}
+
+/** EFI SNP component name protocol */
+static EFI_COMPONENT_NAME2_PROTOCOL efi_snp_name = {
+ efi_snp_get_driver_name,
+ efi_snp_get_controller_name,
+ "en"
+};
+
+/**
+ * Install EFI SNP driver
+ *
+ * @ret rc Return status code
+ */
+int efi_snp_install ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_DRIVER_BINDING_PROTOCOL *driver = &efi_snp_binding;
+ EFI_STATUS efirc;
+
+ driver->ImageHandle = efi_image_handle;
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &driver->DriverBindingHandle,
+ &efi_driver_binding_protocol_guid, driver,
+ &efi_component_name2_protocol_guid, &efi_snp_name,
+ NULL ) ) != 0 ) {
+ DBGC ( driver, "SNPDRV %p could not install protocols: "
+ "%s\n", driver, efi_strerror ( efirc ) );
+ return EFIRC_TO_RC ( efirc );
+ }
+
+ DBGC ( driver, "SNPDRV %p driver binding installed as %p\n",
+ driver, driver->DriverBindingHandle );
+ return 0;
+}
diff --git a/gpxe/src/interface/efi/efi_strerror.c b/gpxe/src/interface/efi/efi_strerror.c
new file mode 100644
index 00000000..adfeaed5
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_strerror.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE error message formatting for EFI
+ *
+ */
+
+/**
+ * Format EFI status code
+ *
+ * @v efirc EFI status code
+ * @v efi_strerror EFI status code string
+ */
+const char * efi_strerror ( EFI_STATUS efirc ) {
+ static char errbuf[32];
+
+ if ( ! efirc )
+ return "No error";
+
+ snprintf ( errbuf, sizeof ( errbuf ), "Error %lld",
+ ( unsigned long long ) ( efirc ^ MAX_BIT ) );
+ return errbuf;
+}
diff --git a/gpxe/src/interface/efi/efi_timer.c b/gpxe/src/interface/efi/efi_timer.c
new file mode 100644
index 00000000..d1ba43af
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_timer.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <limits.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/timer.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/Cpu.h>
+
+/** @file
+ *
+ * gPXE timer API for EFI
+ *
+ */
+
+/** Scale factor to apply to CPU timer 0
+ *
+ * The timer is scaled down in order to ensure that reasonable values
+ * for "number of ticks" don't exceed the size of an unsigned long.
+ */
+#define EFI_TIMER0_SHIFT 12
+
+/** Calibration time */
+#define EFI_CALIBRATE_DELAY_MS 1
+
+/** CPU protocol */
+static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static void efi_udelay ( unsigned long usecs ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+
+ if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
+ DBG ( "EFI could not delay for %ldus: %s\n",
+ usecs, efi_strerror ( efirc ) );
+ /* Probably screwed */
+ }
+}
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ */
+static unsigned long efi_currticks ( void ) {
+ UINT64 time;
+ EFI_STATUS efirc;
+
+ /* Read CPU timer 0 (TSC) */
+ if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
+ NULL ) ) != 0 ) {
+ DBG ( "EFI could not read CPU timer: %s\n",
+ efi_strerror ( efirc ) );
+ /* Probably screwed */
+ return -1UL;
+ }
+
+ return ( time >> EFI_TIMER0_SHIFT );
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static unsigned long efi_ticks_per_sec ( void ) {
+ static unsigned long ticks_per_sec = 0;
+
+ /* Calibrate timer, if necessary. EFI does nominally provide
+ * the timer speed via the (optional) TimerPeriod parameter to
+ * the GetTimerValue() call, but it gets the speed slightly
+ * wrong. By up to three orders of magnitude. Not helpful.
+ */
+ if ( ! ticks_per_sec ) {
+ unsigned long start;
+ unsigned long elapsed;
+
+ DBG ( "Calibrating EFI timer with a %d ms delay\n",
+ EFI_CALIBRATE_DELAY_MS );
+ start = currticks();
+ mdelay ( EFI_CALIBRATE_DELAY_MS );
+ elapsed = ( currticks() - start );
+ ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
+ DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
+ "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
+ ticks_per_sec );
+ }
+
+ return ticks_per_sec;
+}
+
+PROVIDE_TIMER ( efi, udelay, efi_udelay );
+PROVIDE_TIMER ( efi, currticks, efi_currticks );
+PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
diff --git a/gpxe/src/interface/efi/efi_uaccess.c b/gpxe/src/interface/efi/efi_uaccess.c
new file mode 100644
index 00000000..1c54c031
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_uaccess.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/uaccess.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE user access API for EFI
+ *
+ */
+
+PROVIDE_UACCESS_INLINE ( efi, phys_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_phys );
+PROVIDE_UACCESS_INLINE ( efi, virt_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_virt );
+PROVIDE_UACCESS_INLINE ( efi, userptr_add );
+PROVIDE_UACCESS_INLINE ( efi, memcpy_user );
+PROVIDE_UACCESS_INLINE ( efi, memmove_user );
+PROVIDE_UACCESS_INLINE ( efi, memset_user );
+PROVIDE_UACCESS_INLINE ( efi, strlen_user );
+PROVIDE_UACCESS_INLINE ( efi, memchr_user );
diff --git a/gpxe/src/interface/efi/efi_umalloc.c b/gpxe/src/interface/efi/efi_umalloc.c
new file mode 100644
index 00000000..4de3789d
--- /dev/null
+++ b/gpxe/src/interface/efi/efi_umalloc.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE user memory allocation API for EFI
+ *
+ */
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/**
+ * 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 efi_urealloc ( userptr_t old_ptr, size_t new_size ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_PHYSICAL_ADDRESS phys_addr;
+ unsigned int new_pages, old_pages;
+ userptr_t new_ptr = UNOWHERE;
+ size_t old_size;
+ EFI_STATUS efirc;
+
+ /* Allocate new memory if necessary. If allocation fails,
+ * return without touching the old block.
+ */
+ if ( new_size ) {
+ new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 );
+ if ( ( efirc = bs->AllocatePages ( AllocateAnyPages,
+ EfiBootServicesData,
+ new_pages,
+ &phys_addr ) ) != 0 ) {
+ DBG ( "EFI could not allocate %d pages: %s\n",
+ new_pages, efi_strerror ( efirc ) );
+ return UNULL;
+ }
+ assert ( phys_addr != 0 );
+ new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE );
+ copy_to_user ( new_ptr, -EFI_PAGE_SIZE,
+ &new_size, sizeof ( new_size ) );
+ DBG ( "EFI allocated %d pages at %llx\n",
+ new_pages, phys_addr );
+ }
+
+ /* Copy across relevant part of the old data region (if any),
+ * then free it. Note that at this point either (a) new_ptr
+ * is valid, or (b) new_size is 0; either way, the memcpy() is
+ * valid.
+ */
+ if ( old_ptr && ( old_ptr != UNOWHERE ) ) {
+ copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE,
+ sizeof ( old_size ) );
+ memcpy_user ( new_ptr, 0, old_ptr, 0,
+ ( (old_size < new_size) ? old_size : new_size ));
+ old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 );
+ phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE );
+ if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){
+ DBG ( "EFI could not free %d pages at %llx: %s\n",
+ old_pages, phys_addr, efi_strerror ( efirc ) );
+ /* Not fatal; we have leaked memory but successfully
+ * allocated (if asked to do so).
+ */
+ }
+ DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr );
+ }
+
+ return new_ptr;
+}
+
+PROVIDE_UMALLOC ( efi, urealloc, efi_urealloc );
diff --git a/gpxe/src/interface/pxe/pxe_errors.c b/gpxe/src/interface/pxe/pxe_errors.c
deleted file mode 100644
index f884ef8a..00000000
--- a/gpxe/src/interface/pxe/pxe_errors.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <errno.h>
-#include <gpxe/errortab.h>
-
-/*
- * This table was generated from the relevant section of errno.h using
- *
- * perl -ne 'if ( /(PXENV_STATUS_(\S+))/ ) {
- * $code = $1; $msg = $2;
- * $msg =~ s/_/ /g; $msg = ucfirst lc $msg;
- * $msg =~ s/(tftp|udp|arp|undi|bis|binl|pxenv|pxe|dhcp)/uc $1/ieg;
- * print "\t{ $code, \"$msg\" },\n";
- * }'
- *
- * followed by a little manual tweaking.
- *
- */
-struct errortab pxe_errortab[] __errortab = {
- { PXENV_STATUS_SUCCESS, "Success" },
- { PXENV_STATUS_FAILURE, "Failure" },
- { PXENV_STATUS_BAD_FUNC, "Bad function" },
- { PXENV_STATUS_UNSUPPORTED, "Unsupported function" },
- { PXENV_STATUS_KEEP_UNDI, "Keep UNDI" },
- { PXENV_STATUS_KEEP_ALL, "Keep all" },
- { PXENV_STATUS_OUT_OF_RESOURCES, "Out of resources" },
- { PXENV_STATUS_ARP_TIMEOUT, "ARP timeout" },
- { PXENV_STATUS_UDP_CLOSED, "UDP closed" },
- { PXENV_STATUS_UDP_OPEN, "UDP open" },
- { PXENV_STATUS_TFTP_CLOSED, "TFTP closed" },
- { PXENV_STATUS_TFTP_OPEN, "TFTP open" },
- { PXENV_STATUS_MCOPY_PROBLEM, "Memory copy problem" },
- { PXENV_STATUS_BIS_INTEGRITY_FAILURE, "BIS integrity failure" },
- { PXENV_STATUS_BIS_VALIDATE_FAILURE, "BIS validation failure" },
- { PXENV_STATUS_BIS_INIT_FAILURE, "BIS init failure" },
- { PXENV_STATUS_BIS_SHUTDOWN_FAILURE, "BIS shutdown failure" },
- { PXENV_STATUS_BIS_GBOA_FAILURE, "BIS GBOA failure" },
- { PXENV_STATUS_BIS_FREE_FAILURE, "BIS free failure" },
- { PXENV_STATUS_BIS_GSI_FAILURE, "BIS GSI failure" },
- { PXENV_STATUS_BIS_BAD_CKSUM, "BIS bad checksum" },
- { PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS, "TFTP cannot ARP address" },
- { PXENV_STATUS_TFTP_OPEN_TIMEOUT, "TFTP open timeout" },
- { PXENV_STATUS_TFTP_UNKNOWN_OPCODE, "TFTP unknown opcode" },
- { PXENV_STATUS_TFTP_READ_TIMEOUT, "TFTP read timeout" },
- { PXENV_STATUS_TFTP_ERROR_OPCODE, "TFTP error opcode" },
- { PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION,
- "TFTP cannot open connection" },
- { PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION,
- "TFTP cannot read from connection" },
- { PXENV_STATUS_TFTP_TOO_MANY_PACKAGES, "TFTP too many packages" },
- { PXENV_STATUS_TFTP_FILE_NOT_FOUND, "TFTP file not found" },
- { PXENV_STATUS_TFTP_ACCESS_VIOLATION, "TFTP access violation" },
- { PXENV_STATUS_TFTP_NO_MCAST_ADDRESS, "TFTP no mcast address" },
- { PXENV_STATUS_TFTP_NO_FILESIZE, "TFTP no filesize" },
- { PXENV_STATUS_TFTP_INVALID_PACKET_SIZE, "TFTP invalid packet size" },
- { PXENV_STATUS_DHCP_TIMEOUT, "DHCP timeout" },
- { PXENV_STATUS_DHCP_NO_IP_ADDRESS, "DHCP no ip address" },
- { PXENV_STATUS_DHCP_NO_BOOTFILE_NAME, "DHCP no bootfile name" },
- { PXENV_STATUS_DHCP_BAD_IP_ADDRESS, "DHCP bad ip address" },
- { PXENV_STATUS_UNDI_INVALID_FUNCTION, "UNDI invalid function" },
- { PXENV_STATUS_UNDI_MEDIATEST_FAILED, "UNDI mediatest failed" },
- { PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST,
- "UNDI cannot initialise NIC for multicast" },
- { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC,
- "UNDI cannot initialise NIC" },
- { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY,
- "UNDI cannot initialise PHY" },
- { PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA,
- "UNDI cannot read config data" },
- { PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA,
- "UNDI cannot read init data" },
- { PXENV_STATUS_UNDI_BAD_MAC_ADDRESS, "UNDI bad MAC address" },
- { PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM, "UNDI bad EEPROM checksum" },
- { PXENV_STATUS_UNDI_ERROR_SETTING_ISR, "UNDI error setting ISR" },
- { PXENV_STATUS_UNDI_INVALID_STATE, "UNDI invalid state" },
- { PXENV_STATUS_UNDI_TRANSMIT_ERROR, "UNDI transmit error" },
- { PXENV_STATUS_UNDI_INVALID_PARAMETER, "UNDI invalid parameter" },
- { PXENV_STATUS_BSTRAP_PROMPT_MENU, "Bootstrap prompt menu" },
- { PXENV_STATUS_BSTRAP_MCAST_ADDR, "Bootstrap mcast addr" },
- { PXENV_STATUS_BSTRAP_MISSING_LIST, "Bootstrap missing list" },
- { PXENV_STATUS_BSTRAP_NO_RESPONSE, "Bootstrap no response" },
- { PXENV_STATUS_BSTRAP_FILE_TOO_BIG, "Bootstrap file too big" },
- { PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE,
- "BINL canceled by keystroke" },
- { PXENV_STATUS_BINL_NO_PXE_SERVER, "BINL no PXE server" },
- { PXENV_STATUS_NOT_AVAILABLE_IN_PMODE,
- "Not available in protected mode" },
- { PXENV_STATUS_NOT_AVAILABLE_IN_RMODE, "Not available in real mode" },
- { PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED,
- "BUSD device not supported" },
- { PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY,
- "Loader no free base memory" },
- { PXENV_STATUS_LOADER_NO_BC_ROMID, "Loader no Base Code ROM ID" },
- { PXENV_STATUS_LOADER_BAD_BC_ROMID, "Loader bad Base Code ROM ID" },
- { PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE,
- "Loader bad Base Code runtime image" },
- { PXENV_STATUS_LOADER_NO_UNDI_ROMID, "Loader no UNDI ROM ID" },
- { PXENV_STATUS_LOADER_BAD_UNDI_ROMID, "Loader bad UNDI ROM ID" },
- { PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE,
- "Loader bad UNDI driver image" },
- { PXENV_STATUS_LOADER_NO_PXE_STRUCT, "Loader no !PXE struct" },
- { PXENV_STATUS_LOADER_NO_PXENV_STRUCT, "Loader no PXENV+ struct" },
- { PXENV_STATUS_LOADER_UNDI_START, "Loader UNDI start" },
- { PXENV_STATUS_LOADER_BC_START, "Loader Base Code start" },
-};
diff --git a/gpxe/src/interface/pxe/pxe_file.c b/gpxe/src/interface/pxe/pxe_file.c
deleted file mode 100644
index 41674588..00000000
--- a/gpxe/src/interface/pxe/pxe_file.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/** @file
- *
- * PXE FILE API
- *
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/posix_io.h>
-#include <gpxe/features.h>
-#include <pxe.h>
-
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
-
-/**
- * FILE OPEN
- *
- * @v file_open Pointer to a struct s_PXENV_FILE_OPEN
- * @v s_PXENV_FILE_OPEN::FileName URL of file to open
- * @ret #PXENV_EXIT_SUCCESS File was opened
- * @ret #PXENV_EXIT_FAILURE File was not opened
- * @ret s_PXENV_FILE_OPEN::Status PXE status code
- * @ret s_PXENV_FILE_OPEN::FileHandle Handle of opened file
- *
- */
-PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
- userptr_t filename;
- size_t filename_len;
- int fd;
-
- DBG ( "PXENV_FILE_OPEN" );
-
- /* Copy name from external program, and open it */
- filename = real_to_user ( file_open->FileName.segment,
- file_open->FileName.offset );
- filename_len = strlen_user ( filename, 0 );
- {
- char uri_string[ filename_len + 1 ];
-
- copy_from_user ( uri_string, filename, 0,
- sizeof ( uri_string ) );
- DBG ( " %s", uri_string );
- fd = open ( uri_string );
- }
-
- if ( fd < 0 ) {
- file_open->Status = PXENV_STATUS ( fd );
- return PXENV_EXIT_FAILURE;
- }
-
- DBG ( " as file %d", fd );
-
- file_open->FileHandle = fd;
- file_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * FILE CLOSE
- *
- * @v file_close Pointer to a struct s_PXENV_FILE_CLOSE
- * @v s_PXENV_FILE_CLOSE::FileHandle File handle
- * @ret #PXENV_EXIT_SUCCESS File was closed
- * @ret #PXENV_EXIT_FAILURE File was not closed
- * @ret s_PXENV_FILE_CLOSE::Status PXE status code
- *
- */
-PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
-
- DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
-
- close ( file_close->FileHandle );
- file_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * FILE SELECT
- *
- * @v file_select Pointer to a struct s_PXENV_FILE_SELECT
- * @v s_PXENV_FILE_SELECT::FileHandle File handle
- * @ret #PXENV_EXIT_SUCCESS File has been checked for readiness
- * @ret #PXENV_EXIT_FAILURE File has not been checked for readiness
- * @ret s_PXENV_FILE_SELECT::Status PXE status code
- * @ret s_PXENV_FILE_SELECT::Ready Indication of readiness
- *
- */
-PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
- fd_set fdset;
- int ready;
-
- DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle );
-
- FD_ZERO ( &fdset );
- FD_SET ( file_select->FileHandle, &fdset );
- if ( ( ready = select ( &fdset, 0 ) ) < 0 ) {
- file_select->Status = PXENV_STATUS ( ready );
- return PXENV_EXIT_FAILURE;
- }
-
- file_select->Ready = ( ready ? RDY_READ : 0 );
- file_select->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * FILE READ
- *
- * @v file_read Pointer to a struct s_PXENV_FILE_READ
- * @v s_PXENV_FILE_READ::FileHandle File handle
- * @v s_PXENV_FILE_READ::BufferSize Size of data buffer
- * @v s_PXENV_FILE_READ::Buffer Data buffer
- * @ret #PXENV_EXIT_SUCCESS Data has been read from file
- * @ret #PXENV_EXIT_FAILURE Data has not been read from file
- * @ret s_PXENV_FILE_READ::Status PXE status code
- * @ret s_PXENV_FILE_READ::Ready Indication of readiness
- * @ret s_PXENV_FILE_READ::BufferSize Length of data read
- *
- */
-PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
- userptr_t buffer;
- ssize_t len;
-
- DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
- file_read->Buffer.segment, file_read->Buffer.offset,
- file_read->BufferSize );
-
- buffer = real_to_user ( file_read->Buffer.segment,
- file_read->Buffer.offset );
- if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
- file_read->BufferSize ) ) < 0 ) {
- file_read->Status = PXENV_STATUS ( len );
- return PXENV_EXIT_FAILURE;
- }
-
- DBG ( " read %04zx", ( ( size_t ) len ) );
-
- file_read->BufferSize = len;
- file_read->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * GET FILE SIZE
- *
- * @v get_file_size Pointer to a struct s_PXENV_GET_FILE_SIZE
- * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle
- * @ret #PXENV_EXIT_SUCCESS File size has been determined
- * @ret #PXENV_EXIT_FAILURE File size has not been determined
- * @ret s_PXENV_GET_FILE_SIZE::Status PXE status code
- * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file
- */
-PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE
- *get_file_size ) {
- ssize_t filesize;
-
- DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle );
-
- filesize = fsize ( get_file_size->FileHandle );
- if ( filesize < 0 ) {
- get_file_size->Status = PXENV_STATUS ( filesize );
- return PXENV_EXIT_FAILURE;
- }
-
- DBG ( " is %zd", ( ( size_t ) filesize ) );
-
- get_file_size->FileSize = filesize;
- get_file_size->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * FILE EXEC
- *
- * @v file_exec Pointer to a struct s_PXENV_FILE_EXEC
- * @v s_PXENV_FILE_EXEC::Command Command to execute
- * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
- * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
- * @ret s_PXENV_FILE_EXEC::Status PXE status code
- *
- */
-PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
- userptr_t command;
- size_t command_len;
- int rc;
-
- DBG ( "PXENV_FILE_EXEC" );
-
- /* Copy name from external program, and exec it */
- command = real_to_user ( file_exec->Command.segment,
- file_exec->Command.offset );
- command_len = strlen_user ( command, 0 );
- {
- char command_string[ command_len + 1 ];
-
- copy_from_user ( command_string, command, 0,
- sizeof ( command_string ) );
- DBG ( " %s", command_string );
-
- if ( ( rc = system ( command_string ) ) != 0 ) {
- file_exec->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
- }
-
- file_exec->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * FILE API CHECK
- *
- * @v file_exec Pointer to a struct s_PXENV_FILE_API_CHECK
- * @v s_PXENV_FILE_API_CHECK::Magic Inbound magic number (0x91d447b2)
- * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
- * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
- * @ret s_PXENV_FILE_API_CHECK::Status PXE status code
- * @ret s_PXENV_FILE_API_CHECK::Magic Outbound magic number (0xe9c17b20)
- * @ret s_PXENV_FILE_API_CHECK::Provider "gPXE" (0x45585067)
- * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
- * @ret s_PXENV_FILE_API_CHECK::Flags Reserved
- *
- */
-PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
- DBG ( "PXENV_FILE_API_CHECK" );
-
- if ( file_api_check->Magic != 0x91d447b2 ) {
- file_api_check->Status = PXENV_STATUS_BAD_FUNC;
- return PXENV_EXIT_FAILURE;
- } else if ( file_api_check->Size <
- sizeof(struct s_PXENV_FILE_API_CHECK) ) {
- file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
- } else {
- file_api_check->Status = PXENV_STATUS_SUCCESS;
- file_api_check->Size = sizeof(struct s_PXENV_FILE_API_CHECK);
- file_api_check->Magic = 0xe9c17b20;
- file_api_check->Provider = 0x45585067; /* "gPXE" */
- file_api_check->APIMask = 0x0000007f; /* Functions e0-e6 */
- file_api_check->Flags = 0; /* None defined */
- return PXENV_EXIT_SUCCESS;
- }
-}
diff --git a/gpxe/src/interface/pxe/pxe_loader.c b/gpxe/src/interface/pxe/pxe_loader.c
deleted file mode 100644
index d228a36d..00000000
--- a/gpxe/src/interface/pxe/pxe_loader.c
+++ /dev/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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <gpxe/init.h>
-#include "pxe.h"
-#include "pxe_call.h"
-
-/** @file
- *
- * PXE UNDI loader
- *
- */
-
-/* PXENV_UNDI_LOADER
- *
- */
-PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
-
- /* Perform one-time initialisation (e.g. heap) */
- initialise();
-
- DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
- undi_loader->UNDI_CS, undi_loader->UNDI_DS );
-
- /* Set up PXE data structures */
- pxe_init_structures();
-
- /* Fill in UNDI loader structure */
- undi_loader->PXEptr.segment = rm_cs;
- undi_loader->PXEptr.offset = __from_text16 ( &ppxe );
- undi_loader->PXENVptr.segment = rm_cs;
- undi_loader->PXENVptr.offset = __from_text16 ( &pxenv );
-
- undi_loader->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
diff --git a/gpxe/src/interface/pxe/pxe_preboot.c b/gpxe/src/interface/pxe/pxe_preboot.c
deleted file mode 100644
index 8220d1f2..00000000
--- a/gpxe/src/interface/pxe/pxe_preboot.c
+++ /dev/null
@@ -1,352 +0,0 @@
-/** @file
- *
- * PXE Preboot API
- *
- */
-
-/* PXE API interface for Etherboot.
- *
- * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/fakedhcp.h>
-#include <gpxe/device.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/isapnp.h>
-#include <gpxe/init.h>
-#include <gpxe/if_ether.h>
-#include <basemem_packet.h>
-#include "pxe.h"
-#include "pxe_call.h"
-
-/* Avoid dragging in isapnp.o unnecessarily */
-uint16_t isapnp_read_port;
-
-/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
-enum pxe_cached_info_indices {
- CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ),
- CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ),
- CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ),
- NUM_CACHED_INFOS
-};
-
-/** A cached DHCP packet */
-union pxe_cached_info {
- struct dhcphdr dhcphdr;
- /* This buffer must be *exactly* the size of a BOOTPLAYER_t
- * structure, otherwise WinPE will die horribly. It takes the
- * size of *our* buffer and feeds it in to us as the size of
- * one of *its* buffers. If our buffer is larger than it
- * expects, we therefore end up overwriting part of its data
- * segment, since it tells us to do so. (D'oh!)
- *
- * Note that a BOOTPLAYER_t is not necessarily large enough to
- * hold a DHCP packet; this is a flaw in the PXE spec.
- */
- BOOTPLAYER_t packet;
-} __attribute__ (( packed ));
-
-/** A PXE DHCP packet creator */
-struct pxe_dhcp_packet_creator {
- /** Create DHCP packet
- *
- * @v netdev Network device
- * @v data Buffer for DHCP packet
- * @v max_len Size of DHCP packet buffer
- * @ret rc Return status code
- */
- int ( * create ) ( struct net_device *netdev, void *data,
- size_t max_len );
-};
-
-/** PXE DHCP packet creators */
-static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = {
- [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover },
- [CACHED_INFO_DHCPACK] = { create_fakedhcpack },
- [CACHED_INFO_BINL] = { create_fakeproxydhcpack },
-};
-
-/* The case in which the caller doesn't supply a buffer is really
- * awkward to support given that we have multiple sources of options,
- * and that we don't actually store the DHCP packets. (We may not
- * even have performed DHCP; we may have obtained all configuration
- * from non-volatile stored options or from the command line.)
- *
- * Some NBPs rely on the buffers we provide being persistent, so we
- * can't just use the temporary packet buffer. 4.5kB of base memory
- * always wasted just because some clients are too lazy to provide
- * their own buffers...
- */
-static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] );
-#define cached_info __use_data16 ( cached_info )
-
-/**
- * Set PXE cached TFTP filename
- *
- * @v filename TFTP filename
- *
- * This is a bug-for-bug compatibility hack needed in order to work
- * with Microsoft Remote Installation Services (RIS). The filename
- * used in a call to PXENV_RESTART_TFTP or PXENV_TFTP_READ_FILE must
- * be returned as the DHCP filename in subsequent calls to
- * PXENV_GET_CACHED_INFO.
- */
-void pxe_set_cached_filename ( const unsigned char *filename ) {
- memcpy ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file, filename,
- sizeof ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file ) );
- memcpy ( cached_info[CACHED_INFO_BINL].dhcphdr.file, filename,
- sizeof ( cached_info[CACHED_INFO_BINL].dhcphdr.file ) );
-}
-
-/**
- * UNLOAD BASE CODE STACK
- *
- * @v None -
- * @ret ...
- *
- */
-PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
- DBG ( "PXENV_UNLOAD_STACK" );
-
- unload_stack->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_GET_CACHED_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
- *get_cached_info ) {
- struct pxe_dhcp_packet_creator *creator;
- union pxe_cached_info *info;
- unsigned int idx;
- size_t len;
- userptr_t buffer;
- int rc;
-
- DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
-
- DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment,
- get_cached_info->Buffer.offset, get_cached_info->BufferSize );
-
- /* Sanity check */
- idx = ( get_cached_info->PacketType - 1 );
- if ( idx >= NUM_CACHED_INFOS ) {
- DBG ( " bad PacketType" );
- goto err;
- }
- info = &cached_info[idx];
-
- /* Construct cached version of packet, if not already constructed. */
- if ( ! info->dhcphdr.op ) {
- /* Construct DHCP packet */
- creator = &pxe_dhcp_packet_creators[idx];
- if ( ( rc = creator->create ( pxe_netdev, info,
- sizeof ( *info ) ) ) != 0 ) {
- DBG ( " failed to build packet" );
- goto err;
- }
- }
-
- len = get_cached_info->BufferSize;
- if ( len == 0 ) {
- /* Point client at our cached buffer.
- *
- * To add to the fun, Intel decided at some point in
- * the evolution of the PXE specification to add the
- * BufferLimit field, which we are meant to fill in
- * with the length of our packet buffer, so that the
- * caller can safely modify the boot server reply
- * packet stored therein. However, this field was not
- * present in earlier versions of the PXE spec, and
- * there is at least one PXE NBP (Altiris) which
- * allocates only exactly enough space for this
- * earlier, shorter version of the structure. If we
- * actually fill in the BufferLimit field, we
- * therefore risk trashing random areas of the
- * caller's memory. If we *don't* fill it in, then
- * the caller is at liberty to assume that whatever
- * random value happened to be in that location
- * represents the length of the buffer we've just
- * passed back to it.
- *
- * Since older PXE stacks won't fill this field in
- * anyway, it's probably safe to assume that no
- * callers actually rely on it, so we choose to not
- * fill it in.
- */
- get_cached_info->Buffer.segment = rm_ds;
- get_cached_info->Buffer.offset = __from_data16 ( info );
- get_cached_info->BufferSize = sizeof ( *info );
- DBG ( " returning %04x:%04x+%04x['%x']",
- get_cached_info->Buffer.segment,
- get_cached_info->Buffer.offset,
- get_cached_info->BufferSize,
- get_cached_info->BufferLimit );
- } else {
- /* Copy packet to client buffer */
- if ( len > sizeof ( *info ) )
- len = sizeof ( *info );
- if ( len < sizeof ( *info ) )
- DBG ( " buffer may be too short" );
- buffer = real_to_user ( get_cached_info->Buffer.segment,
- get_cached_info->Buffer.offset );
- copy_to_user ( buffer, 0, info, len );
- get_cached_info->BufferSize = len;
- }
-
- get_cached_info->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-
- err:
- get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_RESTART_TFTP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
- *restart_tftp ) {
- PXENV_EXIT_t tftp_exit;
-
- DBG ( "PXENV_RESTART_TFTP " );
-
- /* Intel bug-for-bug hack */
- pxe_set_cached_filename ( restart_tftp->FileName );
-
- /* Words cannot describe the complete mismatch between the PXE
- * specification and any possible version of reality...
- */
- restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
- restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
- tftp_exit = pxenv_tftp_read_file ( restart_tftp );
- if ( tftp_exit != PXENV_EXIT_SUCCESS )
- return tftp_exit;
-
- /* Fire up the new NBP */
- restart_tftp->Status = pxe_start_nbp();
-
- /* Not sure what "SUCCESS" actually means, since we can only
- * return if the new NBP failed to boot...
- */
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_START_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
- unsigned int bus_type;
- unsigned int location;
- struct net_device *netdev;
-
- DBG ( "PXENV_START_UNDI %04x:%04x:%04x",
- start_undi->AX, start_undi->BX, start_undi->DX );
-
- /* Determine bus type and location. Use a heuristic to decide
- * whether we are PCI or ISAPnP
- */
- if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
- ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
- ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
- ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
- bus_type = BUS_TYPE_ISAPNP;
- location = start_undi->BX;
- /* Record ISAPnP read port for use by isapnp.c */
- isapnp_read_port = start_undi->DX;
- } else {
- bus_type = BUS_TYPE_PCI;
- location = start_undi->AX;
- }
-
- /* Probe for devices, etc. */
- startup();
-
- /* Look for a matching net device */
- netdev = find_netdev_by_location ( bus_type, location );
- if ( ! netdev ) {
- DBG ( " no net device found" );
- start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
- return PXENV_EXIT_FAILURE;
- }
- DBG ( " using netdev %s", netdev->name );
-
- /* Save as PXE net device */
- pxe_set_netdev ( netdev );
-
- /* Hook INT 1A */
- pxe_hook_int1a();
-
- start_undi->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_STOP_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
- DBG ( "PXENV_STOP_UNDI" );
-
- /* Unhook INT 1A */
- pxe_unhook_int1a();
-
- /* Clear PXE net device */
- pxe_set_netdev ( NULL );
-
- /* Prepare for unload */
- shutdown ( SHUTDOWN_BOOT );
-
- stop_undi->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_START_BASE
- *
- * Status: won't implement (requires major structural changes)
- */
-PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
- DBG ( "PXENV_START_BASE" );
-
- start_base->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_STOP_BASE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
- DBG ( "PXENV_STOP_BASE" );
-
- /* The only time we will be called is when the NBP is trying
- * to shut down the PXE stack. There's nothing we need to do
- * in this call.
- */
-
- stop_base->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
diff --git a/gpxe/src/interface/pxe/pxe_tftp.c b/gpxe/src/interface/pxe/pxe_tftp.c
deleted file mode 100644
index f5e76206..00000000
--- a/gpxe/src/interface/pxe/pxe_tftp.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/** @file
- *
- * PXE TFTP API
- *
- */
-
-/*
- * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/in.h>
-#include <gpxe/tftp.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/process.h>
-#include <pxe.h>
-
-/** A PXE TFTP connection */
-struct pxe_tftp_connection {
- /** Data transfer interface */
- struct xfer_interface xfer;
- /** Data buffer */
- userptr_t buffer;
- /** Size of data buffer */
- size_t size;
- /** Starting offset of data buffer */
- size_t start;
- /** File position */
- size_t offset;
- /** Maximum file position */
- size_t max_offset;
- /** Block size */
- size_t blksize;
- /** Block index */
- unsigned int blkidx;
- /** Overall return status code */
- int rc;
-};
-
-/** The PXE TFTP connection */
-static struct pxe_tftp_connection pxe_tftp = {
- .xfer = XFER_INIT ( &null_xfer_ops ),
-};
-
-/**
- * Close PXE TFTP connection
- *
- * @v rc Final status code
- */
-static void pxe_tftp_close ( int rc ) {
- xfer_nullify ( &pxe_tftp.xfer );
- xfer_close ( &pxe_tftp.xfer, rc );
- pxe_tftp.rc = rc;
-}
-
-/**
- * Receive new data
- *
- * @v xfer Data transfer interface
- * @v iobuf I/O buffer
- * @v meta Transfer metadata
- * @ret rc Return status code
- */
-static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- size_t len = iob_len ( iobuf );
- int rc = 0;
-
- /* Calculate new buffer position */
- if ( meta->whence != SEEK_CUR )
- pxe_tftp.offset = 0;
- pxe_tftp.offset += meta->offset;
-
- /* Copy data block to buffer */
- if ( len == 0 ) {
- /* No data (pure seek); treat as success */
- } else if ( pxe_tftp.offset < pxe_tftp.start ) {
- DBG ( " buffer underrun at %zx (min %zx)",
- pxe_tftp.offset, pxe_tftp.start );
- rc = -ENOBUFS;
- } else if ( ( pxe_tftp.offset + len ) >
- ( pxe_tftp.start + pxe_tftp.size ) ) {
- DBG ( " buffer overrun at %zx (max %zx)",
- ( pxe_tftp.offset + len ),
- ( pxe_tftp.start + pxe_tftp.size ) );
- rc = -ENOBUFS;
- } else {
- copy_to_user ( pxe_tftp.buffer,
- ( pxe_tftp.offset - pxe_tftp.start ),
- iobuf->data, len );
- }
-
- /* Calculate new buffer position */
- pxe_tftp.offset += len;
-
- /* Mildly ugly hack; assume that the first non-zero seek
- * indicates the block size.
- */
- if ( pxe_tftp.blksize == 0 )
- pxe_tftp.blksize = pxe_tftp.offset;
-
- /* Record maximum offset as the file size */
- if ( pxe_tftp.max_offset < pxe_tftp.offset )
- pxe_tftp.max_offset = pxe_tftp.offset;
-
- /* Terminate transfer on error */
- if ( rc != 0 )
- pxe_tftp_close ( rc );
-
- free_iob ( iobuf );
- return rc;
-}
-
-/**
- * Handle close() event
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
- int rc ) {
- pxe_tftp_close ( rc );
-}
-
-static struct xfer_interface_operations pxe_tftp_xfer_ops = {
- .close = pxe_tftp_xfer_close,
- .vredirect = xfer_vopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = pxe_tftp_xfer_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * Maximum length of a PXE TFTP URI
- *
- * The PXE TFTP API provides 128 characters for the filename; the
- * extra 128 bytes allow for the remainder of the URI.
- */
-#define PXE_TFTP_URI_LEN 256
-
-/**
- * Open PXE TFTP connection
- *
- * @v ipaddress IP address
- * @v port TFTP server port
- * @v filename File name
- * @v blksize Requested block size
- * @ret rc Return status code
- */
-static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
- const unsigned char *filename, size_t blksize ) {
- char uri_string[PXE_TFTP_URI_LEN];
- struct in_addr address;
- int rc;
-
- /* Intel bug-for-bug hack */
- pxe_set_cached_filename ( filename );
-
- /* Reset PXE TFTP connection structure */
- memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
- xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL );
- pxe_tftp.rc = -EINPROGRESS;
-
- /* Construct URI string */
- address.s_addr = ipaddress;
- if ( ! port )
- port = htons ( TFTP_PORT );
- if ( blksize < TFTP_DEFAULT_BLKSIZE )
- blksize = TFTP_DEFAULT_BLKSIZE;
- snprintf ( uri_string, sizeof ( uri_string ),
- "tftp://%s:%d%s%s?blksize=%zd",
- inet_ntoa ( address ), ntohs ( port ),
- ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
- DBG ( " %s", uri_string );
-
- /* Open PXE TFTP connection */
- if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer,
- uri_string ) ) != 0 ) {
- DBG ( " could not open (%s)\n", strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
-
-/**
- * TFTP OPEN
- *
- * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN
- * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
- * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
- * @v s_PXENV_TFTP_OPEN::FileName Name of file to open
- * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port
- * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request
- * @ret #PXENV_EXIT_SUCCESS File was opened
- * @ret #PXENV_EXIT_FAILURE File was not opened
- * @ret s_PXENV_TFTP_OPEN::Status PXE status code
- * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated blksize
- * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small
- *
- * Opens a TFTP connection for downloading a file a block at a time
- * using pxenv_tftp_read().
- *
- * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
- * routing will take place. See the relevant
- * @ref pxe_routing "implementation note" for more details.
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note According to the PXE specification version 2.1, this call
- * "opens a file for reading/writing", though how writing is to be
- * achieved without the existence of an API call %pxenv_tftp_write()
- * is not made clear.
- *
- * @note Despite the existence of the numerous statements within the
- * PXE specification of the form "...if a TFTP/MTFTP or UDP connection
- * is active...", you cannot use pxenv_tftp_open() and
- * pxenv_tftp_read() to read a file via MTFTP; only via plain old
- * TFTP. If you want to use MTFTP, use pxenv_tftp_read_file()
- * instead. Astute readers will note that, since
- * pxenv_tftp_read_file() is an atomic operation from the point of
- * view of the PXE API, it is conceptually impossible to issue any
- * other PXE API call "if an MTFTP connection is active".
- */
-PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
- int rc;
-
- DBG ( "PXENV_TFTP_OPEN" );
-
- /* Guard against callers that fail to close before re-opening */
- pxe_tftp_close ( 0 );
-
- /* Open connection */
- if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
- tftp_open->TFTPPort,
- tftp_open->FileName,
- tftp_open->PacketSize ) ) != 0 ) {
- tftp_open->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- /* Wait for OACK to arrive so that we have the block size */
- while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
- ( pxe_tftp.blksize == 0 ) ) {
- step();
- }
- tftp_open->PacketSize = pxe_tftp.blksize;
-
- /* EINPROGRESS is normal; we don't wait for the whole transfer */
- if ( rc == -EINPROGRESS )
- rc = 0;
-
- tftp_open->Status = PXENV_STATUS ( rc );
- return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
-}
-
-/**
- * TFTP CLOSE
- *
- * @v tftp_close Pointer to a struct s_PXENV_TFTP_CLOSE
- * @ret #PXENV_EXIT_SUCCESS File was closed successfully
- * @ret #PXENV_EXIT_FAILURE File was not closed
- * @ret s_PXENV_TFTP_CLOSE::Status PXE status code
- * @err None -
- *
- * Close a connection previously opened with pxenv_tftp_open(). You
- * must have previously opened a connection with pxenv_tftp_open().
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- */
-PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
- DBG ( "PXENV_TFTP_CLOSE" );
-
- pxe_tftp_close ( 0 );
- tftp_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * TFTP READ
- *
- * @v tftp_read Pointer to a struct s_PXENV_TFTP_READ
- * @v s_PXENV_TFTP_READ::Buffer Address of data buffer
- * @ret #PXENV_EXIT_SUCCESS Data was read successfully
- * @ret #PXENV_EXIT_FAILURE Data was not read
- * @ret s_PXENV_TFTP_READ::Status PXE status code
- * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number
- * @ret s_PXENV_TFTP_READ::BufferSize Length of data written into buffer
- *
- * Reads a single packet from a connection previously opened with
- * pxenv_tftp_open() into the data buffer pointed to by
- * s_PXENV_TFTP_READ::Buffer. You must have previously opened a
- * connection with pxenv_tftp_open(). The data written into
- * s_PXENV_TFTP_READ::Buffer is just the file data; the various
- * network headers have already been removed.
- *
- * The buffer must be large enough to contain a packet of the size
- * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the
- * pxenv_tftp_open() call. It is worth noting that the PXE
- * specification does @b not require the caller to fill in
- * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so
- * the PXE stack is free to ignore whatever value the caller might
- * place there and just assume that the buffer is large enough. That
- * said, it may be worth the caller always filling in
- * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that
- * mistake it for an input parameter.
- *
- * The length of the TFTP data packet will be returned via
- * s_PXENV_TFTP_READ::BufferSize. If this length is less than the
- * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to
- * pxenv_tftp_open(), this indicates that the block is the last block
- * in the file. Note that zero is a valid length for
- * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of
- * the file is a multiple of the blksize.
- *
- * The PXE specification doesn't actually state that calls to
- * pxenv_tftp_read() will return the data packets in strict sequential
- * order, though most PXE stacks will probably do so. The sequence
- * number of the packet will be returned in
- * s_PXENV_TFTP_READ::PacketNumber. The first packet in the file has
- * a sequence number of one, not zero.
- *
- * To guard against flawed PXE stacks, the caller should probably set
- * s_PXENV_TFTP_READ::PacketNumber to one less than the expected
- * returned value (i.e. set it to zero for the first call to
- * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ
- * parameter block for subsequent calls without modifying
- * s_PXENV_TFTP_READ::PacketNumber between calls). The caller should
- * also guard against potential problems caused by flawed
- * implementations returning the occasional duplicate packet, by
- * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber
- * is as expected (i.e. one greater than that returned from the
- * previous call to pxenv_tftp_read()).
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- */
-PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
- int rc;
-
- DBG ( "PXENV_TFTP_READ to %04x:%04x",
- tftp_read->Buffer.segment, tftp_read->Buffer.offset );
-
- /* Read single block into buffer */
- pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
- tftp_read->Buffer.offset );
- pxe_tftp.size = pxe_tftp.blksize;
- pxe_tftp.start = pxe_tftp.offset;
- while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
- ( pxe_tftp.offset == pxe_tftp.start ) )
- step();
- pxe_tftp.buffer = UNULL;
- tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
- tftp_read->PacketNumber = ++pxe_tftp.blkidx;
-
- /* EINPROGRESS is normal if we haven't reached EOF yet */
- if ( rc == -EINPROGRESS )
- rc = 0;
-
- tftp_read->Status = PXENV_STATUS ( rc );
- return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
-}
-
-/**
- * TFTP/MTFTP read file
- *
- * @v tftp_read_file Pointer to a struct s_PXENV_TFTP_READ_FILE
- * @v s_PXENV_TFTP_READ_FILE::FileName File name
- * @v s_PXENV_TFTP_READ_FILE::BufferSize Size of the receive buffer
- * @v s_PXENV_TFTP_READ_FILE::Buffer Address of the receive buffer
- * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress TFTP server IP address
- * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress Relay agent IP address
- * @v s_PXENV_TFTP_READ_FILE::McastIPAddress File's multicast IP address
- * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort Client multicast UDP port
- * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort Server multicast UDP port
- * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut Time to wait for first packet
- * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay MTFTP inactivity timeout
- * @ret #PXENV_EXIT_SUCCESS File downloaded successfully
- * @ret #PXENV_EXIT_FAILURE File not downloaded
- * @ret s_PXENV_TFTP_READ_FILE::Status PXE status code
- * @ret s_PXENV_TFTP_READ_FILE::BufferSize Length of downloaded file
- *
- * Downloads an entire file via either TFTP or MTFTP into the buffer
- * pointed to by s_PXENV_TFTP_READ_FILE::Buffer.
- *
- * The PXE specification does not make it clear how the caller
- * requests that MTFTP be used rather than TFTP (or vice versa). One
- * reasonable guess is that setting
- * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP
- * to be used instead of MTFTP, though it is conceivable that some PXE
- * stacks would interpret that as "use the DHCP-provided multicast IP
- * address" instead. Some PXE stacks will not implement MTFTP at all,
- * and will always use TFTP.
- *
- * It is not specified whether or not
- * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server
- * port for TFTP (rather than MTFTP) downloads. Callers should assume
- * that the only way to access a TFTP server on a non-standard port is
- * to use pxenv_tftp_open() and pxenv_tftp_read().
- *
- * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP
- * routing will take place. See the relevant
- * @ref pxe_routing "implementation note" for more details.
- *
- * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an
- * #ADDR32_t type, i.e. nominally a flat physical address. Some PXE
- * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real
- * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above
- * 1MB. This means that PXE stacks must be prepared to write to areas
- * outside base memory. Exactly how this is to be achieved is not
- * specified, though using INT 15,87 is as close to a standard method
- * as any, and should probably be used. Switching to protected-mode
- * in order to access high memory will fail if pxenv_tftp_read_file()
- * is called in V86 mode; it is reasonably to expect that a V86
- * monitor would intercept the relatively well-defined INT 15,87 if it
- * wants the PXE stack to be able to write to high memory.
- *
- * Things get even more interesting if pxenv_tftp_read_file() is
- * called in protected mode, because there is then absolutely no way
- * for the PXE stack to write to an absolute physical address. You
- * can't even get around the problem by creating a special "access
- * everything" segment in the s_PXE data structure, because the
- * #SEGDESC_t descriptors are limited to 64kB in size.
- *
- * Previous versions of the PXE specification (e.g. WfM 1.1a) provide
- * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to
- * work around this problem. The s_PXENV_TFTP_READ_FILE_PMODE
- * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into
- * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and
- * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a
- * protected-mode segment:offset address for the data buffer. This
- * API call is no longer present in version 2.1 of the PXE
- * specification.
- *
- * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer
- * is an offset relative to the caller's data segment, when
- * pxenv_tftp_read_file() is called in protected mode.
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note Microsoft's NTLDR assumes that the filename passed in via
- * s_PXENV_TFTP_READ_FILE::FileName will be stored in the "file" field
- * of the stored DHCPACK packet, whence it will be returned via any
- * subsequent calls to pxenv_get_cached_info(). Though this is
- * essentially a bug in the Intel PXE implementation (not, for once,
- * in the specification!), it is a bug that Microsoft relies upon, and
- * so we implement this bug-for-bug compatibility by overwriting the
- * filename stored DHCPACK packet with the filename passed in
- * s_PXENV_TFTP_READ_FILE::FileName.
- *
- */
-PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
- *tftp_read_file ) {
- int rc;
-
- DBG ( "PXENV_TFTP_READ_FILE to %08lx+%lx", tftp_read_file->Buffer,
- tftp_read_file->BufferSize );
-
- /* Open TFTP file */
- if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
- tftp_read_file->FileName, 0 ) ) != 0 ) {
- tftp_read_file->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- /* Read entire file */
- pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
- pxe_tftp.size = tftp_read_file->BufferSize;
- while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
- step();
- pxe_tftp.buffer = UNULL;
- tftp_read_file->BufferSize = pxe_tftp.max_offset;
-
- /* Close TFTP file */
- pxe_tftp_close ( rc );
-
- tftp_read_file->Status = PXENV_STATUS ( rc );
- return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
-}
-
-/**
- * TFTP GET FILE SIZE
- *
- * @v tftp_get_fsize Pointer to a struct s_PXENV_TFTP_GET_FSIZE
- * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress TFTP server IP address
- * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress Relay agent IP address
- * @v s_PXENV_TFTP_GET_FSIZE::FileName File name
- * @ret #PXENV_EXIT_SUCCESS File size was determined successfully
- * @ret #PXENV_EXIT_FAILURE File size was not determined
- * @ret s_PXENV_TFTP_GET_FSIZE::Status PXE status code
- * @ret s_PXENV_TFTP_GET_FSIZE::FileSize File size
- *
- * Determine the size of a file on a TFTP server. This uses the
- * "tsize" TFTP option, and so will not work with a TFTP server that
- * does not support TFTP options, or that does not support the "tsize"
- * option.
- *
- * The PXE specification states that this API call will @b not open a
- * TFTP connection for subsequent use with pxenv_tftp_read(). (This
- * is somewhat daft, since the only way to obtain the file size via
- * the "tsize" option involves issuing a TFTP open request, but that's
- * life.)
- *
- * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP
- * connection is open.
- *
- * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP
- * routing will take place. See the relevant
- * @ref pxe_routing "implementation note" for more details.
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note There is no way to specify the TFTP server port with this API
- * call. Though you can open a file using a non-standard TFTP server
- * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially,
- * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of
- * a file from a TFTP server listening on the standard TFTP port.
- * "Consistency" is not a word in Intel's vocabulary.
- */
-PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
- *tftp_get_fsize ) {
- int rc;
-
- DBG ( "PXENV_TFTP_GET_FSIZE" );
-
- /* Open TFTP file */
- if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
- tftp_get_fsize->FileName, 0 ) ) != 0 ) {
- tftp_get_fsize->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- /* Wait for initial seek to arrive, and record size */
- while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
- ( pxe_tftp.max_offset == 0 ) ) {
- step();
- }
- tftp_get_fsize->FileSize = pxe_tftp.max_offset;
-
- /* EINPROGRESS is normal; we don't wait for the whole transfer */
- if ( rc == -EINPROGRESS )
- rc = 0;
-
- /* Close TFTP file */
- pxe_tftp_close ( rc );
-
- tftp_get_fsize->Status = PXENV_STATUS ( rc );
- return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
-}
diff --git a/gpxe/src/interface/pxe/pxe_udp.c b/gpxe/src/interface/pxe/pxe_udp.c
deleted file mode 100644
index 033b1ad9..00000000
--- a/gpxe/src/interface/pxe/pxe_udp.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/** @file
- *
- * PXE UDP API
- *
- */
-
-#include <string.h>
-#include <byteswap.h>
-#include <gpxe/xfer.h>
-#include <gpxe/udp.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/process.h>
-#include <pxe.h>
-
-/*
- * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/** A PXE UDP connection */
-struct pxe_udp_connection {
- /** Data transfer interface to UDP stack */
- struct xfer_interface xfer;
- /** Local address */
- struct sockaddr_in local;
- /** Current PXENV_UDP_READ parameter block */
- struct s_PXENV_UDP_READ *pxenv_udp_read;
-};
-
-/**
- * Receive PXE UDP data
- *
- * @v xfer Data transfer interface
- * @v iobuf I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- *
- * Receives a packet as part of the current pxenv_udp_read()
- * operation.
- */
-static int pxe_udp_deliver_iob ( struct xfer_interface *xfer,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct pxe_udp_connection *pxe_udp =
- container_of ( xfer, struct pxe_udp_connection, xfer );
- struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
- struct sockaddr_in *sin_src;
- struct sockaddr_in *sin_dest;
- userptr_t buffer;
- size_t len;
- int rc = 0;
-
- if ( ! pxenv_udp_read ) {
- DBG ( "PXE discarded UDP packet\n" );
- rc = -ENOBUFS;
- goto done;
- }
-
- /* Copy packet to buffer and record length */
- buffer = real_to_user ( pxenv_udp_read->buffer.segment,
- pxenv_udp_read->buffer.offset );
- len = iob_len ( iobuf );
- if ( len > pxenv_udp_read->buffer_size )
- len = pxenv_udp_read->buffer_size;
- copy_to_user ( buffer, 0, iobuf->data, len );
- pxenv_udp_read->buffer_size = len;
-
- /* Fill in source/dest information */
- assert ( meta );
- sin_src = ( struct sockaddr_in * ) meta->src;
- assert ( sin_src );
- assert ( sin_src->sin_family == AF_INET );
- pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
- pxenv_udp_read->s_port = sin_src->sin_port;
- sin_dest = ( struct sockaddr_in * ) meta->dest;
- assert ( sin_dest );
- assert ( sin_dest->sin_family == AF_INET );
- pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
- pxenv_udp_read->d_port = sin_dest->sin_port;
-
- /* Mark as received */
- pxe_udp->pxenv_udp_read = NULL;
-
- done:
- free_iob ( iobuf );
- return rc;
-}
-
-/** PXE UDP data transfer interface operations */
-static struct xfer_interface_operations pxe_udp_xfer_operations = {
- .close = ignore_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = pxe_udp_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/** The PXE UDP connection */
-static struct pxe_udp_connection pxe_udp = {
- .xfer = XFER_INIT ( &pxe_udp_xfer_operations ),
- .local = {
- .sin_family = AF_INET,
- },
-};
-
-/**
- * UDP OPEN
- *
- * @v pxenv_udp_open Pointer to a struct s_PXENV_UDP_OPEN
- * @v s_PXENV_UDP_OPEN::src_ip IP address of this station, or 0.0.0.0
- * @ret #PXENV_EXIT_SUCCESS Always
- * @ret s_PXENV_UDP_OPEN::Status PXE status code
- * @err #PXENV_STATUS_UDP_OPEN UDP connection already open
- * @err #PXENV_STATUS_OUT_OF_RESOURCES Could not open connection
- *
- * Prepares the PXE stack for communication using pxenv_udp_write()
- * and pxenv_udp_read().
- *
- * The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be
- * recorded and used as the local station's IP address for all further
- * communication, including communication by means other than
- * pxenv_udp_write() and pxenv_udp_read(). (If
- * s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address
- * will remain unchanged.)
- *
- * You can only have one open UDP connection at a time. This is not a
- * meaningful restriction, since pxenv_udp_write() and
- * pxenv_udp_read() allow you to specify arbitrary local and remote
- * ports and an arbitrary remote address for each packet. According
- * to the PXE specifiation, you cannot have a UDP connection open at
- * the same time as a TFTP connection; this restriction does not apply
- * to Etherboot.
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note The PXE specification does not make it clear whether the IP
- * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only
- * for this UDP connection, or retained for all future communication.
- * The latter seems more consistent with typical PXE stack behaviour.
- *
- * @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip
- * parameter.
- *
- */
-PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
- int rc;
-
- DBG ( "PXENV_UDP_OPEN" );
-
- /* Record source IP address */
- pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
- DBG ( " %s", inet_ntoa ( pxe_udp.local.sin_addr ) );
-
- /* Open promiscuous UDP connection */
- xfer_close ( &pxe_udp.xfer, 0 );
- if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
- pxenv_udp_open->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * UDP CLOSE
- *
- * @v pxenv_udp_close Pointer to a struct s_PXENV_UDP_CLOSE
- * @ret #PXENV_EXIT_SUCCESS Always
- * @ret s_PXENV_UDP_CLOSE::Status PXE status code
- * @err None -
- *
- * Closes a UDP connection opened with pxenv_udp_open().
- *
- * You can only have one open UDP connection at a time. You cannot
- * have a UDP connection open at the same time as a TFTP connection.
- * You cannot use pxenv_udp_close() to close a TFTP connection; use
- * pxenv_tftp_close() instead.
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- */
-PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
- DBG ( "PXENV_UDP_CLOSE" );
-
- /* Close UDP connection */
- xfer_close ( &pxe_udp.xfer, 0 );
-
- pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * UDP WRITE
- *
- * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE
- * @v s_PXENV_UDP_WRITE::ip Destination IP address
- * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0
- * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0
- * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port
- * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload
- * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload
- * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully
- * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted
- * @ret s_PXENV_UDP_WRITE::Status PXE status code
- * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
- * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
- *
- * Transmits a single UDP packet. A valid IP and UDP header will be
- * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
- * should not contain precomputed IP and UDP headers, nor should it
- * contain space allocated for these headers. The first byte of the
- * buffer will be transmitted as the first byte following the UDP
- * header.
- *
- * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
- * place. See the relevant @ref pxe_routing "implementation note" for
- * more details.
- *
- * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
- *
- * You must have opened a UDP connection with pxenv_udp_open() before
- * calling pxenv_udp_write().
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
- * parameter.
- *
- */
-PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
- struct sockaddr_in dest;
- struct xfer_metadata meta = {
- .src = ( struct sockaddr * ) &pxe_udp.local,
- .dest = ( struct sockaddr * ) &dest,
- .netdev = pxe_netdev,
- };
- size_t len;
- struct io_buffer *iobuf;
- userptr_t buffer;
- int rc;
-
- DBG ( "PXENV_UDP_WRITE" );
-
- /* Construct destination socket address */
- memset ( &dest, 0, sizeof ( dest ) );
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = pxenv_udp_write->ip;
- dest.sin_port = pxenv_udp_write->dst_port;
-
- /* Set local (source) port. PXE spec says source port is 2069
- * if not specified. Really, this ought to be set at UDP open
- * time but hey, we didn't design this API.
- */
- pxe_udp.local.sin_port = pxenv_udp_write->src_port;
- if ( ! pxe_udp.local.sin_port )
- pxe_udp.local.sin_port = htons ( 2069 );
-
- /* FIXME: we ignore the gateway specified, since we're
- * confident of being able to do our own routing. We should
- * probably allow for multiple gateways.
- */
-
- /* Allocate and fill data buffer */
- len = pxenv_udp_write->buffer_size;
- iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
- if ( ! iobuf ) {
- pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
- }
- buffer = real_to_user ( pxenv_udp_write->buffer.segment,
- pxenv_udp_write->buffer.offset );
- copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );
-
- DBG ( " %04x:%04x+%x %d->%s:%d", pxenv_udp_write->buffer.segment,
- pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
- ntohs ( pxenv_udp_write->src_port ),
- inet_ntoa ( dest.sin_addr ),
- ntohs ( pxenv_udp_write->dst_port ) );
-
- /* Transmit packet */
- if ( ( rc = xfer_deliver_iob_meta ( &pxe_udp.xfer, iobuf,
- &meta ) ) != 0 ) {
- pxenv_udp_write->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/**
- * UDP READ
- *
- * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ
- * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0
- * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0
- * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer
- * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer
- * @ret #PXENV_EXIT_SUCCESS A packet has been received
- * @ret #PXENV_EXIT_FAILURE No packet has been received
- * @ret s_PXENV_UDP_READ::Status PXE status code
- * @ret s_PXENV_UDP_READ::src_ip Source IP address
- * @ret s_PXENV_UDP_READ::dest_ip Destination IP address
- * @ret s_PXENV_UDP_READ::s_port Source UDP port
- * @ret s_PXENV_UDP_READ::d_port Destination UDP port
- * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload
- * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
- * @err #PXENV_STATUS_FAILURE No packet was ready to read
- *
- * Receive a single UDP packet. This is a non-blocking call; if no
- * packet is ready to read, the call will return instantly with
- * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
- *
- * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
- * any IP address will be accepted and may be returned to the caller.
- *
- * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
- * port will be accepted and may be returned to the caller.
- *
- * You must have opened a UDP connection with pxenv_udp_open() before
- * calling pxenv_udp_read().
- *
- * On x86, you must set the s_PXE::StatusCallout field to a nonzero
- * value before calling this function in protected mode. You cannot
- * call this function with a 32-bit stack segment. (See the relevant
- * @ref pxe_x86_pmode16 "implementation note" for more details.)
- *
- * @note The PXE specification (version 2.1) does not state that we
- * should fill in s_PXENV_UDP_READ::dest_ip and
- * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
- * expects us to do so, and will fail if we don't.
- *
- */
-PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
- struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
- struct in_addr dest_ip;
- uint16_t d_port_wanted = pxenv_udp_read->d_port;
- uint16_t d_port;
-
- DBG ( "PXENV_UDP_READ" );
-
- /* Try receiving a packet */
- pxe_udp.pxenv_udp_read = pxenv_udp_read;
- step();
- if ( pxe_udp.pxenv_udp_read ) {
- /* No packet received */
- pxe_udp.pxenv_udp_read = NULL;
- goto no_packet;
- }
- dest_ip.s_addr = pxenv_udp_read->dest_ip;
- d_port = pxenv_udp_read->d_port;
-
- /* Filter on destination address and/or port */
- if ( dest_ip_wanted.s_addr &&
- ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
- DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
- DBG ( " (wanted %s)", inet_ntoa ( dest_ip_wanted ) );
- goto no_packet;
- }
- if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
- DBG ( " wrong port %d ", htons ( d_port ) );
- DBG ( " (wanted %d)", htons ( d_port_wanted ) );
- goto no_packet;
- }
-
- DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
- pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
- inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
- DBG ( "%d<-%s:%d", ntohs ( pxenv_udp_read->s_port ),
- inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
- ntohs ( pxenv_udp_read->d_port ) );
-
- pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-
- no_packet:
- pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
- return PXENV_EXIT_FAILURE;
-}
diff --git a/gpxe/src/interface/pxe/pxe_undi.c b/gpxe/src/interface/pxe/pxe_undi.c
deleted file mode 100644
index 5d06f2d8..00000000
--- a/gpxe/src/interface/pxe/pxe_undi.c
+++ /dev/null
@@ -1,682 +0,0 @@
-/** @file
- *
- * PXE UNDI API
- *
- */
-
-/*
- * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <byteswap.h>
-#include <basemem_packet.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/device.h>
-#include <gpxe/pci.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/ip.h>
-#include <gpxe/arp.h>
-#include <gpxe/rarp.h>
-#include "pxe.h"
-
-/**
- * Count of outstanding transmitted packets
- *
- * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
- * decremented each time that PXENV_UNDI_ISR is called with the TX
- * queue empty, stopping when the count reaches zero. This allows us
- * to provide a pessimistic approximation of TX completion events to
- * the PXE NBP simply by monitoring the netdev's TX queue.
- */
-static int undi_tx_count = 0;
-
-struct net_device *pxe_netdev = NULL;
-
-/**
- * Set network device as current PXE network device
- *
- * @v netdev Network device, or NULL
- */
-void pxe_set_netdev ( struct net_device *netdev ) {
- if ( pxe_netdev )
- netdev_put ( pxe_netdev );
- pxe_netdev = NULL;
- if ( netdev )
- pxe_netdev = netdev_get ( netdev );
-}
-
-/**
- * Open PXE network device
- *
- * @ret rc Return status code
- */
-static int pxe_netdev_open ( void ) {
- int rc;
-
- if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
- return rc;
-
- netdev_irq ( pxe_netdev, 1 );
- return 0;
-}
-
-/**
- * Close PXE network device
- *
- */
-static void pxe_netdev_close ( void ) {
- netdev_irq ( pxe_netdev, 0 );
- netdev_close ( pxe_netdev );
- undi_tx_count = 0;
-}
-
-/* PXENV_UNDI_STARTUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
- DBG ( "PXENV_UNDI_STARTUP" );
-
- undi_startup->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLEANUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
- DBG ( "PXENV_UNDI_CLEANUP" );
-
- pxe_netdev_close();
-
- undi_cleanup->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_INITIALIZE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
- *undi_initialize ) {
- DBG ( "PXENV_UNDI_INITIALIZE" );
-
- undi_initialize->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_RESET_ADAPTER
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
- *undi_reset_adapter ) {
- int rc;
-
- DBG ( "PXENV_UNDI_RESET_ADAPTER" );
-
- pxe_netdev_close();
- if ( ( rc = pxe_netdev_open() ) != 0 ) {
- undi_reset_adapter->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SHUTDOWN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
- *undi_shutdown ) {
- DBG ( "PXENV_UNDI_SHUTDOWN" );
-
- pxe_netdev_close();
-
- undi_shutdown->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
- int rc;
-
- DBG ( "PXENV_UNDI_OPEN" );
-
- if ( ( rc = pxe_netdev_open() ) != 0 ) {
- undi_open->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- undi_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
- DBG ( "PXENV_UNDI_CLOSE" );
-
- pxe_netdev_close();
-
- undi_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_TRANSMIT
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
- *undi_transmit ) {
- struct s_PXENV_UNDI_TBD tbd;
- struct DataBlk *datablk;
- struct io_buffer *iobuf;
- struct net_protocol *net_protocol;
- char destaddr[MAX_LL_ADDR_LEN];
- const void *ll_dest;
- size_t ll_hlen = pxe_netdev->ll_protocol->ll_header_len;
- size_t len;
- unsigned int i;
- int rc;
-
- DBG ( "PXENV_UNDI_TRANSMIT" );
-
- /* Identify network-layer protocol */
- switch ( undi_transmit->Protocol ) {
- case P_IP: net_protocol = &ipv4_protocol; break;
- case P_ARP: net_protocol = &arp_protocol; break;
- case P_RARP: net_protocol = &rarp_protocol; break;
- case P_UNKNOWN:
- net_protocol = NULL;
- ll_hlen = 0;
- break;
- default:
- undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
- return PXENV_EXIT_FAILURE;
- }
- DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
-
- /* Calculate total packet length */
- copy_from_real ( &tbd, undi_transmit->TBD.segment,
- undi_transmit->TBD.offset, sizeof ( tbd ) );
- len = tbd.ImmedLength;
- DBG ( " %d", tbd.ImmedLength );
- for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
- datablk = &tbd.DataBlock[i];
- len += datablk->TDDataLen;
- DBG ( "+%d", datablk->TDDataLen );
- }
-
- /* Allocate and fill I/O buffer */
- iobuf = alloc_iob ( ll_hlen + len );
- if ( ! iobuf ) {
- undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
- }
- iob_reserve ( iobuf, ll_hlen );
- copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
- tbd.Xmit.offset, tbd.ImmedLength );
- for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
- datablk = &tbd.DataBlock[i];
- copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
- datablk->TDDataPtr.segment,
- datablk->TDDataPtr.offset,
- datablk->TDDataLen );
- }
-
- /* Add link-layer header, if required to do so */
- if ( net_protocol != NULL ) {
-
- /* Calculate destination address */
- if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
- copy_from_real ( destaddr,
- undi_transmit->DestAddr.segment,
- undi_transmit->DestAddr.offset,
- pxe_netdev->ll_protocol->ll_addr_len );
- ll_dest = destaddr;
- } else {
- DBG ( " BCAST" );
- ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
- }
-
- /* Add link-layer header */
- if ( ( rc = pxe_netdev->ll_protocol->push ( iobuf, pxe_netdev,
- net_protocol,
- ll_dest )) != 0 ){
- free_iob ( iobuf );
- undi_transmit->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
- }
-
- /* Transmit packet */
- if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
- undi_transmit->Status = PXENV_STATUS ( rc );
- return PXENV_EXIT_FAILURE;
- }
-
- /* Flag transmission as in-progress */
- undi_tx_count++;
-
- undi_transmit->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SET_MCAST_ADDRESS
- *
- * Status: stub (no PXE multicast support)
- */
-PXENV_EXIT_t
-pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
- *undi_set_mcast_address ) {
- DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
-
- undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_SET_STATION_ADDRESS
- *
- * Status: working
- */
-PXENV_EXIT_t
-pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
- *undi_set_station_address ) {
-
- DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
-
- /* If adapter is open, the change will have no effect; return
- * an error
- */
- if ( pxe_netdev->state & NETDEV_OPEN ) {
- undi_set_station_address->Status =
- PXENV_STATUS_UNDI_INVALID_STATE;
- return PXENV_EXIT_FAILURE;
- }
-
- /* Update MAC address */
- memcpy ( pxe_netdev->ll_addr,
- &undi_set_station_address->StationAddress,
- pxe_netdev->ll_protocol->ll_addr_len );
-
- undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SET_PACKET_FILTER
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t
-pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
- *undi_set_packet_filter ) {
- DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
-
- undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_GET_INFORMATION
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
- *undi_get_information ) {
- struct device *dev = pxe_netdev->dev;
- struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
-
- DBG ( "PXENV_UNDI_GET_INFORMATION" );
-
- undi_get_information->BaseIo = dev->desc.ioaddr;
- undi_get_information->IntNumber = dev->desc.irq;
- /* Cheat: assume all cards can cope with this */
- undi_get_information->MaxTranUnit = ETH_MAX_MTU;
- undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
- undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
- /* Cheat: assume card is always configured with its permanent
- * node address. This is a valid assumption within Etherboot
- * at the time of writing.
- */
- memcpy ( &undi_get_information->CurrentNodeAddress,
- pxe_netdev->ll_addr,
- sizeof ( undi_get_information->CurrentNodeAddress ) );
- memcpy ( &undi_get_information->PermNodeAddress,
- pxe_netdev->ll_addr,
- sizeof ( undi_get_information->PermNodeAddress ) );
- undi_get_information->ROMAddress = 0;
- /* nic.rom_info->rom_segment; */
- /* We only provide the ability to receive or transmit a single
- * packet at a time. This is a bootloader, not an OS.
- */
- undi_get_information->RxBufCt = 1;
- undi_get_information->TxBufCt = 1;
-
- undi_get_information->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_STATISTICS
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
- *undi_get_statistics ) {
- DBG ( "PXENV_UNDI_GET_STATISTICS" );
-
- undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok;
- undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok;
- undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err;
- undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err;
-
- undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLEAR_STATISTICS
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
- *undi_clear_statistics ) {
- DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
-
- memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
-
- undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_INITIATE_DIAGS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
- *undi_initiate_diags ) {
- DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
-
- undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_FORCE_INTERRUPT
- *
- * Status: won't implement (would require driver API changes for no
- * perceptible benefit)
- */
-PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
- *undi_force_interrupt ) {
- DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
-
- undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_GET_MCAST_ADDRESS
- *
- * Status: stub (no PXE multicast support)
- */
-PXENV_EXIT_t
-pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
- *undi_get_mcast_address ) {
- DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
-
- undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_GET_NIC_TYPE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
- *undi_get_nic_type ) {
- struct device *dev = pxe_netdev->dev;
-
- DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
-
- memset ( &undi_get_nic_type->info, 0,
- sizeof ( undi_get_nic_type->info ) );
-
- switch ( dev->desc.bus_type ) {
- case BUS_TYPE_PCI: {
- struct pci_nic_info *info = &undi_get_nic_type->info.pci;
-
- undi_get_nic_type->NicType = PCI_NIC;
- info->Vendor_ID = dev->desc.vendor;
- info->Dev_ID = dev->desc.device;
- info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
- info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
- info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
- info->BusDevFunc = dev->desc.location;
- /* Cheat: remaining fields are probably unnecessary,
- * and would require adding extra code to pci.c.
- */
- undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
- undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
- break; }
- case BUS_TYPE_ISAPNP: {
- struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
-
- undi_get_nic_type->NicType = PnP_NIC;
- info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
- dev->desc.device );
- info->CardSelNum = dev->desc.location;
- /* Cheat: remaining fields are probably unnecessary,
- * and would require adding extra code to isapnp.c.
- */
- break; }
- default:
- undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
- return PXENV_EXIT_FAILURE;
- }
-
- undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_IFACE_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
- *undi_get_iface_info ) {
- DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
-
- /* Just hand back some info, doesn't really matter what it is.
- * Most PXE stacks seem to take this approach.
- */
- snprintf ( ( char * ) undi_get_iface_info->IfaceType,
- sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
- undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
- undi_get_iface_info->ServiceFlags = 0;
- memset ( undi_get_iface_info->Reserved, 0,
- sizeof(undi_get_iface_info->Reserved) );
-
- undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_STATE
- *
- * Status: impossible
- */
-PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
- *undi_get_state ) {
- DBG ( "PXENV_UNDI_GET_STATE" );
-
- undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-};
-
-/* PXENV_UNDI_ISR
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
- struct io_buffer *iobuf;
- size_t len;
- struct ll_protocol *ll_protocol;
- const void *ll_source;
- uint16_t net_proto;
- size_t ll_hlen;
- struct net_protocol *net_protocol;
- unsigned int prottype;
- int rc;
-
- DBG ( "PXENV_UNDI_ISR" );
-
- /* Just in case some idiot actually looks at these fields when
- * we weren't meant to fill them in...
- */
- undi_isr->BufferLength = 0;
- undi_isr->FrameLength = 0;
- undi_isr->FrameHeaderLength = 0;
- undi_isr->ProtType = 0;
- undi_isr->PktType = 0;
-
- switch ( undi_isr->FuncFlag ) {
- case PXENV_UNDI_ISR_IN_START :
- DBG ( " START" );
-
- /* Call poll(). This should acknowledge the device
- * interrupt and queue up any received packet.
- */
- netdev_poll ( pxe_netdev );
-
- /* Disable interrupts to avoid interrupt storm */
- netdev_irq ( pxe_netdev, 0 );
-
- /* Always say it was ours for the sake of simplicity */
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
- break;
- case PXENV_UNDI_ISR_IN_PROCESS :
- DBG ( " PROCESS" );
- /* Fall through */
- case PXENV_UNDI_ISR_IN_GET_NEXT :
- DBG ( " GET_NEXT" );
-
- /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
- * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
- * they just sit in a tight polling loop merrily
- * violating the PXE spec with repeated calls to
- * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to
- * cope with these out-of-spec clients.
- */
- netdev_poll ( pxe_netdev );
-
- /* If we have not yet marked a TX as complete, and the
- * netdev TX queue is empty, report the TX completion.
- */
- if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
- DBG ( " TXC" );
- undi_tx_count--;
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
- break;
- }
-
- /* Remove first packet from netdev RX queue */
- iobuf = netdev_rx_dequeue ( pxe_netdev );
- if ( ! iobuf ) {
- DBG ( " DONE" );
- /* No more packets remaining */
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
- /* Re-enable interrupts */
- netdev_irq ( pxe_netdev, 1 );
- break;
- }
-
- /* Copy packet to base memory buffer */
- len = iob_len ( iobuf );
- DBG ( " RX %zd", len );
- if ( len > sizeof ( basemem_packet ) ) {
- /* Should never happen */
- len = sizeof ( basemem_packet );
- }
- memcpy ( basemem_packet, iobuf->data, len );
-
- /* Strip link-layer header */
- ll_protocol = pxe_netdev->ll_protocol;
- if ( ( rc = ll_protocol->pull ( iobuf, pxe_netdev,
- &net_proto,
- &ll_source ) ) != 0 ) {
- /* Assume unknown net_proto and no ll_source */
- net_proto = 0;
- ll_source = NULL;
- }
- ll_hlen = ( len - iob_len ( iobuf ) );
-
- /* Determine network-layer protocol */
- switch ( net_proto ) {
- case htons ( ETH_P_IP ):
- net_protocol = &ipv4_protocol;
- prottype = P_IP;
- break;
- case htons ( ETH_P_ARP ):
- net_protocol = &arp_protocol;
- prottype = P_ARP;
- break;
- case htons ( ETH_P_RARP ):
- net_protocol = &rarp_protocol;
- prottype = P_RARP;
- break;
- default:
- net_protocol = NULL;
- prottype = P_UNKNOWN;
- break;
- }
- DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
-
- /* Fill in UNDI_ISR structure */
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
- undi_isr->BufferLength = len;
- undi_isr->FrameLength = len;
- undi_isr->FrameHeaderLength = ll_hlen;
- undi_isr->Frame.segment = rm_ds;
- undi_isr->Frame.offset = __from_data16 ( basemem_packet );
- undi_isr->ProtType = prottype;
- undi_isr->PktType = XMT_DESTADDR;
-
- /* Free packet */
- free_iob ( iobuf );
- break;
- default :
- DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
-
- /* Should never happen */
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
- undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
- return PXENV_EXIT_FAILURE;
- }
-
- undi_isr->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
diff --git a/gpxe/src/interface/smbios/smbios.c b/gpxe/src/interface/smbios/smbios.c
new file mode 100644
index 00000000..8207c1fa
--- /dev/null
+++ b/gpxe/src/interface/smbios/smbios.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/smbios.h>
+
+/** @file
+ *
+ * System Management BIOS
+ *
+ */
+
+/** SMBIOS entry point descriptor */
+static struct smbios smbios = {
+ .address = UNULL,
+};
+
+/**
+ * Find SMBIOS strings terminator
+ *
+ * @v offset Offset to start of strings
+ * @ret offset Offset to strings terminator, or 0 if not found
+ */
+static size_t find_strings_terminator ( size_t offset ) {
+ size_t max_offset = ( smbios.len - 2 );
+ uint16_t nulnul;
+
+ for ( ; offset <= max_offset ; offset++ ) {
+ copy_from_user ( &nulnul, smbios.address, offset, 2 );
+ if ( nulnul == 0 )
+ return ( offset + 1 );
+ }
+ return 0;
+}
+
+/**
+ * Find specific structure type within SMBIOS
+ *
+ * @v type Structure type to search for
+ * @v structure SMBIOS structure descriptor to fill in
+ * @ret rc Return status code
+ */
+int find_smbios_structure ( unsigned int type,
+ struct smbios_structure *structure ) {
+ unsigned int count = 0;
+ size_t offset = 0;
+ size_t strings_offset;
+ size_t terminator_offset;
+ int rc;
+
+ /* Find SMBIOS */
+ if ( ( smbios.address == UNULL ) &&
+ ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
+ return rc;
+ assert ( smbios.address != UNULL );
+
+ /* Scan through list of structures */
+ while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len )
+ && ( count < smbios.count ) ) {
+
+ /* Read next SMBIOS structure header */
+ copy_from_user ( &structure->header, smbios.address, offset,
+ sizeof ( structure->header ) );
+
+ /* Determine start and extent of strings block */
+ strings_offset = ( offset + structure->header.len );
+ if ( strings_offset > smbios.len ) {
+ DBG ( "SMBIOS structure at offset %zx with length "
+ "%x extends beyond SMBIOS\n", offset,
+ structure->header.len );
+ return -ENOENT;
+ }
+ terminator_offset = find_strings_terminator ( strings_offset );
+ if ( ! terminator_offset ) {
+ DBG ( "SMBIOS structure at offset %zx has "
+ "unterminated strings section\n", offset );
+ return -ENOENT;
+ }
+ structure->strings_len = ( terminator_offset - strings_offset);
+
+ DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
+ "strings length %zx\n", offset, structure->header.type,
+ structure->header.len, structure->strings_len );
+
+ /* If this is the structure we want, return */
+ if ( structure->header.type == type ) {
+ structure->offset = offset;
+ return 0;
+ }
+
+ /* Move to next SMBIOS structure */
+ offset = ( terminator_offset + 1 );
+ count++;
+ }
+
+ DBG ( "SMBIOS structure type %d not found\n", type );
+ return -ENOENT;
+}
+
+/**
+ * Copy SMBIOS structure
+ *
+ * @v structure SMBIOS structure descriptor
+ * @v data Buffer to hold SMBIOS structure
+ * @v len Length of buffer
+ * @ret rc Return status code
+ */
+int read_smbios_structure ( struct smbios_structure *structure,
+ void *data, size_t len ) {
+
+ assert ( smbios.address != UNULL );
+
+ if ( len > structure->header.len )
+ len = structure->header.len;
+ copy_from_user ( data, smbios.address, structure->offset, len );
+ return 0;
+}
+
+/**
+ * Find indexed string within SMBIOS structure
+ *
+ * @v structure SMBIOS structure descriptor
+ * @v index String index
+ * @v data Buffer for string
+ * @v len Length of string buffer
+ * @ret rc Length of string, or negative error
+ */
+int read_smbios_string ( struct smbios_structure *structure,
+ unsigned int index, void *data, size_t len ) {
+ size_t strings_start = ( structure->offset + structure->header.len );
+ size_t strings_end = ( strings_start + structure->strings_len );
+ size_t offset;
+ size_t string_len;
+
+ assert ( smbios.address != UNULL );
+
+ /* String numbers start at 1 (0 is used to indicate "no string") */
+ if ( ! index )
+ return -ENOENT;
+
+ for ( offset = strings_start ; offset < strings_end ;
+ offset += ( string_len + 1 ) ) {
+ /* Get string length. This is known safe, since the
+ * smbios_strings struct is constructed so as to
+ * always end on a string boundary.
+ */
+ string_len = strlen_user ( smbios.address, offset );
+ if ( --index == 0 ) {
+ /* Copy string, truncating as necessary. */
+ if ( len > string_len )
+ len = string_len;
+ copy_from_user ( data, smbios.address, offset, len );
+ return string_len;
+ }
+ }
+
+ DBG ( "SMBIOS string index %d not found\n", index );
+ return -ENOENT;
+}
diff --git a/gpxe/src/interface/smbios/smbios_settings.c b/gpxe/src/interface/smbios/smbios_settings.c
new file mode 100644
index 00000000..61c2d919
--- /dev/null
+++ b/gpxe/src/interface/smbios/smbios_settings.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/settings.h>
+#include <gpxe/init.h>
+#include <gpxe/uuid.h>
+#include <gpxe/smbios.h>
+
+/** SMBIOS settings tag magic number */
+#define SMBIOS_TAG_MAGIC 0x5B /* "SmBios" */
+
+/**
+ * Construct SMBIOS empty tag
+ *
+ * @ret tag SMBIOS setting tag
+ */
+#define SMBIOS_EMPTY_TAG ( SMBIOS_TAG_MAGIC << 24 )
+
+/**
+ * Construct SMBIOS raw-data tag
+ *
+ * @v _type SMBIOS structure type number
+ * @v _structure SMBIOS structure data type
+ * @v _field Field within SMBIOS structure data type
+ * @ret tag SMBIOS setting tag
+ */
+#define SMBIOS_RAW_TAG( _type, _structure, _field ) \
+ ( ( SMBIOS_TAG_MAGIC << 24 ) | \
+ ( (_type) << 16 ) | \
+ ( offsetof ( _structure, _field ) << 8 ) | \
+ ( sizeof ( ( ( _structure * ) 0 )->_field ) ) )
+
+/**
+ * Construct SMBIOS string tag
+ *
+ * @v _type SMBIOS structure type number
+ * @v _structure SMBIOS structure data type
+ * @v _field Field within SMBIOS structure data type
+ * @ret tag SMBIOS setting tag
+ */
+#define SMBIOS_STRING_TAG( _type, _structure, _field ) \
+ ( ( SMBIOS_TAG_MAGIC << 24 ) | \
+ ( (_type) << 16 ) | \
+ ( offsetof ( _structure, _field ) << 8 ) )
+
+/**
+ * Store value of SMBIOS setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int smbios_store ( struct settings *settings __unused,
+ struct setting *setting __unused,
+ const void *data __unused, size_t len __unused ) {
+ /* Cannot write data into SMBIOS */
+ return -ENOTSUP;
+}
+
+/**
+ * Fetch value of SMBIOS setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int smbios_fetch ( struct settings *settings __unused,
+ struct setting *setting,
+ void *data, size_t len ) {
+ struct smbios_structure structure;
+ unsigned int tag_magic;
+ unsigned int tag_type;
+ unsigned int tag_offset;
+ unsigned int tag_len;
+ int rc;
+
+ /* Split tag into type, offset and length */
+ tag_magic = ( setting->tag >> 24 );
+ tag_type = ( ( setting->tag >> 16 ) & 0xff );
+ tag_offset = ( ( setting->tag >> 8 ) & 0xff );
+ tag_len = ( setting->tag & 0xff );
+ if ( tag_magic != SMBIOS_TAG_MAGIC )
+ return -ENOENT;
+
+ /* Find SMBIOS structure */
+ if ( ( rc = find_smbios_structure ( tag_type, &structure ) ) != 0 )
+ return rc;
+
+ {
+ uint8_t buf[structure.header.len];
+
+ /* Read SMBIOS structure */
+ if ( ( rc = read_smbios_structure ( &structure, buf,
+ sizeof ( buf ) ) ) != 0 )
+ return rc;
+
+ if ( tag_len == 0 ) {
+ /* String */
+ return read_smbios_string ( &structure,
+ buf[tag_offset],
+ data, len );
+ } else {
+ /* Raw data */
+ if ( len > tag_len )
+ len = tag_len;
+ memcpy ( data, &buf[tag_offset], len );
+ return tag_len;
+ }
+ }
+}
+
+/** SMBIOS settings operations */
+static struct settings_operations smbios_settings_operations = {
+ .store = smbios_store,
+ .fetch = smbios_fetch,
+};
+
+/** SMBIOS settings */
+static struct settings smbios_settings = {
+ .refcnt = NULL,
+ .name = "smbios",
+ .tag_magic = SMBIOS_EMPTY_TAG,
+ .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ),
+ .children = LIST_HEAD_INIT ( smbios_settings.children ),
+ .op = &smbios_settings_operations,
+};
+
+/** Initialise SMBIOS settings */
+static void smbios_init ( void ) {
+ int rc;
+
+ if ( ( rc = register_settings ( &smbios_settings, NULL ) ) != 0 ) {
+ DBG ( "SMBIOS could not register settings: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** SMBIOS settings initialiser */
+struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = smbios_init,
+};
+
+/** UUID setting obtained via SMBIOS */
+struct setting uuid_setting __setting = {
+ .name = "uuid",
+ .description = "UUID",
+ .tag = SMBIOS_RAW_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION,
+ struct smbios_system_information, uuid ),
+ .type = &setting_type_uuid,
+};
+
+/** Other SMBIOS named settings */
+struct setting smbios_named_settings[] __setting = {
+ {
+ .name = "manufacturer",
+ .description = "Manufacturer",
+ .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION,
+ struct smbios_system_information,
+ manufacturer ),
+ .type = &setting_type_string,
+ },
+ {
+ .name = "product",
+ .description = "Product name",
+ .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION,
+ struct smbios_system_information,
+ product ),
+ .type = &setting_type_string,
+ },
+ {
+ .name = "serial",
+ .description = "Serial number",
+ .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION,
+ struct smbios_system_information,
+ serial ),
+ .type = &setting_type_string,
+ },
+};