summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LOG9
-rw-r--r--VERSION2
-rw-r--r--contrib/bin2intelhex/bin2intelhex.c4
-rw-r--r--contrib/initrd/ChangeLog4
-rw-r--r--src/Config14
-rw-r--r--src/Makefile6
-rw-r--r--src/Makefile.housekeeping29
-rw-r--r--src/arch/armnommu/Config6
-rw-r--r--src/arch/armnommu/core/serial.c4
-rw-r--r--src/arch/armnommu/drivers/net/p2001_eth.c365
-rw-r--r--src/arch/armnommu/include/hardware.h17
-rw-r--r--src/arch/armnommu/include/lxt971a.h30
-rw-r--r--src/arch/armnommu/include/stdint.h12
-rw-r--r--src/arch/e1/include/stdint.h12
-rw-r--r--src/arch/i386/Config2
-rw-r--r--src/arch/i386/Makefile2
-rw-r--r--src/arch/i386/core/freebsd_loader.c6
-rw-r--r--src/arch/i386/core/hooks.c10
-rw-r--r--src/arch/i386/firmware/pcbios/basemem.c8
-rw-r--r--src/arch/i386/image/nbi.c164
-rw-r--r--src/arch/i386/image/pxe_image.c (renamed from src/arch/i386/core/pxe_loader.c)1
-rw-r--r--src/arch/i386/include/basemem.h2
-rw-r--r--src/arch/i386/include/bochs.h15
-rw-r--r--src/arch/i386/include/hooks.h2
-rw-r--r--src/arch/i386/include/librm.h4
-rw-r--r--src/arch/i386/include/pxe_addr.h17
-rw-r--r--src/arch/i386/include/pxe_callbacks.h10
-rw-r--r--src/arch/i386/include/pxe_types.h35
-rw-r--r--src/arch/i386/include/realmode.h5
-rw-r--r--src/arch/i386/include/registers.h103
-rw-r--r--src/arch/i386/include/stdint.h12
-rw-r--r--src/arch/i386/include/virtaddr.h3
-rw-r--r--src/arch/i386/prefix/int19exit.c26
-rw-r--r--src/arch/i386/prefix/romprefix.S6
-rw-r--r--src/arch/i386/prefix/select_isapnp.c6
-rw-r--r--src/arch/i386/prefix/select_pci.c4
-rw-r--r--src/arch/i386/transitions/libkir.S10
-rw-r--r--src/arch/i386/transitions/librm.S34
-rw-r--r--src/arch/i386/transitions/librm_mgmt.c4
-rw-r--r--src/arch/ia64/include/stdint.h12
-rw-r--r--src/config.h21
-rw-r--r--src/core/background.c47
-rw-r--r--src/core/buffer.c278
-rw-r--r--src/core/config.c3
-rw-r--r--src/core/console.c91
-rw-r--r--src/core/errno.c61
-rw-r--r--src/core/main.c11
-rw-r--r--src/core/nic.c165
-rw-r--r--src/core/pxe_export.c1445
-rw-r--r--src/core/vsprintf.c79
-rw-r--r--src/doc/build_sys.dox419
-rw-r--r--src/doxygen.cfg208
-rw-r--r--src/drivers/bus/isapnp.c254
-rw-r--r--src/drivers/net/3c515.c4
-rw-r--r--src/drivers/net/amd8111e.c681
-rw-r--r--src/drivers/net/amd8111e.h629
-rw-r--r--src/drivers/net/davicom.c7
-rw-r--r--src/drivers/net/depca.c7
-rw-r--r--src/drivers/net/dmfe.c7
-rw-r--r--src/drivers/net/e1000.c1076
-rw-r--r--src/drivers/net/eepro.c30
-rw-r--r--src/drivers/net/eepro100.c7
-rw-r--r--src/drivers/net/forcedeth.c811
-rw-r--r--src/drivers/net/mtd80x.c7
-rw-r--r--src/drivers/net/natsemi.c11
-rwxr-xr-xsrc/drivers/net/ns83820.c7
-rw-r--r--src/drivers/net/pcnet32.c7
-rw-r--r--src/drivers/net/r8169.c7
-rw-r--r--src/drivers/net/sis900.c4
-rw-r--r--src/drivers/net/sis900.h7
-rw-r--r--src/drivers/net/smc9000.c449
-rw-r--r--src/drivers/net/smc9000.h114
-rw-r--r--src/drivers/net/sundance.c7
-rw-r--r--src/drivers/net/tg3.c4
-rw-r--r--src/drivers/net/tg3.h2
-rw-r--r--src/drivers/net/tlan.h20
-rw-r--r--src/drivers/net/tulip.c18
-rw-r--r--src/drivers/net/w89c840.c7
-rw-r--r--src/include/background.h52
-rw-r--r--src/include/buffer.h96
-rw-r--r--src/include/compiler.h127
-rw-r--r--src/include/console.h83
-rw-r--r--src/include/dns.h4
-rw-r--r--src/include/errno.h141
-rw-r--r--src/include/etherboot.h55
-rw-r--r--src/include/igmp.h33
-rw-r--r--src/include/nmb.h2
-rw-r--r--src/include/pci_ids.h1
-rw-r--r--src/include/pxe.h956
-rw-r--r--src/include/pxe_api.h1696
-rw-r--r--src/include/pxe_export.h61
-rw-r--r--src/include/pxe_types.h126
-rw-r--r--src/include/tables.h214
-rw-r--r--src/include/tftp.h169
-rw-r--r--src/include/tftpcore.h33
-rw-r--r--src/include/vsprintf.h44
-rw-r--r--src/interface/pxe/pxe.c311
-rw-r--r--src/interface/pxe/pxe_errors.c102
-rw-r--r--src/interface/pxe/pxe_loader.c83
-rw-r--r--src/interface/pxe/pxe_preboot.c249
-rw-r--r--src/interface/pxe/pxe_tftp.c623
-rw-r--r--src/interface/pxe/pxe_udp.c332
-rw-r--r--src/interface/pxe/pxe_undi.c538
-rw-r--r--src/proto/fsp.c245
-rw-r--r--src/proto/http.c15
-rw-r--r--src/proto/igmp.c166
-rw-r--r--src/proto/nmb.c7
-rw-r--r--src/proto/tcp.c2
-rw-r--r--src/proto/tftm.c607
-rw-r--r--src/proto/tftp.c282
-rw-r--r--src/proto/tftpcore.c541
-rwxr-xr-xsrc/util/makerom.pl28
112 files changed, 11297 insertions, 4756 deletions
diff --git a/LOG b/LOG
index 79ad4c06..39179ac1 100644
--- a/LOG
+++ b/LOG
@@ -2861,3 +2861,12 @@ driver
+ Hermann Gausterer sent a patch to support additional Broadcom PHYs
++ Timothy Legge updated the forcedeth driver to the latest Linux version
+2.6.10 (untested and broken)
+
++ YhLu fixed the updated forcedeth driver so that it worked and confirmed
+that it supports the Gigabit nVidia NICs
+
++ Timothy Legge fixed relocation issues with the eepro driver
+
++ Jan Kiszka provided a patch for the smc9000 for missing phy-setup
diff --git a/VERSION b/VERSION
index 85570a8a..3328f158 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.4.0 2005-04-01
+5.5.0 2005-05-17
diff --git a/contrib/bin2intelhex/bin2intelhex.c b/contrib/bin2intelhex/bin2intelhex.c
index 92c2728a..75b88c15 100644
--- a/contrib/bin2intelhex/bin2intelhex.c
+++ b/contrib/bin2intelhex/bin2intelhex.c
@@ -24,8 +24,8 @@
/*
* $Id$
* $Log$
- * Revision 1.1.1.1 2005/05/17 16:45:06 mcb30
- * Import from Etherboot 5.4
+ * Revision 1.1 2005/05/17 16:45:06 mcb30
+ * Initial revision
*
* Revision 1.9 1997/12/14 05:14:54 install
* - some documentation....
diff --git a/contrib/initrd/ChangeLog b/contrib/initrd/ChangeLog
index 74d3ce11..94f8f6aa 100644
--- a/contrib/initrd/ChangeLog
+++ b/contrib/initrd/ChangeLog
@@ -3,8 +3,8 @@ mkinitrd-net ChangeLog
Last Modified: Fri Jul 26 23:08:28 2002
$Log$
-Revision 1.1.1.1 2005/05/17 16:45:02 mcb30
-Import from Etherboot 5.4
+Revision 1.1 2005/05/17 16:45:02 mcb30
+Initial revision
Revision 1.1 2002/11/06 06:31:06 ken_yap
Contributed by Michael Brown.
diff --git a/src/Config b/src/Config
index ca069c76..2f3dea84 100644
--- a/src/Config
+++ b/src/Config
@@ -195,6 +195,18 @@
#
# Obscure options you probably don't need to touch:
#
+# -DZPXE_SUFFIX_STRIP
+# If the last 5 characters of the filename passed to Etherboot is
+# ".zpxe" then strip it off. This is useful in cases where a DHCP server
+# is not able to be configured to support conditionals. The way it works
+# is that the DHCP server is configured with a filename like
+# "foo.nbi.zpxe" so that when PXE asks for a filename it gets that, and
+# loads Etherboot from that file. Etherboot then starts up and once
+# again asks the DHCP server for a filename and once again gets
+# foo.nbi.zpxe, but with this option turned on loads "foo.nbi" instead.
+# This allows people to use Etherboot who might not otherwise be able to
+# because their DHCP servers won't let them.
+#
# -DPOWERSAVE
# Halt the processor when waiting for keyboard input
# which saves power while waiting for user interaction.
@@ -295,7 +307,7 @@ CFLAGS+= -DALLOW_ONLY_ENCAPSULATED
# Limit the delay on packet loss/congestion to a more bearable value. See
# description above. If unset, do not limit the delay between resend.
-CFLAGS+= -DBACKOFF_LIMIT=7 -DCONGESTED
+CFLAGS+= -DBACKOFF_LIMIT=5 -DCONGESTED
# More optional features
# CFLAGS+= -DTRY_FLOPPY_FIRST=4
diff --git a/src/Makefile b/src/Makefile
index fa7b3f3f..cb6b944d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -29,7 +29,7 @@ include arch/$(ARCH)/Config
# If invoked with no build target, print out a helpfully suggestive
# message.
#
-noargs : blib
+noargs :
@echo '===================================================='
@echo 'No target specified. To specify a target, do: '
@echo
@@ -79,6 +79,7 @@ MKCONFIG ?= $(PERL) ./util/mkconfig.pl
SYMCHECK ?= $(PERL) ./util/symcheck.pl
SORTOBJDUMP ?= $(PERL) ./util/sortobjdump.pl
NRV2B ?= ./util/nrv2b
+DOXYGEN ?= doxygen
# Location to place generated files
#
@@ -130,10 +131,11 @@ DEBUG_TARGETS += dbg2.o dbg.o c s
#
SRCDIRS += core
SRCDIRS += proto
-SRCDIRS += image
+#SRCDIRS += image
SRCDIRS += drivers/bus
SRCDIRS += drivers/net
#SRCDIRS += drivers/disk
+SRCDIRS += interface/pxe
# NON_AUTO_SRCS lists files that are excluded from the normal
# automatic build system.
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping
index 1f06d698..2a950326 100644
--- a/src/Makefile.housekeeping
+++ b/src/Makefile.housekeeping
@@ -10,8 +10,8 @@ CLEANUP := $(BIN)/*.* # *.* to avoid catching the "CVS" directory
# Version number calculations
#
VERSION_MAJOR = 5
-VERSION_MINOR = 3
-VERSION_PATCH = 14
+VERSION_MINOR = 5
+VERSION_PATCH = 0
EXTRAVERSION =
MM_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR)
VERSION = $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION)
@@ -422,6 +422,31 @@ $(BIN)/%.rebuild :
rm -f $(BIN)/$*
$(MAKE) $(MAKEFLAGS) $(BIN)/$*
+# Documentation
+#
+$(BIN)/doxygen.cfg : doxygen.cfg $(MAKEDEPS)
+ $(PERL) -pe 's{\@SRCDIRS\@}{$(SRCDIRS)}; ' \
+ -e 's{\@BIN\@}{$(BIN)}; ' \
+ -e 's{\@ARCH\@}{$(ARCH)}; ' \
+ $< > $@
+
+$(BIN)/doc : $(BIN)/doxygen.cfg
+ $(DOXYGEN) $<
+
+.PHONY : $(BIN)/doc
+
+VERYCLEANUP += $(BIN)/doc
+
+doc : $(BIN)/doc
+
+docview :
+ @[ -f $(BIN)/doc/html/index.html ] || $(MAKE) $(BIN)/doc
+ @if [ -n "$$BROWSER" ] ; then \
+ ( $$BROWSER $(BIN)/doc/html/index.html & ) ; \
+ else \
+ echo "Documentation index in $(BIN)/doc/html/index.html" ; \
+ fi
+
# Clean-up
#
clean :
diff --git a/src/arch/armnommu/Config b/src/arch/armnommu/Config
index 4c220cd5..57942038 100644
--- a/src/arch/armnommu/Config
+++ b/src/arch/armnommu/Config
@@ -17,6 +17,12 @@ CFLAGS+= -DRAWADDR=0x40100000
# NIC Debug Outputs
#CFLAGS+= -DDEBUG_NIC
+# Reduced Media Independent Interface
+# MAZBR LPEC2001: MII (Intel LXT971ALE at 0..1)
+# Elmeg D@VOS : RMII (Altima AC104-QF at 4..7)
+# Telekom XI521 : RMII (Altima AC104-QF at 4..7)
+#CFLAGS+= -DRMII
+
# Fixed MAC address
# p2001_eth has no flash and fixed mac address
#CFLAGS+= -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'"
diff --git a/src/arch/armnommu/core/serial.c b/src/arch/armnommu/core/serial.c
index 3ae98d47..0fb6e79b 100644
--- a/src/arch/armnommu/core/serial.c
+++ b/src/arch/armnommu/core/serial.c
@@ -17,7 +17,7 @@ void serial_putc(int ch)
{
/* wait for room in the 32 byte tx FIFO */
while ((P2001_UART->r.STATUS & 0x3f) > /* 30 */ 0) ;
- P2001_UART->w.TX1 = ch & 0xff;
+ P2001_UART->w.TX[0] = ch & 0xff;
}
/*
@@ -27,7 +27,7 @@ void serial_putc(int ch)
int serial_getc(void)
{
while (((P2001_UART->r.STATUS >> 6) & 0x3f) == 0) ;
- return P2001_UART->r.RX1 & 0xff;
+ return P2001_UART->r.RX[0] & 0xff;
}
/*
diff --git a/src/arch/armnommu/drivers/net/p2001_eth.c b/src/arch/armnommu/drivers/net/p2001_eth.c
index 81bc84c3..bdf2f0e9 100644
--- a/src/arch/armnommu/drivers/net/p2001_eth.c
+++ b/src/arch/armnommu/drivers/net/p2001_eth.c
@@ -1,10 +1,10 @@
/**************************************************************************
-Etherboot - BOOTP/TFTP Bootstrap Program
-P2001 NIC driver for Etherboot
-***************************************************************************/
+ * Etherboot - BOOTP/TFTP Bootstrap Program
+ * P2001 NIC driver for Etherboot
+ **************************************************************************/
/*
- * Copyright (C) 2004 Tobias Lorenz
+ * Copyright (C) 2005 Tobias Lorenz
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,7 @@ P2001 NIC driver for Etherboot
#include "isa.h"
#include "hardware.h"
-#include "lxt971a.h"
+#include "mii.h"
#include "timer.h"
@@ -31,25 +31,20 @@ static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
#define DMA_BUF_SIZE 2048 /* Buffer size */
static DMA_DSC txd __attribute__ ((__section__(".dma.desc")));
static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc")));
-static unsigned char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
-static unsigned char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
static unsigned int cur_rx;
/* Device selectors */
static unsigned int cur_channel; // DMA channel : 0..3
static unsigned int cur_phy; // PHY Address : 0..31
static P2001_ETH_regs_ptr EU; // Ethernet Unit : 0x0018_000 with _=0..3
-static P2001_ETH_regs_ptr MU; // Management Unit: 0x00180000
-#define MDIO_MAXCOUNT 1000 /* mdio abort */
-static unsigned int mdio_error; /* mdio error */
-
-/* Function prototypes */
-static void p2001_eth_mdio_init ();
-static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data);
-static unsigned int p2001_eth_mdio_read (unsigned int phyadr, unsigned int regadr);
-extern unsigned int p2001_eth_mdio_error;
+/* mdio handling */
+static int p2001_eth_mdio_read (int phy_id, int location);
+static void p2001_eth_mdio_write(int phy_id, int location, int val);
+/* net_device functions */
static int p2001_eth_poll (struct nic *nic, int retrieve);
static void p2001_eth_transmit (struct nic *nic, const char *d,
unsigned int t, unsigned int s, const char *p);
@@ -60,103 +55,107 @@ static void p2001_eth_init ();
static void p2001_eth_disable (struct dev *dev);
static int p2001_eth_check_link(unsigned int phy);
+static int link;
+static void p2001_eth_phyreset ();
static int p2001_eth_probe (struct dev *dev, unsigned short *probe_addrs __unused);
+/* Supported MII list */
+static struct mii_chip_info {
+ const char * name;
+ unsigned int physid; // (MII_PHYSID2 << 16) | MII_PHYSID1
+} mii_chip_table[] = {
+ { "Intel LXT971A", 0x78e20013 },
+ { "Altima AC104-QF", 0x55410022 },
+ {NULL,0},
+};
+
+
/**************************************************************************
-PHY MANAGEMENT UNIT - Read/write
-***************************************************************************/
-static void p2001_eth_mdio_init()
+ * PHY MANAGEMENT UNIT - Read/write
+ **************************************************************************/
+
+/**
+ * mdio_read - read MII PHY register
+ * @dev: the net device to read
+ * @regadr: the phy register id to read
+ *
+ * Read MII registers through MDIO and MDC
+ * using MDIO management frame structure and protocol(defined by ISO/IEC).
+ */
+static int p2001_eth_mdio_read(int phy_id, int location)
{
- /* reset ethernet PHYs */
- printf("Resetting PHYs...\n");
+ int result, boguscnt = 1000;
- /* GPIO24/25: TX_ER2/TX_ER0 */
- /* GPIO26/27: PHY_RESET/TX_ER1 */
- P2001_GPIO->PIN_MUX |= 0x0018;
- // 31-16: 0000 1111 0000 0000
- P2001_GPIO->GPIO2_En |= 0x0400;
+ do {
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while (P2001_MU->MU_CNTL & 0x8000)
+ barrier();
- P2001_GPIO->GPIO2_Out |= 0x04000000;
- P2001_GPIO->GPIO2_Out &= ~0x0400;
- mdelay(500);
- P2001_GPIO->GPIO2_Out |= 0x0400;
+ /* Schreiben MU_CNTL */
+ P2001_MU->MU_CNTL = location + (phy_id<<5) + (2<<10);
- /* set management unit clock divisor */
- // max. MDIO CLK = 2.048 MHz (EU.doc)
- // max. MDIO CLK = 8.000 MHz (LXT971A)
- // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz
- // n >= sysclk/4.096 MHz - 1
-#if SYSCLK == 73728000
- P2001_MU->MU_DIV = 17; // 73.728 MHZ =17=> 2.020 MHz
-#else
- //MU->MU_DIV = (SYSCLK/4.096)-1;
-#error "Please define a proper MDIO CLK divisor for that sysclk."
-#endif
- asm("nop \n nop");
+ /* Warten bis Hardware aktiv (MIU = "1") */
+ while ((P2001_MU->MU_CNTL & 0x8000) == 0)
+ barrier();
+ //asm("nop \r\n nop");
+
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while (P2001_MU->MU_CNTL & 0x8000)
+ barrier();
+
+ /* Fehler, wenn MDIO Read Error (MRE = "1") */
+ } while ((P2001_MU->MU_CNTL & 0x4000) && (--boguscnt > 0));
+
+ /* Lesen MU_DATA */
+ result = P2001_MU->MU_DATA;
+
+ if (boguscnt == 0)
+ return 0;
+ if ((result & 0xffff) == 0xffff)
+ return 0;
+
+ return result & 0xffff;
}
-static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data)
-{
- static unsigned int count;
- count = 0;
+/**
+ * mdio_write - write MII PHY register
+ * @dev: the net device to write
+ * @regadr: the phy register id to write
+ * @value: the register value to write with
+ *
+ * Write MII registers with @value through MDIO and MDC
+ * using MDIO management frame structure and protocol(defined by ISO/IEC)
+ */
+static void p2001_eth_mdio_write(int phy_id, int location, int val)
+{
/* Warten bis Hardware inaktiv (MIU = "0") */
- while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
- count++;
+ while (P2001_MU->MU_CNTL & 0x8000)
+ barrier();
/* Schreiben MU_DATA */
- MU->MU_DATA = data;
+ P2001_MU->MU_DATA = val;
/* Schreiben MU_CNTL */
- MU->MU_CNTL = regadr + (phyadr<<5) + (1<<10);
+ P2001_MU->MU_CNTL = location + (phy_id<<5) + (1<<10);
/* Warten bis Hardware aktiv (MIU = "1") */
- while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
- count++;
+ while ((P2001_MU->MU_CNTL & 0x8000) == 0)
+ barrier();
//asm("nop \r\n nop");
/* Warten bis Hardware inaktiv (MIU = "0") */
- while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
- count++;
-
- mdio_error = (count >= MDIO_MAXCOUNT);
+ while (P2001_MU->MU_CNTL & 0x8000)
+ barrier();
}
-static unsigned int p2001_eth_mdio_read(unsigned int phyadr, unsigned int regadr)
-{
- static unsigned int count;
- count = 0;
-
- do {
- /* Warten bis Hardware inaktiv (MIU = "0") */
- while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
- count++;
-
- /* Schreiben MU_CNTL */
- MU->MU_CNTL = regadr + (phyadr<<5) + (2<<10);
-
- /* Warten bis Hardware aktiv (MIU = "1") */
- while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
- count++;
- //asm("nop \r\n nop");
-
- /* Warten bis Hardware inaktiv (MIU = "0") */
- while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
- count++;
-
- /* Fehler, wenn MDIO Read Error (MRE = "1") */
- } while ((MU->MU_CNTL & 0x4000) && (count < MDIO_MAXCOUNT));
-
- /* Lesen MU_DATA */
- mdio_error = (count >= MDIO_MAXCOUNT);
- return MU->MU_DATA;
-}
/**************************************************************************
-POLL - Wait for a frame
-***************************************************************************/
+ * POLL - Wait for a frame
+ **************************************************************************/
+
/* Function: p2001_eth_poll
*
* Description: checks for a received packet and returns it if found.
@@ -231,9 +230,11 @@ static int p2001_eth_poll(struct nic *nic, int retrieve)
}
+
/**************************************************************************
-TRANSMIT - Transmit a frame
-***************************************************************************/
+ * TRANSMIT - Transmit a frame
+ **************************************************************************/
+
/* Function: p2001_eth_transmit
*
* Description: transmits a packet and waits for completion or timeout.
@@ -271,7 +272,7 @@ static void p2001_eth_transmit(
// TMAC_CNTL.ATP does the same
#ifdef DEBUG_NIC
- printf("p2001_eth_transmit: packet from %! to %! sent\n", txb+ETH_ALEN, txb);
+ printf("p2001_eth_transmit: packet from %! to %! sent (size: %d)\n", txb+ETH_ALEN, txb, s);
#endif
/* configure descriptor */
@@ -281,12 +282,12 @@ static void p2001_eth_transmit(
/* restart the transmitter */
EU->TMAC_DMA_EN = 0x01; /* set run bit */
- while(EU->TMAC_DMA_EN & 0x01) ; /* wait */
+ while(EU->TMAC_DMA_EN & 0x01); /* wait */
#ifdef DEBUG_NIC
/* check status */
status = EU->TMAC_DMA_STAT;
- if (status & ~(0x40))
+ if (status & ~(0x40)) // not END
printf("p2001_eth_transmit: dma status=0x%hx\n", status);
printf("TMAC_MIB6..7: %d:%d\n", EU->TMAC_MIB6, EU->TMAC_MIB7);
@@ -294,9 +295,11 @@ static void p2001_eth_transmit(
}
+
/**************************************************************************
-IRQ - Enable, Disable or Force Interrupts
-***************************************************************************/
+ * IRQ - Enable, Disable or Force Interrupts
+ **************************************************************************/
+
/* Function: p2001_eth_irq
*
* Description: Enable, Disable, or Force, interrupts
@@ -321,9 +324,11 @@ p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused)
}
+
/**************************************************************************
-INIT - Initialize device
-***************************************************************************/
+ * INIT - Initialize device
+ **************************************************************************/
+
/* Function: p2001_init
*
* Description: resets the ethernet controller chip and various
@@ -335,6 +340,23 @@ static void p2001_eth_init()
{
static int i;
+ /* activate MII 3 */
+ if (cur_channel == 3)
+ P2001_GPIO->PIN_MUX |= (1<<8); // MII_3_en = 1
+
+#ifdef RMII
+ /* RMII init sequence */
+ if (link & LPA_100) {
+ EU->CONF_RMII = (1<<2) | (1<<1); // softres | 100Mbit
+ EU->CONF_RMII = (1<<2) | (1<<1) | (1<<0); // softres | 100Mbit | RMII
+ EU->CONF_RMII = (1<<1) | (1<<0); // 100 Mbit | RMII
+ } else {
+ EU->CONF_RMII = (1<<2); // softres
+ EU->CONF_RMII = (1<<2) | (1<<0); // softres | RMII
+ EU->CONF_RMII = (1<<0); // RMII
+ }
+#endif
+
/* disable transceiver */
// EU->TMAC_DMA_EN = 0; /* clear run bit */
// EU->RMAC_DMA_EN = 0; /* clear run bit */
@@ -353,7 +375,7 @@ static void p2001_eth_init()
// txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END
// txd.cntl = cur_channel << 16; // DSC1 CHANNEL
// txd.cntl |= DMA_BUF_SIZE; // DSC1 LEN
- txd.buf = &txb; // DSC2 BUFFER
+ txd.buf = (char *)&txb; // DSC2 BUFFER
txd.next = &txd; // DSC3 NEXTDSC @self
EU->TMAC_DMA_DESC = &txd;
@@ -371,9 +393,12 @@ static void p2001_eth_init()
EU->RMAC_DMA_DESC = &rxd[0];
/* set transmitter mode */
- EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */
- //(1<<3) | /* CSI: Carrier Sense ignore */
- (1<<2); /* ATP: Automatic Transmit Padding */
+ if (link & LPA_DUPLEX)
+ EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */
+ (1<<3) | /* CSI: Carrier Sense ignore */
+ (1<<2); /* ATP: Automatic Transmit Padding */
+ else
+ EU->TMAC_CNTL = (1<<2); /* ATP: Automatic Transmit Padding */
/* set receive mode */
EU->RMAC_CNTL = (1<<3) | /* BROAD: Broadcast packets */
@@ -384,9 +409,10 @@ static void p2001_eth_init()
}
+
/**************************************************************************
-DISABLE - Turn off ethernet interface
-***************************************************************************/
+ * DISABLE - Turn off ethernet interface
+ **************************************************************************/
static void p2001_eth_disable(struct dev *dev __unused)
{
/* put the card in its initial state */
@@ -408,40 +434,52 @@ static void p2001_eth_disable(struct dev *dev __unused)
}
+
/**************************************************************************
-LINK - Check for valid link
-***************************************************************************/
+ * LINK - Check for valid link
+ **************************************************************************/
static int p2001_eth_check_link(unsigned int phy)
{
static int status;
- static unsigned int count;
- count = 0;
+ static unsigned int i, physid;
+
+ /* print some information about out PHY */
+ physid = (p2001_eth_mdio_read(phy, MII_PHYSID2) << 16) |
+ p2001_eth_mdio_read(phy, MII_PHYSID1);
+ printf("PHY %d, ID 0x%x ", phy, physid);
+ for (i = 0; mii_chip_table[i].physid; i++)
+ if (mii_chip_table[i].physid == physid) {
+ printf("(%s).\n", mii_chip_table[i].name);
+ break;
+ }
+ if (!mii_chip_table[i].physid)
+ printf("(unknown).\n");
/* Use 0x3300 for restarting NWay */
printf("Starting auto-negotiation... ");
- p2001_eth_mdio_write(phy, Adr_LXT971A_Control, 0x3300);
- if (mdio_error)
- goto failed;
+ p2001_eth_mdio_write(phy, MII_BMCR, 0x3300);
- /* Bits 1.5 and 17.7 are set to 1 once the Auto-Negotiation process to completed. */
+ /* Bit 1.5 is set once the Auto-Negotiation process is completed. */
+ i = 0;
do {
mdelay(500);
- status = p2001_eth_mdio_read(phy, Adr_LXT971A_Status1);
- if (mdio_error || (count++ > 6)) // 6*500ms = 3s timeout
+ status = p2001_eth_mdio_read(phy, MII_BMSR);
+ if (!status || (i++ > 6)) // 6*500ms = 3s timeout
goto failed;
- } while (!(status & 0x20));
-
- /* Bits 1.2 and 17.10 are set to 1 once the link is established. */
- if (p2001_eth_mdio_read(phy, Adr_LXT971A_Status1) & 0x04) {
- /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */
- printf("Valid link, operating at: %sMb-%s\n",
- (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x4000) ? "100" : "10",
- (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x0200) ? "FD" : "HD");
- return 1;
+ } while (!(status & BMSR_ANEGCOMPLETE));
+
+ /* Bits 1.2 is set once the link is established. */
+ if ((status = p2001_eth_mdio_read(phy, MII_BMSR)) & BMSR_LSTATUS) {
+ link = p2001_eth_mdio_read(phy, MII_ADVERTISE) &
+ p2001_eth_mdio_read(phy, MII_LPA);
+ printf(" Valid link, operating at: %sMb-%s\n",
+ (link & LPA_100) ? "100" : "10",
+ (link & LPA_DUPLEX) ? "FD" : "HD");
+ return 1;
}
failed:
- if (mdio_error)
+ if (!status)
printf("Failed\n");
else
printf("No valid link\n");
@@ -449,49 +487,79 @@ failed:
}
+
+/**************************************************************************
+ * PHYRESET - hardware reset all MII PHYs
+ **************************************************************************/
+
+/**
+ * p2001_eth_phyreset - hardware reset all MII PHYs
+ */
+static void p2001_eth_phyreset()
+{
+ /* GPIO24/25: TX_ER2/TX_ER0 */
+ /* GPIO26/27: PHY_RESET/TX_ER1 */
+ P2001_GPIO->PIN_MUX |= 0x0018;
+ // 31-16: 0000 1111 0000 0000
+ P2001_GPIO->GPIO2_En |= 0x0400;
+
+ P2001_GPIO->GPIO2_Out |= 0x04000000;
+ P2001_GPIO->GPIO2_Out &= ~0x0400;
+ mdelay(500);
+ P2001_GPIO->GPIO2_Out |= 0x0400;
+
+#ifdef RMII
+ /* RMII_clk_sel = 0xxb no RMII (default) */
+ /* RMII_clk_sel = 100b COL_0 */
+ /* RMII_clk_sel = 101b COL_1 */
+ /* RMII_clk_sel = 110b COL_2 */
+ /* RMII_clk_sel = 111b COL_3 */
+ P2001_GPIO->PIN_MUX |= (4 << 13);
+#endif
+}
+
+
+
/**************************************************************************
-PROBE - Look for an adapter, this routine's visible to the outside
-***************************************************************************/
+ * PROBE - Look for an adapter, this routine's visible to the outside
+ **************************************************************************/
+
static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused)
{
struct nic *nic = (struct nic *)dev;
/* if probe_addrs is 0, then routine can use a hardwired default */
- static int board_found;
- static int valid_link;
/* reset phys and configure mdio clk */
- p2001_eth_mdio_init();
+ printf("Resetting PHYs...\n");
+ p2001_eth_phyreset();
+
+ /* set management unit clock divisor */
+ // max. MDIO CLK = 2.048 MHz (EU.doc)
+ P2001_MU->MU_DIV = (SYSCLK/4096000)-1; // 2.048 MHz
+ //asm("nop \n nop");
/* find the correct PHY/DMA/MAC combination */
- MU = P2001_MU; // MU for all PHYs is only in EU0
printf("Searching for P2001 NICs...\n");
+ cur_phy = -1;
for (cur_channel=0; cur_channel<4; cur_channel++) {
- switch(cur_channel) {
- case 0:
- EU = P2001_EU0;
- cur_phy = 0;
- break;
- case 1:
- EU = P2001_EU1;
- cur_phy = 1;
- break;
- case 2:
- EU = P2001_EU2;
- cur_phy = 2;
- break;
- case 3:
- EU = P2001_EU3;
- cur_phy = 3;
+ EU = P2001_EU(cur_channel);
+
+ /* find next phy */
+ while (++cur_phy < 16) {
+ //printf("phy detect %d\n", cur_phy);
+ if (p2001_eth_mdio_read(cur_phy, MII_BMSR) != 0)
break;
}
+ if (cur_phy == 16) {
+ printf("no more MII PHYs found\n");
+ break;
+ }
/* first a non destructive test for initial value RMAC_TLEN=1518 */
- board_found = (EU->RMAC_TLEN == 1518);
- if (board_found) {
+ if (EU->RMAC_TLEN == 1518) {
printf("Checking EU%d...\n", cur_channel);
- valid_link = p2001_eth_check_link(cur_phy);
- if (valid_link) {
+ if (p2001_eth_check_link(cur_phy)) {
/* initialize device */
p2001_eth_init(nic);
@@ -507,7 +575,6 @@ static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused
/* Report the ISA pnp id of the board */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
- dev->devid.vendor_id = htons(0x1234);
return 1;
}
}
diff --git a/src/arch/armnommu/include/hardware.h b/src/arch/armnommu/include/hardware.h
index 203b78b7..49264d63 100644
--- a/src/arch/armnommu/include/hardware.h
+++ b/src/arch/armnommu/include/hardware.h
@@ -100,10 +100,7 @@ typedef struct { // 0x00130000U
typedef union { // 0x00140000U
struct { // write
- volatile unsigned int TX1; // 0x00000000U
- volatile unsigned int TX2; // 0x00000004U
- volatile unsigned int TX3; // 0x00000008U
- volatile unsigned int TX4; // 0x0000000CU
+ volatile unsigned int TX[4]; // 0x00000000-0x000CU
volatile unsigned int Baudrate; // 0x00000010U
volatile unsigned int reserved1[0x3];
volatile unsigned int Config; // 0x00000020U
@@ -113,10 +110,7 @@ typedef union { // 0x00140000U
} w; // write
struct { // read
- volatile unsigned int RX1; // 0x00000000U
- volatile unsigned int RX2; // 0x00000004U
- volatile unsigned int RX3; // 0x00000008U
- volatile unsigned int RX4; // 0x0000000CU
+ volatile unsigned int RX[4]; // 0x00000000-0x000CU
volatile unsigned int reserved1[0x4];
volatile unsigned int PRE_STATUS; // 0x00000020U
volatile unsigned int STATUS; // 0x00000024U
@@ -168,11 +162,8 @@ typedef struct { // 0x0018_000U _=0,1,2,3
volatile unsigned int TMAC_DMA_DATA; // 0x00000FF8U
volatile unsigned int TMAC_DMA_ADR; // 0x00000FFCU
} *P2001_ETH_regs_ptr;
-#define P2001_EU0 ((volatile P2001_ETH_regs_ptr) 0x00180000)
-#define P2001_EU1 ((volatile P2001_ETH_regs_ptr) 0x00181000)
-#define P2001_EU2 ((volatile P2001_ETH_regs_ptr) 0x00182000)
-#define P2001_EU3 ((volatile P2001_ETH_regs_ptr) 0x00183000)
-#define P2001_MU P2001_EU0
+#define P2001_EU(x) ((volatile P2001_ETH_regs_ptr) ((unsigned int) 0x00180000UL+(0x1000UL*(x)))) /* x = 0..3 */
+#define P2001_MU P2001_EU(0)
#endif
diff --git a/src/arch/armnommu/include/lxt971a.h b/src/arch/armnommu/include/lxt971a.h
deleted file mode 100644
index 16314ec2..00000000
--- a/src/arch/armnommu/include/lxt971a.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2004 Tobias Lorenz
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/*
- * Intel LXT971ALE (MII-compatible PHY)
- */
-
-#define Adr_LXT971A_Control 0 /* Control Register */
-#define Adr_LXT971A_Status1 1 /* MII Status Register #1 */
-#define Adr_LXT971A_PHY_ID1 2 /* PHY Identification Register 1 */
-#define Adr_LXT971A_PHY_ID2 3 /* PHY Identification Register 2 */
-#define Adr_LXT971A_AN_Advertise 4 /* Auto Negotiation Advertisement Register */
-#define Adr_LXT971A_AN_Link_Ability 5 /* Auto Negotiation Link Partner Base Page Ability Register */
-#define Adr_LXT971A_AN_Expansion 6 /* Auto Negotiation Expansion */
-#define Adr_LXT971A_AN_Next_Page_Txmit 7 /* Auto Negotiation Next Page Transmit Register */
-#define Adr_LXT971A_AN_Link_Next_Page 8 /* Auto Negotiation Link Partner Next Page Receive Register */
-#define Adr_LXT971A_Fast_Control 9 /* Not Implemented */
-#define Adr_LXT971A_Fast_Status 10 /* Not Implemented */
-#define Adr_LXT971A_Extended_Status 15 /* Not Implemented */
-#define Adr_LXT971A_Port_Config 16 /* Configuration Register */
-#define Adr_LXT971A_Status2 17 /* Status Register #2 */
-#define Adr_LXT971A_Interrupt_Enable 18 /* Interrupt Enable Register */
-#define Adr_LXT971A_Interrupt_Status 19 /* Interrupt Status Register */
-#define Adr_LXT971A_LED_Config 20 /* LED Configuration Register */
-#define Adr_LXT971A_Transmit_Control 30 /* Transmit Control Register */
diff --git a/src/arch/armnommu/include/stdint.h b/src/arch/armnommu/include/stdint.h
index 3f5dc3f9..1cb00851 100644
--- a/src/arch/armnommu/include/stdint.h
+++ b/src/arch/armnommu/include/stdint.h
@@ -20,4 +20,16 @@ typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed long s32;
+typedef unsigned int u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+
#endif /* STDINT_H */
diff --git a/src/arch/e1/include/stdint.h b/src/arch/e1/include/stdint.h
index 8a7ad978..505cc375 100644
--- a/src/arch/e1/include/stdint.h
+++ b/src/arch/e1/include/stdint.h
@@ -13,4 +13,16 @@ typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed long s32;
+typedef unsigned int u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+
#endif /* STDINT_H */
diff --git a/src/arch/i386/Config b/src/arch/i386/Config
index db80b38e..03836c8b 100644
--- a/src/arch/i386/Config
+++ b/src/arch/i386/Config
@@ -133,7 +133,7 @@ CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1
endif
GCC_MINORVERSION = $(word 2, $(GCC_VERSION))
ifneq ($(GCC_MINORVERSION),4)
-CFLAGS+= -mcpu=i386
+CFLAGS+= -march=i386
endif
LDFLAGS+= -N
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
index 9f207ea0..0c258ae8 100644
--- a/src/arch/i386/Makefile
+++ b/src/arch/i386/Makefile
@@ -1,7 +1,7 @@
# i386-specific directories containing source files
#
SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
-SRCDIRS += arch/i386/firmware/pcbios arch/i386/firmware/linuxbios
+SRCDIRS += arch/i386/firmware/pcbios
SRCDIRS += arch/i386/image
SRCDIRS += arch/i386/drivers/bus
SRCDIRS += arch/i386/drivers/net
diff --git a/src/arch/i386/core/freebsd_loader.c b/src/arch/i386/core/freebsd_loader.c
index 4e820e8e..464f6d93 100644
--- a/src/arch/i386/core/freebsd_loader.c
+++ b/src/arch/i386/core/freebsd_loader.c
@@ -216,7 +216,7 @@ static int elf_freebsd_debug_loader(unsigned int offset)
estate.toread, estate.curaddr);
#endif
/* Save where we are loading this... */
- symtab_load = phys_to_virt(estate.curaddr);
+ symtab_load = estate.curaddr;
*((long *)phys_to_virt(estate.curaddr)) = estate.toread;
estate.curaddr += sizeof(long);
@@ -244,7 +244,7 @@ static int elf_freebsd_debug_loader(unsigned int offset)
estate.toread, estate.curaddr);
#endif
/* Save where we are loading this... */
- symstr_load = phys_to_virt(estate.curaddr);
+ symstr_load = estate.curaddr;
*((long *)phys_to_virt(estate.curaddr)) = estate.toread;
estate.curaddr += sizeof(long);
@@ -290,7 +290,7 @@ static void elf_freebsd_boot(unsigned long entry)
/* Assumes size of long is a power of 2... */
bsdinfo.bi_esymtab = (symstr_load +
sizeof(long) +
- *((long *)symstr_load) +
+ *((long *)phys_to_virt(symstr_load)) +
sizeof(long) - 1) & ~(sizeof(long) - 1);
/* Where we will build the meta data... */
diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c
index b2c82a1e..313dc618 100644
--- a/src/arch/i386/core/hooks.c
+++ b/src/arch/i386/core/hooks.c
@@ -13,20 +13,20 @@
* the prefix requested.
*
*/
-void arch_main ( struct i386_all_regs *regs ) {
- void (*exit_path) ( struct i386_all_regs *regs );
+void arch_main ( struct i386_all_regs *ix86 ) {
+ void (*exit_path) ( struct i386_all_regs *ix86 );
/* Determine exit path requested by prefix */
- exit_path = ( typeof ( exit_path ) ) regs->eax;
+ exit_path = ( typeof ( exit_path ) ) ix86->regs.eax;
/* Call to main() */
- regs->eax = main();
+ ix86->regs.eax = main();
if ( exit_path ) {
/* Prefix requested that we use a particular function
* as the exit path, so we call this function, which
* must not return.
*/
- exit_path ( regs );
+ exit_path ( ix86 );
}
}
diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c
index 0bc9ca98..7dad640e 100644
--- a/src/arch/i386/firmware/pcbios/basemem.c
+++ b/src/arch/i386/firmware/pcbios/basemem.c
@@ -130,8 +130,8 @@ void free_base_memory ( void *ptr, size_t size ) {
*/
for ( ; size_kb > 0 ; free_block++, size_kb-- ) {
/* Mark this block as unused */
- free_block->magic = FREE_BLOCK_MAGIC;
- free_block->size_kb = size_kb;
+ free_block->header.magic = FREE_BLOCK_MAGIC;
+ free_block->header.size_kb = size_kb;
}
/* Free up unused base memory */
@@ -161,12 +161,12 @@ static void free_unused_base_memory ( void ) {
* if this is not a free block
*/
if ( ( fbms == FBMS_MAX ) ||
- ( free_block->magic != FREE_BLOCK_MAGIC ) ) {
+ ( free_block->header.magic != FREE_BLOCK_MAGIC ) ) {
break;
}
/* Return memory to BIOS */
- fbms += free_block->size_kb;
+ fbms += free_block->header.size_kb;
DBG ( "Freed %d kB of base memory at [%hx:0000,%hx:0000), "
"%d kB now free\n",
diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c
index f361aa94..f5d9e382 100644
--- a/src/arch/i386/image/nbi.c
+++ b/src/arch/i386/image/nbi.c
@@ -4,22 +4,44 @@
#include "gateA20.h"
#include "osloader.h"
#include "etherboot.h"
+#include "errno.h"
-/* An NBI image header */
+/** @file
+ *
+ * NBI image format.
+ *
+ * The Net Boot Image format is defined by the "Draft Net Boot Image
+ * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now
+ * considered to be a legacy format, but it still included because a
+ * large amount of software (e.g. nymph, LTSP) makes use of NBI files.
+ *
+ * Etherboot does not implement the INT 78 callback interface
+ * described by the NBI specification. For a callback interface on
+ * x86 architecture, use PXE.
+ *
+ */
+
+/**
+ * An NBI image header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
struct imgheader {
- unsigned long magic;
+ unsigned long magic; /**< Magic number (NBI_MAGIC) */
union {
- unsigned char length;
- unsigned long flags;
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned long flags; /**< Image flags */
};
- segoff_t location;
+ segoff_t location; /**< 16-bit seg:off header location */
union {
- segoff_t segoff;
- unsigned long linear;
+ segoff_t segoff; /**< 16-bit seg:off entry point */
+ unsigned long linear; /**< 32-bit entry point */
} execaddr;
} __attribute__ (( packed ));
-/* NBI magic number */
+/** NBI magic number */
#define NBI_MAGIC 0x1B031336UL
/* Interpretation of the "length" fields */
@@ -31,18 +53,24 @@ struct imgheader {
#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) )
#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) )
-/* NBI header length */
+/** NBI header length */
#define NBI_HEADER_LENGTH 512
-/* An NBI segment header */
+/**
+ * An NBI segment header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
struct segheader {
- unsigned char length;
- unsigned char vendortag;
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned char vendortag; /**< Vendor-defined private tag */
unsigned char reserved;
- unsigned char flags;
- unsigned long loadaddr;
- unsigned long imglength;
- unsigned long memlength;
+ unsigned char flags; /**< Segment flags */
+ unsigned long loadaddr; /**< Load address */
+ unsigned long imglength; /**< Segment length in NBI file */
+ unsigned long memlength; /**< Segment length in memory */
};
/* Interpretation of the "flags" fields */
@@ -53,28 +81,41 @@ struct segheader {
#define NBI_LOADADDR_BEFORE 0x03
#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) )
-/* Info passed to NBI image */
+/** Info passed to NBI image */
static struct ebinfo loaderinfo = {
VERSION_MAJOR, VERSION_MINOR,
0
};
-/*
+/**
* Determine whether or not this is a valid NBI image
*
+ * @v start Address of the image
+ * @v len Length of the image
+ * @v context NBI image context
+ * @ret True Image is a valid NBI image
+ * @ret False Image is not a valid NBI image
+ * @err EBADIMG Image is not a valid NBI image
+ *
+ * "context" is filled in with a context pointer suitable for passing to
+ * nbi_load() and nbi_boot().
+ *
*/
static int nbi_probe ( physaddr_t start, off_t len, void **context ) {
static struct imgheader imgheader;
if ( (unsigned)len < sizeof ( imgheader ) ) {
DBG ( "NBI image too small\n" );
+ errno = EBADIMG;
return 0;
}
copy_from_phys ( &imgheader, start, sizeof ( imgheader ) );
- if ( imgheader.magic != NBI_MAGIC )
+ if ( imgheader.magic != NBI_MAGIC ) {
+ errno = EBADIMG;
return 0;
+ }
/* Record image context */
DBG ( "NBI found valid image\n" );
@@ -82,9 +123,17 @@ static int nbi_probe ( physaddr_t start, off_t len, void **context ) {
return 1;
}
-/*
+/**
* Prepare a segment for an NBI image
*
+ * @v dest Address of segment
+ * @v imglen Length of initialised-data portion of the segment
+ * @v memlen Total length of the segment
+ * @v src Source for initialised data
+ * @ret True Segment can be used
+ * @ret False Segment cannot be used
+ * @err other As returned by prep_segment()
+ *
*/
static int nbi_prepare_segment ( physaddr_t dest, off_t imglen, off_t memlen,
physaddr_t src __unused ) {
@@ -93,9 +142,15 @@ static int nbi_prepare_segment ( physaddr_t dest, off_t imglen, off_t memlen,
return prep_segment ( dest, dest + imglen, dest + memlen );
}
-/*
+/**
* Load a segment for an NBI image
*
+ * @v dest Address of segment
+ * @v imglen Length of initialised-data portion of the segment
+ * @v memlen Total length of the segment
+ * @v src Source for initialised data
+ * @ret True Always
+ *
*/
static int nbi_load_segment ( physaddr_t dest, off_t imglen,
off_t memlen __unused, physaddr_t src ) {
@@ -104,9 +159,18 @@ static int nbi_load_segment ( physaddr_t dest, off_t imglen,
return 1;
}
-/*
+/**
* Process segments of an NBI image
*
+ * @v start Address of the image
+ * @v len Length of the image
+ * @v imgheader Image header information
+ * @v process Function to call for each segment
+ * @ret True All segments were processed successfully
+ * @ret False An error occurred processing a segment
+ * @err EBADIMG Image is not a valid NBI image
+ * @err other As returned by the "process" function
+ *
*/
static int nbi_process_segments ( physaddr_t start, off_t len,
struct imgheader *imgheader,
@@ -136,6 +200,7 @@ static int nbi_process_segments ( physaddr_t start, off_t len,
if ( sh.length == 0 ) {
/* Avoid infinite loop? */
DBG ( "NBI invalid segheader length 0\n" );
+ errno = EBADIMG;
return 0;
}
@@ -159,8 +224,8 @@ static int nbi_process_segments ( physaddr_t start, off_t len,
- sh.loadaddr;
break;
default:
- DBG ( "NBI can't count up to three\n" );
- return 0;
+ /* Cannot be reached */
+ DBG ( "NBI can't count up to three!\n" );
}
/* Process this segment */
@@ -175,6 +240,7 @@ static int nbi_process_segments ( physaddr_t start, off_t len,
sh_off += NBI_LENGTH ( sh.length );
if ( sh_off >= NBI_HEADER_LENGTH ) {
DBG ( "NBI header overflow\n" );
+ errno = EBADIMG;
return 0;
}
@@ -183,22 +249,35 @@ static int nbi_process_segments ( physaddr_t start, off_t len,
if ( offset != len ) {
DBG ( "NBI length mismatch (file %d, metadata %d)\n",
len, offset );
+ errno = EBADIMG;
return 0;
}
return 1;
}
-/*
+/**
* Load an NBI image into memory
*
+ * @v start Address of image
+ * @v len Length of image
+ * @v context NBI context (as returned by nbi_probe())
+ * @ret True Image loaded into memory
+ * @ret False Image not loaded into memory
+ * @err EBADIMG Image is not a valid NBI image
+ * @err other As returned by nbi_process_segments()
+ * @err other As returned by nbi_prepare_segment()
+ * @err other As returned by nbi_load_segment()
+ *
*/
static int nbi_load ( physaddr_t start, off_t len, void *context ) {
struct imgheader *imgheader = context;
/* If we don't have enough data give up */
- if ( len < NBI_HEADER_LENGTH )
+ if ( len < NBI_HEADER_LENGTH ) {
+ errno = EBADIMG;
return 0;
+ }
DBG ( "NBI placing header at %hx:%hx\n",
imgheader->location.segment, imgheader->location.offset );
@@ -220,9 +299,14 @@ static int nbi_load ( physaddr_t start, off_t len, void *context ) {
return 1;
}
-/*
+/**
* Boot a 16-bit NBI image
*
+ * @v imgheader Image header information
+ * @ret Never NBI program booted successfully
+ * @ret False NBI program returned
+ * @err EIMGRET NBI program returned
+ *
*/
static int nbi_boot16 ( struct imgheader *imgheader ) {
uint16_t basemem_bootp;
@@ -256,12 +340,23 @@ static int nbi_boot16 ( struct imgheader *imgheader ) {
CLOBBER ( "eax", "ecx", "edx", "ebp" ) );
BASEMEM_PARAMETER_DONE ( bootp_data );
+ errno = EIMGRET;
return 0;
}
-/*
+/**
* Boot a 32-bit NBI image
*
+ * @v imgheader Image header information
+ * @ret False NBI program should not have returned
+ * @ret other As returned by NBI program
+ * @err EIMGRET NBI program should not have returned
+ *
+ * To distinguish between the case of an NBI program returning false,
+ * and an NBI program that should not have returned, check errno.
+ * errno will be set to EIMGRET only if the NBI program should not
+ * have returned.
+ *
*/
static int nbi_boot32 ( struct imgheader *imgheader ) {
int rc = 0;
@@ -270,6 +365,7 @@ static int nbi_boot32 ( struct imgheader *imgheader ) {
imgheader->execaddr.linear );
/* no gateA20_unset for PM call */
+ errno = ENOERR;
rc = xstart32 ( imgheader->execaddr.linear,
virt_to_phys ( &loaderinfo ),
( ( imgheader->location.segment << 4 ) +
@@ -278,15 +374,24 @@ static int nbi_boot32 ( struct imgheader *imgheader ) {
printf ( "Secondary program returned %d\n", rc );
if ( ! NBI_PROGRAM_RETURNS ( imgheader->flags ) ) {
/* We shouldn't have returned */
+ errno = EIMGRET;
rc = 0;
}
return rc;
}
-/*
+/**
* Boot a loaded NBI image
*
+ * @v context NBI context (as returned by nbi_probe())
+ * @ret Never NBI program booted successfully
+ * @ret False NBI program should not have returned
+ * @ret other As returned by NBI program
+ * @err EIMGRET NBI program should not have returned
+ *
+ * See also nbi_boot16() and nbi_boot32().
+ *
*/
static int nbi_boot ( void *context ) {
struct imgheader *imgheader = context;
@@ -298,6 +403,7 @@ static int nbi_boot ( void *context ) {
}
}
+/** Declaration of the NBI image format */
static struct image nbi_image __image = {
.name = "NBI",
.probe = nbi_probe,
diff --git a/src/arch/i386/core/pxe_loader.c b/src/arch/i386/image/pxe_image.c
index 1b611891..bd1bad35 100644
--- a/src/arch/i386/core/pxe_loader.c
+++ b/src/arch/i386/image/pxe_image.c
@@ -13,7 +13,6 @@
#include "etherboot.h"
#include "pxe_callbacks.h"
-#include "pxe_export.h"
#include "pxe.h"
unsigned long pxe_load_offset;
diff --git a/src/arch/i386/include/basemem.h b/src/arch/i386/include/basemem.h
index 6e7c22dd..289824eb 100644
--- a/src/arch/i386/include/basemem.h
+++ b/src/arch/i386/include/basemem.h
@@ -19,7 +19,7 @@ struct free_base_memory_header {
};
union free_base_memory_block {
- struct free_base_memory_header;
+ struct free_base_memory_header header;
char bytes[1024];
};
diff --git a/src/arch/i386/include/bochs.h b/src/arch/i386/include/bochs.h
index 73f43c36..9d090fc1 100644
--- a/src/arch/i386/include/bochs.h
+++ b/src/arch/i386/include/bochs.h
@@ -1,9 +1,16 @@
#ifndef BOCHS_H
#define BOCHS_H
-/*
- * This file defines "bochsbp", the magic breakpoint instruction that
- * is incredibly useful when debugging under bochs.
+/** @file
+ *
+ * bochs breakpoints
+ *
+ * This file defines @c bochsbp, the magic breakpoint instruction that
+ * is incredibly useful when debugging under bochs. This file should
+ * never be included in production code.
+ *
+ * Use the pseudo-instruction @c bochsbp in assembly code, or the
+ * bochsbp() function in C code.
*
*/
@@ -15,7 +22,7 @@
#else /* ASSEMBLY */
-/* Breakpoint for when debugging under bochs */
+/** Breakpoint for when debugging under bochs */
static inline void bochsbp ( void ) {
__asm__ __volatile__ ( "xchgw %bx, %bx" );
}
diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h
index 95b9aaa3..3cef262f 100644
--- a/src/arch/i386/include/hooks.h
+++ b/src/arch/i386/include/hooks.h
@@ -1,6 +1,6 @@
#ifndef HOOKS_H
#define HOOKS_H
-extern void arch_main ( struct i386_all_regs *regs );
+extern void arch_main ( struct i386_all_regs *ix86 );
#endif /* HOOKS_H */
diff --git a/src/arch/i386/include/librm.h b/src/arch/i386/include/librm.h
index 2edc1096..1b82a982 100644
--- a/src/arch/i386/include/librm.h
+++ b/src/arch/i386/include/librm.h
@@ -17,8 +17,8 @@
/* Real-mode call parameter block, as passed to real_call */
struct real_call_params {
- struct i386_seg_regs;
- struct i386_regs;
+ struct i386_seg_regs segs;
+ struct i386_regs regs;
segoff_t rm_code;
segoff_t reserved;
} PACKED;
diff --git a/src/arch/i386/include/pxe_addr.h b/src/arch/i386/include/pxe_addr.h
new file mode 100644
index 00000000..954551e8
--- /dev/null
+++ b/src/arch/i386/include/pxe_addr.h
@@ -0,0 +1,17 @@
+/*
+ * Architecture-specific portion of pxe.h for Etherboot
+ *
+ * This file has to define the types SEGOFF16_t, SEGDESC_t and
+ * SEGSEL_t for use in other PXE structures. See pxe.h for details.
+ */
+
+#ifndef PXE_ADDR_H
+#define PXE_ADDR_H
+
+#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
+#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
+#define PTR_TO_SEGOFF16(ptr,segoff16) \
+ (segoff16).segment = SEGMENT(ptr); \
+ (segoff16).offset = OFFSET(ptr);
+
+#endif /* PXE_ADDR_H */
diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h
index cf5a7a87..974a3c30 100644
--- a/src/arch/i386/include/pxe_callbacks.h
+++ b/src/arch/i386/include/pxe_callbacks.h
@@ -5,12 +5,12 @@
#define PXE_CALLBACKS_H
#include "etherboot.h"
-#include "pxe.h"
+#include "pxe_types.h"
typedef struct {
- segoff_t orig_retaddr;
- uint16_t opcode;
- segoff_t segoff;
+ SEGOFF16_t orig_retaddr;
+ UINT16_t opcode;
+ SEGOFF16_t segoff;
} PACKED pxe_call_params_t;
/*
@@ -22,7 +22,7 @@ typedef struct {
/* Function prototypes
*/
-extern pxe_stack_t * install_pxe_stack ( void *base );
+extern struct pxe_stack * install_pxe_stack ( void *base );
extern void use_undi_ds_for_rm_stack ( uint16_t ds );
extern int hook_pxe_stack ( void );
extern int unhook_pxe_stack ( void );
diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/i386/include/pxe_types.h
deleted file mode 100644
index 45736a2b..00000000
--- a/src/arch/i386/include/pxe_types.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Architecture-specific portion of pxe.h for Etherboot
- *
- * This file has to define the types SEGOFF16_t, SEGDESC_t and
- * SEGSEL_t for use in other PXE structures. See pxe.h for details.
- */
-
-#ifndef PXE_TYPES_H
-#define PXE_TYPES_H
-
-/* SEGOFF16_t defined in separate header
- */
-#include "realmode.h"
-typedef segoff_t I386_SEGOFF16_t;
-#define SEGOFF16_t I386_SEGOFF16_t
-
-#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
-#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
-#define PTR_TO_SEGOFF16(ptr,segoff16) \
- (segoff16).segment = SEGMENT(ptr); \
- (segoff16).offset = OFFSET(ptr);
-
-typedef struct {
- uint16_t Seg_Addr;
- uint32_t Phy_Addr;
- uint16_t Seg_Size;
-} PACKED I386_SEGDESC_t; /* PACKED is required, otherwise gcc pads
- * this out to 12 bytes -
- * mbrown@fensystems.co.uk (mcb30) 17/5/03 */
-#define SEGDESC_t I386_SEGDESC_t
-
-typedef uint16_t I386_SEGSEL_t;
-#define SEGSEL_t I386_SEGSEL_t
-
-#endif /* PXE_TYPES_H */
diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h
index cd6fcfc7..fe011184 100644
--- a/src/arch/i386/include/realmode.h
+++ b/src/arch/i386/include/realmode.h
@@ -12,11 +12,6 @@
*
*/
-/* All i386 registers, as passed in by prot_call or kir_call */
-struct real_mode_regs {
- struct i386_all_regs;
-} PACKED;
-
/* Segment:offset structure. Note that the order within the structure
* is offset:segment.
*/
diff --git a/src/arch/i386/include/registers.h b/src/arch/i386/include/registers.h
index 155fffbd..66653238 100644
--- a/src/arch/i386/include/registers.h
+++ b/src/arch/i386/include/registers.h
@@ -1,9 +1,25 @@
#ifndef REGISTERS_H
#define REGISTERS_H
+/** @file
+ *
+ * i386 registers.
+ *
+ * This file defines data structures that allow easy access to i386
+ * register dumps.
+ *
+ */
+
+#include "compiler.h" /* for doxygen */
#include "stdint.h"
-/* Basic 16-bit and 32-bit register types */
+/**
+ * A 16-bit general register.
+ *
+ * This type encapsulates a 16-bit register such as %ax, %bx, %cx,
+ * %dx, %si, %di, %bp or %sp.
+ *
+ */
typedef union {
struct {
union {
@@ -15,12 +31,33 @@ typedef union {
uint16_t word;
} PACKED reg16_t;
+/**
+ * A 32-bit general register.
+ *
+ * This type encapsulates a 32-bit register such as %eax, %ebx, %ecx,
+ * %edx, %esi, %edi, %ebp or %esp.
+ *
+ */
typedef union {
- reg16_t;
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
uint32_t dword;
} PACKED reg32_t;
-/* As created by pushal / read by popal */
+/**
+ * A 32-bit general register dump.
+ *
+ * This is the data structure that is created on the stack by the @c
+ * pushal instruction, and can be read back using the @c popal
+ * instruction.
+ *
+ */
struct i386_regs {
union {
uint16_t di;
@@ -72,7 +109,31 @@ struct i386_regs {
};
} PACKED;
-/* Our pushal/popal equivalent for segment registers */
+/**
+ * A segment register dump.
+ *
+ * The i386 has no equivalent of the @c pushal or @c popal
+ * instructions for the segment registers. We adopt the convention of
+ * always using the sequences
+ *
+ * @code
+ *
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ *
+ * @endcode
+ *
+ * This is the data structure that is created and read back by these
+ * instruction sequences.
+ *
+ */
struct i386_seg_regs {
uint16_t cs;
uint16_t ss;
@@ -82,11 +143,37 @@ struct i386_seg_regs {
uint16_t gs;
} PACKED;
-/* All i386 registers, as passed in by prot_call or kir_call */
+/**
+ * A full register dump.
+ *
+ * This data structure is created by the instructions
+ *
+ * @code
+ *
+ * pushfl
+ * pushal
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and can be read back using the instructions
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ * popal
+ * popfl
+ *
+ * @endcode
+ *
+ * prot_call() and kir_call() create this data structure on the stack
+ * and pass in a pointer to this structure.
+ *
+ */
struct i386_all_regs {
- struct i386_seg_regs;
- struct i386_regs;
- uint32_t i386_flags;
+ struct i386_seg_regs segs;
+ struct i386_regs regs;
+ uint32_t flags;
} PACKED;
#endif /* REGISTERS_H */
diff --git a/src/arch/i386/include/stdint.h b/src/arch/i386/include/stdint.h
index b9ade4e0..c801cf6f 100644
--- a/src/arch/i386/include/stdint.h
+++ b/src/arch/i386/include/stdint.h
@@ -16,4 +16,16 @@ typedef signed long long int64_t;
typedef unsigned long physaddr_t;
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed int s32;
+typedef unsigned int u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+
#endif /* STDINT_H */
diff --git a/src/arch/i386/include/virtaddr.h b/src/arch/i386/include/virtaddr.h
index d48b6c6c..4d248b0a 100644
--- a/src/arch/i386/include/virtaddr.h
+++ b/src/arch/i386/include/virtaddr.h
@@ -46,7 +46,8 @@ static inline void * phys_to_virt ( unsigned long phys_addr ) {
return ( void * ) ( phys_addr - virt_offset );
}
-static inline void copy_to_phys ( physaddr_t dest, void *src, size_t len ) {
+static inline void copy_to_phys ( physaddr_t dest, const void *src,
+ size_t len ) {
memcpy ( phys_to_virt ( dest ), src, len );
}
diff --git a/src/arch/i386/prefix/int19exit.c b/src/arch/i386/prefix/int19exit.c
index e7be0624..e1333926 100644
--- a/src/arch/i386/prefix/int19exit.c
+++ b/src/arch/i386/prefix/int19exit.c
@@ -1,13 +1,31 @@
#include "bochs.h"
#include "realmode.h"
-/*
- * The "exit via INT 19" exit path. INT 19 is the old (pre-BBS) "boot
- * system" interrupt.
+/** @file
+ *
+ * The "exit via INT 19" exit path.
+ *
+ * INT 19 is the old (pre-BBS) "boot system" interrupt. It is
+ * conventionally used now to return from a failed boot from floppy
+ * disk.
*
*/
-void exit_via_int19 ( struct real_mode_regs *rm_regs ) {
+/**
+ * Exit via INT19
+ *
+ * @v ix86 i386 register values to be loaded on exit
+ * @ret Never -
+ * @err None -
+ *
+ * Exit back to the BIOS by switching to real mode, reloading the
+ * registers as they were before Etherboot started, and executing INT
+ * 19.
+ *
+ * @bug Not yet implemented
+ *
+ */
+void exit_via_int19 ( struct i386_all_regs *ix86 ) {
bochsbp();
/* Placeholder */
}
diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S
index 9bdc4a02..4a5bd2e6 100644
--- a/src/arch/i386/prefix/romprefix.S
+++ b/src/arch/i386/prefix/romprefix.S
@@ -110,9 +110,9 @@ UNDIROMID:
.byte 0 /* Structure revision */
.byte 0,1,2 /* PXE version 2.1.0 */
.word UNDILoader - _prefix /* Offset to loader routine */
- .word UNDIStackSize /* Stack segment size */
- .word UNDIDataSize /* Data segment size */
- .word UNDICodeSize /* Code segment size */
+ .word _real_mode_stack_size /* Stack segment size */
+ .word _real_mode_stack_size /* Data segment size */
+ .word _pxe_stack_size /* Code segment size */
.ascii "PCIR"
/* The code segment contains our pxe_stack_t plus the PXE and
diff --git a/src/arch/i386/prefix/select_isapnp.c b/src/arch/i386/prefix/select_isapnp.c
index 54ac1c9a..6a539eb5 100644
--- a/src/arch/i386/prefix/select_isapnp.c
+++ b/src/arch/i386/prefix/select_isapnp.c
@@ -11,7 +11,7 @@
* would cause linker symbol pollution.
*
*/
-void i386_select_isapnp_device ( struct i386_all_regs *regs ) {
+void i386_select_isapnp_device ( struct i386_all_regs *ix86 ) {
/*
* PnP BIOS passes card select number in %bx and read port
* address in %dx.
@@ -23,10 +23,10 @@ void i386_select_isapnp_device ( struct i386_all_regs *regs ) {
} u;
/* Set ISAPnP read port */
- isapnp_read_port = regs->dx;
+ isapnp_read_port = ix86->regs.dx;
/* Select ISAPnP bus and specified CSN as first boot device */
memset ( &u, 0, sizeof ( u ) );
- u.isapnp_loc.csn = regs->bx;
+ u.isapnp_loc.csn = ix86->regs.bx;
select_device ( &dev, &isapnp_driver, &u.bus_loc );
}
diff --git a/src/arch/i386/prefix/select_pci.c b/src/arch/i386/prefix/select_pci.c
index c9a62d52..e143b992 100644
--- a/src/arch/i386/prefix/select_pci.c
+++ b/src/arch/i386/prefix/select_pci.c
@@ -11,7 +11,7 @@
* that would cause linker symbol pollution.
*
*/
-void i386_select_pci_device ( struct i386_all_regs *regs ) {
+void i386_select_pci_device ( struct i386_all_regs *ix86 ) {
/*
* PCI BIOS passes busdevfn in %ax
*
@@ -23,6 +23,6 @@ void i386_select_pci_device ( struct i386_all_regs *regs ) {
/* Select PCI bus and specified busdevfn as first boot device */
memset ( &u, 0, sizeof ( u ) );
- u.pci_loc.busdevfn = regs->ax;
+ u.pci_loc.busdevfn = ix86->regs.ax;
select_device ( &dev, &pci_driver, &u.bus_loc );
}
diff --git a/src/arch/i386/transitions/libkir.S b/src/arch/i386/transitions/libkir.S
index 79a0aa00..e0d6c57c 100644
--- a/src/arch/i386/transitions/libkir.S
+++ b/src/arch/i386/transitions/libkir.S
@@ -135,12 +135,12 @@ kir_to_ext:
*
* Call a specific C function in the internal code. The prototype of
* the C function must be
- * void function ( struct real_mode_regs *rm_regs );
- * rm_regs will point to a struct containing the real-mode registers
+ * void function ( struct i386_all_resg *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
* at entry to kir_call.
*
* All registers will be preserved across kir_call(), unless the C
- * function explicitly overwrites values in rm_regs. Interrupt status
+ * function explicitly overwrites values in ix86. Interrupt status
* will also be preserved.
*
* Parameters:
@@ -151,7 +151,7 @@ kir_to_ext:
* lcall $UNDI_CS, $kir_call
* addw $2, %sp
* to call in to the C function
- * void pxe_api_call ( struct real_mode_regs *rm_regs );
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
****************************************************************************
*/
@@ -190,7 +190,7 @@ kir_call:
pushl %cs:ext_ds_and_es
pushl %cs:ext_cs_and_ss
- /* Push &rm_regs on stack and call function */
+ /* Push &ix86 on stack and call function */
pushl %esp
data32 call *%cs:save_function
popl %eax /* discard */
diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S
index 6e2f1229..2e6ac47b 100644
--- a/src/arch/i386/transitions/librm.S
+++ b/src/arch/i386/transitions/librm.S
@@ -106,11 +106,11 @@
/* Size of various C data structures */
#define SIZEOF_I386_SEG_REGS 12
#define SIZEOF_I386_REGS 32
-#define SIZEOF_I386_ALL_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
+#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
#define SIZEOF_I386_FLAGS 4
-#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_ALL_REGS + SIZEOF_I386_FLAGS )
+#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
#define SIZEOF_SEGOFF_T 4
-#define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_I386_ALL_REGS + 2 * SIZEOF_SEGOFF_T )
+#define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_REAL_MODE_REGS + 2 * SIZEOF_SEGOFF_T )
.text
.arch i386
@@ -461,12 +461,12 @@ p2r_ljmp:
*
* Call a specific C function in the protected-mode code. The
* prototype of the C function must be
- * void function ( struct real_mode_regs *rm_regs );
- * rm_regs will point to a struct containing the real-mode registers
+ * void function ( struct i386_all_regs *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
* at entry to prot_call.
*
* All registers will be preserved across prot_call(), unless the C
- * function explicitly overwrites values in rm_regs. Interrupt status
+ * function explicitly overwrites values in ix86. Interrupt status
* will also be preserved. Gate A20 will be enabled.
*
* The protected-mode code may install librm to a new location. If it
@@ -495,12 +495,12 @@ p2r_ljmp:
* lcall $LIBRM_SEGMENT, $prot_call
* addw $4, %sp
* to call in to the C function
- * void pxe_api_call ( struct real_mode_regs *rm_regs );
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
****************************************************************************
*/
-#define PC_OFFSET_RM_REGS ( 0 )
-#define PC_OFFSET_RETADDR ( PC_OFFSET_RM_REGS + SIZEOF_REAL_MODE_REGS )
+#define PC_OFFSET_IX86 ( 0 )
+#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
.code16
@@ -534,14 +534,14 @@ EXPORT(prot_call):
call real_to_prot
.code32
- /* Copy rm_regs from RM stack to PM stack */
- movl $SIZEOF_REAL_MODE_REGS, %ecx
+ /* Copy ix86 from RM stack to PM stack */
+ movl $SIZEOF_I386_ALL_REGS, %ecx
subl %ecx, %esp
movl %esp, %edi
pushl %esi
cld
rep movsb
- popl %edi /* %edi = phys addr of RM copy of rm_regs */
+ popl %edi /* %edi = phys addr of RM copy of ix86 */
/* Switch to virtual addresses. */
call 1f
@@ -555,7 +555,7 @@ EXPORT(prot_call):
popl %eax /* discard */
popal
- /* Push &rm_regs on the stack, and call function */
+ /* Push &ix86 on the stack, and call function */
pushl %esp
call *%ebx
popl %eax /* discard */
@@ -564,16 +564,16 @@ EXPORT(prot_call):
lcall $VIRTUAL_CS, $_virt_to_phys
popl %eax /* discard */
- /* Copy rm_regs from PM stack to RM stack, and remove rm_regs
+ /* Copy ix86 from PM stack to RM stack, and remove ix86
* from PM stack. (%edi still contains physical address of
- * rm_regs on RM stack from earlier, since C code preserves
+ * ix86 on RM stack from earlier, since C code preserves
* %edi).
*/
movl %esp, %esi
- movl $SIZEOF_REAL_MODE_REGS, %ecx
+ movl $SIZEOF_I386_ALL_REGS, %ecx
cld
rep movsb
- movl %esi, %esp /* remove rm_regs from PM stack */
+ movl %esi, %esp /* remove ix86 from PM stack */
/* Obtain physical base address of installed copy of librm in
* %ebx. (It's possible that this *isn't* the physical base
diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c
index ffd55ff6..956408f5 100644
--- a/src/arch/i386/transitions/librm_mgmt.c
+++ b/src/arch/i386/transitions/librm_mgmt.c
@@ -139,7 +139,7 @@ POST_RELOC_FN ( POST_RELOC_LIBRM, librm_post_reloc );
* pointer to this new librm's entry point via es:di.
*
*/
-void initialise_via_librm ( struct i386_all_regs *regs ) {
+void initialise_via_librm ( struct i386_all_regs *ix86 ) {
/* Hand off to initialise() */
initialise ();
@@ -147,7 +147,7 @@ void initialise_via_librm ( struct i386_all_regs *regs ) {
* already set up by setup16, so all we need to do is point
* es:0000 to the start of the new librm.
*/
- regs->es = librm_base >> 4;
+ ix86->segs.es = librm_base >> 4;
}
/*
diff --git a/src/arch/ia64/include/stdint.h b/src/arch/ia64/include/stdint.h
index 2f9c592c..8d8270be 100644
--- a/src/arch/ia64/include/stdint.h
+++ b/src/arch/ia64/include/stdint.h
@@ -13,4 +13,16 @@ typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long int64_t;
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed int s32;
+typedef unsigned int u32;
+
+typedef signed long s64;
+typedef unsigned long u64;
+
#endif /* STDINT_H */
diff --git a/src/config.h b/src/config.h
index d41a403f..ec4b1801 100644
--- a/src/config.h
+++ b/src/config.h
@@ -75,6 +75,7 @@
#undef DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */
#undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */
#undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
+#undef DOWNLOAD_PROTO_FSP /* FSP? */
/* @END general.h */
@@ -91,6 +92,26 @@
/* @BEGIN general.h
*
+ * Image types
+ *
+ * Etherboot supports various image formats. Select whichever ones
+ * you want to use.
+ *
+ */
+#define TAGGED_IMAGE /* NBI image support */
+#undef ELF64_IMAGE /* ELF64 image support */
+#undef ELF_IMAGE /* ELF image support */
+#undef COFF_IMAGE /* COFF image support */
+#undef IMAGE_FREEBSD /* FreeBSD kernel image support */
+#undef IMAGE_MULTIBOOT /* MultiBoot image support */
+#undef AOUT_IMAGE /* a.out image support */
+#undef WINCE_IMAGE /* WinCE image support */
+#undef PXE_IMAGE /* PXE image support */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
* Obscure configuration options
*
* You probably don't need to touch these.
diff --git a/src/core/background.c b/src/core/background.c
new file mode 100644
index 00000000..1cec05a5
--- /dev/null
+++ b/src/core/background.c
@@ -0,0 +1,47 @@
+#include "background.h"
+
+static struct background backgrounds[0] __table_start ( background );
+static struct background backgrounds_end[0] __table_end ( background );
+
+/** @file */
+
+/**
+ * Call send method of all background protocols
+ *
+ * @v timestamp Current time
+ * @ret None -
+ * @err None -
+ *
+ * This calls each background protocol's background::send() method.
+ */
+void background_send ( unsigned long timestamp ) {
+ struct background *background;
+
+ for ( background = backgrounds ; background < backgrounds_end ;
+ background++ ) {
+ if ( background->send )
+ background->send ( timestamp );
+ }
+}
+
+/**
+ * Call process method of all background protocols
+ *
+ * @v timestamp Current time
+ * @v ptype Packet type
+ * @v ip IP header, if present
+ * @ret None -
+ * @err None -
+ *
+ * This calls each background protocol's background::process() method.
+ */
+void background_process ( unsigned long timestamp, unsigned short ptype,
+ struct iphdr *ip ) {
+ struct background *background;
+
+ for ( background = backgrounds ; background < backgrounds_end ;
+ background++ ) {
+ if ( background->process )
+ background->process ( timestamp, ptype, ip );
+ }
+}
diff --git a/src/core/buffer.c b/src/core/buffer.c
index b300d4ec..b1a0464e 100644
--- a/src/core/buffer.c
+++ b/src/core/buffer.c
@@ -1,35 +1,59 @@
-/*
- * Routines for filling a buffer with data received piecemeal, where
- * the size of the data is not necessarily known in advance.
+
+/** @file
+ *
+ * Buffer internals.
*
- * Some protocols do not provide a mechanism for us to know the size
- * of the file before we happen to receive a particular block
- * (e.g. the final block in an MTFTP transfer). In addition, some
- * protocols (all the multicast protocols plus any TCP-based protocol)
- * can, in theory, provide the data in any order.
+ * A buffer consists of a single, contiguous area of memory, some of
+ * which is "filled" and the remainder of which is "free". The
+ * "filled" and "free" spaces are not necessarily contiguous.
*
- * Rather than requiring each protocol to implement its own equivalent
- * of "dd" to arrange the data into well-sized pieces before handing
- * off to the image loader, we provide these generic buffer functions
- * which assemble a file into a single contiguous block. The whole
- * block is then passed to the image loader.
+ * When a buffer is initialised via init_buffer(), it consists of a
+ * single free space. As data is added to the buffer via
+ * fill_buffer(), this free space decreases and can become fragmented.
*
+ * Each free block within a buffer starts with a "tail byte". If the
+ * tail byte is non-zero, this indicates that the free block is the
+ * tail of the buffer, i.e. occupies all the remaining space up to the
+ * end of the buffer. When the tail byte is non-zero, it indicates
+ * that a descriptor (a @c struct @c buffer_free_block) follows the
+ * tail byte. The descriptor describes the size of the free block and
+ * the address of the next free block.
*
+ * We cannot simply always start a free block with a descriptor,
+ * because it is conceivable that we will, at some point, encounter a
+ * situation in which the final free block of a buffer is too small to
+ * contain a descriptor. Consider a protocol with a blocksize of 512
+ * downloading a 1025-byte file into a 1025-byte buffer. Suppose that
+ * the first two blocks are received; we have now filled 1024 of the
+ * 1025 bytes in the buffer, and our only free block consists of the
+ * 1025th byte. Using a "tail byte" solves this problem.
+ *
+ *
* Note that the rather convoluted way of manipulating the buffer
* descriptors (using copy_{to,from}_phys rather than straightforward
* pointers) is needed to cope with operation as a PXE stack, when we
* may be running in real mode or 16-bit protected mode, and therefore
- * cannot directly access arbitrary areas of memory.
+ * cannot directly access arbitrary areas of memory using simple
+ * pointers.
*
*/
#include "stddef.h"
#include "string.h"
#include "io.h"
+#include "errno.h"
#include "buffer.h"
-/*
- * Initialise a buffer
+/**
+ * Initialise a buffer.
+ *
+ * @v buffer The buffer to be initialised
+ * @ret None -
+ * @err None -
+ *
+ * Set @c buffer->start and @c buffer->end before calling init_buffer().
+ * init_buffer() will initialise the buffer to the state of being
+ * empty.
*
*/
void init_buffer ( struct buffer *buffer ) {
@@ -42,77 +66,107 @@ void init_buffer ( struct buffer *buffer ) {
DBG ( "BUFFER [%x,%x) initialised\n", buffer->start, buffer->end );
}
-/*
- * Split a free block
+/**
+ * Move to the next block in the free list
*
- */
-static void split_free_block ( struct buffer_free_block *desc,
- physaddr_t block, physaddr_t split ) {
- /* If split point is before start of block, do nothing */
- if ( split <= block )
- return;
-
- /* If split point is after end of block, do nothing */
- if ( split >= desc->end )
- return;
-
- DBG ( "BUFFER splitting [%x,%x) -> [%x,%x) [%x,%x)\n",
- block, desc->end, block, split, split, desc->end );
-
- /* Create descriptor for new free block */
- copy_to_phys ( split, &desc->tail, sizeof ( desc->tail ) );
- if ( ! desc->tail )
- copy_to_phys ( split, desc, sizeof ( *desc ) );
-
- /* Update descriptor for old free block */
- desc->tail = 0;
- desc->next_free = split;
- desc->end = split;
- copy_to_phys ( block, desc, sizeof ( *desc ) );
-}
-
-/*
- * Mark a free block as used
+ * @v block The current free block
+ * @v buffer The buffer
+ * @ret True Successfully moved to the next free block
+ * @ret False There are no more free blocks
+ * @ret block The next free block
+ * @err None -
*
+ * Move to the next block in the free block list, filling in @c block
+ * with the descriptor for this next block. If the next block is the
+ * tail block, @c block will be filled with the values calculated for
+ * the tail block, otherwise the descriptor will be read from the free
+ * block itself.
+ *
+ * If there are no more free blocks, next_free_block() returns False
+ * and leaves @c block with invalid contents.
+ *
+ * Set <tt> block->next = buffer->start + buffer->fill </tt> for the
+ * first call to next_free_block().
*/
-static inline void unfree_block ( struct buffer *buffer,
- struct buffer_free_block *desc,
- physaddr_t prev_block ) {
- struct buffer_free_block prev_desc;
-
- /* If this is the first block, just update buffer->fill */
- if ( ! prev_block ) {
- DBG ( "BUFFER marking [%x,%x) as used\n",
- buffer->start + buffer->fill, desc->end );
- buffer->fill = desc->next_free - buffer->start;
- return;
- }
+static inline int next_free_block ( struct buffer_free_block *block,
+ struct buffer *buffer ) {
+ /* Move to next block */
+ block->start = block->next;
+
+ /* If at end of buffer, return 0 */
+ if ( block->start >= buffer->end )
+ return 0;
+
+ /* Set up ->next and ->end as for a tail block */
+ block->next = block->end = buffer->end;
+
+ /* Read tail marker from block */
+ copy_from_phys ( &block->tail, block->start, sizeof ( block->tail ) );
- /* Get descriptor for previous block (which cannot be a tail block) */
- copy_from_phys ( &prev_desc, prev_block, sizeof ( prev_desc ) );
+ /* If not a tail block, read whole block descriptor from block */
+ if ( ! block->tail ) {
+ copy_from_phys ( block, block->start, sizeof ( *block ) );
+ }
- DBG ( "BUFFER marking [%x,%x) as used\n",
- prev_desc.next_free, desc->end );
+ return 1;
+}
- /* Modify descriptor for previous block and write it back */
- prev_desc.next_free = desc->next_free;
- copy_to_phys ( prev_block, &prev_desc, sizeof ( prev_desc ) );
+/**
+ * Store a free block descriptor
+ *
+ * @v block The free block descriptor to store
+ * @ret None -
+ * @err None -
+ *
+ * Writes a free block descriptor back to a free block. If the block
+ * is a tail block, only the tail marker will be written, otherwise
+ * the whole block descriptor will be written.
+ */
+static inline void store_free_block ( struct buffer_free_block *block ) {
+ copy_to_phys ( block->start, block,
+ ( block->tail ?
+ sizeof ( block->tail ) : sizeof ( *block ) ) );
}
-/*
- * Write data into a buffer
+/**
+ * Write data into a buffer.
+ *
+ * @v buffer The buffer into which to write the data
+ * @v data The data to be written
+ * @v offset Offset within the buffer at which to write the data
+ * @v len Length of data to be written
+ * @ret True Data was successfully written
+ * @ret False Data was not written
+ * @err ENOMEM Buffer is too small to contain the data
+ *
+ * Writes a block of data into the buffer. The block need not be
+ * aligned to any particular boundary, or be of any particular size,
+ * and it may overlap blocks already in the buffer (i.e. duplicate
+ * calls to fill_buffer() are explicitly permitted).
*
- * It is the caller's responsibility to ensure that the boundaries
- * between data blocks are more than sizeof(struct buffer_free_block)
- * apart. If this condition is not satisfied, data corruption will
- * occur.
+ * @c buffer->fill will be updated to indicate the fill level of the
+ * buffer, i.e. the offset to the first gap within the buffer. If the
+ * filesize is known (e.g. as with the SLAM protocol), you can test
+ * for end-of-file by checking for @c buffer->fill==filesize. If the
+ * filesize is not known, but there is a well-defined end-of-file test
+ * (e.g. as with the TFTP protocol), you can read @c buffer->fill to
+ * determine the final filesize. If blocks are known to be delivered
+ * in a strictly sequential order with no packet loss or duplication,
+ * then you can pass in @c offset==buffer->fill.
+ *
+ * @b NOTE: It is the caller's responsibility to ensure that the
+ * boundaries between data blocks are more than @c sizeof(struct @c
+ * buffer_free_block) apart. If this condition is not satisfied, data
+ * corruption will occur.
+ *
+ * In practice this is not a problem. Callers of fill_buffer() will
+ * be download protocols such as TFTP, and very few protocols have a
+ * block size smaller than @c sizeof(struct @c buffer_free_block).
*
- * Returns 1 for success, 0 for failure (e.g. buffer too small).
*/
int fill_buffer ( struct buffer *buffer, const void *data,
off_t offset, size_t len ) {
- struct buffer_free_block desc;
- physaddr_t block, prev_block;
+ struct buffer_free_block block, before, after;
physaddr_t data_start, data_end;
/* Calculate start and end addresses of data */
@@ -125,41 +179,57 @@ int fill_buffer ( struct buffer *buffer, const void *data,
if ( data_end > buffer->end ) {
DBG ( "BUFFER [%x,%x) too small for data!\n",
buffer->start, buffer->end );
+ errno = ENOMEM;
return 0;
}
- /* Iterate through the buffer's free blocks */
- prev_block = 0;
- block = buffer->start + buffer->fill;
- while ( block < buffer->end ) {
- /* Read block descriptor */
- desc.next_free = buffer->end;
- desc.end = buffer->end;
- copy_from_phys ( &desc.tail, block, sizeof ( desc.tail ) );
- if ( ! desc.tail )
- copy_from_phys ( &desc, block, sizeof ( desc ) );
-
- /* Split block at data start and end markers */
- split_free_block ( &desc, block, data_start );
- split_free_block ( &desc, block, data_end );
-
- /* Block is now either completely contained by or
- * completely outside the data area
- */
- if ( ( block >= data_start ) && ( block < data_end ) ) {
- /* Block is within the data area */
- unfree_block ( buffer, &desc, prev_block );
- copy_to_phys ( block, data + ( block - data_start ),
- desc.end - block );
- } else {
- /* Block is outside the data area */
- prev_block = block;
- }
-
- /* Move to next free block */
- block = desc.next_free;
+ /* Find 'before' and 'after' blocks, if any */
+ before.start = before.end = 0;
+ after.start = after.end = buffer->end;
+ block.next = buffer->start + buffer->fill;
+ while ( next_free_block ( &block, buffer ) ) {
+ if ( ( block.start < data_start ) &&
+ ( block.start >= before.start ) )
+ memcpy ( &before, &block, sizeof ( before ) );
+ if ( ( block.end > data_end ) &&
+ ( block.end <= after.end ) )
+ memcpy ( &after, &block, sizeof ( after ) );
+ }
+
+ /* Truncate 'before' and 'after' blocks around data. */
+ if ( data_start < before.end )
+ before.end = data_start;
+ if ( data_end > after.start )
+ after.start = data_end;
+
+ /* Link 'after' block to 'before' block */
+ before.next = after.start;
+
+ /* Write back 'before' block, if any */
+ if ( before.start ) {
+ before.tail = 0;
+ ASSERT ( ( before.end - before.start ) >=
+ sizeof ( struct buffer_free_block ) );
+ store_free_block ( &before );
+ } else {
+ buffer->fill = before.next - buffer->start;
}
+ /* Write back 'after' block, if any */
+ if ( after.start < buffer->end ) {
+ ASSERT ( after.tail ||
+ ( ( after.end - after.start ) >=
+ sizeof ( struct buffer_free_block ) ) );
+ store_free_block ( &after );
+ }
+
+ DBG ( "BUFFER [%x,%x) before [%x,%x) after [%x,%x)\n",
+ buffer->start, buffer->end, before.start, before.end,
+ after.start, after.end );
+
+ /* Copy data into buffer */
+ copy_to_phys ( data_start, data, len );
+
DBG ( "BUFFER [%x,%x) full up to %x\n",
buffer->start, buffer->end, buffer->start + buffer->fill );
diff --git a/src/core/config.c b/src/core/config.c
index f8683063..7756c634 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -74,6 +74,9 @@ void print_config ( void ) {
#ifdef DOWNLOAD_PROTO_TFTP
"TFTP "
#endif
+#ifdef DOWNLOAD_PROTO_FSP
+ "FSP "
+#endif
#ifdef DOWNLOAD_PROTO_NFS
"NFS "
#endif
diff --git a/src/core/console.c b/src/core/console.c
index 2fbf8638..cd3239d6 100644
--- a/src/core/console.c
+++ b/src/core/console.c
@@ -1,31 +1,24 @@
-/*
- * Central console switch. Various console devices can be selected
- * via the build options CONSOLE_FIRMWARE, CONSOLE_SERIAL etc.
- * config.c picks up on these definitions and drags in the relevant
- * objects. The linker compiles the console_drivers table for us; we
- * simply delegate to each console_driver that we find in the table.
- *
- * Doing it this way allows for changing CONSOLE_XXX without
- * rebuilding anything other than config.o. This is extremely useful
- * for rom-o-matic.
- */
-
#include "stddef.h"
#include "console.h"
-/* FIXME: we need a cleaner way to pick up cpu_nap(). It makes a
- * real-mode call, and so we don't want to use it with LinuxBIOS.
- */
+/** @file */
+
#include "bios.h"
static struct console_driver console_drivers[0] __table_start ( console );
static struct console_driver console_drivers_end[0] __table_end ( console );
-/*****************************************************************************
- * putchar : write a single character to each console
- *****************************************************************************
+/**
+ * Write a single character to each console device.
+ *
+ * @v character Character to be written
+ * @ret None -
+ * @err None -
+ *
+ * The character is written out to all enabled console devices, using
+ * each device's console_driver::putchar() method.
+ *
*/
-
void putchar ( int character ) {
struct console_driver *console;
@@ -40,10 +33,18 @@ void putchar ( int character ) {
}
}
-/*****************************************************************************
- * has_input : check to see if any input is available on any console,
- * and return a pointer to the console device if so
- *****************************************************************************
+/**
+ * Check to see if any input is available on any console.
+ *
+ * @v None -
+ * @ret console Console device that has input available, if any.
+ * @ret NULL No console device has input available.
+ * @err None -
+ *
+ * All enabled console devices are checked once for available input
+ * using each device's console_driver::iskey() method. The first
+ * console device that has available input will be returned, if any.
+ *
*/
static struct console_driver * has_input ( void ) {
struct console_driver *console;
@@ -58,13 +59,30 @@ static struct console_driver * has_input ( void ) {
return NULL;
}
-/*****************************************************************************
- * getchar : read a single character from any console
+/**
+ * Read a single character from any console.
+ *
+ * @v None -
+ * @ret character Character read from a console.
+ * @err None -
+ *
+ * A character will be read from the first enabled console device that
+ * has input available using that console's console_driver::getchar()
+ * method. If no console has input available to be read, this method
+ * will block. To perform a non-blocking read, use something like
+ *
+ * @code
+ *
+ * int key = iskey() ? getchar() : -1;
+ *
+ * @endcode
+ *
+ * The character read will not be echoed back to any console.
+ *
+ * @bug We need a cleaner way to pick up cpu_nap(). It makes a
+ * real-mode call, and so we don't want to use it with LinuxBIOS.
*
- * NOTE : this function does not echo the character, and it does block
- *****************************************************************************
*/
-
int getchar ( void ) {
struct console_driver *console;
int character = 256;
@@ -92,11 +110,20 @@ int getchar ( void ) {
return character;
}
-/*****************************************************************************
- * iskey : check to see if any input is available on any console
- *****************************************************************************
+/** Check for available input on any console.
+ *
+ * @v None -
+ * @ret True Input is available on a console
+ * @ret False Input is not available on any console
+ * @err None -
+ *
+ * All enabled console devices are checked once for available input
+ * using each device's console_driver::iskey() method. If any console
+ * device has input available, this call will return True. If this
+ * call returns True, you can then safely call getchar() without
+ * blocking.
+ *
*/
-
int iskey ( void ) {
return has_input() ? 1 : 0;
}
diff --git a/src/core/errno.c b/src/core/errno.c
new file mode 100644
index 00000000..83c8564b
--- /dev/null
+++ b/src/core/errno.c
@@ -0,0 +1,61 @@
+#include "etherboot.h"
+#include "errno.h"
+#include "vsprintf.h"
+
+/** @file
+ *
+ * Error codes and descriptions.
+ *
+ * This file provides the global variable #errno and the function
+ * strerror(). These function much like their standard C library
+ * equivalents.
+ *
+ * The error numbers used by Etherboot are a superset of those defined
+ * by the PXE specification version 2.1. See errno.h for a listing of
+ * the error values.
+ *
+ * To save space in ROM images, error string tables are optional. Use
+ * the ERRORMSG_XXX options in config.h to select which error string
+ * tables you want to include. If an error string table is omitted,
+ * strerror() will simply return the text "Error 0x<errno>".
+ *
+ */
+
+/**
+ * Global "last error" number.
+ *
+ * This is valid only when a function has just returned indicating a
+ * failure.
+ *
+ */
+int errno;
+
+static struct errortab errortab_start[0] __table_start(errortab);
+static struct errortab errortab_end[0] __table_end(errortab);
+
+/**
+ * Retrieve string representation of error number.
+ *
+ * @v errno Error number
+ * @ret strerror Pointer to error text
+ *
+ * If the error is not found in the linked-in error tables, generates
+ * a generic "Error 0x<errno>" message.
+ *
+ * The pointer returned by strerror() is valid only until the next
+ * call to strerror().
+ *
+ */
+const char * strerror ( int errno ) {
+ static char *generic_message = "Error 0x0000";
+ struct errortab *errortab;
+
+ for ( errortab = errortab_start ; errortab < errortab_end ;
+ errortab++ ) {
+ if ( errortab->errno == errno )
+ return errortab->text;
+ }
+
+ sprintf ( generic_message + 8, "%hx", errno );
+ return generic_message;
+}
diff --git a/src/core/main.c b/src/core/main.c
index 3a112eee..e0cac222 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -31,6 +31,9 @@ Literature dealing with the network protocols:
#include <lib.h>
#endif
+/* Linker symbols */
+extern char _bss[], _ebss[];
+
jmp_buf restart_etherboot;
int url_port;
@@ -199,7 +202,7 @@ int main ( void ) {
/* Probe boot device */
if ( ! probe ( &dev ) ) {
/* Device found on bus, but probe failed */
- printf ( "...probe failed\n" );
+ printf ( "...probe failed: %m\n" );
continue;
}
@@ -212,14 +215,14 @@ int main ( void ) {
/* Configure boot device */
if ( ! configure ( &dev ) ) {
/* Configuration (e.g. DHCP) failed */
- printf ( "...configuration failed\n" );
+ printf ( "...configuration failed: %m\n" );
continue;
}
/* Load boot file from the device */
if ( ! autoload ( &dev, &image, &image_context ) ) {
/* Load (e.g. TFTP) failed */
- printf ( "...load failed\n" );
+ printf ( "...load failed: %m\n" );
continue;
}
@@ -233,7 +236,7 @@ int main ( void ) {
/* Boot the image */
if ( ! image->boot ( image_context ) ) {
/* Boot failed */
- printf ( "...boot failed\n" );
+ printf ( "...boot failed: %m\n" );
continue;
}
diff --git a/src/core/nic.c b/src/core/nic.c
index 3909f548..f80caabb 100644
--- a/src/core/nic.c
+++ b/src/core/nic.c
@@ -21,13 +21,10 @@ Literature dealing with the network protocols:
#include "resolv.h"
#include "dev.h"
#include "nic.h"
+#include "background.h"
#include "elf.h" /* FOR EM_CURRENT */
struct arptable_t arptable[MAX_ARP];
-#if MULTICAST_LEVEL2
-unsigned long last_igmpv1 = 0;
-struct igmptable_t igmptable[MAX_IGMP];
-#endif
/* Put rom_info in .nocompress section so romprefix.S can write to it */
struct rom_info rom __attribute__ ((section (".text16.nocompress"))) = {0,0};
static unsigned long netmask;
@@ -427,7 +424,6 @@ int ip_transmit(int len, const void *buf)
destip = ip->dest.s_addr;
if (destip == IP_BROADCAST) {
eth_transmit(broadcast, ETH_P_IP, len, buf);
-#ifdef MULTICAST_LEVEL1
} else if ((destip & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
unsigned char multicast[6];
unsigned long hdestip;
@@ -439,7 +435,6 @@ int ip_transmit(int len, const void *buf)
multicast[4] = (hdestip >> 8) & 0xff;
multicast[5] = hdestip & 0xff;
eth_transmit(multicast, ETH_P_IP, len, buf);
-#endif
} else {
if (((destip & netmask) !=
(arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
@@ -740,7 +735,7 @@ static int bootp(void)
remaining_time = rfc2131_sleep_interval(BOOTP_TIMEOUT, retry++);
stop_time = currticks() + remaining_time;
#ifdef NO_DHCP_SUPPORT
- if (await_reply(await_bootp, 0, NULL, timeout))
+ if (await_reply(await_bootp, 0, NULL, remaining_time))
return(1);
#else
while ( remaining_time > 0 ) {
@@ -827,140 +822,6 @@ uint16_t tcpudpchksum(struct iphdr *ip)
return checksum;
}
-#ifdef MULTICAST_LEVEL2
-static void send_igmp_reports(unsigned long now)
-{
- int i;
- for(i = 0; i < MAX_IGMP; i++) {
- if (igmptable[i].time && (now >= igmptable[i].time)) {
- struct igmp_ip_t igmp;
- igmp.router_alert[0] = 0x94;
- igmp.router_alert[1] = 0x04;
- igmp.router_alert[2] = 0;
- igmp.router_alert[3] = 0;
- build_ip_hdr(igmptable[i].group.s_addr,
- 1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
- igmp.igmp.type = IGMPv2_REPORT;
- if (last_igmpv1 &&
- (now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT)) {
- igmp.igmp.type = IGMPv1_REPORT;
- }
- igmp.igmp.response_time = 0;
- igmp.igmp.chksum = 0;
- igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
- igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp.igmp));
- ip_transmit(sizeof(igmp), &igmp);
-#ifdef MDEBUG
- printf("Sent IGMP report to: %@\n", igmp.igmp.group.s_addr);
-#endif
- /* Don't send another igmp report until asked */
- igmptable[i].time = 0;
- }
- }
-}
-
-static void process_igmp(struct iphdr *ip, unsigned long now)
-{
- struct igmp *igmp;
- int i;
- unsigned iplen;
- if (!ip || (ip->protocol == IP_IGMP) ||
- (nic.packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) {
- return;
- }
- iplen = (ip->verhdrlen & 0xf)*4;
- igmp = (struct igmp *)&nic.packet[sizeof(struct iphdr)];
- if (ipchksum(igmp, ntohs(ip->len) - iplen) != 0)
- return;
- if ((igmp->type == IGMP_QUERY) &&
- (ip->dest.s_addr == htonl(GROUP_ALL_HOSTS))) {
- unsigned long interval = IGMP_INTERVAL;
- if (igmp->response_time == 0) {
- last_igmpv1 = now;
- } else {
- interval = (igmp->response_time * TICKS_PER_SEC)/10;
- }
-
-#ifdef MDEBUG
- printf("Received IGMP query for: %@\n", igmp->group.s_addr);
-#endif
- for(i = 0; i < MAX_IGMP; i++) {
- uint32_t group = igmptable[i].group.s_addr;
- if ((group == 0) || (group == igmp->group.s_addr)) {
- unsigned long time;
- time = currticks() + rfc1112_sleep_interval(interval, 0);
- if (time < igmptable[i].time) {
- igmptable[i].time = time;
- }
- }
- }
- }
- if (((igmp->type == IGMPv1_REPORT) || (igmp->type == IGMPv2_REPORT)) &&
- (ip->dest.s_addr == igmp->group.s_addr)) {
-#ifdef MDEBUG
- printf("Received IGMP report for: %@\n", igmp->group.s_addr);
-#endif
- for(i = 0; i < MAX_IGMP; i++) {
- if ((igmptable[i].group.s_addr == igmp->group.s_addr) &&
- igmptable[i].time != 0) {
- igmptable[i].time = 0;
- }
- }
- }
-}
-
-void leave_group(int slot)
-{
- /* Be very stupid and always send a leave group message if
- * I have subscribed. Imperfect but it is standards
- * compliant, easy and reliable to implement.
- *
- * The optimal group leave method is to only send leave when,
- * we were the last host to respond to a query on this group,
- * and igmpv1 compatibility is not enabled.
- */
- if (igmptable[slot].group.s_addr) {
- struct igmp_ip_t igmp;
- igmp.router_alert[0] = 0x94;
- igmp.router_alert[1] = 0x04;
- igmp.router_alert[2] = 0;
- igmp.router_alert[3] = 0;
- build_ip_hdr(htonl(GROUP_ALL_HOSTS),
- 1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
- igmp.igmp.type = IGMP_LEAVE;
- igmp.igmp.response_time = 0;
- igmp.igmp.chksum = 0;
- igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
- igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp));
- ip_transmit(sizeof(igmp), &igmp);
-#ifdef MDEBUG
- printf("Sent IGMP leave for: %@\n", igmp.igmp.group.s_addr);
-#endif
- }
- memset(&igmptable[slot], 0, sizeof(igmptable[0]));
-}
-
-void join_group(int slot, unsigned long group)
-{
- /* I have already joined */
- if (igmptable[slot].group.s_addr == group)
- return;
- if (igmptable[slot].group.s_addr) {
- leave_group(slot);
- }
- /* Only join a group if we are given a multicast ip, this way
- * code can be given a non-multicast (broadcast or unicast ip)
- * and still work...
- */
- if ((group & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
- igmptable[slot].group.s_addr = group;
- igmptable[slot].time = currticks();
- }
-}
-#else
-#define send_igmp_reports(now) do {} while(0)
-#define process_igmp(ip, now) do {} while(0)
-#endif
#include "proto_eth_slow.c"
@@ -985,8 +846,8 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
*/
for (;;) {
now = currticks();
+ background_send(now);
send_eth_slow_reports(now);
- send_igmp_reports(now);
result = eth_poll(1);
if (result == 0) {
/* We don't have anything */
@@ -1104,8 +965,8 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
#endif /* MDEBUG */
}
}
+ background_process(now, ptype, ip);
process_eth_slow(ptype, now);
- process_igmp(ip, now);
}
return(0);
}
@@ -1289,28 +1150,10 @@ RFC2131_SLEEP_INTERVAL - sleep for expotentially longer times (base << exp) +- 1
long rfc2131_sleep_interval(long base, int exp)
{
unsigned long tmo;
-#ifdef BACKOFF_LIMIT
if (exp > BACKOFF_LIMIT)
exp = BACKOFF_LIMIT;
-#endif
tmo = (base << exp) + (TICKS_PER_SEC - (random()/TWO_SECOND_DIVISOR));
return tmo;
}
-#ifdef MULTICAST_LEVEL2
-/**************************************************************************
-RFC1112_SLEEP_INTERVAL - sleep for expotentially longer times, up to (base << exp)
-**************************************************************************/
-long rfc1112_sleep_interval(long base, int exp)
-{
- unsigned long divisor, tmo;
-#ifdef BACKOFF_LIMIT
- if (exp > BACKOFF_LIMIT)
- exp = BACKOFF_LIMIT;
-#endif
- divisor = RAND_MAX/(base << exp);
- tmo = random()/divisor;
- return tmo;
-}
-#endif /* MULTICAST_LEVEL_2 */
diff --git a/src/core/pxe_export.c b/src/core/pxe_export.c
deleted file mode 100644
index 8b878947..00000000
--- a/src/core/pxe_export.c
+++ /dev/null
@@ -1,1445 +0,0 @@
-/* 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.
- */
-
-/* Tags used in this file:
- *
- * FIXME : obvious
- * PXESPEC : question over interpretation of the PXE spec.
- */
-
-#ifdef PXE_EXPORT
-
-#include "etherboot.h"
-#include "pxe.h"
-#include "pxe_export.h"
-#include "pxe_callbacks.h"
-#include "dev.h"
-#include "nic.h"
-#include "pci.h"
-#include "cpu.h"
-#include "timer.h"
-
-#undef DBG
-#if TRACE_PXE
-#define DBG(...) printf ( __VA_ARGS__ )
-#else
-#define DBG(...)
-#endif
-
-/* Not sure why this isn't a globally-used structure within Etherboot.
- * (Because I didn't want to use packed to prevent gcc from aligning
- * source however it liked. Also nstype is a u16, not a uint. - Ken)
- */
-typedef struct {
- char dest[ETH_ALEN];
- char source[ETH_ALEN];
- unsigned int nstype;
-} media_header_t;
-static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
-
-/* Global pointer to currently installed PXE stack */
-pxe_stack_t *pxe_stack = NULL;
-
-/* Various startup/shutdown routines. The startup/shutdown call
- * sequence is incredibly badly defined in the Intel PXE spec, for
- * example:
- *
- * PXENV_UNDI_INITIALIZE says that the parameters used to initialize
- * the adaptor should be those supplied to the most recent
- * PXENV_UNDI_STARTUP call. PXENV_UNDI_STARTUP takes no parameters.
- *
- * PXENV_UNDI_CLEANUP says that the rest of the API will not be
- * available after making this call. Figure 3-3 ("Early UNDI API
- * usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
- * call to the supposedly now unavailable PXENV_STOP_UNDI.
- *
- * PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
- * occupied by the PXE stack. Figure 4-3 ("PXE IPL") shows a call
- * to PXENV_STOP_UNDI being made after the call to
- * PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
- * should have been freed (and, potentially, zeroed).
- *
- * Nothing, anywhere, seems to mention who's responsible for freeing
- * up the base memory allocated for the stack segment. It's not
- * even clear whether or not this is expected to be in free base
- * memory rather than claimed base memory.
- *
- * Consequently, we adopt a rather defensive strategy, designed to
- * work with any conceivable sequence of initialisation or shutdown
- * calls. We have only two things that we care about:
- *
- * 1. Have we hooked INT 1A and INT 15,E820(etc.)?
- * 2. Is the NIC initialised?
- *
- * The NIC should never be initialised without the vectors being
- * hooked, similarly the vectors should never be unhooked with the NIC
- * still initialised. We do, however, want to be able to have the
- * vectors hooked with the NIC shutdown. We therefore have three
- * possible states:
- *
- * 1. Ready to unload: interrupts unhooked, NIC shutdown.
- * 2. Midway: interrupts hooked, NIC shutdown.
- * 3. Fully ready: interrupts hooked, NIC initialised.
- *
- * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
- * these, and the call pxe_ensure_state() to ensure that the stack is
- * in the specified state. All our PXE API call implementations
- * should use this call to ensure that the state is as required for
- * that PXE API call. This enables us to cope with whatever the
- * end-user's interpretation of the PXE spec may be. It even allows
- * for someone calling e.g. PXENV_START_UNDI followed by
- * PXENV_UDP_WRITE, without bothering with any of the intervening
- * calls.
- *
- * pxe_ensure_state() returns 1 for success, 0 for failure. In the
- * event of failure (which can arise from e.g. asking for state READY
- * when we don't know where our NIC is), the error code
- * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
- * The macros ENSURE_XXX() can be used to achieve this without lots of
- * duplicated code.
- */
-
-/* pxe_[un]hook_stack are architecture-specific and provided in
- * pxe_callbacks.c
- */
-
-int pxe_initialise_nic ( void ) {
- if ( pxe_stack->state >= READY ) return 1;
-
-#warning "device probing mechanism has completely changed"
-#if 0
-
- /* Check if NIC is initialised. dev.disable is set to 0
- * when disable() is called, so we use this.
- */
- if ( dev.disable ) {
- /* NIC may have been initialised independently
- * (e.g. when we set up the stack prior to calling the
- * NBP).
- */
- pxe_stack->state = READY;
- return 1;
- }
-
- /* If we already have a NIC defined, reuse that one with
- * PROBE_AWAKE. If one was specifed via PXENV_START_UNDI, try
- * that one first. Otherwise, set PROBE_FIRST.
- */
-
- if ( dev.state.pci.dev.use_specified == 1 ) {
- dev.how_probe = PROBE_NEXT;
- DBG ( " initialising NIC specified via START_UNDI" );
- } else if ( dev.state.pci.dev.driver ) {
- DBG ( " reinitialising NIC" );
- dev.how_probe = PROBE_AWAKE;
- } else {
- DBG ( " probing for any NIC" );
- dev.how_probe = PROBE_FIRST;
- }
-
- /* Call probe routine to bring up the NIC */
- if ( eth_probe ( &dev ) != PROBE_WORKED ) {
- DBG ( " failed" );
- return 0;
- }
-#endif
-
-
- pxe_stack->state = READY;
- return 1;
-}
-
-int pxe_shutdown_nic ( void ) {
- if ( pxe_stack->state <= MIDWAY ) return 1;
-
- eth_irq ( DISABLE );
- disable ( &dev );
- pxe_stack->state = MIDWAY;
- return 1;
-}
-
-int ensure_pxe_state ( pxe_stack_state_t wanted ) {
- int success = 1;
-
- if ( ! pxe_stack ) return 0;
- if ( wanted >= MIDWAY )
- success = success & hook_pxe_stack();
- if ( wanted > MIDWAY ) {
- success = success & pxe_initialise_nic();
- } else {
- success = success & pxe_shutdown_nic();
- }
- if ( wanted < MIDWAY )
- success = success & unhook_pxe_stack();
- return success;
-}
-
-#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \
- structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
- return PXENV_EXIT_FAILURE; }
-#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \
- structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
- return PXENV_EXIT_FAILURE; }
-#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \
- structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
- return PXENV_EXIT_FAILURE; }
-
-/*****************************************************************************
- *
- * Actual PXE API calls
- *
- *****************************************************************************/
-
-/* PXENV_START_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI *start_undi ) {
- unsigned char bus, devfn;
-
- DBG ( "PXENV_START_UNDI" );
- ENSURE_MIDWAY(start_undi);
-
- /* Record PCI bus & devfn passed by caller, so we know which
- * NIC they want to use.
- *
- * If they don't match our already-existing NIC structure, set
- * values to ensure that the specified NIC is used at the next
- * call to pxe_intialise_nic().
- */
- bus = ( start_undi->ax >> 8 ) & 0xff;
- devfn = start_undi->ax & 0xff;
-
-#warning "device probing mechanism has completely changed"
-#if 0
- if ( ( pci->dev.driver == NULL ) ||
- ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
- /* This is quite a bit of a hack and relies on
- * knowledge of the internal operation of Etherboot's
- * probe mechanism.
- */
- DBG ( " set PCI %hhx:%hhx.%hhx",
- bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
- dev->type = BOOT_NIC;
- dev->to_probe = PROBE_PCI;
- memset ( &dev->state, 0, sizeof(dev->state) );
- pci->advance = 1;
- pci->dev.use_specified = 1;
- pci->dev.bus = bus;
- pci->dev.devfn = devfn;
- }
-#endif
-
- start_undi->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_STARTUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP *undi_startup ) {
- DBG ( "PXENV_UNDI_STARTUP" );
- ENSURE_MIDWAY(undi_startup);
-
- undi_startup->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLEANUP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP *undi_cleanup ) {
- DBG ( "PXENV_UNDI_CLEANUP" );
- ENSURE_CAN_UNLOAD ( undi_cleanup );
-
- undi_cleanup->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_INITIALIZE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE
- *undi_initialize ) {
- DBG ( "PXENV_UNDI_INITIALIZE" );
- ENSURE_MIDWAY ( 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 ( t_PXENV_UNDI_RESET_ADAPTER
- *undi_reset_adapter ) {
- DBG ( "PXENV_UNDI_RESET_ADAPTER" );
-
- ENSURE_MIDWAY ( undi_reset_adapter );
- ENSURE_READY ( undi_reset_adapter );
-
- undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_SHUTDOWN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
- DBG ( "PXENV_UNDI_SHUTDOWN" );
- ENSURE_MIDWAY ( undi_shutdown );
-
- undi_shutdown->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN *undi_open ) {
- DBG ( "PXENV_UNDI_OPEN" );
- ENSURE_READY ( undi_open );
-
- /* PXESPEC: This is where we choose to enable interrupts.
- * Can't actually find where we're meant to in the PXE spec,
- * but this should work.
- */
- eth_irq ( ENABLE );
-
- undi_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE *undi_close ) {
- DBG ( "PXENV_UNDI_CLOSE" );
- ENSURE_MIDWAY ( undi_close );
-
- undi_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_TRANSMIT
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT *undi_transmit ) {
- t_PXENV_UNDI_TBD *tbd;
- const char *dest;
- unsigned int type;
- unsigned int length;
- const char *data;
- media_header_t *media_header;
-
- DBG ( "PXENV_UNDI_TRANSMIT" );
- ENSURE_READY ( undi_transmit );
-
- /* We support only the "immediate" portion of the TBD. Who
- * knows what Intel's "engineers" were smoking when they came
- * up with the array of transmit data blocks...
- */
- tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
- if ( tbd->DataBlkCount > 0 ) {
- undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
- return PXENV_EXIT_FAILURE;
- }
- data = SEGOFF16_TO_PTR ( tbd->Xmit );
- length = tbd->ImmedLength;
-
- /* If destination is broadcast, we need to supply the MAC address */
- if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
- dest = broadcast_mac;
- } else {
- dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
- }
-
- /* We can't properly support P_UNKNOWN without rewriting all
- * the driver transmit() methods, so we cheat: if P_UNKNOWN is
- * specified we rip the destination address and type out of
- * the pre-assembled packet, then skip over the header.
- */
- switch ( undi_transmit->Protocol ) {
- case P_IP: type = IP; break;
- case P_ARP: type = ARP; break;
- case P_RARP: type = RARP; break;
- case P_UNKNOWN:
- media_header = (media_header_t*)data;
- dest = media_header->dest;
- type = ntohs ( media_header->nstype );
- data += ETH_HLEN;
- length -= ETH_HLEN;
- break;
- default:
- undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
- return PXENV_EXIT_FAILURE;
- }
-
- /* Send the packet */
- eth_transmit ( dest, type, length, data );
-
- 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 ( t_PXENV_UNDI_SET_MCAST_ADDRESS
- *undi_set_mcast_address ) {
- DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
- /* ENSURE_READY ( undi_set_mcast_address ); */
- undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_SET_STATION_ADDRESS
- *
- * Status: working (deliberately incomplete)
- */
-PXENV_EXIT_t pxenv_undi_set_station_address ( t_PXENV_UNDI_SET_STATION_ADDRESS
- *undi_set_station_address ) {
- DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
- ENSURE_READY ( undi_set_station_address );
-
- /* We don't offer a facility to set the MAC address; this
- * would require adding extra code to all the Etherboot
- * drivers, for very little benefit. If we're setting it to
- * the current value anyway then return success, otherwise
- * return UNSUPPORTED.
- */
- if ( memcmp ( nic.node_addr,
- &undi_set_station_address->StationAddress,
- ETH_ALEN ) == 0 ) {
- undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
- }
- undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* 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 ( t_PXENV_UNDI_SET_PACKET_FILTER
- *undi_set_packet_filter ) {
- DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
- /* ENSURE_READY ( 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 ( t_PXENV_UNDI_GET_INFORMATION
- *undi_get_information ) {
- DBG ( "PXENV_UNDI_GET_INFORMATION" );
- ENSURE_READY ( undi_get_information );
-
- undi_get_information->BaseIo = nic.ioaddr;
- undi_get_information->IntNumber = nic.irqno;
- /* Cheat: assume all cards can cope with this */
- undi_get_information->MaxTranUnit = ETH_MAX_MTU;
- /* Cheat: we only ever have Ethernet cards */
- undi_get_information->HwType = ETHER_TYPE;
- undi_get_information->HwAddrLen = ETH_ALEN;
- /* 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, nic.node_addr,
- ETH_ALEN );
- memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
- ETH_ALEN );
- 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: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS
- *undi_get_statistics ) {
- DBG ( "PXENV_UNDI_GET_STATISTICS" );
- /* ENSURE_READY ( undi_get_statistics ); */
- undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_CLEAR_STATISTICS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_clear_statistics ( t_PXENV_UNDI_CLEAR_STATISTICS
- *undi_clear_statistics ) {
- DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
- /* ENSURE_READY ( undi_clear_statistics ); */
- undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_INITIATE_DIAGS
- *
- * Status: won't implement (would require driver API changes for no
- * real benefit)
- */
-PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS
- *undi_initiate_diags ) {
- DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
- /* ENSURE_READY ( undi_initiate_diags ); */
- undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_UNDI_FORCE_INTERRUPT
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_force_interrupt ( t_PXENV_UNDI_FORCE_INTERRUPT
- *undi_force_interrupt ) {
- DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
- ENSURE_READY ( undi_force_interrupt );
-
- eth_irq ( FORCE );
- undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNDI_GET_MCAST_ADDRESS
- *
- * Status: stub (no PXE multicast support)
- */
-PXENV_EXIT_t pxenv_undi_get_mcast_address ( t_PXENV_UNDI_GET_MCAST_ADDRESS
- *undi_get_mcast_address ) {
- DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
- /* ENSURE_READY ( 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 ( t_PXENV_UNDI_GET_NIC_TYPE
- *undi_get_nic_type ) {
-#warning "device probing mechanism has changed completely"
-
-#if 0
- struct dev *dev = &dev;
-
- DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
- ENSURE_READY ( undi_get_nic_type );
-
- if ( dev->to_probe == PROBE_PCI ) {
- struct pci_device *pci = &dev->state.pci.dev;
-
- undi_get_nic_type->NicType = PCI_NIC;
- undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
- undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
- undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
- undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
- undi_get_nic_type->info.pci.BusDevFunc =
- ( pci->bus << 8 ) | pci->devfn;
- /* Cheat: these fields are probably unnecessary, and
- * would require adding extra code to pci.c.
- */
- undi_get_nic_type->info.pci.Prog_Intf = 0;
- undi_get_nic_type->info.pci.Rev = 0;
- undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
- undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
- } else if ( dev->to_probe == PROBE_ISA ) {
- /* const struct isa_driver *isa = dev->state.isa.driver; */
-
- undi_get_nic_type->NicType = PnP_NIC;
- /* Don't think anything fills these fields in, and
- * probably no-one will ever be interested in them.
- */
- undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
- undi_get_nic_type->info.pnp.Base_Class = 0;
- undi_get_nic_type->info.pnp.Sub_Class = 0;
- undi_get_nic_type->info.pnp.Prog_Intf = 0;
- undi_get_nic_type->info.pnp.CardSelNum = 0;
- } else {
- /* PXESPEC: There doesn't seem to be an "unknown type"
- * defined.
- */
- undi_get_nic_type->NicType = 0;
- }
- undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-
-#endif
-}
-
-/* PXENV_UNDI_GET_IFACE_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO
- *undi_get_iface_info ) {
- DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
- ENSURE_READY ( undi_get_iface_info );
-
- /* Just hand back some info, doesn't really matter what it is.
- * Most PXE stacks seem to take this approach.
- */
- sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
- 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_ISR
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) {
- media_header_t *media_header = (media_header_t*)nic.packet;
-
- DBG ( "PXENV_UNDI_ISR" );
- /* We can't call ENSURE_READY, because this could be being
- * called as part of an interrupt service routine. Instead,
- * we should simply die if we're not READY.
- */
- if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
- undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
- return PXENV_EXIT_FAILURE;
- }
-
- /* 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 :
- /* Is there a packet waiting? If so, disable
- * interrupts on the NIC and return "it's ours". Do
- * *not* necessarily acknowledge the interrupt; this
- * can happen later when eth_poll(1) is called. As
- * long as the interrupt is masked off so that it
- * doesn't immediately retrigger the 8259A then all
- * should be well.
- */
- DBG ( " START" );
- if ( eth_poll ( 0 ) ) {
- DBG ( " OURS" );
- eth_irq ( DISABLE );
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
- } else {
- DBG ( " NOT_OURS" );
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
- }
- break;
- case PXENV_UNDI_ISR_IN_PROCESS :
- /* Call poll(), return packet. If no packet, return "done".
- */
- DBG ( " PROCESS" );
- if ( eth_poll ( 1 ) ) {
- DBG ( " RECEIVE %d", nic.packetlen );
- if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
- /* Should never happen */
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
- undi_isr->Status =
- PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
- }
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
- undi_isr->BufferLength = nic.packetlen;
- undi_isr->FrameLength = nic.packetlen;
- undi_isr->FrameHeaderLength = ETH_HLEN;
- memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
- PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
- switch ( ntohs(media_header->nstype) ) {
- case IP : undi_isr->ProtType = P_IP; break;
- case ARP : undi_isr->ProtType = P_ARP; break;
- case RARP : undi_isr->ProtType = P_RARP; break;
- default : undi_isr->ProtType = P_UNKNOWN;
- }
- if ( memcmp ( media_header->dest, broadcast_mac,
- sizeof(broadcast_mac) ) ) {
- undi_isr->PktType = XMT_BROADCAST;
- } else {
- undi_isr->PktType = XMT_DESTADDR;
- }
- break;
- } else {
- /* No break - fall through to IN_GET_NEXT */
- }
- case PXENV_UNDI_ISR_IN_GET_NEXT :
- /* We only ever return one frame at a time */
- DBG ( " GET_NEXT DONE" );
- /* Re-enable interrupts */
- eth_irq ( ENABLE );
- /* Force an interrupt if there's a packet still
- * waiting, since we only handle one packet per
- * interrupt.
- */
- if ( eth_poll ( 0 ) ) {
- DBG ( " (RETRIGGER)" );
- eth_irq ( FORCE );
- }
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
- break;
- default :
- /* 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;
-}
-
-/* PXENV_STOP_UNDI
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI *stop_undi ) {
- DBG ( "PXENV_STOP_UNDI" );
-
- if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
- stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
- return PXENV_EXIT_FAILURE;
- }
-
- stop_undi->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN *tftp_open ) {
- struct sockaddr_in tftp_server;
- struct tftpreq_info_t request;
- struct tftpblk_info_t block;
-
- DBG ( "PXENV_TFTP_OPEN" );
- ENSURE_READY ( tftp_open );
-
- /* Set server address and port */
- tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
- ? tftp_open->ServerIPAddress
- : arptable[ARP_SERVER].ipaddr.s_addr;
- tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
-#ifdef WORK_AROUND_BPBATCH_BUG
- /* Force use of port 69; BpBatch tries to use port 4 for some
- * bizarre reason. */
- tftp_server.sin_port = TFTP_PORT;
-#endif
- /* Ignore gateway address; we can route properly */
- /* Fill in request structure */
- request.server = &tftp_server;
- request.name = tftp_open->FileName;
- request.blksize = tftp_open->PacketSize;
- DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
- tftp_open->TFTPPort, request.name, request.blksize );
- if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
- /* Make request and get first packet */
- if ( !tftp_block ( &request, &block ) ) {
- tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
- return PXENV_EXIT_FAILURE;
- }
- /* Fill in PacketSize */
- tftp_open->PacketSize = request.blksize;
- /* Store first block for later retrieval by TFTP_READ */
- pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
- pxe_stack->tftpdata.len = block.len;
- pxe_stack->tftpdata.eof = block.eof;
- memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
-
- tftp_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE *tftp_close ) {
- DBG ( "PXENV_TFTP_CLOSE" );
- ENSURE_READY ( tftp_close );
- tftp_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_READ
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ *tftp_read ) {
- struct tftpblk_info_t block;
-
- DBG ( "PXENV_TFTP_READ" );
- ENSURE_READY ( tftp_read );
-
- /* Do we have a block pending */
- if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
- block.data = pxe_stack->tftpdata.data;
- block.len = pxe_stack->tftpdata.len;
- block.eof = pxe_stack->tftpdata.eof;
- block.block = 1; /* Will be the first block */
- pxe_stack->tftpdata.magic_cookie = 0;
- } else {
- if ( !tftp_block ( NULL, &block ) ) {
- tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
- return PXENV_EXIT_FAILURE;
- }
- }
-
- /* Return data */
- tftp_read->PacketNumber = block.block;
- tftp_read->BufferSize = block.len;
- memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
- DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
- tftp_read->Buffer.offset );
-
- tftp_read->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_READ_FILE
- *
- * Status: working
- */
-
-int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused,
- unsigned int len, int eof ) {
- if ( pxe_stack->readfile.buffer ) {
- if ( pxe_stack->readfile.offset + len >=
- pxe_stack->readfile.bufferlen ) return -1;
- memcpy ( pxe_stack->readfile.buffer +
- pxe_stack->readfile.offset, data, len );
- }
- pxe_stack->readfile.offset += len;
- return eof ? 0 : 1;
-}
-
-PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE *tftp_read_file ) {
- struct sockaddr_in tftp_server;
- int rc;
-
- DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
- tftp_read_file->Buffer,
- tftp_read_file->Buffer + tftp_read_file->BufferSize );
- ENSURE_READY ( tftp_read_file );
-
- /* inserted by Klaus Wittemeier */
- /* KERNEL_BUF stores the name of the last required file */
- /* This is a fix to make Microsoft Remote Install Services work (RIS) */
- memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
- /* end of insertion */
-
- /* Set server address and port */
- tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
- ? tftp_read_file->ServerIPAddress
- : arptable[ARP_SERVER].ipaddr.s_addr;
- tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
-
- pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
- pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
- pxe_stack->readfile.offset = 0;
-
- rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
- pxe_tftp_read_block );
- if ( rc ) {
- tftp_read_file->Status = PXENV_STATUS_FAILURE;
- return PXENV_EXIT_FAILURE;
- }
- tftp_read_file->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_TFTP_GET_FSIZE
- *
- * Status: working, though ugly (we actually read the whole file,
- * because it's too ugly to make Etherboot request the tsize option
- * and hand it to us).
- */
-PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE *tftp_get_fsize ) {
- int rc;
-
- DBG ( "PXENV_TFTP_GET_FSIZE" );
- ENSURE_READY ( tftp_get_fsize );
-
- pxe_stack->readfile.buffer = NULL;
- pxe_stack->readfile.bufferlen = 0;
- pxe_stack->readfile.offset = 0;
-
-#warning "Rewrite pxenv_tftp_get_fsize, please"
- if ( rc ) {
- tftp_get_fsize->FileSize = 0;
- tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
- return PXENV_EXIT_FAILURE;
- }
- tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
- tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_OPEN
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN *udp_open ) {
- DBG ( "PXENV_UDP_OPEN" );
- ENSURE_READY ( udp_open );
-
- if ( udp_open->src_ip &&
- udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
- /* Overwrite our IP address */
- DBG ( " with new IP %@", udp_open->src_ip );
- arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
- }
-
- udp_open->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_CLOSE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE *udp_close ) {
- DBG ( "PXENV_UDP_CLOSE" );
- ENSURE_READY ( udp_close );
- udp_close->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_READ
- *
- * Status: working
- */
-int await_pxe_udp ( int ival __unused, void *ptr,
- unsigned short ptype __unused,
- struct iphdr *ip, struct udphdr *udp,
- struct tcphdr *tcp __unused ) {
- t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr;
- uint16_t d_port;
- size_t size;
-
- /* Ignore non-UDP packets */
- if ( !udp ) {
- DBG ( " non-UDP" );
- return 0;
- }
-
- /* Check dest_ip */
- if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
- DBG ( " wrong dest IP (got %@, wanted %@)",
- ip->dest.s_addr, udp_read->dest_ip );
- return 0;
- }
-
- /* Check dest_port */
- d_port = ntohs ( udp_read->d_port );
- if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
- DBG ( " wrong dest port (got %d, wanted %d)",
- ntohs(udp->dest), d_port );
- return 0;
- }
-
- /* Copy packet to buffer and fill in information */
- udp_read->src_ip = ip->src.s_addr;
- udp_read->s_port = udp->src; /* Both in network order */
- size = ntohs(udp->len) - sizeof(*udp);
- /* Workaround: NTLDR expects us to fill these in, even though
- * PXESPEC clearly defines them as input parameters.
- */
- udp_read->dest_ip = ip->dest.s_addr;
- udp_read->d_port = udp->dest;
- DBG ( " %@:%d->%@:%d (%d)",
- udp_read->src_ip, ntohs(udp_read->s_port),
- udp_read->dest_ip, ntohs(udp_read->d_port), size );
- if ( udp_read->buffer_size < size ) {
- /* PXESPEC: what error code should we actually return? */
- DBG ( " buffer too small (%d)", udp_read->buffer_size );
- udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return 0;
- }
- memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
- udp_read->buffer_size = size;
-
- return 1;
-}
-
-PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ *udp_read ) {
- DBG ( "PXENV_UDP_READ" );
- ENSURE_READY ( udp_read );
-
- /* Use await_reply with a timeout of zero */
- /* Allow await_reply to change Status if necessary */
- udp_read->Status = PXENV_STATUS_FAILURE;
- if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
- return PXENV_EXIT_FAILURE;
- }
-
- udp_read->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UDP_WRITE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE *udp_write ) {
- uint16_t src_port;
- uint16_t dst_port;
- struct udppacket *packet = (struct udppacket *)nic.packet;
- int packet_size;
-
- DBG ( "PXENV_UDP_WRITE" );
- ENSURE_READY ( udp_write );
-
- /* PXE spec says source port is 2069 if not specified */
- src_port = ntohs(udp_write->src_port);
- if ( src_port == 0 ) src_port = 2069;
- dst_port = ntohs(udp_write->dst_port);
- DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
- udp_write->buffer_size );
-
- /* 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.
- */
-
- /* Copy payload to packet buffer */
- packet_size = ( (void*)&packet->payload - (void*)packet )
- + udp_write->buffer_size;
- if ( packet_size > ETH_FRAME_LEN ) {
- udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
- return PXENV_EXIT_FAILURE;
- }
- memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
- udp_write->buffer_size );
-
- /* Transmit packet */
- if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
- packet_size, packet ) ) {
- udp_write->Status = PXENV_STATUS_FAILURE;
- return PXENV_EXIT_FAILURE;
- }
-
- udp_write->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_UNLOAD_STACK
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK *unload_stack ) {
- int success;
-
- DBG ( "PXENV_UNLOAD_STACK" );
- success = ensure_pxe_state ( CAN_UNLOAD );
-
- /* We need to call cleanup() at some point. The network card
- * has already been disabled by ENSURE_CAN_UNLOAD(), but for
- * the sake of completeness we should call the console_fini()
- * etc. that are part of cleanup().
- *
- * There seems to be a lack of consensus on which is the final
- * PXE API call to make, but it's a fairly safe bet that all
- * the potential shutdown sequences will include a call to
- * PXENV_UNLOAD_STACK at some point, so we may as well do it
- * here.
- */
- cleanup();
-
- if ( ! success ) {
- unload_stack->Status = PXENV_STATUS_KEEP_ALL;
- return PXENV_EXIT_FAILURE;
- }
-
- unload_stack->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_GET_CACHED_INFO
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO
- *get_cached_info ) {
- BOOTPLAYER *cached_info = &pxe_stack->cached_info;
- DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
- ENSURE_READY ( get_cached_info );
-
- /* Fill in cached_info structure in our pxe_stack */
-
- /* I don't think there's actually any way we can be called in
- * the middle of a DHCP request...
- */
- cached_info->opcode = BOOTP_REP;
- /* We only have Ethernet drivers */
- cached_info->Hardware = ETHER_TYPE;
- cached_info->Hardlen = ETH_ALEN;
- /* PXESPEC: "Client sets" says the spec, but who's filling in
- * this structure? It ain't the client.
- */
- cached_info->Gatehops = 0;
- cached_info->ident = 0;
- cached_info->seconds = 0;
- cached_info->Flags = BOOTP_BCAST;
- /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
- cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
- cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
- cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
- /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
- cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
- memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
- /* Nullify server name */
- cached_info->Sname[0] = '\0';
- memcpy ( cached_info->bootfile, KERNEL_BUF,
- sizeof(cached_info->bootfile) );
- /* Copy DHCP vendor options */
- memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
- sizeof(cached_info->vendor.d) );
-
- /* Copy to user-specified buffer, or set pointer to our buffer */
- get_cached_info->BufferLimit = sizeof(*cached_info);
- /* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
- * 0, but what are we supposed to do with a null buffer of
- * non-zero size?!
- */
- if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
- /* Point back to our buffer */
- PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
- get_cached_info->BufferSize = sizeof(*cached_info);
- } else {
- /* Copy to user buffer */
- size_t size = sizeof(*cached_info);
- void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
- if ( get_cached_info->BufferSize < size )
- size = get_cached_info->BufferSize;
- DBG ( " to %x", virt_to_phys ( buffer ) );
- memcpy ( buffer, cached_info, size );
- /* PXESPEC: Should we return an error if the user
- * buffer is too small? We do return the actual size
- * of the buffer via BufferLimit, so the user does
- * have a way to detect this already.
- */
- }
-
- get_cached_info->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_RESTART_TFTP
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP *restart_tftp ) {
- PXENV_EXIT_t tftp_exit;
-
- DBG ( "PXENV_RESTART_TFTP" );
- ENSURE_READY ( restart_tftp );
-
- /* Words cannot describe the complete mismatch between the PXE
- * specification and any possible version of reality...
- */
- restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
- restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
- DBG ( "(" );
- tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (t_PXENV_ANY*)restart_tftp );
- DBG ( ")" );
- if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
-
- /* Fire up the new NBP */
- restart_tftp->Status = xstartpxe();
-
- /* Not sure what "SUCCESS" actually means, since we can only
- * return if the new NBP failed to boot...
- */
- return PXENV_EXIT_SUCCESS;
-}
-
-/* PXENV_START_BASE
- *
- * Status: won't implement (requires major structural changes)
- */
-PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE *start_base ) {
- DBG ( "PXENV_START_BASE" );
- /* ENSURE_READY ( start_base ); */
- start_base->Status = PXENV_STATUS_UNSUPPORTED;
- return PXENV_EXIT_FAILURE;
-}
-
-/* PXENV_STOP_BASE
- *
- * Status: working
- */
-PXENV_EXIT_t pxenv_stop_base ( t_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;
-}
-
-/* PXENV_UNDI_LOADER
- *
- * Status: working
- *
- * NOTE: This is not a genuine PXE API call; the loader has a separate
- * entry point. However, to simplify the mapping of the PXE API to
- * the internal Etherboot API, both are directed through the same
- * interface.
- */
-PXENV_EXIT_t pxenv_undi_loader ( undi_loader_t *loader ) {
- uint32_t loader_phys = virt_to_phys ( loader );
-
- DBG ( "PXENV_UNDI_LOADER" );
-
- /* Set UNDI DS as our real-mode stack */
- use_undi_ds_for_rm_stack ( loader->undi_ds );
-
- /* FIXME: These lines are borrowed from main.c. There should
- * probably be a single initialise() function that does all
- * this, but it's currently split interestingly between main()
- * and main_loop()...
- */
-
-
- /* CHECKME: Our init functions have probably already been
- called by the ROM prefix's call to setup(), haven't
- they? */
-
-
-
- /* We have relocated; the loader pointer is now invalid */
- loader = phys_to_virt ( loader_phys );
-
- /* Install PXE stack to area specified by NBP */
- install_pxe_stack ( VIRTUAL ( loader->undi_cs, 0 ) );
-
- /* Call pxenv_start_undi to set parameters. Why the hell PXE
- * requires these parameters to be provided twice is beyond
- * the wit of any sane man. Don't worry if it fails; the NBP
- * should call PXENV_START_UNDI separately anyway.
- */
- pxenv_start_undi ( &loader->start_undi );
- /* Unhook stack; the loader is not meant to hook int 1a etc,
- * but the call the pxenv_start_undi will cause it to happen.
- */
- ENSURE_CAN_UNLOAD ( loader );
-
- /* Fill in addresses of !PXE and PXENV+ structures */
- PTR_TO_SEGOFF16 ( &pxe_stack->pxe, loader->pxe_ptr );
- PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, loader->pxenv_ptr );
-
- loader->Status = PXENV_STATUS_SUCCESS;
- return PXENV_EXIT_SUCCESS;
-}
-
-/* API call dispatcher
- *
- * Status: complete
- */
-PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params ) {
- PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
-
- /* Set default status in case child routine fails to do so */
- params->Status = PXENV_STATUS_FAILURE;
-
- DBG ( "[" );
-
- /* Hand off to relevant API routine */
- switch ( opcode ) {
- case PXENV_START_UNDI:
- ret = pxenv_start_undi ( &params->start_undi );
- break;
- case PXENV_UNDI_STARTUP:
- ret = pxenv_undi_startup ( &params->undi_startup );
- break;
- case PXENV_UNDI_CLEANUP:
- ret = pxenv_undi_cleanup ( &params->undi_cleanup );
- break;
- case PXENV_UNDI_INITIALIZE:
- ret = pxenv_undi_initialize ( &params->undi_initialize );
- break;
- case PXENV_UNDI_RESET_ADAPTER:
- ret = pxenv_undi_reset_adapter ( &params->undi_reset_adapter );
- break;
- case PXENV_UNDI_SHUTDOWN:
- ret = pxenv_undi_shutdown ( &params->undi_shutdown );
- break;
- case PXENV_UNDI_OPEN:
- ret = pxenv_undi_open ( &params->undi_open );
- break;
- case PXENV_UNDI_CLOSE:
- ret = pxenv_undi_close ( &params->undi_close );
- break;
- case PXENV_UNDI_TRANSMIT:
- ret = pxenv_undi_transmit ( &params->undi_transmit );
- break;
- case PXENV_UNDI_SET_MCAST_ADDRESS:
- ret = pxenv_undi_set_mcast_address (
- &params->undi_set_mcast_address );
- break;
- case PXENV_UNDI_SET_STATION_ADDRESS:
- ret = pxenv_undi_set_station_address (
- &params->undi_set_station_address );
- break;
- case PXENV_UNDI_SET_PACKET_FILTER:
- ret = pxenv_undi_set_packet_filter (
- &params->undi_set_packet_filter );
- break;
- case PXENV_UNDI_GET_INFORMATION:
- ret = pxenv_undi_get_information (
- &params->undi_get_information );
- break;
- case PXENV_UNDI_GET_STATISTICS:
- ret = pxenv_undi_get_statistics (
- &params->undi_get_statistics );
- break;
- case PXENV_UNDI_CLEAR_STATISTICS:
- ret = pxenv_undi_clear_statistics (
- &params->undi_clear_statistics );
- break;
- case PXENV_UNDI_INITIATE_DIAGS:
- ret = pxenv_undi_initiate_diags (
- &params->undi_initiate_diags );
- break;
- case PXENV_UNDI_FORCE_INTERRUPT:
- ret = pxenv_undi_force_interrupt (
- &params->undi_force_interrupt );
- break;
- case PXENV_UNDI_GET_MCAST_ADDRESS:
- ret = pxenv_undi_get_mcast_address (
- &params->undi_get_mcast_address );
- break;
- case PXENV_UNDI_GET_NIC_TYPE:
- ret = pxenv_undi_get_nic_type ( &params->undi_get_nic_type );
- break;
- case PXENV_UNDI_GET_IFACE_INFO:
- ret = pxenv_undi_get_iface_info (
- &params->undi_get_iface_info );
- break;
- case PXENV_UNDI_ISR:
- ret = pxenv_undi_isr ( &params->undi_isr );
- break;
- case PXENV_STOP_UNDI:
- ret = pxenv_stop_undi ( &params->stop_undi );
- break;
- case PXENV_TFTP_OPEN:
- ret = pxenv_tftp_open ( &params->tftp_open );
- break;
- case PXENV_TFTP_CLOSE:
- ret = pxenv_tftp_close ( &params->tftp_close );
- break;
- case PXENV_TFTP_READ:
- ret = pxenv_tftp_read ( &params->tftp_read );
- break;
- case PXENV_TFTP_READ_FILE:
- ret = pxenv_tftp_read_file ( &params->tftp_read_file );
- break;
- case PXENV_TFTP_GET_FSIZE:
- ret = pxenv_tftp_get_fsize ( &params->tftp_get_fsize );
- break;
- case PXENV_UDP_OPEN:
- ret = pxenv_udp_open ( &params->udp_open );
- break;
- case PXENV_UDP_CLOSE:
- ret = pxenv_udp_close ( &params->udp_close );
- break;
- case PXENV_UDP_READ:
- ret = pxenv_udp_read ( &params->udp_read );
- break;
- case PXENV_UDP_WRITE:
- ret = pxenv_udp_write ( &params->udp_write );
- break;
- case PXENV_UNLOAD_STACK:
- ret = pxenv_unload_stack ( &params->unload_stack );
- break;
- case PXENV_GET_CACHED_INFO:
- ret = pxenv_get_cached_info ( &params->get_cached_info );
- break;
- case PXENV_RESTART_TFTP:
- ret = pxenv_restart_tftp ( &params->restart_tftp );
- break;
- case PXENV_START_BASE:
- ret = pxenv_start_base ( &params->start_base );
- break;
- case PXENV_STOP_BASE:
- ret = pxenv_stop_base ( &params->stop_base );
- break;
- case PXENV_UNDI_LOADER:
- ret = pxenv_undi_loader ( &params->loader );
- break;
-
- default:
- DBG ( "PXENV_UNKNOWN_%hx", opcode );
- params->Status = PXENV_STATUS_UNSUPPORTED;
- ret = PXENV_EXIT_FAILURE;
- break;
- }
-
- if ( params->Status != PXENV_STATUS_SUCCESS ) {
- DBG ( " %hx", params->Status );
- }
- if ( ret != PXENV_EXIT_SUCCESS ) {
- DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
- }
- DBG ( "]" );
-
- return ret;
-}
-
-#endif /* PXE_EXPORT */
diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c
index 414b4509..fda0e96c 100644
--- a/src/core/vsprintf.c
+++ b/src/core/vsprintf.c
@@ -2,6 +2,7 @@
#include "if_ether.h" /* for ETH_ALEN */
#include "limits.h" /* for CHAR_BIT */
#include "console.h"
+#include "errno.h"
#include "vsprintf.h"
#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
@@ -9,29 +10,26 @@
#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
-/**************************************************************************
-PRINTF and friends
+/** @file */
- Formats:
- %[#]x - 4 bytes int (8 hex digits, lower case)
- %[#]X - 4 bytes int (8 hex digits, upper case)
- %[#]lx - 8 bytes long (16 hex digits, lower case)
- %[#]lX - 8 bytes long (16 hex digits, upper case)
- %[#]hx - 2 bytes int (4 hex digits, lower case)
- %[#]hX - 2 bytes int (4 hex digits, upper case)
- %[#]hhx - 1 byte int (2 hex digits, lower case)
- %[#]hhX - 1 byte int (2 hex digits, upper case)
- - optional # prefixes 0x or 0X
- %d - decimal int
- %c - char
- %s - string
- %@ - Internet address in ddd.ddd.ddd.ddd notation
- %! - Ethernet address in xx:xx:xx:xx:xx:xx notation
- Note: width specification ignored
-**************************************************************************/
+/**
+ * Write a formatted string to a buffer.
+ *
+ * @v buf Buffer into which to write the string, or NULL
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of string written to buffer (if buf != NULL)
+ * @ret 0 (if buf == NULL)
+ * @err None -
+ *
+ * If #buf==NULL, then the string will be written to the console
+ * directly using putchar().
+ *
+ */
static int vsprintf(char *buf, const char *fmt, va_list args)
{
- char *p, *s;
+ const char *p;
+ char *s;
s = buf;
for ( ; *fmt != '\0'; ++fmt) {
if (*fmt != '%') {
@@ -49,8 +47,10 @@ static int vsprintf(char *buf, const char *fmt, va_list args)
if (*fmt == 's') {
for(p = va_arg(args, char *); *p != '\0'; p++)
buf ? *s++ = *p : putchar(*p);
- }
- else { /* Length of item is bounded */
+ } else if (*fmt == 'm') {
+ for(p = strerror(errno); *p != '\0'; p++)
+ buf ? *s++ = *p : putchar(*p);
+ } else { /* Length of item is bounded */
char tmp[40], *q = tmp;
int alt = 0;
int shift = INT_SHIFT;
@@ -93,7 +93,7 @@ static int vsprintf(char *buf, const char *fmt, va_list args)
*q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
}
else if (*fmt == 'd') {
- char *r;
+ char *r, *t;
long i;
if (shift > INT_SHIFT) {
i = va_arg(args, long);
@@ -104,17 +104,17 @@ static int vsprintf(char *buf, const char *fmt, va_list args)
*q++ = '-';
i = -i;
}
- p = q; /* save beginning of digits */
+ t = q; /* save beginning of digits */
do {
*q++ = '0' + (i % 10);
i /= 10;
} while (i);
/* reverse digits, stop in middle */
r = q; /* don't alter q */
- while (--r > p) {
+ while (--r > t) {
i = *r;
- *r = *p;
- *p++ = i;
+ *r = *t;
+ *t++ = i;
}
}
else if (*fmt == '@') {
@@ -129,7 +129,7 @@ static int vsprintf(char *buf, const char *fmt, va_list args)
--q;
}
else if (*fmt == '!') {
- char *r;
+ const char *r;
p = va_arg(args, char *);
for (r = p + ETH_ALEN; p < r; ++p)
q += sprintf(q, "%hhX:", *p);
@@ -149,6 +149,20 @@ static int vsprintf(char *buf, const char *fmt, va_list args)
return (s - buf);
}
+/**
+ * Write a formatted string to a buffer.
+ *
+ * @v buf Buffer into which to write the string, or NULL
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of string written to buffer (if buf != NULL)
+ * @ret 0 (if buf == NULL)
+ * @err None -
+ *
+ * If #buf==NULL, then the string will be written to the console
+ * directly using putchar().
+ *
+ */
int sprintf(char *buf, const char *fmt, ...)
{
va_list args;
@@ -159,6 +173,15 @@ int sprintf(char *buf, const char *fmt, ...)
return i;
}
+/**
+ * Write a formatted string to the console.
+ *
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret None -
+ * @err None -
+ *
+ */
void printf(const char *fmt, ...)
{
va_list args;
diff --git a/src/doc/build_sys.dox b/src/doc/build_sys.dox
new file mode 100644
index 00000000..9466f662
--- /dev/null
+++ b/src/doc/build_sys.dox
@@ -0,0 +1,419 @@
+/** @page build_sys Build system
+
+@section overview Overview
+
+Building an Etherboot image consists of three stages:
+
+ -# @ref compilation : Compiling all the source files into object files
+ -# @ref linking : Linking a particular image from selected object files
+ -# @ref finalisation : Producing the final output binary
+
+Though this is a remarkably complex process, it is important to note
+that it all happens automatically. Whatever state your build tree is
+in, you can always type, for example
+
+@code
+
+ make bin/rtl8139.dsk
+
+@endcode
+
+and know that you will get a floppy disk image with an RTL8139 driver
+built from the current sources.
+
+@section compilation Compilation
+
+@subsection comp_overview Overview
+
+Each source file (a @c .c or a @c .S file) is compiled into a @c .o
+file in the @c bin/ directory. Etherboot makes minimal use of
+conditional compilation (see @ref ifdef_harmful), and so you will find
+that all objects get built, even the objects that correspond to
+features that you are not intending to include in your image. For
+example, all network card drivers will be compiled even if you are
+just building a ROM for a 3c509 card. This is a deliberate design
+decision; please do @b not attempt to "fix" the build system to avoid
+doing this.
+
+Source files are defined to be any @c .c or @c .S files found in a
+directory listed in the Makefile variable #SRCDIRS. You therefore do
+@b not need to edit the Makefile just because you have added a new
+source file (although you will need to edit the Makefile if you have
+added a new source directory). To see a list of all source
+directories and source files that the build system currently knows
+about, you can use the commands
+
+@code
+
+ make srcdirs
+ make srcs
+
+@endcode
+
+Rules for compiling @c .c and @c .S files are defined in the Makefile
+variables #RULE_c and #RULE_S. Makefile rules are automatically
+generated for each source file using these rules. The generated rules
+can be found in the @c .d file corresponding to each source file;
+these are located in <tt>bin/deps/</tt>. For example, the rules
+generated for <tt>drivers/net/rtl8139.c</tt> can be found in
+<tt>bin/deps/drivers/net/rtl8139.c.d</tt>. These rules allow you to
+type, for example
+
+@code
+
+ make bin/rtl8139.o
+
+@endcode
+
+and have <tt>rtl8139.o</tt> be built from
+<tt>drivers/net/rtl8139.c</tt> using the generic rule #RULE_c for
+compiling @c .c files.
+
+You can see the full list of object files that will be built using
+
+@code
+
+ make bobjs
+
+@endcode
+
+@subsection comp_ar After compilation
+
+Once all objects have been compiled, they will be collected into a
+build library ("blib") in <tt>bin/blib.a</tt>.
+
+@subsection comp_custom Customising compilation
+
+The Makefile rules for a particular object can be customised to a
+certain extent by defining the Makefile variable CFLAGS_@<object@>.
+For example, if you were to set
+
+@code
+
+ CFLAGS_rtl8139 = -DFOO
+
+@endcode
+
+then <tt>bin/rtl8139.o</tt> would be compiled with the additional
+flags <tt>-DFOO</tt>. To see the flags that will be used when
+compiling a particular object, you can use e.g.
+
+@code
+
+ make bin/rtl8139.flags
+
+@endcode
+
+If you need more flexibility than the CFLAGS_@<object@> mechanism
+provides, then you can exclude source files from the automatic rule
+generation process by listing them in the Makefile variable
+#NON_AUTO_SRCS. The command
+
+@code
+
+ make autosrcs
+
+@endcode
+
+will show you which files are currently part of the automatic rule
+generation process.
+
+@subsection comp_multiobj Multiple objects
+
+A single source file can be used to generate multiple object files.
+This is used, for example, to generate the decompressing and the
+non-decompressing prefixes from the same source files.
+
+By default, a single object will be built from each source file. To
+override the list of objects for a source file, you can define the
+Makefile variable OBJS_@<object@>. For example, the
+<tt>arch/i386/prefix/dskprefix.S</tt> source file is built into two
+objects, <tt>bin/dskprefix.o</tt> and <tt>zdskprefix.o</tt> by
+defining the Makefile variable
+
+@code
+
+ OBJS_dskprefix = dskprefix zdskprefix
+
+@endcode
+
+Since there would be little point in building two identical objects,
+customised compilation flags (see @ref comp_custom) are defined as
+
+@code
+
+ CFLAGS_zdskprefix = -DCOMPRESS
+
+@endcode
+
+Thus, <tt>arch/i386/prefix/dskprefix.S</tt> is built into @c
+dskprefix.o using the normal set of flags, and into @c zdskprefix.o
+using the normal set of flags plus <tt>-DCOMPRESS</tt>.
+
+@subsection comp_debug Special debugging targets
+
+In addition to the basic rules #RULE_c and #RULE_S for compiling
+source files into object files, there are various other rules that can
+be useful for debugging.
+
+@subsubsection comp_debug_c_to_c Preprocessed C
+
+You can see the results of preprocessing a @c .c file (including the
+per-object flags defined via CFLAGS_@<object@> if applicable) using
+e.g.
+
+@code
+
+ make bin/rtl8139.c
+
+@endcode
+
+and examining the resulting file (<tt>bin/rtl8139.c</tt> in this
+case).
+
+@subsubsection comp_debug_x_to_s Assembler
+
+You can see the results of assembling a @c .c file, or of
+preprocessing a @c .S file, using e.g.
+
+@code
+
+ make bin/rtl8139.s
+ make bin/zdskprefix.s
+
+@endcode
+
+@subsubsection comp_debug_dbg Debugging-enabled targets
+
+You can build targets with debug messages (DBG()) enabled using e.g.
+
+@code
+
+ make bin/rtl8139.dbg.o
+ make bin/rtl8139.dbg2.o
+
+@endcode
+
+You will probably not need to use these targets directly, since a
+mechanism exists to select debugging levels at build time; see @ref
+debug.
+
+@section linking Linking
+
+@subsection link_overview Overview
+
+Etherboot is designed to be small and extremely customisable. This is
+achieved by linking in only the features that are really wanted in any
+particular build.
+
+There are two places from which the list of desired features is
+obtained:
+
+ -# @ref link_config_h
+ -# @ref link_cmdline
+
+@subsection link_config_h config.h
+
+The config.h file is used to define global build options that are
+likely to apply to all images that you build, such as the console
+types, supported download protocols etc. See the documentation for
+config.h for more details.
+
+@subsection link_cmdline The make command line
+
+When you type a command such as
+
+@code
+
+ make bin/dfe538.zrom
+
+@endcode
+
+it is used to derive the following information:
+
+ - We are building a compressed ROM image
+ - The DFE538 is a PCI NIC, so we need the decompressing PCI ROM prefix
+ - The PCI IDs for the DFE538 are 1186:1300
+ - The DFE538 is an rtl8139-based card, therefore we need the rtl8139 driver
+
+You can see this process in action using the command
+
+@code
+
+ make bin/dfe538.zrom.info
+
+@endcode
+
+which will print
+
+@code
+
+ Elements : dfe538
+ Prefix : zrom
+ Drivers : rtl8139
+ ROM name : dfe538
+ Media : rom
+
+ ROM type : pci
+ PCI vendor : 0x1186
+ PCI device : 0x1300
+
+ LD driver symbols : obj_rtl8139
+ LD prefix symbols : obj_zpciprefix
+ LD ID symbols : pci_vendor_id=0x1186 pci_device_id=0x1300
+
+ LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300
+
+@endcode
+
+This should be interpreted as follows:
+
+@code
+
+ Elements : dfe538
+ Prefix : zrom
+
+@endcode
+
+"Elements" is the list of components preceding the first dot in the
+target name. "Prefix" is the component following the first dot in the
+target name. (It's called a "prefix" because the code that makes it a
+@c .zrom (rather than a @c .dsk, @c .zpxe or any other type of target)
+usually ends up at the start of the resulting binary image.)
+
+@code
+
+ Drivers : rtl8139
+
+@endcode
+
+"Drivers" is the list of drivers corresponding to the "Elements".
+Most drivers support several network cards. The PCI_ROM() and
+ISA_ROM() macros are used in the driver source files to list the cards
+that a particular driver can support.
+
+@code
+
+ ROM name : dfe538
+
+@endcode
+
+"ROM name" is the first element in the "Elements" list. It is used to
+select the PCI IDs for a PCI ROM.
+
+@code
+
+ Media : rom
+
+@endcode
+
+"Media" is the "Prefix" minus the leading @c z, if any.
+
+@code
+
+ ROM type : pci
+ PCI vendor : 0x1186
+ PCI device : 0x1300
+
+@endcode
+
+These are derived from the "ROM name" and the PCI_ROM() or ISA_ROM()
+macros in the driver source files.
+
+@code
+
+ LD driver symbols : obj_rtl8139
+ LD prefix symbols : obj_zpciprefix
+
+@endcode
+
+This is the interesting part. At this point, we have established that
+we need the rtl8139 driver (i.e. @c rtl8139.o) and the decompressing
+PCI prefix (i.e. @c zpciprefix.o). Our build system (via the
+compiler.h header file) arranges that every object exports a symbol
+obj_@<object@>; this can be seen by e.g.
+
+@code
+
+ objdump -t bin/rtl8139.o
+
+@endcode
+
+which will show the line
+
+@code
+
+ 00000000 g *ABS* 00000000 obj_rtl8139
+
+@endcode
+
+By instructing the linker that we need the symbols @c obj_rtl8139 and
+@c obj_zpciprefix, we can therefore ensure that these two objects are
+included in our build. (The linker will also include any objects that
+these two objects require, since that's the whole purpose of the
+linker.)
+
+In a similar way, we always instruct the linker that we need the
+symbol @c obj_config, in order to include the object @c config.o. @c
+config.o is used to drag in the objects that were specified via
+config.h; see @ref link_config_h.
+
+@code
+
+ LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300
+
+@endcode
+
+These are the flags that we pass to the linker in order to include the
+objects that we want in our build, and to check that they really get
+included. (This latter check is needed to work around what seems to
+be a bug in @c ld).
+
+The linker does its job of linking all the required objects together
+into a coherent build. The best way to see what is happening is to
+look at one of the resulting linker maps; try, for example
+
+@code
+
+ make bin/dfe538.dsk.map
+
+@endcode
+
+The linker map includes, amongst others:
+
+ - A list of which objects are included in the build, and why.
+ - The results of processing the linker script, line-by-line.
+ - A complete symbol table of the resulting build.
+
+It is worth spending time examining the linker map to see how an
+Etherboot image is assembled.
+
+Whatever format is selected, the Etherboot image is built into an ELF
+file, simply because this is the default format used by @c ld.
+
+@section finalisation Finalisation
+
+@subsection final_overview Overview
+
+The ELF file resulting from @ref linking "linking" needs to be
+converted into the final binary image. Usually, this is just a case
+of running
+
+@code
+
+ objcopy -O binary <elf file> <output file>
+
+@endcode
+
+to convert the ELF file into a raw binary image. Certain image
+formats require special additional treatment.
+
+@subsection final_rom ROM images
+
+ROM images must be rounded up to a suitable ROM size (e.g. 16kB or
+32kB), and certain header information such as checksums needs to be
+filled in. This is done by the @c makerom.pl program.
+
+@section debug Debugging-enabled builds
+
+*/
diff --git a/src/doxygen.cfg b/src/doxygen.cfg
new file mode 100644
index 00000000..b0bc4b9a
--- /dev/null
+++ b/src/doxygen.cfg
@@ -0,0 +1,208 @@
+# Doxyfile 1.2.17
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Etherboot
+PROJECT_NUMBER =
+OUTPUT_DIRECTORY = @BIN@/doc
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH =
+INTERNAL_DOCS = YES
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = YES
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = NO
+JAVADOC_AUTOBRIEF = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = NO
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ALIASES = v=@param \
+ ret=@retval \
+ err=@exception
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_FORMAT =
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = @SRCDIRS@ \
+ include \
+ arch/@ARCH@/include \
+ doc
+FILE_PATTERNS = *.c \
+ *.h \
+ *.S \
+ *.dox
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT =
+HTML_FILE_EXTENSION =
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT =
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = YES
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT =
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = YES
+MAN_OUTPUT =
+MAN_EXTENSION =
+MAN_LINKS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_SCHEMA =
+XML_DTD =
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = YES
+SEARCH_INCLUDES = YES
+INCLUDE_PATH = include \
+ arch/@ARCH@/include
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = DOXYGEN=1
+EXPAND_AS_DEFINED = __attribute__ \
+ PACKED \
+ __unused \
+ __used \
+ __aligned \
+ __table \
+ __table_start \
+ __table_end
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH =
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME =
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH =
+EXT_DOC_PATHS =
diff --git a/src/drivers/bus/isapnp.c b/src/drivers/bus/isapnp.c
index 6b7c79e4..6476398c 100644
--- a/src/drivers/bus/isapnp.c
+++ b/src/drivers/bus/isapnp.c
@@ -33,16 +33,18 @@
*
***************************************************************************/
-#include "string.h"
-#include "timer.h"
-#include "io.h"
-#include "console.h"
-#include "isapnp.h"
-
-/*
- * We can have only one ISAPnP bus in a system. Once the read port is
- * known and all cards have been allocated CSNs, there's nothing to be
- * gained by re-scanning for cards.
+/** @file
+ *
+ * ISAPnP bus support
+ *
+ * Etherboot orignally gained ISAPnP support in a very limited way for
+ * the 3c515 NIC. The current implementation is almost a complete
+ * rewrite based on the ISAPnP specification, with passing reference
+ * to the Linux ISAPnP code.
+ *
+ * There can be only one ISAPnP bus in a system. Once the read port
+ * is known and all cards have been allocated CSNs, there's nothing to
+ * be gained by re-scanning for cards.
*
* However, we shouldn't make scanning the ISAPnP bus an INIT_FN(),
* because even ISAPnP probing can still screw up other devices on the
@@ -50,18 +52,30 @@
* an ISAPnP device.
*
* External code (e.g. the ISAPnP ROM prefix) may already know the
- * read port address, in which case it can initialise this value.
- * Note that setting the read port address will prevent further
- * isolation from taking place; you should set the read port address
- * only if you know that devices have already been allocated CSNs.
+ * read port address, in which case it can store it in
+ * #isapnp_read_port. Note that setting the read port address in this
+ * way will prevent further isolation from taking place; you should
+ * set the read port address only if you know that devices have
+ * already been allocated CSNs.
+ *
+ */
+
+#include "string.h"
+#include "timer.h"
+#include "io.h"
+#include "console.h"
+#include "isapnp.h"
+
+/**
+ * ISAPnP Read Port address.
*
*/
uint16_t isapnp_read_port;
-/*
+/**
* Highest assigned CSN.
*
- * Note that *we* do not necessarily assign CSNs; it could be done by
+ * Note that @b we do not necessarily assign CSNs; it could be done by
* the PnP BIOS instead. We therefore set this only when we first try
* to Wake[CSN] a device and find that there's nothing there. Page 16
* (PDF page 22) of the ISAPnP spec states that "Valid Card Select
@@ -116,22 +130,56 @@ static inline uint16_t isapnp_read_word ( uint8_t address ) {
+ isapnp_read_byte ( address + 1 ) );
}
+/** Inform cards of a new read port address */
static inline void isapnp_set_read_port ( void ) {
isapnp_write_byte ( ISAPNP_READPORT, isapnp_read_port >> 2 );
}
+/**
+ * Enter the Isolation state.
+ *
+ * Only cards currently in the Sleep state will respond to this
+ * command.
+ *
+ */
static inline void isapnp_serialisolation ( void ) {
isapnp_write_address ( ISAPNP_SERIALISOLATION );
}
+/**
+ * Enter the Wait for Key state.
+ *
+ * All cards will respond to this command, regardless of their current
+ * state.
+ *
+ */
static inline void isapnp_wait_for_key ( void ) {
isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY );
}
+/**
+ * Reset (i.e. remove) Card Select Number.
+ *
+ * Only cards currently in the Sleep state will respond to this
+ * command.
+ *
+ */
static inline void isapnp_reset_csn ( void ) {
isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN );
}
+/**
+ * Place a specified card into the Config state.
+ *
+ * @v csn Card Select Number
+ * @ret None -
+ * @err None -
+ *
+ * Only cards currently in the Sleep, Isolation, or Config states will
+ * respond to this command. The card that has the specified CSN will
+ * enter the Config state, all other cards will enter the Sleep state.
+ *
+ */
static inline void isapnp_wake ( uint8_t csn ) {
isapnp_write_byte ( ISAPNP_WAKE, csn );
}
@@ -144,6 +192,19 @@ static inline uint8_t isapnp_read_status ( void ) {
return isapnp_read_byte ( ISAPNP_STATUS );
}
+/**
+ * Assign a Card Select Number to a card, and enter the Config state.
+ *
+ * @v csn Card Select Number
+ * @ret None -
+ * @err None -
+ *
+ * Only cards in the Isolation state will respond to this command.
+ * The isolation protocol is designed so that only one card will
+ * remain in the Isolation state by the time the isolation protocol
+ * completes.
+ *
+ */
static inline void isapnp_write_csn ( uint8_t csn ) {
isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn );
}
@@ -174,12 +235,20 @@ static void isapnp_delay ( void ) {
udelay ( 1000 );
}
-/*
- * The linear feedback shift register as described in Appendix B of
- * the PnP ISA spec. The hardware implementation uses eight D-type
- * latches and two XOR gates. I think this is probably the smallest
- * possible implementation in software. Six instructions when input_bit
- * is a constant 0 (for isapnp_send_key). :)
+/**
+ * Linear feedback shift register.
+ *
+ * @v lfsr Current value of the LFSR
+ * @v input_bit Current input bit to the LFSR
+ * @ret lfsr Next value of the LFSR
+ * @err None -
+ *
+ * This routine implements the linear feedback shift register as
+ * described in Appendix B of the PnP ISA spec. The hardware
+ * implementation uses eight D-type latches and two XOR gates. I
+ * think this is probably the smallest possible implementation in
+ * software. Six instructions when input_bit is a constant 0 (for
+ * isapnp_send_key). :)
*
*/
static inline uint8_t isapnp_lfsr_next ( uint8_t lfsr, int input_bit ) {
@@ -190,8 +259,11 @@ static inline uint8_t isapnp_lfsr_next ( uint8_t lfsr, int input_bit ) {
return lfsr_next;
}
-/*
- * Send the ISAPnP initiation key
+/**
+ * Send the ISAPnP initiation key.
+ *
+ * Sending the key causes all ISAPnP cards that are currently in the
+ * Wait for Key state to transition into the Sleep state.
*
*/
static void isapnp_send_key ( void ) {
@@ -209,8 +281,12 @@ static void isapnp_send_key ( void ) {
}
}
-/*
- * Compute ISAPnP identifier checksum
+/**
+ * Compute ISAPnP identifier checksum
+ *
+ * @v identifier ISAPnP identifier
+ * @ret checksum Expected checksum value
+ * @err None -
*
*/
static uint8_t isapnp_checksum ( struct isapnp_identifier *identifier ) {
@@ -248,9 +324,16 @@ static inline uint8_t isapnp_peek_byte ( void ) {
return 0xff;
}
-/*
- * Read n bytes of resource data from the current location. If buf is
- * NULL, discard data.
+/**
+ * Read resource data.
+ *
+ * @v buf Buffer in which to store data, or NULL
+ * @v bytes Number of bytes to read
+ * @ret None -
+ * @err None -
+ *
+ * Resource data is read from the current location. If #buf is NULL,
+ * the data is discarded.
*
*/
static void isapnp_peek ( uint8_t *buf, size_t bytes ) {
@@ -265,12 +348,19 @@ static void isapnp_peek ( uint8_t *buf, size_t bytes ) {
}
}
-/*
- * Scan through the resource data until we find a particular tag, and
- * read its contents into a buffer.
+/**
+ * Find a tag within the resource data.
*
- * It is the caller's responsibility to ensure that buf is large
- * enough to contain a tag of the requested size.
+ * @v wanted_tag The tag that we're looking for
+ * @v buf Buffer in which to store the tag's contents
+ * @ret True Tag was found
+ * @ret False Tag was not found
+ * @err None -
+ *
+ * Scan through the resource data until we find a particular tag, and
+ * read its contents into a buffer. It is the caller's responsibility
+ * to ensure that #buf is large enough to contain a tag of the
+ * requested size.
*
*/
static int isapnp_find_tag ( uint8_t wanted_tag, uint8_t *buf ) {
@@ -300,10 +390,13 @@ static int isapnp_find_tag ( uint8_t wanted_tag, uint8_t *buf ) {
return 0;
}
-/*
- * Try isolating ISAPnP cards at the current read port. Return the
- * number of ISAPnP cards found. <0 indicates "try a new read port",
- * 0 indicates "definitely no cards".
+/**
+ * Try isolating ISAPnP cards at the current read port.
+ *
+ * @ret \>0 Number of ISAPnP cards found
+ * @ret 0 There are no ISAPnP cards in the system
+ * @ret \<0 A conflict was detected; try a new read port
+ * @err None -
*
* The state diagram on page 18 (PDF page 24) of the PnP ISA spec
* gives the best overview of what happens here.
@@ -429,8 +522,8 @@ static int isapnp_try_isolate ( void ) {
return csn;
}
-/*
- * Isolate all ISAPnP cards, locating a valid read port in the process.
+/**
+ * Find a valid read port and isolate all ISAPnP cards.
*
*/
static void isapnp_isolate ( void ) {
@@ -450,10 +543,17 @@ static void isapnp_isolate ( void ) {
}
}
-/*
- * Increment a bus_loc structure to the next possible ISAPnP location.
- * Leave the structure zeroed and return 0 if there are no more valid
- * locations.
+/**
+ * Increment a #bus_loc structure to the next possible ISAPnP
+ * location.
+ *
+ * @v bus_loc Bus location
+ * @ret True #bus_loc contains a valid ISAPnP location
+ * @ret False There are no more valid ISAPnP locations
+ * @err None -
+ *
+ * If there are no more valid locations, the #bus_loc structure will
+ * be zeroed.
*
*/
static int isapnp_next_location ( struct bus_loc *bus_loc ) {
@@ -471,10 +571,14 @@ static int isapnp_next_location ( struct bus_loc *bus_loc ) {
return ( ++isapnp_loc->logdev ? 1 : ++isapnp_loc->csn );
}
-/*
- * Fill in parameters for an ISAPnP device based on CSN
+/**
+ * Fill in parameters for an ISAPnP device based on CSN.
*
- * Return 1 if device present, 0 otherwise
+ * @v bus_dev Bus device to be filled in
+ * @v bus_loc Bus location as filled in by isapnp_next_location()
+ * @ret True A device is present at this location
+ * @ret False No device is present at this location
+ * @err None -
*
*/
static int isapnp_fill_device ( struct bus_dev *bus_dev,
@@ -566,9 +670,15 @@ static int isapnp_fill_device ( struct bus_dev *bus_dev,
return 1;
}
-/*
+/**
* Test whether or not a driver is capable of driving the device.
*
+ * @v bus_dev Bus device as filled in by isapnp_fill_device()
+ * @v device_driver Device driver
+ * @ret True Driver is capable of driving this device
+ * @ret False Driver is not capable of driving this device
+ * @err None -
+ *
*/
static int isapnp_check_driver ( struct bus_dev *bus_dev,
struct device_driver *device_driver ) {
@@ -598,8 +708,15 @@ static int isapnp_check_driver ( struct bus_dev *bus_dev,
return 0;
}
-/*
- * Describe an ISAPnP device
+/**
+ * Describe an ISAPnP device.
+ *
+ * @v bus_dev Bus device as filled in by isapnp_fill_device()
+ * @ret string Printable string describing the device
+ * @err None -
+ *
+ * The string returned by isapnp_describe_device() is valid only until
+ * the next call to isapnp_describe_device().
*
*/
static char * isapnp_describe_device ( struct bus_dev *bus_dev ) {
@@ -611,8 +728,15 @@ static char * isapnp_describe_device ( struct bus_dev *bus_dev ) {
return isapnp_description;
}
-/*
- * Name an ISAPnP device
+/**
+ * Name an ISAPnP device.
+ *
+ * @v bus_dev Bus device as filled in by isapnp_fill_device()
+ * @ret string Printable string naming the device
+ * @err None -
+ *
+ * The string returned by isapnp_name_device() is valid only until the
+ * next call to isapnp_name_device().
*
*/
static const char * isapnp_name_device ( struct bus_dev *bus_dev ) {
@@ -634,12 +758,17 @@ struct bus_driver isapnp_driver __bus_driver = {
.name_device = isapnp_name_device,
};
-/*
- * Activate or deactivate an ISAPnP device
+/**
+ * Activate or deactivate an ISAPnP device.
+ *
+ * @v isapnp ISAPnP device
+ * @v activation True to enable, False to disable the device
+ * @ret None -
+ * @err None -
*
* This routine simply activates the device in its current
- * configuration. It does not attempt any kind of resource
- * arbitration.
+ * configuration, or deactivates the device. It does not attempt any
+ * kind of resource arbitration.
*
*/
void isapnp_device_activation ( struct isapnp_device *isapnp,
@@ -662,8 +791,17 @@ void isapnp_device_activation ( struct isapnp_device *isapnp,
isapnp->csn, isapnp->logdev );
}
-/*
- * Fill in a nic structure
+/**
+ * Fill in a nic structure.
+ *
+ * @v nic NIC structure to be filled in
+ * @v isapnp ISAPnP device
+ * @ret None -
+ * @err None -
+ *
+ * This fills in generic NIC parameters (e.g. I/O address and IRQ
+ * number) that can be determined directly from the ISAPnP device,
+ * without any driver-specific knowledge.
*
*/
void isapnp_fill_nic ( struct nic *nic, struct isapnp_device *isapnp ) {
diff --git a/src/drivers/net/3c515.c b/src/drivers/net/3c515.c
index 7fcc9f0f..c8749500 100644
--- a/src/drivers/net/3c515.c
+++ b/src/drivers/net/3c515.c
@@ -61,10 +61,6 @@ static void t3c515_wait(unsigned int nticks)
/* TJL definations */
#define HZ 100
-#define u16 unsigned short
-#define u32 unsigned long
-#define s16 signed short
-#define s32 signed long
static int if_port;
static struct corkscrew_private *vp;
/* Brought directly from 3c515.c by Becker */
diff --git a/src/drivers/net/amd8111e.c b/src/drivers/net/amd8111e.c
new file mode 100644
index 00000000..611cbc85
--- /dev/null
+++ b/src/drivers/net/amd8111e.c
@@ -0,0 +1,681 @@
+/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2004 Advanced Micro Devices
+ * Copyright (C) 2005 Liu Tao <liutao1980@gmail.com> [etherboot port]
+ *
+ * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ]
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c]
+ * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ]
+ * Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.[ pcnet32.c ]
+ * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ]
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "mii.h"
+#include "pci.h"
+#include "timer.h"
+#include "string.h"
+#include "stdint.h"
+#include "amd8111e.h"
+
+
+/* driver definitions */
+#define NUM_TX_SLOTS 2
+#define NUM_RX_SLOTS 4
+#define TX_SLOTS_MASK 1
+#define RX_SLOTS_MASK 3
+
+#define TX_BUF_LEN 1536
+#define RX_BUF_LEN 1536
+
+#define TX_PKT_LEN_MAX (ETH_FRAME_LEN - ETH_HLEN)
+#define RX_PKT_LEN_MIN 60
+#define RX_PKT_LEN_MAX ETH_FRAME_LEN
+
+#define TX_TIMEOUT 3000
+#define TX_PROCESS_TIME 10
+#define TX_RETRY (TX_TIMEOUT / TX_PROCESS_TIME)
+
+#define PHY_RW_RETRY 10
+
+
+struct amd8111e_tx_desc {
+ u16 buf_len;
+ u16 tx_flags;
+ u16 tag_ctrl_info;
+ u16 tag_ctrl_cmd;
+ u32 buf_phy_addr;
+ u32 reserved;
+};
+
+struct amd8111e_rx_desc {
+ u32 reserved;
+ u16 msg_len;
+ u16 tag_ctrl_info;
+ u16 buf_len;
+ u16 rx_flags;
+ u32 buf_phy_addr;
+};
+
+struct eth_frame {
+ u8 dst_addr[ETH_ALEN];
+ u8 src_addr[ETH_ALEN];
+ u16 type;
+ u8 data[ETH_FRAME_LEN - ETH_HLEN];
+} __attribute__((packed));
+
+struct amd8111e_priv {
+ struct amd8111e_tx_desc tx_ring[NUM_TX_SLOTS];
+ struct amd8111e_rx_desc rx_ring[NUM_RX_SLOTS];
+ unsigned char tx_buf[NUM_TX_SLOTS][TX_BUF_LEN];
+ unsigned char rx_buf[NUM_RX_SLOTS][RX_BUF_LEN];
+ unsigned long tx_idx, rx_idx;
+ int tx_consistent;
+
+ char opened;
+ char link;
+ char speed;
+ char duplex;
+ int ext_phy_addr;
+ u32 ext_phy_id;
+
+ struct pci_device *pdev;
+ struct nic *nic;
+ void *mmio;
+};
+
+static struct amd8111e_priv amd8111e;
+
+
+/********************************************************
+ * locale functions *
+ ********************************************************/
+static void amd8111e_init_hw_default(struct amd8111e_priv *lp);
+static int amd8111e_start(struct amd8111e_priv *lp);
+static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val);
+#if 0
+static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val);
+#endif
+static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp);
+static void amd8111e_disable_interrupt(struct amd8111e_priv *lp);
+static void amd8111e_enable_interrupt(struct amd8111e_priv *lp);
+static void amd8111e_force_interrupt(struct amd8111e_priv *lp);
+static int amd8111e_get_mac_address(struct amd8111e_priv *lp);
+static int amd8111e_init_rx_ring(struct amd8111e_priv *lp);
+static int amd8111e_init_tx_ring(struct amd8111e_priv *lp);
+static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index);
+static void amd8111e_wait_link(struct amd8111e_priv *lp);
+static void amd8111e_poll_link(struct amd8111e_priv *lp);
+static void amd8111e_restart(struct amd8111e_priv *lp);
+
+
+/*
+ * This function clears necessary the device registers.
+ */
+static void amd8111e_init_hw_default(struct amd8111e_priv *lp)
+{
+ unsigned int reg_val;
+ unsigned int logic_filter[2] = {0,};
+ void *mmio = lp->mmio;
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ /* Clear RCV_RING_BASE_ADDR */
+ writel(0, mmio + RCV_RING_BASE_ADDR0);
+
+ /* Clear XMT_RING_BASE_ADDR */
+ writel(0, mmio + XMT_RING_BASE_ADDR0);
+ writel(0, mmio + XMT_RING_BASE_ADDR1);
+ writel(0, mmio + XMT_RING_BASE_ADDR2);
+ writel(0, mmio + XMT_RING_BASE_ADDR3);
+
+ /* Clear CMD0 */
+ writel(CMD0_CLEAR, mmio + CMD0);
+
+ /* Clear CMD2 */
+ writel(CMD2_CLEAR, mmio + CMD2);
+
+ /* Clear CMD7 */
+ writel(CMD7_CLEAR, mmio + CMD7);
+
+ /* Clear DLY_INT_A and DLY_INT_B */
+ writel(0x0, mmio + DLY_INT_A);
+ writel(0x0, mmio + DLY_INT_B);
+
+ /* Clear FLOW_CONTROL */
+ writel(0x0, mmio + FLOW_CONTROL);
+
+ /* Clear INT0 write 1 to clear register */
+ reg_val = readl(mmio + INT0);
+ writel(reg_val, mmio + INT0);
+
+ /* Clear STVAL */
+ writel(0x0, mmio + STVAL);
+
+ /* Clear INTEN0 */
+ writel(INTEN0_CLEAR, mmio + INTEN0);
+
+ /* Clear LADRF */
+ writel(0x0, mmio + LADRF);
+
+ /* Set SRAM_SIZE & SRAM_BOUNDARY registers */
+ writel(0x80010, mmio + SRAM_SIZE);
+
+ /* Clear RCV_RING0_LEN */
+ writel(0x0, mmio + RCV_RING_LEN0);
+
+ /* Clear XMT_RING0/1/2/3_LEN */
+ writel(0x0, mmio + XMT_RING_LEN0);
+ writel(0x0, mmio + XMT_RING_LEN1);
+ writel(0x0, mmio + XMT_RING_LEN2);
+ writel(0x0, mmio + XMT_RING_LEN3);
+
+ /* Clear XMT_RING_LIMIT */
+ writel(0x0, mmio + XMT_RING_LIMIT);
+
+ /* Clear MIB */
+ writew(MIB_CLEAR, mmio + MIB_ADDR);
+
+ /* Clear LARF */
+ amd8111e_writeq(*(u64*)logic_filter, mmio + LADRF);
+
+ /* SRAM_SIZE register */
+ reg_val = readl(mmio + SRAM_SIZE);
+
+ /* Set default value to CTRL1 Register */
+ writel(CTRL1_DEFAULT, mmio + CTRL1);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD2);
+}
+
+/*
+ * This function initializes the device registers and starts the device.
+ */
+static int amd8111e_start(struct amd8111e_priv *lp)
+{
+ struct nic *nic = lp->nic;
+ void *mmio = lp->mmio;
+ int i, reg_val;
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */
+ writew(0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0);
+
+ /* enable the port manager and set auto negotiation always */
+ writel(VAL1 | EN_PMGR, mmio + CMD3 );
+ writel(XPHYANE | XPHYRST, mmio + CTRL2);
+
+ /* set control registers */
+ reg_val = readl(mmio + CTRL1);
+ reg_val &= ~XMTSP_MASK;
+ writel(reg_val | XMTSP_128 | CACHE_ALIGN, mmio + CTRL1);
+
+ /* initialize tx and rx ring base addresses */
+ amd8111e_init_tx_ring(lp);
+ amd8111e_init_rx_ring(lp);
+ writel(virt_to_bus(lp->tx_ring), mmio + XMT_RING_BASE_ADDR0);
+ writel(virt_to_bus(lp->rx_ring), mmio + RCV_RING_BASE_ADDR0);
+ writew(NUM_TX_SLOTS, mmio + XMT_RING_LEN0);
+ writew(NUM_RX_SLOTS, mmio + RCV_RING_LEN0);
+
+ /* set default IPG to 96 */
+ writew(DEFAULT_IPG, mmio + IPG);
+ writew(DEFAULT_IPG - IFS1_DELTA, mmio + IFS1);
+
+ /* AutoPAD transmit, Retransmit on Underflow */
+ writel(VAL0 | APAD_XMT | REX_RTRY | REX_UFLO, mmio + CMD2);
+
+ /* JUMBO disabled */
+ writel(JUMBO, mmio + CMD3);
+
+ /* Setting the MAC address to the device */
+ for(i = 0; i < ETH_ALEN; i++)
+ writeb(nic->node_addr[i], mmio + PADR + i);
+
+ /* set RUN bit to start the chip, interrupt not enabled */
+ writel(VAL2 | RDMD0 | VAL0 | RUN, mmio + CMD0);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD0);
+ return 0;
+}
+
+/*
+This function will read the PHY registers.
+*/
+static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val)
+{
+ void *mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int retry = PHY_RW_RETRY;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl(mmio + PHY_ACCESS);
+
+ writel(PHY_RD_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16),
+ mmio + PHY_ACCESS);
+ do {
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write data */
+ } while (--retry && (reg_val & PHY_CMD_ACTIVE));
+
+ if (reg_val & PHY_RD_ERR) {
+ *val = 0;
+ return -1;
+ }
+
+ *val = reg_val & 0xffff;
+ return 0;
+}
+
+/*
+This function will write into PHY registers.
+*/
+#if 0
+static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val)
+{
+ void *mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int retry = PHY_RW_RETRY;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl(mmio + PHY_ACCESS);
+
+ writel(PHY_WR_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16) | val,
+ mmio + PHY_ACCESS);
+ do {
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write the data */
+ } while (--retry && (reg_val & PHY_CMD_ACTIVE));
+
+ if(reg_val & PHY_RD_ERR)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->ext_phy_id = 0;
+ lp->ext_phy_addr = 1;
+
+ for (i = 0x1e; i >= 0; i--) {
+ u32 id1, id2;
+
+ if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1))
+ continue;
+ if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2))
+ continue;
+ lp->ext_phy_id = (id1 << 16) | id2;
+ lp->ext_phy_addr = i;
+ break;
+ }
+
+ if (lp->ext_phy_id)
+ printf("Found MII PHY ID 0x%08x at address 0x%02x\n",
+ lp->ext_phy_id, lp->ext_phy_addr);
+ else
+ printf("Couldn't detect MII PHY, assuming address 0x01\n");
+}
+
+static void amd8111e_disable_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+ unsigned int int0;
+
+ writel(INTREN, mmio + CMD0);
+ writel(INTEN0_CLEAR, mmio + INTEN0);
+ int0 = readl(mmio + INT0);
+ writel(int0, mmio + INT0);
+ readl(mmio + INT0);
+}
+
+static void amd8111e_enable_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+
+ writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0);
+ writel(VAL0 | INTREN, mmio + CMD0);
+ readl(mmio + CMD0);
+}
+
+static void amd8111e_force_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+
+ writel(VAL0 | UINTCMD, mmio + CMD0);
+ readl(mmio + CMD0);
+}
+
+static int amd8111e_get_mac_address(struct amd8111e_priv *lp)
+{
+ struct nic *nic = lp->nic;
+ void *mmio = lp->mmio;
+ int i;
+
+ /* BIOS should have set mac address to PADR register,
+ * so we read PADR to get it.
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ nic->node_addr[i] = readb(mmio + PADR + i);
+ printf("Ethernet addr: %!\n", nic->node_addr);
+
+ return 0;
+}
+
+static int amd8111e_init_rx_ring(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->rx_idx = 0;
+
+ /* Initilaizing receive descriptors */
+ for (i = 0; i < NUM_RX_SLOTS; i++) {
+ lp->rx_ring[i].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[i]));
+ lp->rx_ring[i].buf_len = cpu_to_le16(RX_BUF_LEN);
+ wmb();
+ lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT);
+ }
+
+ return 0;
+}
+
+static int amd8111e_init_tx_ring(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->tx_idx = 0;
+ lp->tx_consistent = 1;
+
+ /* Initializing transmit descriptors */
+ for (i = 0; i < NUM_TX_SLOTS; i++) {
+ lp->tx_ring[i].tx_flags = 0;
+ lp->tx_ring[i].buf_phy_addr = 0;
+ lp->tx_ring[i].buf_len = 0;
+ }
+
+ return 0;
+}
+
+static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index)
+{
+ volatile u16 status;
+ int retry = TX_RETRY;
+
+ status = le16_to_cpu(lp->tx_ring[index].tx_flags);
+ while (--retry && (status & OWN_BIT)) {
+ mdelay(TX_PROCESS_TIME);
+ status = le16_to_cpu(lp->tx_ring[index].tx_flags);
+ }
+ if (status & OWN_BIT) {
+ printf("Error: tx slot %d timeout, stat = 0x%x\n", index, status);
+ amd8111e_restart(lp);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void amd8111e_wait_link(struct amd8111e_priv *lp)
+{
+ unsigned int status;
+ u32 reg_val;
+
+ do {
+ /* read phy to update STAT0 register */
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, &reg_val);
+ status = readl(lp->mmio + STAT0);
+ } while (!(status & AUTONEG_COMPLETE) || !(status & LINK_STATS));
+}
+
+static void amd8111e_poll_link(struct amd8111e_priv *lp)
+{
+ unsigned int status, speed;
+ u32 reg_val;
+
+ if (!lp->link) {
+ /* read phy to update STAT0 register */
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, &reg_val);
+ status = readl(lp->mmio + STAT0);
+
+ if (status & LINK_STATS) {
+ lp->link = 1;
+ speed = (status & SPEED_MASK) >> 7;
+ if (speed == PHY_SPEED_100)
+ lp->speed = 1;
+ else
+ lp->speed = 0;
+ if (status & FULL_DPLX)
+ lp->duplex = 1;
+ else
+ lp->duplex = 0;
+
+ printf("Link is up: %s Mbps %s duplex\n",
+ lp->speed ? "100" : "10", lp->duplex ? "full" : "half");
+ }
+ } else {
+ status = readl(lp->mmio + STAT0);
+ if (!(status & LINK_STATS)) {
+ lp->link = 0;
+ printf("Link is down\n");
+ }
+ }
+}
+
+static void amd8111e_restart(struct amd8111e_priv *lp)
+{
+ printf("\nStarting nic...\n");
+ amd8111e_disable_interrupt(lp);
+ amd8111e_init_hw_default(lp);
+ amd8111e_probe_ext_phy(lp);
+ amd8111e_get_mac_address(lp);
+ amd8111e_start(lp);
+
+ printf("Waiting link up...\n");
+ lp->link = 0;
+ amd8111e_wait_link(lp);
+ amd8111e_poll_link(lp);
+}
+
+
+/********************************************************
+ * Interface Functions *
+ ********************************************************/
+
+static void amd8111e_transmit(struct nic *nic, const char *dst_addr,
+ unsigned int type, unsigned int size, const char *packet)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+ struct eth_frame *frame;
+ unsigned int index;
+
+ /* check packet size */
+ if (size > TX_PKT_LEN_MAX) {
+ printf("amd8111e_transmit(): too large packet, drop\n");
+ return;
+ }
+
+ /* get tx slot */
+ index = lp->tx_idx;
+ if (amd8111e_wait_tx_ring(lp, index))
+ return;
+
+ /* fill frame */
+ frame = (struct eth_frame *)lp->tx_buf[index];
+ memset(frame->data, 0, TX_PKT_LEN_MAX);
+ memcpy(frame->dst_addr, dst_addr, ETH_ALEN);
+ memcpy(frame->src_addr, nic->node_addr, ETH_ALEN);
+ frame->type = htons(type);
+ memcpy(frame->data, packet, size);
+
+ /* start xmit */
+ lp->tx_ring[index].buf_len = cpu_to_le16(ETH_HLEN + size);
+ lp->tx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(frame));
+ wmb();
+ lp->tx_ring[index].tx_flags =
+ cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT | ADD_FCS_BIT | LTINT_BIT);
+ writel(VAL1 | TDMD0, lp->mmio + CMD0);
+ readl(lp->mmio + CMD0);
+
+ /* update slot pointer */
+ lp->tx_idx = (lp->tx_idx + 1) & TX_SLOTS_MASK;
+}
+
+static int amd8111e_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ struct amd8111e_priv *lp = nic->priv_data;
+ u16 status, pkt_len;
+ unsigned int index, pkt_ok;
+
+ amd8111e_poll_link(lp);
+
+ index = lp->rx_idx;
+ status = le16_to_cpu(lp->rx_ring[index].rx_flags);
+ pkt_len = le16_to_cpu(lp->rx_ring[index].msg_len) - 4; /* remove 4bytes FCS */
+
+ if (status & OWN_BIT)
+ return 0;
+
+ if (status & ERR_BIT)
+ pkt_ok = 0;
+ else if (!(status & STP_BIT))
+ pkt_ok = 0;
+ else if (!(status & ENP_BIT))
+ pkt_ok = 0;
+ else if (pkt_len < RX_PKT_LEN_MIN)
+ pkt_ok = 0;
+ else if (pkt_len > RX_PKT_LEN_MAX)
+ pkt_ok = 0;
+ else
+ pkt_ok = 1;
+
+ if (pkt_ok) {
+ if (!retrieve)
+ return 1;
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, lp->rx_buf[index], nic->packetlen);
+ }
+
+ lp->rx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[index]));
+ lp->rx_ring[index].buf_len = cpu_to_le16(RX_BUF_LEN);
+ wmb();
+ lp->rx_ring[index].rx_flags = cpu_to_le16(OWN_BIT);
+ writel(VAL2 | RDMD0, lp->mmio + CMD0);
+ readl(lp->mmio + CMD0);
+
+ lp->rx_idx = (lp->rx_idx + 1) & RX_SLOTS_MASK;
+ return pkt_ok;
+}
+
+static void amd8111e_disable(struct nic *nic)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+
+ /* disable interrupt */
+ amd8111e_disable_interrupt(lp);
+
+ /* stop chip */
+ amd8111e_init_hw_default(lp);
+
+ /* unmap mmio */
+ iounmap(lp->mmio);
+
+ /* update status */
+ lp->opened = 0;
+}
+
+static void amd8111e_irq(struct nic *nic, irq_action_t action)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+
+ switch (action) {
+ case DISABLE:
+ amd8111e_disable_interrupt(lp);
+ break;
+ case ENABLE:
+ amd8111e_enable_interrupt(lp);
+ break;
+ case FORCE:
+ amd8111e_force_interrupt(lp);
+ break;
+ }
+}
+
+static struct nic_operations amd8111e_operations = {
+ .connect = dummy_connect,
+ .poll = amd8111e_poll,
+ .transmit = amd8111e_transmit,
+ .irq = amd8111e_irq,
+};
+
+static int amd8111e_probe(struct nic *nic, struct pci_device *pdev)
+{
+ struct amd8111e_priv *lp = &amd8111e;
+ unsigned long mmio_start, mmio_len;
+
+ pci_fill_nic ( nic, pdev );
+
+ mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0);
+
+ memset(lp, 0, sizeof(*lp));
+ lp->pdev = pdev;
+ lp->nic = nic;
+ lp->mmio = ioremap(mmio_start, mmio_len);
+ lp->opened = 1;
+ adjust_pci_device(pdev);
+
+ nic->priv_data = lp;
+
+ amd8111e_restart(lp);
+
+ nic->nic_op = &amd8111e_operations;
+ return 1;
+}
+
+static struct pci_id amd8111e_nics[] = {
+ PCI_ROM(0x1022, 0x7462, "amd8111e", "AMD8111E"),
+};
+
+PCI_DRIVER ( amd8111e_driver, amd8111e_nics, PCI_NO_CLASS );
+
+DRIVER ( "AMD8111E", nic_driver, pci_driver, amd8111e_driver,
+ amd8111e_probe, amd8111e_disable );
diff --git a/src/drivers/net/amd8111e.h b/src/drivers/net/amd8111e.h
new file mode 100644
index 00000000..82b8f7a3
--- /dev/null
+++ b/src/drivers/net/amd8111e.h
@@ -0,0 +1,629 @@
+/*
+ * Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2003 Advanced Micro Devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+
+Module Name:
+
+ amd8111e.h
+
+Abstract:
+
+ AMD8111 based 10/100 Ethernet Controller driver definitions.
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+ 3.0.0
+ Initial Revision.
+ 3.0.1
+*/
+
+#ifndef _AMD811E_H
+#define _AMD811E_H
+
+/* Command style register access
+
+Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the value bit that specifies the value that will be written into the selected bits of register.
+
+eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered.
+
+*/
+
+/* Offset for Memory Mapped Registers. */
+/* 32 bit registers */
+
+#define ASF_STAT 0x00 /* ASF status register */
+#define CHIPID 0x04 /* Chip ID regsiter */
+#define MIB_DATA 0x10 /* MIB data register */
+#define MIB_ADDR 0x14 /* MIB address register */
+#define STAT0 0x30 /* Status0 register */
+#define INT0 0x38 /* Interrupt0 register */
+#define INTEN0 0x40 /* Interrupt0 enable register*/
+#define CMD0 0x48 /* Command0 register */
+#define CMD2 0x50 /* Command2 register */
+#define CMD3 0x54 /* Command3 resiter */
+#define CMD7 0x64 /* Command7 register */
+
+#define CTRL1 0x6C /* Control1 register */
+#define CTRL2 0x70 /* Control2 register */
+
+#define XMT_RING_LIMIT 0x7C /* Transmit ring limit register */
+
+#define AUTOPOLL0 0x88 /* Auto-poll0 register */
+#define AUTOPOLL1 0x8A /* Auto-poll1 register */
+#define AUTOPOLL2 0x8C /* Auto-poll2 register */
+#define AUTOPOLL3 0x8E /* Auto-poll3 register */
+#define AUTOPOLL4 0x90 /* Auto-poll4 register */
+#define AUTOPOLL5 0x92 /* Auto-poll5 register */
+
+#define AP_VALUE 0x98 /* Auto-poll value register */
+#define DLY_INT_A 0xA8 /* Group A delayed interrupt register */
+#define DLY_INT_B 0xAC /* Group B delayed interrupt register */
+
+#define FLOW_CONTROL 0xC8 /* Flow control register */
+#define PHY_ACCESS 0xD0 /* PHY access register */
+
+#define STVAL 0xD8 /* Software timer value register */
+
+#define XMT_RING_BASE_ADDR0 0x100 /* Transmit ring0 base addr register */
+#define XMT_RING_BASE_ADDR1 0x108 /* Transmit ring1 base addr register */
+#define XMT_RING_BASE_ADDR2 0x110 /* Transmit ring2 base addr register */
+#define XMT_RING_BASE_ADDR3 0x118 /* Transmit ring2 base addr register */
+
+#define RCV_RING_BASE_ADDR0 0x120 /* Transmit ring0 base addr register */
+
+#define PMAT0 0x190 /* OnNow pattern register0 */
+#define PMAT1 0x194 /* OnNow pattern register1 */
+
+/* 16bit registers */
+
+#define XMT_RING_LEN0 0x140 /* Transmit Ring0 length register */
+#define XMT_RING_LEN1 0x144 /* Transmit Ring1 length register */
+#define XMT_RING_LEN2 0x148 /* Transmit Ring2 length register */
+#define XMT_RING_LEN3 0x14C /* Transmit Ring3 length register */
+
+#define RCV_RING_LEN0 0x150 /* Receive Ring0 length register */
+
+#define SRAM_SIZE 0x178 /* SRAM size register */
+#define SRAM_BOUNDARY 0x17A /* SRAM boundary register */
+
+/* 48bit register */
+
+#define PADR 0x160 /* Physical address register */
+
+#define IFS1 0x18C /* Inter-frame spacing Part1 register */
+#define IFS 0x18D /* Inter-frame spacing register */
+#define IPG 0x18E /* Inter-frame gap register */
+/* 64bit register */
+
+#define LADRF 0x168 /* Logical address filter register */
+
+
+/* Register Bit Definitions */
+typedef enum {
+
+ ASF_INIT_DONE = (1 << 1),
+ ASF_INIT_PRESENT = (1 << 0),
+
+}STAT_ASF_BITS;
+
+typedef enum {
+
+ MIB_CMD_ACTIVE = (1 << 15 ),
+ MIB_RD_CMD = (1 << 13 ),
+ MIB_CLEAR = (1 << 12 ),
+ MIB_ADDRESS = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)|
+ (1 << 4) | (1 << 5),
+}MIB_ADDR_BITS;
+
+
+typedef enum {
+
+ PMAT_DET = (1 << 12),
+ MP_DET = (1 << 11),
+ LC_DET = (1 << 10),
+ SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7),
+ FULL_DPLX = (1 << 6),
+ LINK_STATS = (1 << 5),
+ AUTONEG_COMPLETE = (1 << 4),
+ MIIPD = (1 << 3),
+ RX_SUSPENDED = (1 << 2),
+ TX_SUSPENDED = (1 << 1),
+ RUNNING = (1 << 0),
+
+}STAT0_BITS;
+
+#define PHY_SPEED_10 0x2
+#define PHY_SPEED_100 0x3
+
+/* INT0 0x38, 32bit register */
+typedef enum {
+
+ INTR = (1 << 31),
+ PCSINT = (1 << 28),
+ LCINT = (1 << 27),
+ APINT5 = (1 << 26),
+ APINT4 = (1 << 25),
+ APINT3 = (1 << 24),
+ TINT_SUM = (1 << 23),
+ APINT2 = (1 << 22),
+ APINT1 = (1 << 21),
+ APINT0 = (1 << 20),
+ MIIPDTINT = (1 << 19),
+ MCCINT = (1 << 17),
+ MREINT = (1 << 16),
+ RINT_SUM = (1 << 15),
+ SPNDINT = (1 << 14),
+ MPINT = (1 << 13),
+ SINT = (1 << 12),
+ TINT3 = (1 << 11),
+ TINT2 = (1 << 10),
+ TINT1 = (1 << 9),
+ TINT0 = (1 << 8),
+ UINT = (1 << 7),
+ STINT = (1 << 4),
+ RINT0 = (1 << 0),
+
+}INT0_BITS;
+
+typedef enum {
+
+ VAL3 = (1 << 31), /* VAL bit for byte 3 */
+ VAL2 = (1 << 23), /* VAL bit for byte 2 */
+ VAL1 = (1 << 15), /* VAL bit for byte 1 */
+ VAL0 = (1 << 7), /* VAL bit for byte 0 */
+
+}VAL_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ LCINTEN = (1 << 27),
+ APINT5EN = (1 << 26),
+ APINT4EN = (1 << 25),
+ APINT3EN = (1 << 24),
+ /* VAL2 */
+ APINT2EN = (1 << 22),
+ APINT1EN = (1 << 21),
+ APINT0EN = (1 << 20),
+ MIIPDTINTEN = (1 << 19),
+ MCCIINTEN = (1 << 18),
+ MCCINTEN = (1 << 17),
+ MREINTEN = (1 << 16),
+ /* VAL1 */
+ SPNDINTEN = (1 << 14),
+ MPINTEN = (1 << 13),
+ TINTEN3 = (1 << 11),
+ SINTEN = (1 << 12),
+ TINTEN2 = (1 << 10),
+ TINTEN1 = (1 << 9),
+ TINTEN0 = (1 << 8),
+ /* VAL0 */
+ STINTEN = (1 << 4),
+ RINTEN0 = (1 << 0),
+
+ INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */
+
+}INTEN0_BITS;
+
+typedef enum {
+ /* VAL2 */
+ RDMD0 = (1 << 16),
+ /* VAL1 */
+ TDMD3 = (1 << 11),
+ TDMD2 = (1 << 10),
+ TDMD1 = (1 << 9),
+ TDMD0 = (1 << 8),
+ /* VAL0 */
+ UINTCMD = (1 << 6),
+ RX_FAST_SPND = (1 << 5),
+ TX_FAST_SPND = (1 << 4),
+ RX_SPND = (1 << 3),
+ TX_SPND = (1 << 2),
+ INTREN = (1 << 1),
+ RUN = (1 << 0),
+
+ CMD0_CLEAR = 0x000F0F7F, /* Command style register */
+
+}CMD0_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ CONDUIT_MODE = (1 << 29),
+ /* VAL2 */
+ RPA = (1 << 19),
+ DRCVPA = (1 << 18),
+ DRCVBC = (1 << 17),
+ PROM = (1 << 16),
+ /* VAL1 */
+ ASTRP_RCV = (1 << 13),
+ RCV_DROP0 = (1 << 12),
+ EMBA = (1 << 11),
+ DXMT2PD = (1 << 10),
+ LTINTEN = (1 << 9),
+ DXMTFCS = (1 << 8),
+ /* VAL0 */
+ APAD_XMT = (1 << 6),
+ DRTY = (1 << 5),
+ INLOOP = (1 << 4),
+ EXLOOP = (1 << 3),
+ REX_RTRY = (1 << 2),
+ REX_UFLO = (1 << 1),
+ REX_LCOL = (1 << 0),
+
+ CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */
+
+}CMD2_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ ASF_INIT_DONE_ALIAS = (1 << 29),
+ /* VAL2 */
+ JUMBO = (1 << 21),
+ VSIZE = (1 << 20),
+ VLONLY = (1 << 19),
+ VL_TAG_DEL = (1 << 18),
+ /* VAL1 */
+ EN_PMGR = (1 << 14),
+ INTLEVEL = (1 << 13),
+ FORCE_FULL_DUPLEX = (1 << 12),
+ FORCE_LINK_STATUS = (1 << 11),
+ APEP = (1 << 10),
+ MPPLBA = (1 << 9),
+ /* VAL0 */
+ RESET_PHY_PULSE = (1 << 2),
+ RESET_PHY = (1 << 1),
+ PHY_RST_POL = (1 << 0),
+
+}CMD3_BITS;
+
+
+typedef enum {
+
+ /* VAL0 */
+ PMAT_SAVE_MATCH = (1 << 4),
+ PMAT_MODE = (1 << 3),
+ MPEN_SW = (1 << 1),
+ LCMODE_SW = (1 << 0),
+
+ CMD7_CLEAR = 0x0000001B /* Command style register */
+
+}CMD7_BITS;
+
+
+typedef enum {
+
+ RESET_PHY_WIDTH = (0xF << 16) | (0xF<< 20), /* 0x00FF0000 */
+ XMTSP_MASK = (1 << 9) | (1 << 8), /* 9:8 */
+ XMTSP_128 = (1 << 9), /* 9 */
+ XMTSP_64 = (1 << 8),
+ CACHE_ALIGN = (1 << 4),
+ BURST_LIMIT_MASK = (0xF << 0 ),
+ CTRL1_DEFAULT = 0x00010111,
+
+}CTRL1_BITS;
+
+typedef enum {
+
+ FMDC_MASK = (1 << 9)|(1 << 8), /* 9:8 */
+ XPHYRST = (1 << 7),
+ XPHYANE = (1 << 6),
+ XPHYFD = (1 << 5),
+ XPHYSP = (1 << 4) | (1 << 3), /* 4:3 */
+ APDW_MASK = (1 << 2) | (1 << 1) | (1 << 0), /* 2:0 */
+
+}CTRL2_BITS;
+
+/* XMT_RING_LIMIT 0x7C, 32bit register */
+typedef enum {
+
+ XMT_RING2_LIMIT = (0xFF << 16), /* 23:16 */
+ XMT_RING1_LIMIT = (0xFF << 8), /* 15:8 */
+ XMT_RING0_LIMIT = (0xFF << 0), /* 7:0 */
+
+}XMT_RING_LIMIT_BITS;
+
+typedef enum {
+
+ AP_REG0_EN = (1 << 15),
+ AP_REG0_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PHY0_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL0_BITS;
+
+/* AUTOPOLL1 0x8A, 16bit register */
+typedef enum {
+
+ AP_REG1_EN = (1 << 15),
+ AP_REG1_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP1 = (1 << 6),
+ AP_PHY1_DFLT = (1 << 5),
+ AP_PHY1_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL1_BITS;
+
+
+typedef enum {
+
+ AP_REG2_EN = (1 << 15),
+ AP_REG2_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP2 = (1 << 6),
+ AP_PHY2_DFLT = (1 << 5),
+ AP_PHY2_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL2_BITS;
+
+typedef enum {
+
+ AP_REG3_EN = (1 << 15),
+ AP_REG3_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP3 = (1 << 6),
+ AP_PHY3_DFLT = (1 << 5),
+ AP_PHY3_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL3_BITS;
+
+
+typedef enum {
+
+ AP_REG4_EN = (1 << 15),
+ AP_REG4_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP4 = (1 << 6),
+ AP_PHY4_DFLT = (1 << 5),
+ AP_PHY4_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL4_BITS;
+
+
+typedef enum {
+
+ AP_REG5_EN = (1 << 15),
+ AP_REG5_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP5 = (1 << 6),
+ AP_PHY5_DFLT = (1 << 5),
+ AP_PHY5_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL5_BITS;
+
+
+
+
+/* AP_VALUE 0x98, 32bit ragister */
+typedef enum {
+
+ AP_VAL_ACTIVE = (1 << 31),
+ AP_VAL_RD_CMD = ( 1 << 29),
+ AP_ADDR = (1 << 18)|(1 << 17)|(1 << 16), /* 18:16 */
+ AP_VAL = (0xF << 0) | (0xF << 4) |( 0xF << 8) |
+ (0xF << 12), /* 15:0 */
+
+}AP_VALUE_BITS;
+
+typedef enum {
+
+ DLY_INT_A_R3 = (1 << 31),
+ DLY_INT_A_R2 = (1 << 30),
+ DLY_INT_A_R1 = (1 << 29),
+ DLY_INT_A_R0 = (1 << 28),
+ DLY_INT_A_T3 = (1 << 27),
+ DLY_INT_A_T2 = (1 << 26),
+ DLY_INT_A_T1 = (1 << 25),
+ DLY_INT_A_T0 = ( 1 << 24),
+ EVENT_COUNT_A = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_A = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+
+}DLY_INT_A_BITS;
+
+typedef enum {
+
+ DLY_INT_B_R3 = (1 << 31),
+ DLY_INT_B_R2 = (1 << 30),
+ DLY_INT_B_R1 = (1 << 29),
+ DLY_INT_B_R0 = (1 << 28),
+ DLY_INT_B_T3 = (1 << 27),
+ DLY_INT_B_T2 = (1 << 26),
+ DLY_INT_B_T1 = (1 << 25),
+ DLY_INT_B_T0 = ( 1 << 24),
+ EVENT_COUNT_B = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_B = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+}DLY_INT_B_BITS;
+
+
+/* FLOW_CONTROL 0xC8, 32bit register */
+typedef enum {
+
+ PAUSE_LEN_CHG = (1 << 30),
+ FTPE = (1 << 22),
+ FRPE = (1 << 21),
+ NAPA = (1 << 20),
+ NPA = (1 << 19),
+ FIXP = ( 1 << 18),
+ FCCMD = ( 1 << 16),
+ PAUSE_LEN = (0xF << 0) | (0xF << 4) |( 0xF << 8) | (0xF << 12), /* 15:0 */
+
+}FLOW_CONTROL_BITS;
+
+/* PHY_ ACCESS 0xD0, 32bit register */
+typedef enum {
+
+ PHY_CMD_ACTIVE = (1 << 31),
+ PHY_WR_CMD = (1 << 30),
+ PHY_RD_CMD = (1 << 29),
+ PHY_RD_ERR = (1 << 28),
+ PHY_PRE_SUP = (1 << 27),
+ PHY_ADDR = (1 << 21) | (1 << 22) | (1 << 23)|
+ (1 << 24) |(1 << 25),/* 25:21 */
+ PHY_REG_ADDR = (1 << 16) | (1 << 17) | (1 << 18)| (1 << 19) | (1 << 20),/* 20:16 */
+ PHY_DATA = (0xF << 0)|(0xF << 4) |(0xF << 8)|
+ (0xF << 12),/* 15:0 */
+
+}PHY_ACCESS_BITS;
+
+
+/* PMAT0 0x190, 32bit register */
+typedef enum {
+ PMR_ACTIVE = (1 << 31),
+ PMR_WR_CMD = (1 << 30),
+ PMR_RD_CMD = (1 << 29),
+ PMR_BANK = (1 <<28),
+ PMR_ADDR = (0xF << 16)|(1 << 20)|(1 << 21)|
+ (1 << 22),/* 22:16 */
+ PMR_B4 = (0xF << 0) | (0xF << 4),/* 15:0 */
+}PMAT0_BITS;
+
+
+/* PMAT1 0x194, 32bit register */
+typedef enum {
+ PMR_B3 = (0xF << 24) | (0xF <<28),/* 31:24 */
+ PMR_B2 = (0xF << 16) |(0xF << 20),/* 23:16 */
+ PMR_B1 = (0xF << 8) | (0xF <<12), /* 15:8 */
+ PMR_B0 = (0xF << 0)|(0xF << 4),/* 7:0 */
+}PMAT1_BITS;
+
+/************************************************************************/
+/* */
+/* MIB counter definitions */
+/* */
+/************************************************************************/
+
+#define rcv_miss_pkts 0x00
+#define rcv_octets 0x01
+#define rcv_broadcast_pkts 0x02
+#define rcv_multicast_pkts 0x03
+#define rcv_undersize_pkts 0x04
+#define rcv_oversize_pkts 0x05
+#define rcv_fragments 0x06
+#define rcv_jabbers 0x07
+#define rcv_unicast_pkts 0x08
+#define rcv_alignment_errors 0x09
+#define rcv_fcs_errors 0x0A
+#define rcv_good_octets 0x0B
+#define rcv_mac_ctrl 0x0C
+#define rcv_flow_ctrl 0x0D
+#define rcv_pkts_64_octets 0x0E
+#define rcv_pkts_65to127_octets 0x0F
+#define rcv_pkts_128to255_octets 0x10
+#define rcv_pkts_256to511_octets 0x11
+#define rcv_pkts_512to1023_octets 0x12
+#define rcv_pkts_1024to1518_octets 0x13
+#define rcv_unsupported_opcode 0x14
+#define rcv_symbol_errors 0x15
+#define rcv_drop_pkts_ring1 0x16
+#define rcv_drop_pkts_ring2 0x17
+#define rcv_drop_pkts_ring3 0x18
+#define rcv_drop_pkts_ring4 0x19
+#define rcv_jumbo_pkts 0x1A
+
+#define xmt_underrun_pkts 0x20
+#define xmt_octets 0x21
+#define xmt_packets 0x22
+#define xmt_broadcast_pkts 0x23
+#define xmt_multicast_pkts 0x24
+#define xmt_collisions 0x25
+#define xmt_unicast_pkts 0x26
+#define xmt_one_collision 0x27
+#define xmt_multiple_collision 0x28
+#define xmt_deferred_transmit 0x29
+#define xmt_late_collision 0x2A
+#define xmt_excessive_defer 0x2B
+#define xmt_loss_carrier 0x2C
+#define xmt_excessive_collision 0x2D
+#define xmt_back_pressure 0x2E
+#define xmt_flow_ctrl 0x2F
+#define xmt_pkts_64_octets 0x30
+#define xmt_pkts_65to127_octets 0x31
+#define xmt_pkts_128to255_octets 0x32
+#define xmt_pkts_256to511_octets 0x33
+#define xmt_pkts_512to1023_octets 0x34
+#define xmt_pkts_1024to1518_octet 0x35
+#define xmt_oversize_pkts 0x36
+#define xmt_jumbo_pkts 0x37
+
+/* ipg parameters */
+#define DEFAULT_IPG 0x60
+#define IFS1_DELTA 36
+#define IPG_CONVERGE_JIFFIES (HZ/2)
+#define IPG_STABLE_TIME 5
+#define MIN_IPG 96
+#define MAX_IPG 255
+#define IPG_STEP 16
+#define CSTATE 1
+#define SSTATE 2
+
+/* amd8111e decriptor flag definitions */
+typedef enum {
+
+ OWN_BIT = (1 << 15),
+ ADD_FCS_BIT = (1 << 13),
+ LTINT_BIT = (1 << 12),
+ STP_BIT = (1 << 9),
+ ENP_BIT = (1 << 8),
+ KILL_BIT = (1 << 6),
+ TCC_VLAN_INSERT = (1 << 1),
+ TCC_VLAN_REPLACE = (1 << 1) |( 1<< 0),
+
+}TX_FLAG_BITS;
+
+typedef enum {
+ ERR_BIT = (1 << 14),
+ FRAM_BIT = (1 << 13),
+ OFLO_BIT = (1 << 12),
+ CRC_BIT = (1 << 11),
+ PAM_BIT = (1 << 6),
+ LAFM_BIT = (1 << 5),
+ BAM_BIT = (1 << 4),
+ TT_VLAN_TAGGED = (1 << 3) |(1 << 2),/* 0x000 */
+ TT_PRTY_TAGGED = (1 << 3),/* 0x0008 */
+
+}RX_FLAG_BITS;
+
+#define RESET_RX_FLAGS 0x0000
+#define TT_MASK 0x000c
+#define TCC_MASK 0x0003
+
+/* driver ioctl parameters */
+#define AMD8111E_REG_DUMP_LEN 13*sizeof(u32)
+
+/* crc generator constants */
+#define CRC32 0xedb88320
+#define INITCRC 0xFFFFFFFF
+
+/* kernel provided writeq does not write 64 bits into the amd8111e device register instead writes only higher 32bits data into lower 32bits of the register.
+BUG? */
+#define amd8111e_writeq(_UlData,_memMap) \
+ writel(*(u32*)(&_UlData), _memMap); \
+ writel(*(u32*)((u8*)(&_UlData)+4), _memMap+4)
+
+/* maps the external speed options to internal value */
+typedef enum {
+ SPEED_AUTONEG,
+ SPEED10_HALF,
+ SPEED10_FULL,
+ SPEED100_HALF,
+ SPEED100_FULL,
+}EXT_PHY_OPTION;
+
+
+#endif /* _AMD8111E_H */
+
diff --git a/src/drivers/net/davicom.c b/src/drivers/net/davicom.c
index fdbf9834..d087e29e 100644
--- a/src/drivers/net/davicom.c
+++ b/src/drivers/net/davicom.c
@@ -50,13 +50,6 @@
#define TX_TIME_OUT 2*TICKS_PER_SEC
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
/* Register offsets for davicom device */
enum davicom_offsets {
CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
diff --git a/src/drivers/net/depca.c b/src/drivers/net/depca.c
index 3707da64..cc904c71 100644
--- a/src/drivers/net/depca.c
+++ b/src/drivers/net/depca.c
@@ -413,13 +413,6 @@ static char *adapter_name[] = {
#define ALIGN8 ((u32)8 - 1) /* 2 longword (quadword) align */
#define ALIGN ALIGN8 /* Keep the LANCE happy... */
-typedef long s32;
-typedef unsigned long u32;
-typedef short s16;
-typedef unsigned short u16;
-typedef char s8;
-typedef unsigned char u8;
-
/*
** The DEPCA Rx and Tx ring descriptors.
*/
diff --git a/src/drivers/net/dmfe.c b/src/drivers/net/dmfe.c
index 4061ebfc..e1ff44ec 100644
--- a/src/drivers/net/dmfe.c
+++ b/src/drivers/net/dmfe.c
@@ -51,13 +51,6 @@
#define dprintf(x)
#endif
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
diff --git a/src/drivers/net/e1000.c b/src/drivers/net/e1000.c
index d5de2cc4..2eb6b333 100644
--- a/src/drivers/net/e1000.c
+++ b/src/drivers/net/e1000.c
@@ -72,6 +72,9 @@ typedef enum {
* and the corresponding inplace checks inserted instead.
* Pieces such as LED handling that we definitely don't need are deleted.
*
+ * Please keep the function ordering so that it is easy to produce diffs
+ * against the linux driver.
+ *
* The following defines should not be needed normally,
* but may be helpful for debugging purposes. */
@@ -120,7 +123,9 @@ static int e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16
static void e1000_phy_hw_reset(struct e1000_hw *hw);
static int e1000_phy_reset(struct e1000_hw *hw);
static int e1000_detect_gig_phy(struct e1000_hw *hw);
-static void e1000_irq(struct nic *nic, irq_action_t action);
+static int e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static void e1000_init_rx_addrs(struct e1000_hw *hw);
+static void e1000_clear_vfta(struct e1000_hw *hw);
/* Printing macros... */
@@ -174,11 +179,18 @@ static void e1000_irq(struct nic *nic, irq_action_t action);
#define E1000_WRITE_FLUSH(a) {uint32_t x; x = E1000_READ_REG(a, STATUS);}
+
+/******************************************************************************
+ * Inline functions from e1000_main.c of the linux driver
+ ******************************************************************************/
+
+#if 0
static inline uint32_t
e1000_io_read(struct e1000_hw *hw __unused, uint32_t port)
{
return inl(port);
}
+#endif
static inline void
e1000_io_write(struct e1000_hw *hw __unused, uint32_t port, uint32_t value)
@@ -197,529 +209,10 @@ static inline void e1000_pci_clear_mwi(struct e1000_hw *hw)
hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE);
}
-/******************************************************************************
- * Raises the EEPROM's clock input.
- *
- * hw - Struct containing variables accessed by shared code
- * eecd - EECD's current value
- *****************************************************************************/
-static void
-e1000_raise_ee_clk(struct e1000_hw *hw,
- uint32_t *eecd)
-{
- /* Raise the clock input to the EEPROM (by setting the SK bit), and then
- * wait <delay> microseconds.
- */
- *eecd = *eecd | E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, *eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(hw->eeprom.delay_usec);
-}
-
-/******************************************************************************
- * Lowers the EEPROM's clock input.
- *
- * hw - Struct containing variables accessed by shared code
- * eecd - EECD's current value
- *****************************************************************************/
-static void
-e1000_lower_ee_clk(struct e1000_hw *hw,
- uint32_t *eecd)
-{
- /* Lower the clock input to the EEPROM (by clearing the SK bit), and then
- * wait 50 microseconds.
- */
- *eecd = *eecd & ~E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, *eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(hw->eeprom.delay_usec);
-}
-
-/******************************************************************************
- * Shift data bits out to the EEPROM.
- *
- * hw - Struct containing variables accessed by shared code
- * data - data to send to the EEPROM
- * count - number of bits to shift out
- *****************************************************************************/
-static void
-e1000_shift_out_ee_bits(struct e1000_hw *hw,
- uint16_t data,
- uint16_t count)
-{
- struct e1000_eeprom_info *eeprom = &hw->eeprom;
- uint32_t eecd;
- uint32_t mask;
-
- /* We need to shift "count" bits out to the EEPROM. So, value in the
- * "data" parameter will be shifted out to the EEPROM one bit at a time.
- * In order to do this, "data" must be broken down into bits.
- */
- mask = 0x01 << (count - 1);
- eecd = E1000_READ_REG(hw, EECD);
- if (eeprom->type == e1000_eeprom_microwire) {
- eecd &= ~E1000_EECD_DO;
- } else if (eeprom->type == e1000_eeprom_spi) {
- eecd |= E1000_EECD_DO;
- }
- do {
- /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1",
- * and then raising and then lowering the clock (the SK bit controls
- * the clock input to the EEPROM). A "0" is shifted out to the EEPROM
- * by setting "DI" to "0" and then raising and then lowering the clock.
- */
- eecd &= ~E1000_EECD_DI;
-
- if(data & mask)
- eecd |= E1000_EECD_DI;
-
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
-
- udelay(eeprom->delay_usec);
-
- e1000_raise_ee_clk(hw, &eecd);
- e1000_lower_ee_clk(hw, &eecd);
-
- mask = mask >> 1;
-
- } while(mask);
-
- /* We leave the "DI" bit set to "0" when we leave this routine. */
- eecd &= ~E1000_EECD_DI;
- E1000_WRITE_REG(hw, EECD, eecd);
-}
-
-/******************************************************************************
- * Shift data bits in from the EEPROM
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static uint16_t
-e1000_shift_in_ee_bits(struct e1000_hw *hw,
- uint16_t count)
-{
- uint32_t eecd;
- uint32_t i;
- uint16_t data;
-
- /* In order to read a register from the EEPROM, we need to shift 'count'
- * bits in from the EEPROM. Bits are "shifted in" by raising the clock
- * input to the EEPROM (setting the SK bit), and then reading the value of
- * the "DO" bit. During this "shifting in" process the "DI" bit should
- * always be clear.
- */
-
- eecd = E1000_READ_REG(hw, EECD);
-
- eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
- data = 0;
-
- for(i = 0; i < count; i++) {
- data = data << 1;
- e1000_raise_ee_clk(hw, &eecd);
-
- eecd = E1000_READ_REG(hw, EECD);
-
- eecd &= ~(E1000_EECD_DI);
- if(eecd & E1000_EECD_DO)
- data |= 1;
-
- e1000_lower_ee_clk(hw, &eecd);
- }
-
- return data;
-}
-
-/******************************************************************************
- * Prepares EEPROM for access
- *
- * hw - Struct containing variables accessed by shared code
- *
- * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This
- * function should be called before issuing a command to the EEPROM.
- *****************************************************************************/
-static int32_t
-e1000_acquire_eeprom(struct e1000_hw *hw)
-{
- struct e1000_eeprom_info *eeprom = &hw->eeprom;
- uint32_t eecd, i=0;
-
- eecd = E1000_READ_REG(hw, EECD);
-
- /* Request EEPROM Access */
- if(hw->mac_type > e1000_82544) {
- eecd |= E1000_EECD_REQ;
- E1000_WRITE_REG(hw, EECD, eecd);
- eecd = E1000_READ_REG(hw, EECD);
- while((!(eecd & E1000_EECD_GNT)) &&
- (i < E1000_EEPROM_GRANT_ATTEMPTS)) {
- i++;
- udelay(5);
- eecd = E1000_READ_REG(hw, EECD);
- }
- if(!(eecd & E1000_EECD_GNT)) {
- eecd &= ~E1000_EECD_REQ;
- E1000_WRITE_REG(hw, EECD, eecd);
- DEBUGOUT("Could not acquire EEPROM grant\n");
- return -E1000_ERR_EEPROM;
- }
- }
-
- /* Setup EEPROM for Read/Write */
-
- if (eeprom->type == e1000_eeprom_microwire) {
- /* Clear SK and DI */
- eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
- E1000_WRITE_REG(hw, EECD, eecd);
-
- /* Set CS */
- eecd |= E1000_EECD_CS;
- E1000_WRITE_REG(hw, EECD, eecd);
- } else if (eeprom->type == e1000_eeprom_spi) {
- /* Clear SK and CS */
- eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
- E1000_WRITE_REG(hw, EECD, eecd);
- udelay(1);
- }
-
- return E1000_SUCCESS;
-}
-
-/******************************************************************************
- * Returns EEPROM to a "standby" state
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static void
-e1000_standby_eeprom(struct e1000_hw *hw)
-{
- struct e1000_eeprom_info *eeprom = &hw->eeprom;
- uint32_t eecd;
-
- eecd = E1000_READ_REG(hw, EECD);
-
- if(eeprom->type == e1000_eeprom_microwire) {
-
- /* Deselect EEPROM */
- eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
-
- /* Clock high */
- eecd |= E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
-
- /* Select EEPROM */
- eecd |= E1000_EECD_CS;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
-
- /* Clock low */
- eecd &= ~E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
- } else if(eeprom->type == e1000_eeprom_spi) {
- /* Toggle CS to flush commands */
- eecd |= E1000_EECD_CS;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
- eecd &= ~E1000_EECD_CS;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(eeprom->delay_usec);
- }
-}
-
-/******************************************************************************
- * Terminates a command by inverting the EEPROM's chip select pin
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static void
-e1000_release_eeprom(struct e1000_hw *hw)
-{
- uint32_t eecd;
-
- eecd = E1000_READ_REG(hw, EECD);
-
- if (hw->eeprom.type == e1000_eeprom_spi) {
- eecd |= E1000_EECD_CS; /* Pull CS high */
- eecd &= ~E1000_EECD_SK; /* Lower SCK */
-
- E1000_WRITE_REG(hw, EECD, eecd);
-
- udelay(hw->eeprom.delay_usec);
- } else if(hw->eeprom.type == e1000_eeprom_microwire) {
- /* cleanup eeprom */
-
- /* CS on Microwire is active-high */
- eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
-
- E1000_WRITE_REG(hw, EECD, eecd);
-
- /* Rising edge of clock */
- eecd |= E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(hw->eeprom.delay_usec);
-
- /* Falling edge of clock */
- eecd &= ~E1000_EECD_SK;
- E1000_WRITE_REG(hw, EECD, eecd);
- E1000_WRITE_FLUSH(hw);
- udelay(hw->eeprom.delay_usec);
- }
-
- /* Stop requesting EEPROM access */
- if(hw->mac_type > e1000_82544) {
- eecd &= ~E1000_EECD_REQ;
- E1000_WRITE_REG(hw, EECD, eecd);
- }
-}
-
-/******************************************************************************
- * Reads a 16 bit word from the EEPROM.
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static int32_t
-e1000_spi_eeprom_ready(struct e1000_hw *hw)
-{
- uint16_t retry_count = 0;
- uint8_t spi_stat_reg;
-
- /* Read "Status Register" repeatedly until the LSB is cleared. The
- * EEPROM will signal that the command has been completed by clearing
- * bit 0 of the internal status register. If it's not cleared within
- * 5 milliseconds, then error out.
- */
- retry_count = 0;
- do {
- e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
- hw->eeprom.opcode_bits);
- spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8);
- if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
- break;
-
- udelay(5);
- retry_count += 5;
-
- } while(retry_count < EEPROM_MAX_RETRY_SPI);
-
- /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and
- * only 0-5mSec on 5V devices)
- */
- if(retry_count >= EEPROM_MAX_RETRY_SPI) {
- DEBUGOUT("SPI EEPROM Status error\n");
- return -E1000_ERR_EEPROM;
- }
-
- return E1000_SUCCESS;
-}
/******************************************************************************
- * Reads a 16 bit word from the EEPROM.
- *
- * hw - Struct containing variables accessed by shared code
- * offset - offset of word in the EEPROM to read
- * data - word read from the EEPROM
- * words - number of words to read
- *****************************************************************************/
-static int
-e1000_read_eeprom(struct e1000_hw *hw,
- uint16_t offset,
- uint16_t words,
- uint16_t *data)
-{
- struct e1000_eeprom_info *eeprom = &hw->eeprom;
- uint32_t i = 0;
-
- DEBUGFUNC("e1000_read_eeprom");
-
- /* A check for invalid values: offset too large, too many words, and not
- * enough words.
- */
- if((offset > eeprom->word_size) || (words > eeprom->word_size - offset) ||
- (words == 0)) {
- DEBUGOUT("\"words\" parameter out of bounds\n");
- return -E1000_ERR_EEPROM;
- }
-
- /* Prepare the EEPROM for reading */
- if(e1000_acquire_eeprom(hw) != E1000_SUCCESS)
- return -E1000_ERR_EEPROM;
-
- if(eeprom->type == e1000_eeprom_spi) {
- uint16_t word_in;
- uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
-
- if(e1000_spi_eeprom_ready(hw)) {
- e1000_release_eeprom(hw);
- return -E1000_ERR_EEPROM;
- }
-
- e1000_standby_eeprom(hw);
-
- /* Some SPI eeproms use the 8th address bit embedded in the opcode */
- if((eeprom->address_bits == 8) && (offset >= 128))
- read_opcode |= EEPROM_A8_OPCODE_SPI;
-
- /* Send the READ command (opcode + addr) */
- e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
-
- /* Read the data. The address of the eeprom internally increments with
- * each byte (spi) being read, saving on the overhead of eeprom setup
- * and tear-down. The address counter will roll over if reading beyond
- * the size of the eeprom, thus allowing the entire memory to be read
- * starting from any offset. */
- for (i = 0; i < words; i++) {
- word_in = e1000_shift_in_ee_bits(hw, 16);
- data[i] = (word_in >> 8) | (word_in << 8);
- }
- } else if(eeprom->type == e1000_eeprom_microwire) {
- for (i = 0; i < words; i++) {
- /* Send the READ command (opcode + addr) */
- e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
- eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
- eeprom->address_bits);
-
- /* Read the data. For microwire, each word requires the overhead
- * of eeprom setup and tear-down. */
- data[i] = e1000_shift_in_ee_bits(hw, 16);
- e1000_standby_eeprom(hw);
- }
- }
-
- /* End this read operation */
- e1000_release_eeprom(hw);
-
- return E1000_SUCCESS;
-}
-
-/******************************************************************************
- * Verifies that the EEPROM has a valid checksum
- *
- * hw - Struct containing variables accessed by shared code
- *
- * Reads the first 64 16 bit words of the EEPROM and sums the values read.
- * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is
- * valid.
- *****************************************************************************/
-static int
-e1000_validate_eeprom_checksum(struct e1000_hw *hw)
-{
- uint16_t checksum = 0;
- uint16_t i, eeprom_data;
-
- DEBUGFUNC("e1000_validate_eeprom_checksum");
-
- for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
- if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
- DEBUGOUT("EEPROM Read Error\n");
- return -E1000_ERR_EEPROM;
- }
- checksum += eeprom_data;
- }
-
- if(checksum == (uint16_t) EEPROM_SUM)
- return E1000_SUCCESS;
- else {
- DEBUGOUT("EEPROM Checksum Invalid\n");
- return -E1000_ERR_EEPROM;
- }
-}
-
-/******************************************************************************
- * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the
- * second function of dual function devices
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static int
-e1000_read_mac_addr(struct e1000_hw *hw)
-{
- uint16_t offset;
- uint16_t eeprom_data;
- int i;
-
- DEBUGFUNC("e1000_read_mac_addr");
-
- for(i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
- offset = i >> 1;
- if(e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
- DEBUGOUT("EEPROM Read Error\n");
- return -E1000_ERR_EEPROM;
- }
- hw->mac_addr[i] = eeprom_data & 0xff;
- hw->mac_addr[i+1] = (eeprom_data >> 8) & 0xff;
- }
- if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) &&
- (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1))
- /* Invert the last bit if this is the second device */
- hw->mac_addr[5] ^= 1;
- return E1000_SUCCESS;
-}
-
-/******************************************************************************
- * Initializes receive address filters.
- *
- * hw - Struct containing variables accessed by shared code
- *
- * Places the MAC address in receive address register 0 and clears the rest
- * of the receive addresss registers. Clears the multicast table. Assumes
- * the receiver is in reset when the routine is called.
- *****************************************************************************/
-static void
-e1000_init_rx_addrs(struct e1000_hw *hw)
-{
- uint32_t i;
- uint32_t addr_low;
- uint32_t addr_high;
-
- DEBUGFUNC("e1000_init_rx_addrs");
-
- /* Setup the receive address. */
- DEBUGOUT("Programming MAC Address into RAR[0]\n");
- addr_low = (hw->mac_addr[0] |
- (hw->mac_addr[1] << 8) |
- (hw->mac_addr[2] << 16) | (hw->mac_addr[3] << 24));
-
- addr_high = (hw->mac_addr[4] |
- (hw->mac_addr[5] << 8) | E1000_RAH_AV);
-
- E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low);
- E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high);
-
- /* Zero out the other 15 receive addresses. */
- DEBUGOUT("Clearing RAR[1-15]\n");
- for(i = 1; i < E1000_RAR_ENTRIES; i++) {
- E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0);
- E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0);
- }
-}
-
-/******************************************************************************
- * Clears the VLAN filer table
- *
- * hw - Struct containing variables accessed by shared code
- *****************************************************************************/
-static void
-e1000_clear_vfta(struct e1000_hw *hw)
-{
- uint32_t offset;
-
- for(offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++)
- E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0);
-}
+ * Inline functions from e1000_hw.c of the linux driver
+ ******************************************************************************/
/******************************************************************************
* Writes a value to one of the devices registers using port I/O (as opposed to
@@ -733,6 +226,11 @@ static inline void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset,
e1000_io_write(hw, hw->io_base + 4, value);
}
+
+/******************************************************************************
+ * Functions from e1000_hw.c of the linux driver
+ ******************************************************************************/
+
/******************************************************************************
* Set the phy type member in the hw struct.
*
@@ -3252,6 +2750,535 @@ e1000_init_eeprom_params(struct e1000_hw *hw)
}
}
+/******************************************************************************
+ * Raises the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_raise_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Raise the clock input to the EEPROM (by setting the SK bit), and then
+ * wait <delay> microseconds.
+ */
+ *eecd = *eecd | E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Lowers the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_lower_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Lower the clock input to the EEPROM (by clearing the SK bit), and then
+ * wait 50 microseconds.
+ */
+ *eecd = *eecd & ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Shift data bits out to the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * data - data to send to the EEPROM
+ * count - number of bits to shift out
+ *****************************************************************************/
+static void
+e1000_shift_out_ee_bits(struct e1000_hw *hw,
+ uint16_t data,
+ uint16_t count)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+ uint32_t mask;
+
+ /* We need to shift "count" bits out to the EEPROM. So, value in the
+ * "data" parameter will be shifted out to the EEPROM one bit at a time.
+ * In order to do this, "data" must be broken down into bits.
+ */
+ mask = 0x01 << (count - 1);
+ eecd = E1000_READ_REG(hw, EECD);
+ if (eeprom->type == e1000_eeprom_microwire) {
+ eecd &= ~E1000_EECD_DO;
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_DO;
+ }
+ do {
+ /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1",
+ * and then raising and then lowering the clock (the SK bit controls
+ * the clock input to the EEPROM). A "0" is shifted out to the EEPROM
+ * by setting "DI" to "0" and then raising and then lowering the clock.
+ */
+ eecd &= ~E1000_EECD_DI;
+
+ if(data & mask)
+ eecd |= E1000_EECD_DI;
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+
+ udelay(eeprom->delay_usec);
+
+ e1000_raise_ee_clk(hw, &eecd);
+ e1000_lower_ee_clk(hw, &eecd);
+
+ mask = mask >> 1;
+
+ } while(mask);
+
+ /* We leave the "DI" bit set to "0" when we leave this routine. */
+ eecd &= ~E1000_EECD_DI;
+ E1000_WRITE_REG(hw, EECD, eecd);
+}
+
+/******************************************************************************
+ * Shift data bits in from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static uint16_t
+e1000_shift_in_ee_bits(struct e1000_hw *hw,
+ uint16_t count)
+{
+ uint32_t eecd;
+ uint32_t i;
+ uint16_t data;
+
+ /* In order to read a register from the EEPROM, we need to shift 'count'
+ * bits in from the EEPROM. Bits are "shifted in" by raising the clock
+ * input to the EEPROM (setting the SK bit), and then reading the value of
+ * the "DO" bit. During this "shifting in" process the "DI" bit should
+ * always be clear.
+ */
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+ data = 0;
+
+ for(i = 0; i < count; i++) {
+ data = data << 1;
+ e1000_raise_ee_clk(hw, &eecd);
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DI);
+ if(eecd & E1000_EECD_DO)
+ data |= 1;
+
+ e1000_lower_ee_clk(hw, &eecd);
+ }
+
+ return data;
+}
+
+/******************************************************************************
+ * Prepares EEPROM for access
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This
+ * function should be called before issuing a command to the EEPROM.
+ *****************************************************************************/
+static int32_t
+e1000_acquire_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd, i=0;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ /* Request EEPROM Access */
+ if(hw->mac_type > e1000_82544) {
+ eecd |= E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ eecd = E1000_READ_REG(hw, EECD);
+ while((!(eecd & E1000_EECD_GNT)) &&
+ (i < E1000_EEPROM_GRANT_ATTEMPTS)) {
+ i++;
+ udelay(5);
+ eecd = E1000_READ_REG(hw, EECD);
+ }
+ if(!(eecd & E1000_EECD_GNT)) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ DEBUGOUT("Could not acquire EEPROM grant\n");
+ return -E1000_ERR_EEPROM;
+ }
+ }
+
+ /* Setup EEPROM for Read/Write */
+
+ if (eeprom->type == e1000_eeprom_microwire) {
+ /* Clear SK and DI */
+ eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Set CS */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ /* Clear SK and CS */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ udelay(1);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Returns EEPROM to a "standby" state
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_standby_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if(eeprom->type == e1000_eeprom_microwire) {
+
+ /* Deselect EEPROM */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock high */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Select EEPROM */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock low */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ } else if(eeprom->type == e1000_eeprom_spi) {
+ /* Toggle CS to flush commands */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ eecd &= ~E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ }
+}
+
+/******************************************************************************
+ * Terminates a command by inverting the EEPROM's chip select pin
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_release_eeprom(struct e1000_hw *hw)
+{
+ uint32_t eecd;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if (hw->eeprom.type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_CS; /* Pull CS high */
+ eecd &= ~E1000_EECD_SK; /* Lower SCK */
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ udelay(hw->eeprom.delay_usec);
+ } else if(hw->eeprom.type == e1000_eeprom_microwire) {
+ /* cleanup eeprom */
+
+ /* CS on Microwire is active-high */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Rising edge of clock */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+
+ /* Falling edge of clock */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+ }
+
+ /* Stop requesting EEPROM access */
+ if(hw->mac_type > e1000_82544) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ }
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_spi_eeprom_ready(struct e1000_hw *hw)
+{
+ uint16_t retry_count = 0;
+ uint8_t spi_stat_reg;
+
+ /* Read "Status Register" repeatedly until the LSB is cleared. The
+ * EEPROM will signal that the command has been completed by clearing
+ * bit 0 of the internal status register. If it's not cleared within
+ * 5 milliseconds, then error out.
+ */
+ retry_count = 0;
+ do {
+ e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
+ hw->eeprom.opcode_bits);
+ spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8);
+ if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
+ break;
+
+ udelay(5);
+ retry_count += 5;
+
+ } while(retry_count < EEPROM_MAX_RETRY_SPI);
+
+ /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and
+ * only 0-5mSec on 5V devices)
+ */
+ if(retry_count >= EEPROM_MAX_RETRY_SPI) {
+ DEBUGOUT("SPI EEPROM Status error\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int
+e1000_read_eeprom(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t i = 0;
+
+ DEBUGFUNC("e1000_read_eeprom");
+
+ /* A check for invalid values: offset too large, too many words, and not
+ * enough words.
+ */
+ if((offset > eeprom->word_size) || (words > eeprom->word_size - offset) ||
+ (words == 0)) {
+ DEBUGOUT("\"words\" parameter out of bounds\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* Prepare the EEPROM for reading */
+ if(e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
+
+ if(eeprom->type == e1000_eeprom_spi) {
+ uint16_t word_in;
+ uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
+
+ if(e1000_spi_eeprom_ready(hw)) {
+ e1000_release_eeprom(hw);
+ return -E1000_ERR_EEPROM;
+ }
+
+ e1000_standby_eeprom(hw);
+
+ /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+ if((eeprom->address_bits == 8) && (offset >= 128))
+ read_opcode |= EEPROM_A8_OPCODE_SPI;
+
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
+
+ /* Read the data. The address of the eeprom internally increments with
+ * each byte (spi) being read, saving on the overhead of eeprom setup
+ * and tear-down. The address counter will roll over if reading beyond
+ * the size of the eeprom, thus allowing the entire memory to be read
+ * starting from any offset. */
+ for (i = 0; i < words; i++) {
+ word_in = e1000_shift_in_ee_bits(hw, 16);
+ data[i] = (word_in >> 8) | (word_in << 8);
+ }
+ } else if(eeprom->type == e1000_eeprom_microwire) {
+ for (i = 0; i < words; i++) {
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
+ eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
+ eeprom->address_bits);
+
+ /* Read the data. For microwire, each word requires the overhead
+ * of eeprom setup and tear-down. */
+ data[i] = e1000_shift_in_ee_bits(hw, 16);
+ e1000_standby_eeprom(hw);
+ }
+ }
+
+ /* End this read operation */
+ e1000_release_eeprom(hw);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Verifies that the EEPROM has a valid checksum
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Reads the first 64 16 bit words of the EEPROM and sums the values read.
+ * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is
+ * valid.
+ *****************************************************************************/
+static int
+e1000_validate_eeprom_checksum(struct e1000_hw *hw)
+{
+ uint16_t checksum = 0;
+ uint16_t i, eeprom_data;
+
+ DEBUGFUNC("e1000_validate_eeprom_checksum");
+
+ for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
+ if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ checksum += eeprom_data;
+ }
+
+ if(checksum == (uint16_t) EEPROM_SUM)
+ return E1000_SUCCESS;
+ else {
+ DEBUGOUT("EEPROM Checksum Invalid\n");
+ return -E1000_ERR_EEPROM;
+ }
+}
+
+/******************************************************************************
+ * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the
+ * second function of dual function devices
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int
+e1000_read_mac_addr(struct e1000_hw *hw)
+{
+ uint16_t offset;
+ uint16_t eeprom_data;
+ int i;
+
+ DEBUGFUNC("e1000_read_mac_addr");
+
+ for(i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
+ offset = i >> 1;
+ if(e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ hw->mac_addr[i] = eeprom_data & 0xff;
+ hw->mac_addr[i+1] = (eeprom_data >> 8) & 0xff;
+ }
+ if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1))
+ /* Invert the last bit if this is the second device */
+ hw->mac_addr[5] ^= 1;
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Initializes receive address filters.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Places the MAC address in receive address register 0 and clears the rest
+ * of the receive addresss registers. Clears the multicast table. Assumes
+ * the receiver is in reset when the routine is called.
+ *****************************************************************************/
+static void
+e1000_init_rx_addrs(struct e1000_hw *hw)
+{
+ uint32_t i;
+ uint32_t addr_low;
+ uint32_t addr_high;
+
+ DEBUGFUNC("e1000_init_rx_addrs");
+
+ /* Setup the receive address. */
+ DEBUGOUT("Programming MAC Address into RAR[0]\n");
+ addr_low = (hw->mac_addr[0] |
+ (hw->mac_addr[1] << 8) |
+ (hw->mac_addr[2] << 16) | (hw->mac_addr[3] << 24));
+
+ addr_high = (hw->mac_addr[4] |
+ (hw->mac_addr[5] << 8) | E1000_RAH_AV);
+
+ E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low);
+ E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high);
+
+ /* Zero out the other 15 receive addresses. */
+ DEBUGOUT("Clearing RAR[1-15]\n");
+ for(i = 1; i < E1000_RAR_ENTRIES; i++) {
+ E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0);
+ E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0);
+ }
+}
+
+/******************************************************************************
+ * Clears the VLAN filer table
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_clear_vfta(struct e1000_hw *hw)
+{
+ uint32_t offset;
+
+ for(offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++)
+ E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0);
+}
+
+
+/******************************************************************************
+ * Functions from e1000_main.c of the linux driver
+ ******************************************************************************/
+
/**
* e1000_reset - Reset the adapter
*/
@@ -3357,6 +3384,11 @@ e1000_sw_init(struct pci_device *pdev, struct e1000_hw *hw)
return E1000_SUCCESS;
}
+
+/******************************************************************************
+ * Functions not present in the linux driver
+ ******************************************************************************/
+
static void fill_rx (void)
{
struct e1000_rx_desc *rd;
diff --git a/src/drivers/net/eepro.c b/src/drivers/net/eepro.c
index 6c2318bd..65c6c8f5 100644
--- a/src/drivers/net/eepro.c
+++ b/src/drivers/net/eepro.c
@@ -19,6 +19,12 @@ has 34 pins, the top row of 2 are not used.
***************************************************************************/
/*
+
+ timlegge 2005-05-18 remove the relocation changes cards that
+ write directly to the hardware don't need it
+*/
+
+/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
@@ -257,8 +263,8 @@ static unsigned eeprom_reg = EEPROM_REG_PRO;
#define eeprom_delay() { udelay(40); }
#define EE_READ_CMD (6 << 6)
-/* do a full reset */
-#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(40);
+/* do a full reset; data sheet asks for 250us delay */
+#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(255);
/* do a nice reset */
#define eepro_sel_reset(ioaddr) { \
@@ -320,13 +326,15 @@ static void eepro_reset(struct nic *nic)
eepro_sw2bank0(nic->ioaddr); /* Switch back to bank 0 */
eepro_clear_int(nic->ioaddr);
/* Initialise RCV */
- rx_start = (unsigned int)bus_to_virt(RCV_LOWER_LIMIT << 8);
- outw(RCV_LOWER_LIMIT << 8, nic->ioaddr + RCV_BAR);
+ outw(rx_start = (RCV_LOWER_LIMIT << 8), nic->ioaddr + RCV_BAR);
outw(((RCV_UPPER_LIMIT << 8) | 0xFE), nic->ioaddr + RCV_STOP);
+ /* Make sure 1st poll won't find a valid packet header */
+ outw((RCV_LOWER_LIMIT << 8), nic->ioaddr + HOST_ADDRESS_REG);
+ outw(0, nic->ioaddr + IO_PORT);
/* Intialise XMT */
outw((XMT_LOWER_LIMIT << 8), nic->ioaddr + xmt_bar);
eepro_sel_reset(nic->ioaddr);
- tx_start = tx_end = (unsigned int)bus_to_virt(XMT_LOWER_LIMIT << 8);
+ tx_start = tx_end = (XMT_LOWER_LIMIT << 8);
tx_last = 0;
eepro_en_rx(nic->ioaddr);
}
@@ -336,7 +344,7 @@ POLL - Wait for a frame
***************************************************************************/
static int eepro_poll(struct nic *nic, int retrieve)
{
- unsigned int rcv_car = virt_to_bus((void *)rx_start);
+ unsigned int rcv_car = rx_start;
unsigned int rcv_event, rcv_status, rcv_next_frame, rcv_size;
/* return true if there's an ethernet packet ready to read */
@@ -380,8 +388,12 @@ static int eepro_poll(struct nic *nic, int retrieve)
}
#endif
nic->packetlen = rcv_size;
- rcv_car = virt_to_bus((void *) (rx_start + RCV_HEADER + rcv_size));
- rx_start = (unsigned int)bus_to_virt(rcv_next_frame << 8);
+ rcv_car = (rx_start + RCV_HEADER + rcv_size);
+ rx_start = rcv_next_frame;
+/*
+ hex_dump(rcv_car, nic->packetlen);
+*/
+
if (rcv_car == 0)
rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff);
outw(rcv_car - 1, nic->ioaddr + RCV_STOP);
@@ -455,7 +467,7 @@ static void eepro_disable ( struct nic *nic, struct isa_device *isa __unused ) {
eepro_sw2bank0(nic->ioaddr); /* Switch to bank 0 */
/* Flush the Tx and disable Rx */
outb(STOP_RCV_CMD, nic->ioaddr);
- tx_start = tx_end = (unsigned int) (bus_to_virt(XMT_LOWER_LIMIT << 8));
+ tx_start = tx_end = (XMT_LOWER_LIMIT << 8);
tx_last = 0;
/* Reset the 82595 */
eepro_full_reset(nic->ioaddr);
diff --git a/src/drivers/net/eepro100.c b/src/drivers/net/eepro100.c
index 590b37c8..d97b1b18 100644
--- a/src/drivers/net/eepro100.c
+++ b/src/drivers/net/eepro100.c
@@ -110,13 +110,6 @@
static int ioaddr;
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
enum speedo_offsets {
SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
SCBPointer = 4, /* General purpose pointer. */
diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c
index 3297effd..63773836 100644
--- a/src/drivers/net/forcedeth.c
+++ b/src/drivers/net/forcedeth.c
@@ -30,14 +30,16 @@
* (C) 2003 Manfred Spraul
* See Linux Driver for full information
*
-* Linux Driver Version 0.22, 19 Jan 2004
+* Linux Driver Version 0.30, 25 Sep 2004
+* Linux Kernel 2.6.10
*
*
* REVISION HISTORY:
* ================
* v1.0 01-31-2004 timlegge Initial port of Linux driver
* v1.1 02-03-2004 timlegge Large Clean up, first release
-*
+* v1.2 05-14-2005 timlegge Add Linux 0.22 to .030 features
+*
* Indent Options: indent -kr -i8
***************************************************************************/
@@ -49,9 +51,10 @@
#include "pci.h"
/* Include timer support functions */
#include "timer.h"
+#include "mii.h"
-#define drv_version "v1.1"
-#define drv_date "02-03-2004"
+#define drv_version "v1.2"
+#define drv_date "05-14-2005"
//#define TFTM_DEBUG
#ifdef TFTM_DEBUG
@@ -60,12 +63,7 @@
#define dprintf(x)
#endif
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
+#define ETH_DATA_LEN 1500
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
@@ -73,22 +71,35 @@ typedef signed int s32;
static unsigned long BASE;
/* NIC specific static variables go here */
+#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3
+#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066
+#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086
+#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c
+#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6
+#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df
+#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6
+#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056
+#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057
+#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037
+#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038
/*
* Hardware access:
*/
-#define DEV_NEED_LASTPACKET1 0x0001
-#define DEV_IRQMASK_1 0x0002
-#define DEV_IRQMASK_2 0x0004
-#define DEV_NEED_TIMERIRQ 0x0008
+#define DEV_NEED_LASTPACKET1 0x0001 /* set LASTPACKET1 in tx flags */
+#define DEV_IRQMASK_1 0x0002 /* use NVREG_IRQMASK_WANTED_1 for irq mask */
+#define DEV_IRQMASK_2 0x0004 /* use NVREG_IRQMASK_WANTED_2 for irq mask */
+#define DEV_NEED_TIMERIRQ 0x0008 /* set the timer irq flag in the irq mask */
+#define DEV_NEED_LINKTIMER 0x0010 /* poll link settings. Relies on the timer irq */
enum {
NvRegIrqStatus = 0x000,
#define NVREG_IRQSTAT_MIIEVENT 0040
#define NVREG_IRQSTAT_MASK 0x1ff
NvRegIrqMask = 0x004,
+#define NVREG_IRQ_RX_ERROR 0x0001
#define NVREG_IRQ_RX 0x0002
#define NVREG_IRQ_RX_NOBUF 0x0004
#define NVREG_IRQ_TX_ERR 0x0008
@@ -98,7 +109,7 @@ enum {
#define NVREG_IRQ_TX1 0x0100
#define NVREG_IRQMASK_WANTED_1 0x005f
#define NVREG_IRQMASK_WANTED_2 0x0147
-#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
+#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
NvRegUnknownSetupReg6 = 0x008,
#define NVREG_UNKSETUP6_VAL 3
@@ -125,7 +136,7 @@ enum {
NvRegOffloadConfig = 0x90,
#define NVREG_OFFLOAD_HOMEPHY 0x601
-#define NVREG_OFFLOAD_NORMAL 0x5ee
+#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
NvRegReceiverControl = 0x094,
#define NVREG_RCVCTL_START 0x01
NvRegReceiverStatus = 0x98,
@@ -134,6 +145,8 @@ enum {
NvRegRandomSeed = 0x9c,
#define NVREG_RNDSEED_MASK 0x00ff
#define NVREG_RNDSEED_FORCE 0x7f00
+#define NVREG_RNDSEED_FORCE2 0x2d00
+#define NVREG_RNDSEED_FORCE3 0x7400
NvRegUnknownSetupReg1 = 0xA0,
#define NVREG_UNKSETUP1_VAL 0x16070f
@@ -147,6 +160,9 @@ enum {
NvRegMulticastMaskA = 0xB8,
NvRegMulticastMaskB = 0xBC,
+ NvRegPhyInterface = 0xC0,
+#define PHY_RGMII 0x10000000
+
NvRegTxRingPhysAddr = 0x100,
NvRegRxRingPhysAddr = 0x104,
NvRegRingSizes = 0x108,
@@ -155,12 +171,12 @@ enum {
NvRegUnknownTransmitterReg = 0x10c,
NvRegLinkSpeed = 0x110,
#define NVREG_LINKSPEED_FORCE 0x10000
-#define NVREG_LINKSPEED_10 10
+#define NVREG_LINKSPEED_10 1000
#define NVREG_LINKSPEED_100 100
-#define NVREG_LINKSPEED_1000 1000
+#define NVREG_LINKSPEED_1000 50
NvRegUnknownSetupReg5 = 0x130,
#define NVREG_UNKSETUP5_BIT31 (1<<31)
- NvRegUnknownSetupReg3 = 0x134,
+ NvRegUnknownSetupReg3 = 0x13c,
#define NVREG_UNKSETUP3_VAL1 0x200010
NvRegTxRxControl = 0x144,
#define NVREG_TXRXCTL_KICK 0x0001
@@ -168,6 +184,7 @@ enum {
#define NVREG_TXRXCTL_BIT2 0x0004
#define NVREG_TXRXCTL_IDLE 0x0008
#define NVREG_TXRXCTL_RESET 0x0010
+#define NVREG_TXRXCTL_RXCHECK 0x0400
NvRegMIIStatus = 0x180,
#define NVREG_MIISTAT_ERROR 0x0001
#define NVREG_MIISTAT_LINKCHANGE 0x0008
@@ -179,15 +196,15 @@ enum {
NvRegAdapterControl = 0x188,
#define NVREG_ADAPTCTL_START 0x02
#define NVREG_ADAPTCTL_LINKUP 0x04
-#define NVREG_ADAPTCTL_PHYVALID 0x4000
+#define NVREG_ADAPTCTL_PHYVALID 0x40000
#define NVREG_ADAPTCTL_RUNNING 0x100000
#define NVREG_ADAPTCTL_PHYSHIFT 24
NvRegMIISpeed = 0x18c,
#define NVREG_MIISPEED_BIT8 (1<<8)
#define NVREG_MIIDELAY 5
NvRegMIIControl = 0x190,
-#define NVREG_MIICTL_INUSE 0x10000
-#define NVREG_MIICTL_WRITE 0x08000
+#define NVREG_MIICTL_INUSE 0x08000
+#define NVREG_MIICTL_WRITE 0x00400
#define NVREG_MIICTL_ADDRSHIFT 5
NvRegMIIData = 0x194,
NvRegWakeUpFlags = 0x200,
@@ -201,6 +218,7 @@ enum {
#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
+#define NVREG_WAKEUPFLAGS_ENABLE 0x1111
NvRegPatternCRC = 0x204,
NvRegPatternMask = 0x208,
@@ -218,30 +236,61 @@ enum {
#define NVREG_POWERSTATE_D3 0x0003
};
-
-
-#define NV_TX_LASTPACKET (1<<0)
-#define NV_TX_RETRYERROR (1<<3)
-#define NV_TX_LASTPACKET1 (1<<8)
-#define NV_TX_DEFERRED (1<<10)
-#define NV_TX_CARRIERLOST (1<<11)
-#define NV_TX_LATECOLLISION (1<<12)
-#define NV_TX_UNDERFLOW (1<<13)
-#define NV_TX_ERROR (1<<14)
-#define NV_TX_VALID (1<<15)
-
-#define NV_RX_DESCRIPTORVALID (1<<0)
-#define NV_RX_MISSEDFRAME (1<<1)
-#define NV_RX_SUBSTRACT1 (1<<3)
-#define NV_RX_ERROR1 (1<<7)
-#define NV_RX_ERROR2 (1<<8)
-#define NV_RX_ERROR3 (1<<9)
-#define NV_RX_ERROR4 (1<<10)
-#define NV_RX_CRCERR (1<<11)
-#define NV_RX_OVERFLOW (1<<12)
-#define NV_RX_FRAMINGERR (1<<13)
-#define NV_RX_ERROR (1<<14)
-#define NV_RX_AVAIL (1<<15)
+#define FLAG_MASK_V1 0xffff0000
+#define FLAG_MASK_V2 0xffffc000
+#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
+#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
+
+#define NV_TX_LASTPACKET (1<<16)
+#define NV_TX_RETRYERROR (1<<19)
+#define NV_TX_LASTPACKET1 (1<<24)
+#define NV_TX_DEFERRED (1<<26)
+#define NV_TX_CARRIERLOST (1<<27)
+#define NV_TX_LATECOLLISION (1<<28)
+#define NV_TX_UNDERFLOW (1<<29)
+#define NV_TX_ERROR (1<<30)
+#define NV_TX_VALID (1<<31)
+
+#define NV_TX2_LASTPACKET (1<<29)
+#define NV_TX2_RETRYERROR (1<<18)
+#define NV_TX2_LASTPACKET1 (1<<23)
+#define NV_TX2_DEFERRED (1<<25)
+#define NV_TX2_CARRIERLOST (1<<26)
+#define NV_TX2_LATECOLLISION (1<<27)
+#define NV_TX2_UNDERFLOW (1<<28)
+/* error and valid are the same for both */
+#define NV_TX2_ERROR (1<<30)
+#define NV_TX2_VALID (1<<31)
+
+#define NV_RX_DESCRIPTORVALID (1<<16)
+#define NV_RX_MISSEDFRAME (1<<17)
+#define NV_RX_SUBSTRACT1 (1<<18)
+#define NV_RX_ERROR1 (1<<23)
+#define NV_RX_ERROR2 (1<<24)
+#define NV_RX_ERROR3 (1<<25)
+#define NV_RX_ERROR4 (1<<26)
+#define NV_RX_CRCERR (1<<27)
+#define NV_RX_OVERFLOW (1<<28)
+#define NV_RX_FRAMINGERR (1<<29)
+#define NV_RX_ERROR (1<<30)
+#define NV_RX_AVAIL (1<<31)
+
+#define NV_RX2_CHECKSUMMASK (0x1C000000)
+#define NV_RX2_CHECKSUMOK1 (0x10000000)
+#define NV_RX2_CHECKSUMOK2 (0x14000000)
+#define NV_RX2_CHECKSUMOK3 (0x18000000)
+#define NV_RX2_DESCRIPTORVALID (1<<29)
+#define NV_RX2_SUBSTRACT1 (1<<25)
+#define NV_RX2_ERROR1 (1<<18)
+#define NV_RX2_ERROR2 (1<<19)
+#define NV_RX2_ERROR3 (1<<20)
+#define NV_RX2_ERROR4 (1<<21)
+#define NV_RX2_CRCERR (1<<22)
+#define NV_RX2_OVERFLOW (1<<23)
+#define NV_RX2_FRAMINGERR (1<<24)
+/* error and avail are the same for both */
+#define NV_RX2_ERROR (1<<30)
+#define NV_RX2_AVAIL (1<<31)
/* Miscelaneous hardware related defines: */
#define NV_PCI_REGSZ 0x270
@@ -266,27 +315,73 @@ enum {
#define NV_WAKEUPMASKENTRIES 4
/* General driver defaults */
-#define NV_WATCHDOG_TIMEO (2*HZ)
-#define DEFAULT_MTU 1500 /* also maximum supported, at least for now */
+#define NV_WATCHDOG_TIMEO (5*HZ)
#define RX_RING 4
#define TX_RING 2
-/* limited to 1 packet until we understand NV_TX_LASTPACKET */
-#define TX_LIMIT_STOP 10
-#define TX_LIMIT_START 5
+
+/*
+ * If your nic mysteriously hangs then try to reduce the limits
+ * to 1/0: It might be required to set NV_TX_LASTPACKET in the
+ * last valid ring entry. But this would be impossible to
+ * implement - probably a disassembly error.
+ */
+#define TX_LIMIT_STOP 63
+#define TX_LIMIT_START 62
/* rx/tx mac addr + type + vlan + align + slack*/
-#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64)
+#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64)
/* even more slack */
-#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)
+#define RX_ALLOC_BUFSIZE (ETH_DATA_LEN + 128)
#define OOM_REFILL (1+HZ/20)
#define POLL_WAIT (1+HZ/100)
-
+#define LINK_TIMEOUT (3*HZ)
+
+/*
+ * desc_ver values:
+ * This field has two purposes:
+ * - Newer nics uses a different ring layout. The layout is selected by
+ * comparing np->desc_ver with DESC_VER_xy.
+ * - It contains bits that are forced on when writing to NvRegTxRxControl.
+ */
+#define DESC_VER_1 0x0
+#define DESC_VER_2 (0x02100|NVREG_TXRXCTL_RXCHECK)
+
+/* PHY defines */
+#define PHY_OUI_MARVELL 0x5043
+#define PHY_OUI_CICADA 0x03f1
+#define PHYID1_OUI_MASK 0x03ff
+#define PHYID1_OUI_SHFT 6
+#define PHYID2_OUI_MASK 0xfc00
+#define PHYID2_OUI_SHFT 10
+#define PHY_INIT1 0x0f000
+#define PHY_INIT2 0x0e00
+#define PHY_INIT3 0x01000
+#define PHY_INIT4 0x0200
+#define PHY_INIT5 0x0004
+#define PHY_INIT6 0x02000
+#define PHY_GIGABIT 0x0100
+
+#define PHY_TIMEOUT 0x1
+#define PHY_ERROR 0x2
+
+#define PHY_100 0x1
+#define PHY_1000 0x2
+#define PHY_HALF 0x100
+
+/* FIXME: MII defines that should be added to <linux/mii.h> */
+#define MII_1000BT_CR 0x09
+#define MII_1000BT_SR 0x0a
+#define ADVERTISE_1000FULL 0x0200
+#define ADVERTISE_1000HALF 0x0100
+#define LPA_1000FULL 0x0800
+#define LPA_1000HALF 0x0400
+
+/* Big endian: should work, but is untested */
struct ring_desc {
u32 PacketBuffer;
- u16 Length;
- u16 Flags;
+ u32 FlagLen;
};
@@ -310,22 +405,25 @@ static struct forcedeth_private {
u32 linkspeed;
int duplex;
int phyaddr;
+ int wolenabled;
+ unsigned int phy_oui;
+ u16 gigabit;
/* General data: RO fields */
u8 *ring_addr;
u32 orig_mac[2];
u32 irqmask;
+ u32 desc_ver;
/* rx specific fields.
* Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
*/
unsigned int cur_rx, refill_rx;
- unsigned int rx_buf_sz;
/*
* tx specific fields.
*/
unsigned int next_tx, nic_tx;
- u16 tx_flags;
+ u32 tx_flags;
} npx;
static struct forcedeth_private *np;
@@ -335,6 +433,13 @@ static inline void pci_push(u8 * base)
/* force out pending posted writes */
readl(base);
}
+
+static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
+{
+ return le32_to_cpu(prd->FlagLen)
+ & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
+}
+
static int reg_delay(int offset, u32 mask,
u32 target, int delay, int delaymax, const char *msg)
{
@@ -383,18 +488,11 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg,
int value)
{
u8 *base = (u8 *) BASE;
- int was_running;
u32 reg;
int retval;
writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
- was_running = 0;
- reg = readl(base + NvRegAdapterControl);
- if (reg & NVREG_ADAPTCTL_RUNNING) {
- was_running = 1;
- writel(reg & ~NVREG_ADAPTCTL_RUNNING,
- base + NvRegAdapterControl);
- }
+
reg = readl(base + NvRegMIIControl);
if (reg & NVREG_MIICTL_INUSE) {
writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
@@ -402,7 +500,7 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg,
}
reg =
- NVREG_MIICTL_INUSE | (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
+ (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
if (value != MII_READ) {
writel(value, base + NvRegMIIData);
reg |= NVREG_MIICTL_WRITE;
@@ -424,20 +522,123 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg,
miireg, addr));
retval = -1;
} else {
- /* FIXME: why is that required? */
- udelay(50);
retval = readl(base + NvRegMIIData);
dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n",
miireg, addr, retval));
}
- if (was_running) {
- reg = readl(base + NvRegAdapterControl);
- writel(reg | NVREG_ADAPTCTL_RUNNING,
- base + NvRegAdapterControl);
- }
return retval;
}
+static int phy_reset(struct nic *nic)
+{
+
+ u32 miicontrol;
+ unsigned int tries = 0;
+
+ miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ miicontrol |= BMCR_RESET;
+ if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) {
+ return -1;
+ }
+
+ /* wait for 500ms */
+ mdelay(500);
+
+ /* must wait till reset is deasserted */
+ while (miicontrol & BMCR_RESET) {
+ mdelay(10);
+ miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ /* FIXME: 100 tries seem excessive */
+ if (tries++ > 100)
+ return -1;
+ }
+ return 0;
+}
+
+static int phy_init(struct nic *nic)
+{
+ u8 *base = (u8 *) BASE;
+ u32 phyinterface, phy_reserved, mii_status, mii_control,
+ mii_control_1000, reg;
+
+ /* set advertise register */
+ reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
+ reg |=
+ (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
+ ADVERTISE_100FULL | 0x800 | 0x400);
+ if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) {
+ printf("phy write to advertise failed.\n");
+ return PHY_ERROR;
+ }
+
+ /* get phy interface type */
+ phyinterface = readl(base + NvRegPhyInterface);
+
+ /* see if gigabit phy */
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+
+ if (mii_status & PHY_GIGABIT) {
+ np->gigabit = PHY_GIGABIT;
+ mii_control_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+ mii_control_1000 &= ~ADVERTISE_1000HALF;
+ if (phyinterface & PHY_RGMII)
+ mii_control_1000 |= ADVERTISE_1000FULL;
+ else
+ mii_control_1000 &= ~ADVERTISE_1000FULL;
+
+ if (mii_rw
+ (nic, np->phyaddr, MII_1000BT_CR, mii_control_1000)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ } else
+ np->gigabit = 0;
+
+ /* reset the phy */
+ if (phy_reset(nic)) {
+ printf("phy reset failed\n");
+ return PHY_ERROR;
+ }
+
+ /* phy vendor specific configuration */
+ if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) {
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ);
+ phy_reserved &= ~(PHY_INIT1 | PHY_INIT2);
+ phy_reserved |= (PHY_INIT3 | PHY_INIT4);
+ if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ);
+ phy_reserved |= PHY_INIT5;
+ if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ }
+ if (np->phy_oui == PHY_OUI_CICADA) {
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ);
+ phy_reserved |= PHY_INIT6;
+ if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ }
+
+ /* restart auto negotiation */
+ mii_control = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
+ if (mii_rw(nic, np->phyaddr, MII_BMCR, mii_control)) {
+ return PHY_ERROR;
+ }
+
+ return 0;
+}
+
static void start_rx(struct nic *nic __unused)
{
u8 *base = (u8 *) BASE;
@@ -497,11 +698,12 @@ static void txrx_reset(struct nic *nic __unused)
u8 *base = (u8 *) BASE;
dprintf(("txrx_reset\n"));
- writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET,
+ writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
base + NvRegTxRxControl);
+
pci_push(base);
udelay(NV_TXRX_RESET_DELAY);
- writel(NVREG_TXRXCTL_BIT2, base + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
pci_push(base);
}
@@ -519,9 +721,9 @@ static int alloc_rx(struct nic *nic __unused)
//int nr = refill_rx % RX_RING;
rx_ring[i].PacketBuffer =
virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
- rx_ring[i].Length = cpu_to_le16(RX_NIC_BUFSIZE);
wmb();
- rx_ring[i].Flags = cpu_to_le16(NV_RX_AVAIL);
+ rx_ring[i].FlagLen =
+ cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
/* printf("alloc_rx: Packet %d marked as Available\n",
refill_rx); */
refill_rx++;
@@ -534,8 +736,67 @@ static int alloc_rx(struct nic *nic __unused)
static int update_linkspeed(struct nic *nic)
{
- int adv, lpa, newdup;
+ int adv, lpa;
u32 newls;
+ int newdup = np->duplex;
+ u32 mii_status;
+ int retval = 0;
+ u32 control_1000, status_1000, phyreg;
+ u8 *base = (u8 *) BASE;
+ int i;
+
+ /* BMSR_LSTATUS is latched, read it twice:
+ * we want the current value.
+ */
+ mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+
+#if 1
+ //yhlu
+ for(i=0;i<30;i++) {
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+ if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break;
+ mdelay(100);
+ }
+#endif
+
+ if (!(mii_status & BMSR_LSTATUS)) {
+ printf
+ ("no link detected by phy - falling back to 10HD.\n");
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ goto set_speed;
+ }
+
+ /* check auto negotiation is complete */
+ if (!(mii_status & BMSR_ANEGCOMPLETE)) {
+ /* still in autonegotiation - configure nic for 10 MBit HD and wait. */
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ printf("autoneg not completed - falling back to 10HD.\n");
+ goto set_speed;
+ }
+
+ retval = 1;
+ if (np->gigabit == PHY_GIGABIT) {
+ control_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+ status_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ);
+
+ if ((control_1000 & ADVERTISE_1000FULL) &&
+ (status_1000 & LPA_1000FULL)) {
+ printf
+ ("update_linkspeed: GBit ethernet detected.\n");
+ newls =
+ NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
+ newdup = 1;
+ goto set_speed;
+ }
+ }
+
adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
@@ -560,30 +821,81 @@ static int update_linkspeed(struct nic *nic)
newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
newdup = 0;
}
- if (np->duplex != newdup || np->linkspeed != newls) {
- np->duplex = newdup;
- np->linkspeed = newls;
- return 1;
+
+ set_speed:
+ if (np->duplex == newdup && np->linkspeed == newls)
+ return retval;
+
+ dprintf(("changing link setting from %d/%s to %d/%s.\n",
+ np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex"));
+
+ np->duplex = newdup;
+ np->linkspeed = newls;
+
+ if (np->gigabit == PHY_GIGABIT) {
+ phyreg = readl(base + NvRegRandomSeed);
+ phyreg &= ~(0x3FF00);
+ if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
+ phyreg |= NVREG_RNDSEED_FORCE3;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ phyreg |= NVREG_RNDSEED_FORCE2;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ phyreg |= NVREG_RNDSEED_FORCE;
+ writel(phyreg, base + NvRegRandomSeed);
}
- return 0;
-}
+ phyreg = readl(base + NvRegPhyInterface);
+ phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000);
+ if (np->duplex == 0)
+ phyreg |= PHY_HALF;
+ if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ phyreg |= PHY_100;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ phyreg |= PHY_1000;
+ writel(phyreg, base + NvRegPhyInterface);
+ writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
+ base + NvRegMisc1);
+ pci_push(base);
+ writel(np->linkspeed, base + NvRegLinkSpeed);
+ pci_push(base);
+
+ return retval;
+}
+
+#if 0 /* Not used */
+static void nv_linkchange(struct nic *nic)
+{
+ if (update_linkspeed(nic)) {
+// if (netif_carrier_ok(nic)) {
+ stop_rx();
+//= } else {
+ // netif_carrier_on(dev);
+ // printk(KERN_INFO "%s: link up.\n", dev->name);
+ // }
+ start_rx(nic);
+ } else {
+ // if (netif_carrier_ok(dev)) {
+ // netif_carrier_off(dev);
+ // printk(KERN_INFO "%s: link down.\n", dev->name);
+ stop_rx();
+ // }
+ }
+}
+#endif
static int init_ring(struct nic *nic)
{
int i;
np->next_tx = np->nic_tx = 0;
- for (i = 0; i < TX_RING; i++) {
- tx_ring[i].Flags = 0;
- }
+ for (i = 0; i < TX_RING; i++)
+ tx_ring[i].FlagLen = 0;
np->cur_rx = 0;
np->refill_rx = 0;
- for (i = 0; i < RX_RING; i++) {
- rx_ring[i].Flags = 0;
- }
+ for (i = 0; i < RX_RING; i++)
+ rx_ring[i].FlagLen = 0;
return alloc_rx(nic);
}
@@ -637,15 +949,21 @@ static int forcedeth_reset(struct nic *nic)
writel(0, base + NvRegMulticastMaskA);
writel(0, base + NvRegMulticastMaskB);
writel(0, base + NvRegPacketFilterFlags);
+
+ writel(0, base + NvRegTransmitterControl);
+ writel(0, base + NvRegReceiverControl);
+
writel(0, base + NvRegAdapterControl);
+
+ /* 2) initialize descriptor rings */
+ oom = init_ring(nic);
+
writel(0, base + NvRegLinkSpeed);
writel(0, base + NvRegUnknownTransmitterReg);
txrx_reset(nic);
writel(0, base + NvRegUnknownSetupReg6);
- /* 2) initialize descriptor rings */
np->in_shutdown = 0;
- oom = init_ring(nic);
/* 3) set mac address */
{
@@ -661,51 +979,40 @@ static int forcedeth_reset(struct nic *nic)
writel(mac[1], base + NvRegMacAddrB);
}
- /* 4) continue setup */
+ /* 4) give hw rings */
+ writel((u32) virt_to_le32desc(&rx_ring[0]),
+ base + NvRegRxRingPhysAddr);
+ writel((u32) virt_to_le32desc(&tx_ring[0]),
+ base + NvRegTxRingPhysAddr);
+
+ writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
+ ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
+ base + NvRegRingSizes);
+
+ /* 5) continue setup */
np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
np->duplex = 0;
+ writel(np->linkspeed, base + NvRegLinkSpeed);
writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
- writel(0, base + NvRegTxRxControl);
+ writel(np->desc_ver, base + NvRegTxRxControl);
pci_push(base);
- writel(NVREG_TXRXCTL_BIT1, base + NvRegTxRxControl);
-
+ writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl);
reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
NV_SETUP5_DELAYMAX,
"open: SetupReg5, Bit 31 remained off\n");
- writel(0, base + NvRegUnknownSetupReg4);
-
- /* 5) Find a suitable PHY */
- writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
- for (i = 1; i < 32; i++) {
- int id1, id2;
-
- id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
- if (id1 < 0)
- continue;
- id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
- if (id2 < 0)
- continue;
- dprintf(("open: Found PHY %04x:%04x at address %d.\n",
- id1, id2, i));
- np->phyaddr = i;
-
- update_linkspeed(nic);
-
- break;
- }
- if (i == 32) {
- printf("open: failing due to lack of suitable PHY.\n");
- ret = -1;
- goto out_drain;
- }
+ writel(0, base + NvRegUnknownSetupReg4);
+// writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
+#if 0
printf("%d-Mbs Link, %s-Duplex\n",
np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
np->duplex ? "Full" : "Half");
+#endif
+
/* 6) continue setup */
- writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
- base + NvRegMisc1);
+ writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
writel(readl(base + NvRegTransmitterStatus),
base + NvRegTransmitterStatus);
writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
@@ -714,8 +1021,8 @@ static int forcedeth_reset(struct nic *nic)
writel(readl(base + NvRegReceiverStatus),
base + NvRegReceiverStatus);
- /* FIXME: I cheated and used the calculator to get a random number */
- i = 75963081;
+ /* Get a random number */
+ i = random();
writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
base + NvRegRandomSeed);
writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
@@ -724,36 +1031,24 @@ static int forcedeth_reset(struct nic *nic)
writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
writel((np->
phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
- NVREG_ADAPTCTL_PHYVALID, base + NvRegAdapterControl);
+ NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
+ base + NvRegAdapterControl);
+ writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);
- /* 7) start packet processing */
- writel((u32) virt_to_le32desc(&rx_ring[0]),
- base + NvRegRxRingPhysAddr);
- writel((u32) virt_to_le32desc(&tx_ring[0]),
- base + NvRegTxRingPhysAddr);
-
-
- writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
- ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
- base + NvRegRingSizes);
-
i = readl(base + NvRegPowerState);
- if ((i & NVREG_POWERSTATE_POWEREDUP) == 0) {
+ if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
writel(NVREG_POWERSTATE_POWEREDUP | i,
base + NvRegPowerState);
- }
+
pci_push(base);
udelay(10);
writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
base + NvRegPowerState);
- writel(NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
writel(0, base + NvRegIrqMask);
pci_push(base);
- writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
- pci_push(base);
writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
pci_push(base);
@@ -768,22 +1063,32 @@ static int forcedeth_reset(struct nic *nic)
base + NvRegPacketFilterFlags);
set_multicast(nic);
+ /* One manual link speed update: Interrupts are enabled, future link
+ * speed changes cause interrupts and are handled by nv_link_irq().
+ */
+ {
+ u32 miistat;
+ miistat = readl(base + NvRegMIIStatus);
+ writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
+ dprintf(("startup: got 0x%hX.\n", miistat));
+ }
+ ret = update_linkspeed(nic);
+
//start_rx(nic);
start_tx(nic);
- if (!
- (mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ) &
- BMSR_ANEGCOMPLETE)) {
+ if (ret) {
+ //Start Connection netif_carrier_on(dev);
+ } else {
printf("no link during initialization.\n");
}
- udelay(10000);
- out_drain:
return ret;
}
-//extern void hex_dump(const char *data, const unsigned int len);
-
+/*
+ * extern void hex_dump(const char *data, const unsigned int len);
+*/
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
@@ -793,26 +1098,35 @@ static int forcedeth_poll(struct nic *nic, int retrieve)
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
- struct ring_desc *prd;
int len;
int i;
+ u32 Flags;
i = np->cur_rx % RX_RING;
- prd = &rx_ring[i];
- if ( ! (prd->Flags & cpu_to_le16(NV_RX_DESCRIPTORVALID)) ) {
- return 0;
+ Flags = le32_to_cpu(rx_ring[i].FlagLen);
+ len = nv_descr_getlength(&rx_ring[i], np->desc_ver);
+
+ if (Flags & NV_RX_AVAIL)
+ return 0; /* still owned by hardware, */
+
+ if (np->desc_ver == DESC_VER_1) {
+ if (!(Flags & NV_RX_DESCRIPTORVALID))
+ return 0;
+ } else {
+ if (!(Flags & NV_RX2_DESCRIPTORVALID))
+ return 0;
}
- if ( ! retrieve ) return 1;
+ if (!retrieve)
+ return 1;
/* got a valid packet - forward it to the network core */
- len = cpu_to_le16(prd->Length);
nic->packetlen = len;
- //hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
- memcpy(nic->packet, rxb +
- (i * RX_NIC_BUFSIZE), nic->packetlen);
-
+ memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
+/*
+ * hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
+*/
wmb();
np->cur_rx++;
alloc_rx(nic);
@@ -831,7 +1145,6 @@ static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */
/* send the packet to destination */
u8 *ptxb;
u16 nstype;
- //u16 status;
u8 *base = (u8 *) BASE;
int nr = np->next_tx % TX_RING;
@@ -851,14 +1164,12 @@ static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */
ptxb[s++] = '\0';
tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
- tx_ring[nr].Length = cpu_to_le16(s - 1);
wmb();
- tx_ring[nr].Flags = np->tx_flags;
+ tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);
- writel(NVREG_TXRXCTL_KICK, base + NvRegTxRxControl);
+ writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
pci_push(base);
- tx_ring[nr].Flags = np->tx_flags;
np->next_tx++;
}
@@ -896,16 +1207,17 @@ static void forcedeth_disable ( struct nic *nic __unused ) {
/**************************************************************************
IRQ - Enable, Disable, or Force interrupts
***************************************************************************/
-static void forcedeth_irq(struct nic *nic __unused, irq_action_t action __unused)
+static void forcedeth_irq(struct nic *nic __unused,
+ irq_action_t action __unused)
{
- switch ( action ) {
- case DISABLE :
- break;
- case ENABLE :
- break;
- case FORCE :
- break;
- }
+ switch (action) {
+ case DISABLE:
+ break;
+ case ENABLE:
+ break;
+ case FORCE:
+ break;
+ }
}
static struct nic_operations forcedeth_operations = {
@@ -916,14 +1228,6 @@ static struct nic_operations forcedeth_operations = {
};
-static struct pci_id forcedeth_nics[] = {
- PCI_ROM(0x10de, 0x01C3, "nforce", "nForce Ethernet Controller"),
- PCI_ROM(0x10de, 0x0066, "nforce2", "nForce2 Ethernet Controller"),
- PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce3 Ethernet Controller"),
-};
-
-PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
-
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
@@ -935,6 +1239,7 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
unsigned long addr;
int sz;
u8 *base;
+ int i;
if (pci->ioaddr == 0)
return 0;
@@ -956,6 +1261,15 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
BASE = (unsigned long) ioremap(addr, sz);
if (!BASE)
return 0;
+
+ /* handle different descriptor versions */
+ if (pci->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
+ pci->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
+ pci->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_3)
+ np->desc_ver = DESC_VER_1;
+ else
+ np->desc_ver = DESC_VER_2;
+
//rx_ring[0] = rx_ring;
//tx_ring[0] = tx_ring;
@@ -992,36 +1306,121 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
#endif
printf("%s: MAC Address %!, ", pci->name, nic->node_addr);
- np->tx_flags =
- cpu_to_le16(NV_TX_LASTPACKET | NV_TX_LASTPACKET1 |
- NV_TX_VALID);
- switch (pci->device_id) {
- case 0x01C3: // nforce
- np->irqmask = NVREG_IRQMASK_WANTED_2;
- np->irqmask |= NVREG_IRQ_TIMER;
- break;
- case 0x0066: // nforce2
- np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
- np->irqmask = NVREG_IRQMASK_WANTED_2;
- np->irqmask |= NVREG_IRQ_TIMER;
- break;
- case 0x00D6: // nforce3
- np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
- np->irqmask = NVREG_IRQMASK_WANTED_2;
- np->irqmask |= NVREG_IRQ_TIMER;
-
- }
+ /* disable WOL */
+ writel(0, base + NvRegWakeUpFlags);
+ np->wolenabled = 0;
+
+ if (np->desc_ver == DESC_VER_1) {
+ np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID;
+ } else {
+ np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID;
+ }
+
+ switch (pci->device_id) {
+ case 0x01C3: // nforce
+ // DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
+ np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ break;
+ case 0x0066:
+ /* Fall Through */
+ case 0x00D6:
+ // DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ if (np->desc_ver == DESC_VER_1)
+ np->tx_flags |= NV_TX_LASTPACKET1;
+ else
+ np->tx_flags |= NV_TX2_LASTPACKET1;
+ break;
+ case 0x0086:
+ /* Fall Through */
+ case 0x008c:
+ /* Fall Through */
+ case 0x00e6:
+ /* Fall Through */
+ case 0x00df:
+ /* Fall Through */
+ case 0x0056:
+ /* Fall Through */
+ case 0x0057:
+ /* Fall Through */
+ case 0x0037:
+ /* Fall Through */
+ case 0x0038:
+ //DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ if (np->desc_ver == DESC_VER_1)
+ np->tx_flags |= NV_TX_LASTPACKET1;
+ else
+ np->tx_flags |= NV_TX2_LASTPACKET1;
+ break;
+ default:
+ printf
+ ("Your card was undefined in this driver. Review driver_data in Linux driver and send a patch\n");
+ }
+
+ /* find a suitable phy */
+ for (i = 1; i < 32; i++) {
+ int id1, id2;
+ id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
+ if (id1 < 0 || id1 == 0xffff)
+ continue;
+ id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
+ if (id2 < 0 || id2 == 0xffff)
+ continue;
+ id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
+ id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
+ dprintf
+ (("%s: open: Found PHY %hX:%hX at address %d.\n",
+ pci->name, id1, id2, i));
+ np->phyaddr = i;
+ np->phy_oui = id1 | id2;
+ break;
+ }
+ if (i == 32) {
+ /* PHY in isolate mode? No phy attached and user wants to
+ * test loopback? Very odd, but can be correct.
+ */
+ printf
+ ("%s: open: Could not find a valid PHY.\n", pci->name);
+ }
+
+ if (i != 32) {
+ /* reset it */
+ phy_init(nic);
+ }
+
dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
pci->name, pci->vendor, pci->dev_id, pci->name));
+ if(!forcedeth_reset(nic)) return 0; // no valid link
- forcedeth_reset(nic);
-// if (board_found && valid_link)
/* point to NIC specific routines */
nic->nic_op = &forcedeth_operations;
return 1;
-// }
- /* else */
}
+static struct pci_id forcedeth_nics[] = {
+PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"),
+PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"),
+};
+
+PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
+
DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver,
forcedeth_probe, forcedeth_disable );
diff --git a/src/drivers/net/mtd80x.c b/src/drivers/net/mtd80x.c
index 3d974abc..c7754a94 100644
--- a/src/drivers/net/mtd80x.c
+++ b/src/drivers/net/mtd80x.c
@@ -30,13 +30,6 @@
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c
index 003b9798..e8f6d81c 100644
--- a/src/drivers/net/natsemi.c
+++ b/src/drivers/net/natsemi.c
@@ -71,13 +71,6 @@
#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
-typedef uint8_t u8;
-typedef int8_t s8;
-typedef uint16_t u16;
-typedef int16_t s16;
-typedef uint32_t u32;
-typedef int32_t s32;
-
/* helpful macroes if on a big_endian machine for changing byte order.
not strictly needed on Intel */
#define get_unaligned(ptr) (*(ptr))
@@ -611,7 +604,7 @@ natsemi_transmit(struct nic *nic,
const char *p) /* Packet */
{
u32 to, nstype;
- u32 tx_status;
+ volatile u32 tx_status;
/* Stop the transmitter */
outl(TxOff, ioaddr + ChipCmd);
@@ -650,7 +643,7 @@ natsemi_transmit(struct nic *nic,
to = currticks() + TX_TIMEOUT;
- while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
+ while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to))
/* wait */ ;
if (currticks() >= to) {
diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c
index d4fafb68..df9eec5a 100755
--- a/src/drivers/net/ns83820.c
+++ b/src/drivers/net/ns83820.c
@@ -53,13 +53,6 @@
#define dprintf(x)
#endif
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
#define HZ 100
/* Condensed operations for readability. */
diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c
index 5a0bb14c..4255269b 100644
--- a/src/drivers/net/pcnet32.c
+++ b/src/drivers/net/pcnet32.c
@@ -54,13 +54,6 @@
#define drv_version "v1.3"
#define drv_date "03-29-2004"
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
static u32 ioaddr; /* Globally used for the card's io address */
static struct nic_operations pcnet32_operations;
static struct pci_driver pcnet32_driver;
diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c
index ad3b62bd..427fad0f 100644
--- a/src/drivers/net/r8169.c
+++ b/src/drivers/net/r8169.c
@@ -52,13 +52,6 @@
#define drv_version "v1.6"
#define drv_date "03-27-2004"
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
#define HZ 1000
static u32 ioaddr;
diff --git a/src/drivers/net/sis900.c b/src/drivers/net/sis900.c
index b5ba3f08..c2cb94e8 100644
--- a/src/drivers/net/sis900.c
+++ b/src/drivers/net/sis900.c
@@ -1097,7 +1097,7 @@ sis900_transmit(struct nic *nic,
const char *p) /* Packet */
{
u32 to, nstype;
- u32 tx_status;
+ volatile u32 tx_status;
/* Stop the transmitter */
outl(TxDIS | inl(ioaddr + cr), ioaddr + cr);
@@ -1136,7 +1136,7 @@ sis900_transmit(struct nic *nic,
to = currticks() + TX_TIMEOUT;
- while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
+ while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to))
/* wait */ ;
if (currticks() >= to) {
diff --git a/src/drivers/net/sis900.h b/src/drivers/net/sis900.h
index 89aa3aa5..e88e111d 100644
--- a/src/drivers/net/sis900.h
+++ b/src/drivers/net/sis900.h
@@ -363,13 +363,6 @@ enum sis630_revision_id {
#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
/* Time in ticks before concluding the transmitter is hung. */
#define TX_TIMEOUT (4*TICKS_PER_SEC)
diff --git a/src/drivers/net/smc9000.c b/src/drivers/net/smc9000.c
index 7c694777..43f04f06 100644
--- a/src/drivers/net/smc9000.c
+++ b/src/drivers/net/smc9000.c
@@ -29,9 +29,16 @@
#define LINUX_OUT_MACROS 1
#define SMC9000_DEBUG 0
+#if SMC9000_DEBUG > 1
+#define PRINTK2 printf
+#else
+#define PRINTK2(args...)
+#endif
+
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
+#include "timer.h"
#include "smc9000.h"
# define _outb outb
@@ -47,11 +54,449 @@ static const char *chip_ids[ 15 ] = {
NULL,
/* 7 */ "SMC91C100",
/* 8 */ "SMC91C100FD",
- NULL, NULL, NULL,
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL,
NULL, NULL, NULL
};
static const char smc91c96_id[] = "SMC91C96";
+/*------------------------------------------------------------
+ . Reads a register from the MII Management serial interface
+ .-------------------------------------------------------------*/
+static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg)
+{
+ int oldBank;
+ unsigned int i;
+ byte mask;
+ word mii_reg;
+ byte bits[64];
+ int clk_idx = 0;
+ int input_idx;
+ word phydata;
+
+ // 32 consecutive ones on MDO to establish sync
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Start code <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Read command <10>
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ bits[clk_idx++] = MII_MDOE;
+
+ // Output the PHY address, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyaddr & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Output the phy register number, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyreg & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Tristate and turnaround (2 bit times)
+ bits[clk_idx++] = 0;
+ //bits[clk_idx++] = 0;
+
+ // Input starts at this bit time
+ input_idx = clk_idx;
+
+ // Will input 16 bits
+ for (i = 0; i < 16; ++i)
+ bits[clk_idx++] = 0;
+
+ // Final clock bit
+ bits[clk_idx++] = 0;
+
+ // Save the current bank
+ oldBank = inw( ioaddr+BANK_SELECT );
+
+ // Select bank 3
+ SMC_SELECT_BANK(ioaddr, 3);
+
+ // Get the current MII register value
+ mii_reg = inw( ioaddr+MII_REG );
+
+ // Turn off all MII Interface bits
+ mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+ // Clock all 64 cycles
+ for (i = 0; i < sizeof(bits); ++i)
+ {
+ // Clock Low - output data
+ outw( mii_reg | bits[i], ioaddr+MII_REG );
+ udelay(50);
+
+
+ // Clock Hi - input data
+ outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+ udelay(50);
+ bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+ }
+
+ // Return to idle state
+ // Set clock to low, data to low, and output tristated
+ outw( mii_reg, ioaddr+MII_REG );
+ udelay(50);
+
+ // Restore original bank select
+ SMC_SELECT_BANK(ioaddr, oldBank);
+
+ // Recover input data
+ phydata = 0;
+ for (i = 0; i < 16; ++i)
+ {
+ phydata <<= 1;
+
+ if (bits[input_idx++] & MII_MDI)
+ phydata |= 0x0001;
+ }
+
+#if (SMC_DEBUG > 2 )
+ printf("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+ phyaddr, phyreg, phydata);
+#endif
+
+ return(phydata);
+}
+
+
+/*------------------------------------------------------------
+ . Writes a register to the MII Management serial interface
+ .-------------------------------------------------------------*/
+static void smc_write_phy_register(int ioaddr,
+ byte phyaddr, byte phyreg, word phydata)
+{
+ int oldBank;
+ unsigned int i;
+ word mask;
+ word mii_reg;
+ byte bits[65];
+ int clk_idx = 0;
+
+ // 32 consecutive ones on MDO to establish sync
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Start code <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Write command <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Output the PHY address, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyaddr & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Output the phy register number, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyreg & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Tristate and turnaround (2 bit times)
+ bits[clk_idx++] = 0;
+ bits[clk_idx++] = 0;
+
+ // Write out 16 bits of data, msb first
+ mask = 0x8000;
+ for (i = 0; i < 16; ++i)
+ {
+ if (phydata & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Final clock bit (tristate)
+ bits[clk_idx++] = 0;
+
+ // Save the current bank
+ oldBank = inw( ioaddr+BANK_SELECT );
+
+ // Select bank 3
+ SMC_SELECT_BANK(ioaddr, 3);
+
+ // Get the current MII register value
+ mii_reg = inw( ioaddr+MII_REG );
+
+ // Turn off all MII Interface bits
+ mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+ // Clock all cycles
+ for (i = 0; i < sizeof(bits); ++i)
+ {
+ // Clock Low - output data
+ outw( mii_reg | bits[i], ioaddr+MII_REG );
+ udelay(50);
+
+
+ // Clock Hi - input data
+ outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+ udelay(50);
+ bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+ }
+
+ // Return to idle state
+ // Set clock to low, data to low, and output tristated
+ outw( mii_reg, ioaddr+MII_REG );
+ udelay(50);
+
+ // Restore original bank select
+ SMC_SELECT_BANK(ioaddr, oldBank);
+
+#if (SMC_DEBUG > 2 )
+ printf("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+ phyaddr, phyreg, phydata);
+#endif
+}
+
+
+/*------------------------------------------------------------
+ . Finds and reports the PHY address
+ .-------------------------------------------------------------*/
+static int smc_detect_phy(int ioaddr, byte *pphyaddr)
+{
+ word phy_id1;
+ word phy_id2;
+ int phyaddr;
+ int found = 0;
+
+ // Scan all 32 PHY addresses if necessary
+ for (phyaddr = 0; phyaddr < 32; ++phyaddr)
+ {
+ // Read the PHY identifiers
+ phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG);
+ phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG);
+
+ // Make sure it is a valid identifier
+ if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) &&
+ (phy_id1 > 0x0000) && (phy_id1 < 0xffff))
+ {
+ if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000))
+ {
+ // Save the PHY's address
+ *pphyaddr = phyaddr;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ printf("No PHY found\n");
+ return(0);
+ }
+
+ // Set the PHY type
+ if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) )
+ {
+ printf("PHY=LAN83C183 (LAN91C111 Internal)\n");
+ }
+
+ if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) )
+ {
+ printf("PHY=LAN83C180\n");
+ }
+
+ return(1);
+}
+
+/*------------------------------------------------------------
+ . Configures the specified PHY using Autonegotiation. Calls
+ . smc_phy_fixed() if the user has requested a certain config.
+ .-------------------------------------------------------------*/
+static void smc_phy_configure(int ioaddr)
+{
+ int timeout;
+ byte phyaddr;
+ word my_phy_caps; // My PHY capabilities
+ word my_ad_caps; // My Advertised capabilities
+ word status;
+ int failed = 0;
+ int rpc_cur_mode = RPC_DEFAULT;
+ int lastPhy18;
+
+ // Find the address and type of our phy
+ if (!smc_detect_phy(ioaddr, &phyaddr))
+ {
+ return;
+ }
+
+ // Reset the PHY, setting all other bits to zero
+ smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST);
+
+ // Wait for the reset to complete, or time out
+ timeout = 6; // Wait up to 3 seconds
+ while (timeout--)
+ {
+ if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG)
+ & PHY_CNTL_RST))
+ {
+ // reset complete
+ break;
+ }
+
+ mdelay(500); // wait 500 millisecs
+ }
+
+ if (timeout < 1)
+ {
+ PRINTK2("PHY reset timed out\n");
+ return;
+ }
+
+ // Read PHY Register 18, Status Output
+ lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
+
+ // Enable PHY Interrupts (for register 18)
+ // Interrupts listed here are disabled
+ smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+ PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ SMC_SELECT_BANK(ioaddr, 0);
+ outw( rpc_cur_mode, ioaddr + RPC_REG );
+
+ // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
+ my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+ my_ad_caps = PHY_AD_CSMA; // I am CSMA capable
+
+ if (my_phy_caps & PHY_STAT_CAP_T4)
+ my_ad_caps |= PHY_AD_T4;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXF)
+ my_ad_caps |= PHY_AD_TX_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXH)
+ my_ad_caps |= PHY_AD_TX_HDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TF)
+ my_ad_caps |= PHY_AD_10_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TH)
+ my_ad_caps |= PHY_AD_10_HDX;
+
+ // Update our Auto-Neg Advertisement Register
+ smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps);
+
+ PRINTK2("phy caps=%x\n", my_phy_caps);
+ PRINTK2("phy advertised caps=%x\n", my_ad_caps);
+
+ // Restart auto-negotiation process in order to advertise my caps
+ smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
+
+ // Wait for the auto-negotiation to complete. This may take from
+ // 2 to 3 seconds.
+ // Wait for the reset to complete, or time out
+ timeout = 20; // Wait up to 10 seconds
+ while (timeout--)
+ {
+ status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+ if (status & PHY_STAT_ANEG_ACK)
+ {
+ // auto-negotiate complete
+ break;
+ }
+
+ mdelay(500); // wait 500 millisecs
+
+ // Restart auto-negotiation if remote fault
+ if (status & PHY_STAT_REM_FLT)
+ {
+ PRINTK2("PHY remote fault detected\n");
+
+ // Restart auto-negotiation
+ PRINTK2("PHY restarting auto-negotiation\n");
+ smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST |
+ PHY_CNTL_SPEED | PHY_CNTL_DPLX);
+ }
+ }
+
+ if (timeout < 1)
+ {
+ PRINTK2("PHY auto-negotiate timed out\n");
+ failed = 1;
+ }
+
+ // Fail if we detected an auto-negotiate remote fault
+ if (status & PHY_STAT_REM_FLT)
+ {
+ PRINTK2("PHY remote fault detected\n");
+ failed = 1;
+ }
+
+ // Set our sysctl parameters to match auto-negotiation results
+ if ( lastPhy18 & PHY_INT_SPDDET )
+ {
+ PRINTK2("PHY 100BaseT\n");
+ rpc_cur_mode |= RPC_SPEED;
+ }
+ else
+ {
+ PRINTK2("PHY 10BaseT\n");
+ rpc_cur_mode &= ~RPC_SPEED;
+ }
+
+ if ( lastPhy18 & PHY_INT_DPLXDET )
+ {
+ PRINTK2("PHY Full Duplex\n");
+ rpc_cur_mode |= RPC_DPLX;
+ }
+ else
+ {
+ PRINTK2("PHY Half Duplex\n");
+ rpc_cur_mode &= ~RPC_DPLX;
+ }
+
+ // Re-Configure the Receive/Phy Control register
+ outw( rpc_cur_mode, ioaddr + RPC_REG );
+}
+
/*
* Function: smc_reset( int ioaddr )
* Purpose:
@@ -476,6 +921,8 @@ static int smc9000_probe ( struct nic *nic, struct isa_device *isa ) {
nic->ioaddr + CONFIG );
}
+ smc_phy_configure(nic->ioaddr);
+
nic->nic_op = &smc9000_operations;
return 1;
}
diff --git a/src/drivers/net/smc9000.h b/src/drivers/net/smc9000.h
index ac7f9163..ffe5bbdb 100644
--- a/src/drivers/net/smc9000.h
+++ b/src/drivers/net/smc9000.h
@@ -91,6 +91,24 @@ typedef unsigned long int dword;
#define MCR 10
/* 12 is reserved */
+// Receive/Phy Control Register
+/* BANK 0 */
+#define RPC_REG 0x000A
+#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
+#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect
+#define RPC_LED_RES (0x01) // LED = Reserved
+#define RPC_LED_10 (0x02) // LED = 10Mbps link detect
+#define RPC_LED_FD (0x03) // LED = Full Duplex Mode
+#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred
+#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect
+#define RPC_LED_TX (0x06) // LED = TX packet occurred
+#define RPC_LED_RX (0x07) // LED = RX packet occurred
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
+
/* BANK 1 */
#define CONFIG 0
#define CFG_AUI_SELECT 0x100
@@ -151,6 +169,13 @@ typedef unsigned long int dword;
#define MGMT 8
#define REVISION 10 /* ( hi: chip id low: rev # ) */
+// Management Interface Register (MII)
+#define MII_REG 0x0008
+#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE 0x0008 // MII Output Enable
+#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
+#define MII_MDI 0x0002 // MII Input, pin MDI
+#define MII_MDO 0x0001 // MII Output, pin MDO
/* this is NOT on SMC9192 */
#define ERCV 12
@@ -186,6 +211,95 @@ typedef unsigned long int dword;
#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+// PHY Register Addresses (LAN91C111 Internal PHY)
+
+// PHY Control Register
+#define PHY_CNTL_REG 0x00
+#define PHY_CNTL_RST 0x8000 // 1=PHY Reset
+#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback
+#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs
+#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation
+#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode
+#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled
+#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate
+#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex
+#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test
+
+// PHY Status Register
+#define PHY_STAT_REG 0x01
+#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable
+#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable
+#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable
+#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable
+#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable
+#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble
+#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed
+#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected
+#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable
+#define PHY_STAT_LINK 0x0004 // 1=valid link
+#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition
+#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented
+
+// PHY Identifier Registers
+#define PHY_ID1_REG 0x02 // PHY Identifier 1
+#define PHY_ID2_REG 0x03 // PHY Identifier 2
+
+// PHY Auto-Negotiation Advertisement Register
+#define PHY_AD_REG 0x04
+#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page
+#define PHY_AD_ACK 0x4000 // 1=got link code word from remote
+#define PHY_AD_RF 0x2000 // 1=advertise remote fault
+#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4
+#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX
+#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX
+#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX
+#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX
+#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA
+
+// PHY Auto-negotiation Remote End Capability Register
+#define PHY_RMT_REG 0x05
+// Uses same bit definitions as PHY_AD_REG
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG 0x10
+#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
+#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
+#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
+#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
+#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
+#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
+#define PHY_INT_JAB 0x0100 // 1=Jabber detected
+#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG 0x13 // Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
/*-------------------------------------------------------------------------
* I define some macros to make it easier to do somewhat common
* or slightly complicated, repeated tasks.
diff --git a/src/drivers/net/sundance.c b/src/drivers/net/sundance.c
index 05621d4f..701f922a 100644
--- a/src/drivers/net/sundance.c
+++ b/src/drivers/net/sundance.c
@@ -52,13 +52,6 @@
#define drv_version "v1.12"
#define drv_date "2004-03-21"
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
#define HZ 100
/* Condensed operations for readability. */
diff --git a/src/drivers/net/tg3.c b/src/drivers/net/tg3.c
index 2d881f08..ace2dfec 100644
--- a/src/drivers/net/tg3.c
+++ b/src/drivers/net/tg3.c
@@ -9,6 +9,7 @@
/* 11-13-2003 timlegge Fix Issue with NetGear GA302T
* 11-18-2003 ebiederm Generalize NetGear Fix to what the code was supposed to be.
* 01-06-2005 Alf (Frederic Olivie) Add Dell bcm 5751 (0x1677) support
+ * 04-15-2005 Martin Vogt Add Fujitsu Siemens Computer (FSC) 0x1734 bcm 5751 0x105d support
*/
#include "etherboot.h"
@@ -2413,6 +2414,9 @@ static struct subsys_tbl_ent subsys_id_to_phy_id[] = {
{ PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */
{ PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */
{ PCI_VENDOR_ID_DELL, 0x0179, PHY_ID_BCM5751 }, /* EtherXpress */
+
+ /* Fujitsu Siemens Computer */
+ { PCI_VENDOR_ID_FSC, 0x105d, PHY_ID_BCM5751 }, /* Futro C200 */
/* Compaq boards. */
{ PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */
diff --git a/src/drivers/net/tg3.h b/src/drivers/net/tg3.h
index 6e05b9cc..fd038f58 100644
--- a/src/drivers/net/tg3.h
+++ b/src/drivers/net/tg3.h
@@ -2156,7 +2156,7 @@ struct tg3 {
((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \
(X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \
(X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \
- (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5751 || \
+ (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || (X) == PHY_ID_BCM5751 || \
(X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES)
unsigned long regs;
diff --git a/src/drivers/net/tlan.h b/src/drivers/net/tlan.h
index e0379a7b..de57d598 100644
--- a/src/drivers/net/tlan.h
+++ b/src/drivers/net/tlan.h
@@ -34,22 +34,10 @@
* Indent Style: indent -kr -i8
***************************************************************************/
-/*
-#include <asm/io.h>
-#include <asm/types.h>
-#include <linux/netdevice.h>
-*/
-
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
- /*****************************************************************
- * TLan Definitions
- *
- ****************************************************************/
+/*****************************************************************
+* TLan Definitions
+*
+****************************************************************/
#define FALSE 0
#define TRUE 1
diff --git a/src/drivers/net/tulip.c b/src/drivers/net/tulip.c
index 28e38d95..864e32af 100644
--- a/src/drivers/net/tulip.c
+++ b/src/drivers/net/tulip.c
@@ -48,6 +48,8 @@
/*********************************************************************/
/*
+ 08 Feb 2005 Ramesh Chander chhabaramesh at yahoo.co.in added table entries
+ for SGThomson STE10/100A
07 Sep 2003 timlegge Multicast Support Added
11 Apr 2001 mdc [patch to etherboot 4.7.24]
Major rewrite to include Linux tulip driver media detection
@@ -118,13 +120,6 @@ static int tulip_debug = 2; /* 1 normal messages, 0 quiet .. 7 verbo
#define TX_TIME_OUT 2*TICKS_PER_SEC
-typedef uint8_t u8;
-typedef int8_t s8;
-typedef uint16_t u16;
-typedef int16_t s16;
-typedef uint32_t u32;
-typedef int32_t s32;
-
/* helpful macros if on a big_endian machine for changing byte order.
not strictly needed on Intel */
#define get_unaligned(ptr) (*(ptr))
@@ -157,7 +152,7 @@ static const char * const medianame[32] = {
enum tulip_chips {
DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
LC82C168, MX98713, MX98715, MX98725, AX88141, AX88140, PNIC2, COMET,
- COMPEX9881, I21145, XIRCOM
+ COMPEX9881, I21145, XIRCOM, SGThomson, /*Ramesh Chander*/
};
enum pci_id_flags_bits {
@@ -233,6 +228,8 @@ static const struct pci_id_info pci_id_tbl[] = {
TULIP_IOTYPE, 256, MX98715 },
{ "3Com 3cSOHO100B-TX (ADMtek Centuar)", { 0x930010b7, 0xffffffff, 0, 0, 0, 0 },
TULIP_IOTYPE, TULIP_SIZE, COMET },
+ { "SG Thomson STE10/100A", { 0x2774104a, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET }, /*Ramesh Chander*/
{ 0, { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 },
};
@@ -268,6 +265,7 @@ static struct tulip_chip_table {
| HAS_PWRDWN | HAS_NWAY },
{ "Xircom tulip work-alike", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
| HAS_PWRDWN | HAS_NWAY },
+ { "SGThomson STE10/100A", HAS_MII | MC_HASH_ONLY }, /*Ramesh Chander*/
{ 0, 0 },
};
@@ -1705,6 +1703,8 @@ static void init_media(struct nic *nic)
outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
break;
case COMET:
+ /* Enable automatic Tx underrun recovery */
+ outl(inl(ioaddr + 0x88) | 1, ioaddr + 0x88);
tp->if_port = 0;
tp->csr6 = 0x00040000;
break;
@@ -2068,7 +2068,7 @@ PCI_ROM(0x125b, 0x1400, "ax88140", "ASIX AX88140"),
PCI_ROM(0x11f6, 0x9881, "rl100tx", "Compex RL100-TX"),
PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip"),
PCI_ROM(0x104a, 0x0981, "tulip-0981", "Tulip 0x104a 0x0981"),
-PCI_ROM(0x104a, 0x2774, "tulip-2774", "Tulip 0x104a 0x2774"),
+PCI_ROM(0x104a, 0x2774, "SGThomson-STE10100A", "Tulip 0x104a 0x2774"), /*Modified by Ramesh Chander*/
PCI_ROM(0x1113, 0x9511, "tulip-9511", "Tulip 0x1113 0x9511"),
PCI_ROM(0x1186, 0x1561, "tulip-1561", "Tulip 0x1186 0x1561"),
PCI_ROM(0x1259, 0xa120, "tulip-a120", "Tulip 0x1259 0xa120"),
diff --git a/src/drivers/net/w89c840.c b/src/drivers/net/w89c840.c
index 1ed3503c..c3f03f9f 100644
--- a/src/drivers/net/w89c840.c
+++ b/src/drivers/net/w89c840.c
@@ -84,13 +84,6 @@
static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
-
/* Linux support functions */
#define virt_to_le32desc(addr) virt_to_bus(addr)
#define le32desc_to_virt(addr) bus_to_virt(addr)
diff --git a/src/include/background.h b/src/include/background.h
new file mode 100644
index 00000000..24cb3201
--- /dev/null
+++ b/src/include/background.h
@@ -0,0 +1,52 @@
+#ifndef BACKGROUND_H
+#define BACKGROUND_H
+
+/** @file
+ *
+ * Background protocols
+ *
+ * Some protocols (e.g. ARP, IGMP) operate in the background; the
+ * upper layers are not aware of their operation. When an ARP query
+ * for the local station's IP address arrives, Etherboot must reply to
+ * it regardless of what other operations are currently in progress.
+ *
+ * Background protocols are called in two circumstances: when
+ * Etherboot is about to poll for a packet, and when Etherboot has
+ * received a packet that the upper layer (whatever that may currently
+ * be) isn't interested in.
+ *
+ */
+
+#include "tables.h"
+#include "ip.h"
+
+/** A background protocol */
+struct background {
+ /** Send method
+ *
+ * This method will be called whenever Etherboot is about to
+ * poll for a packet. The background protocol should use this
+ * method to send out any periodic transmissions that it may
+ * require.
+ */
+ void ( *send ) ( unsigned long timestamp );
+ /** Process method
+ *
+ * This method will be called whenever Etherboot has received
+ * a packet and doesn't know what to do with it.
+ */
+ void ( *process ) ( unsigned long timestamp, unsigned short ptype,
+ struct iphdr *ip );
+};
+
+/** A member of the background protocols table */
+#define __background __table ( background, 01 )
+
+/* Functions in background.c */
+
+extern void background_send ( unsigned long timestamp );
+
+extern void background_process ( unsigned long timestamp, unsigned short ptype,
+ struct iphdr *ip );
+
+#endif /* BACKGROUND_H */
diff --git a/src/include/buffer.h b/src/include/buffer.h
index 0085c7d1..ac4c3148 100644
--- a/src/include/buffer.h
+++ b/src/include/buffer.h
@@ -1,41 +1,93 @@
#ifndef BUFFER_H
#define BUFFER_H
+#include "compiler.h" /* for doxygen */
#include "stdint.h"
-/*
- * "start" and "end" denote the real boundaries of the buffer. "fill"
- * denotes the offset to the first free block in the buffer. (If the
- * buffer is full, "fill" will equal ( end - start ) ).
+/** @file
+ *
+ * Buffers for loading files.
+ *
+ * This file provides routines for filling a buffer with data received
+ * piecemeal, where the size of the data is not necessarily known in
+ * advance.
+ *
+ * Some protocols do not provide a mechanism for us to know the size
+ * of the file before we happen to receive a particular block
+ * (e.g. the final block in an MTFTP transfer). In addition, some
+ * protocols (all the multicast protocols plus any TCP-based protocol)
+ * can, in theory, provide the data in any order.
+ *
+ * Rather than requiring each protocol to implement its own equivalent
+ * of "dd" to arrange the data into well-sized pieces before handing
+ * off to the image loader, we provide these generic buffer functions
+ * which assemble a file into a single contiguous block. The whole
+ * block is then passed to the image loader.
+ *
+ * Example usage:
+ *
+ * @code
+ *
+ * struct buffer my_buffer;
+ * void *data;
+ * off_t offset;
+ * size_t len;
+ *
+ * // We have an area of memory [buf_start,buf_end) into which we want
+ * // to load a file, where buf_start and buf_end are physical addresses.
+ * buffer->start = buf_start;
+ * buffer->end = buf_end;
+ * init_buffer ( &buffer );
+ * ...
+ * while ( get_file_block ( ... ) ) {
+ * // Downloaded block is stored in [data,data+len), and represents
+ * // the portion of the file at offsets [offset,offset+len)
+ * if ( ! fill_buffer ( &buffer, data, offset, len ) ) {
+ * // An error occurred
+ * return 0;
+ * }
+ * ...
+ * }
+ * ...
+ * // The whole file is now present at [buf_start,buf_start+filesize),
+ * // where buf_start is a physical address. The struct buffer can simply
+ * // be discarded; there is no done_buffer() call.
+ *
+ * @endcode
+ *
+ * For a description of the internal operation, see buffer.c.
+ *
+ */
+
+/**
+ * A buffer
+ *
+ * #start and #end denote the real boundaries of the buffer, and are
+ * physical addresses. #fill denotes the offset to the first free
+ * block in the buffer. (If the buffer is full, #fill will equal
+ * #end-#start.)
*
*/
struct buffer {
- physaddr_t start;
- physaddr_t end;
- off_t fill;
+ physaddr_t start; /**< Start of buffer in memory */
+ physaddr_t end; /**< End of buffer in memory */
+ off_t fill; /**< Offset to first gap in buffer */
};
-/*
- * Free blocks in the buffer start with a "tail byte". If non-zero,
- * this byte indicates that the free block is the tail of the buffer,
- * i.e. occupies all the remaining space up to the end of the buffer.
- * When the tail byte is non-zero, it indicates that the remainder of
- * the descriptor (the struct buffer_free_block) follows the tail
- * byte.
+/**
+ * A free block descriptor.
*
- * This scheme is necessary because we may end up with a tail that is
- * smaller than a struct buffer_free_block.
+ * See buffer.c for a full description of the fields.
*
*/
struct buffer_free_block {
- char tail;
- physaddr_t next_free;
- physaddr_t end;
+ char tail; /**< Tail byte marker */
+ char reserved[3]; /**< Padding */
+ physaddr_t start; /**< Address of this free block */
+ physaddr_t next; /**< Address of next free block */
+ physaddr_t end; /**< End of this block */
} __attribute__ (( packed ));
-/* This must be provided by the architecture-dependent load_buffer.c */
-extern struct buffer load_buffer;
-
/* Functions in buffer.c */
extern void init_buffer ( struct buffer *buffer );
diff --git a/src/include/compiler.h b/src/include/compiler.h
index 4a7c48a1..79781bc7 100644
--- a/src/include/compiler.h
+++ b/src/include/compiler.h
@@ -1,11 +1,30 @@
#ifndef COMPILER_H
#define COMPILER_H
-/* We export the symbol obj_OBJECT (OBJECT is defined on command-line)
- * as a global symbol, so that the linker can drag in selected object
- * files from the library using -u obj_OBJECT.
+/*
+ * Doxygen can't cope with some of the more esoteric areas of C, so we
+ * make its life simpler.
+ *
+ */
+#ifdef DOXYGEN
+#define __attribute__(x)
+#endif
+
+/** @file
+ *
+ * Global compiler definitions.
+ *
+ * This file is implicitly included by every @c .c file in Etherboot.
+ * It defines global macros such as DBG() and ASSERT().
*
- * Not quite sure why cpp requires two levels of macro call in order
+ * We arrange for each object to export the symbol @c obj_OBJECT
+ * (where @c OBJECT is the object name, e.g. @c rtl8139) as a global
+ * symbol, so that the linker can drag in selected object files from
+ * the library using <tt> -u obj_OBJECT </tt>.
+ *
+ */
+
+/* Not quite sure why cpp requires two levels of macro call in order
* to actually expand OBJECT...
*/
#undef _H1
@@ -30,7 +49,9 @@
__asm__ ( ".globl\t" OBJECT_SYMBOL_STR );
__asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" );
-/*
+/**
+ * Drag in an object by object name.
+ *
* Macro to allow objects to explicitly drag in other objects by
* object name. Used by config.c.
*
@@ -38,6 +59,56 @@ __asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" );
#define REQUIRE_OBJECT(object) \
__asm__ ( ".equ\tneed_" #object ", obj_" #object );
+/** @def DBG
+ *
+ * Print a debugging message.
+ *
+ * The debug level is set at build time by specifying the @c DEBUG=
+ * parameter on the @c make command line. For example, to enable
+ * debugging for the PCI bus functions (in pci.c) in a @c .dsk image
+ * for the @c rtl8139 card, you could use the command line
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci
+ *
+ * @endcode
+ *
+ * This will enable the debugging statements (DBG()) in pci.c. If
+ * debugging is not enabled, DBG() statements will be ignored.
+ *
+ * You can enable debugging in several objects simultaneously by
+ * separating them with commas, as in
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci,buffer,heap
+ *
+ * @endcode
+ *
+ * You can increase the debugging level for an object by specifying it
+ * with @c :N, where @c N is the level, as in
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci,buffer:2,heap
+ *
+ * @endcode
+ *
+ * which would enable debugging for the PCI, buffer-handling and
+ * heap-allocation code, with the buffer-handling code at level 2.
+ *
+ */
+
+/** @def DBG2
+ *
+ * Print a level 2 debugging message.
+ *
+ * As for DBG(). DBG2() takes effect only when the debugging level is
+ * 2 or greater.
+ *
+ */
+
/*
* If debug_OBJECT is set to a true value, the macro DBG(...) will
* expand to printf(...) when compiling OBJECT, and the symbol
@@ -67,8 +138,12 @@ __asm__ ( ".equ\tDEBUG_LEVEL, " DEBUG_SYMBOL_STR );
#define DBG2 DBG_PRINT
#endif
-/*
- * ASSERT() macros
+/**
+ * Assert a condition.
+ *
+ * If the condition is not true, a debug message will be printed.
+ * Assertions only take effect if the debug level is non-zero (see
+ * DBG()).
*
*/
#define ASSERT(x)
@@ -84,20 +159,35 @@ __asm__ ( ".equ\tDEBUG_LEVEL, " DEBUG_SYMBOL_STR );
} while (0)
#endif
-/*
- * Commonly-used attributes.
+/** Declare a data structure as packed. */
+#define PACKED __attribute__ (( packed ))
+
+/**
+ * Declare a variable or data structure as unused.
*
- * Note that __used can be used only for functions. If you have a
- * static variable declaration that you want to force to be included,
- * use __unused.
+ * Note that using #__unused on a static global variable (such as a
+ * table structure as mentioned in tables.h) is necessary in order to
+ * inhibit compiler warnings.
*
*/
-#define PACKED __attribute__ (( packed ))
#define __unused __attribute__ (( unused ))
+
+/**
+ * Declare a function as used.
+ *
+ * Necessary only if the function is called only from assembler code.
+ * You cannot use this attribute for static global variables; use
+ * #__unused instead.
+ *
+ */
#define __used __attribute__ (( used ))
+
+/** Declare a data structure to be aligned with 16-byte alignment */
#define __aligned __attribute__ (( aligned ( 16 ) ))
-/*
+/**
+ * Shared data.
+ *
* To save space in the binary when multiple-driver images are
* compiled, uninitialised data areas can be shared between drivers.
* This will typically be used to share statically-allocated receive
@@ -105,13 +195,16 @@ __asm__ ( ".equ\tDEBUG_LEVEL, " DEBUG_SYMBOL_STR );
*
* Use as e.g.
*
- * struct {
+ * @code
+ *
+ * struct {
* char rx_buf[NUM_RX_BUF][RX_BUF_SIZE];
* char tx_buf[TX_BUF_SIZE];
- * } my_static_data __shared;
+ * } my_static_data __shared;
+ *
+ * @endcode
*
*/
-
#define __shared __asm__ ( "_shared_bss" )
#endif /* ASSEMBLY */
diff --git a/src/include/console.h b/src/include/console.h
index 1decb9c3..9e13293a 100644
--- a/src/include/console.h
+++ b/src/include/console.h
@@ -5,19 +5,94 @@
#include "vsprintf.h"
#include "tables.h"
-/*
- * Consoles that cannot be used before their INIT_FN() has completed
- * should set disabled = 1 initially. This allows other console
- * devices to still be used to print out early debugging messages.
+/** @file
+ *
+ * User interaction.
+ *
+ * Various console devices can be selected via the build options
+ * CONSOLE_FIRMWARE, CONSOLE_SERIAL etc. The console functions
+ * putchar(), getchar() and iskey() delegate to the individual console
+ * drivers.
+ *
*/
+/**
+ * A console driver
+ *
+ * Defines the functions that implement a particular console type.
+ * Must be made part of the console drivers table by using
+ * #__console_driver.
+ *
+ * @note Consoles that cannot be used before their INIT_FN() has
+ * completed should set #disabled=1 initially. This allows other
+ * console devices to still be used to print out early debugging
+ * messages.
+ *
+ */
struct console_driver {
+ /** Console is disabled.
+ *
+ * The console's putchar(), getchar() and iskey() methods will
+ * not be called while #disabled==1. Typically the
+ * console's initialisation functions (called via INIT_FN())
+ * will set #disabled=0 upon completion.
+ *
+ */
int disabled;
+
+ /** Write a character to the console.
+ *
+ * @v character Character to be written
+ * @ret None -
+ * @err None -
+ *
+ */
void ( *putchar ) ( int character );
+
+ /** Read a character from the console.
+ *
+ * @v None -
+ * @ret character Character read
+ * @err None -
+ *
+ * If no character is available to be read, this method will
+ * block. The character read should not be echoed back to the
+ * console.
+ *
+ */
int ( *getchar ) ( void );
+
+ /** Check for available input.
+ *
+ * @v None -
+ * @ret True Input is available
+ * @ret False Input is not available
+ * @err None -
+ *
+ * This should return True if a subsequent call to getchar()
+ * will not block.
+ *
+ */
int ( *iskey ) ( void );
};
+/**
+ * Mark a <tt> struct console_driver </tt> as being part of the
+ * console drivers table.
+ *
+ * Use as e.g.
+ *
+ * @code
+ *
+ * struct console_driver my_console __console_driver = {
+ * .putchar = my_putchar,
+ * .getchar = my_getchar,
+ * .iskey = my_iskey,
+ * };
+ *
+ * @endcode
+ *
+ */
#define __console_driver __table ( console, 01 )
/* Function prototypes */
diff --git a/src/include/dns.h b/src/include/dns.h
index 5b8b81f1..33ee73fe 100644
--- a/src/include/dns.h
+++ b/src/include/dns.h
@@ -70,12 +70,12 @@ struct dns_rr_info {
} __attribute__ (( packed ));
struct dns_rr_info_a {
- struct dns_rr_info;
+ struct dns_rr_info info;
struct in_addr in_addr;
} __attribute__ (( packed ));
struct dns_rr_info_cname {
- struct dns_rr_info;
+ struct dns_rr_info info;
char cname[0];
} __attribute__ (( packed ));
diff --git a/src/include/errno.h b/src/include/errno.h
new file mode 100644
index 00000000..e122d9c3
--- /dev/null
+++ b/src/include/errno.h
@@ -0,0 +1,141 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+
+/** @file
+ *
+ * Error codes
+ *
+ */
+
+/* PXE error codes are determined by the PXE specification */
+
+/* Generic errors */
+#define PXENV_STATUS_SUCCESS 0x00
+#define PXENV_STATUS_FAILURE 0x01
+#define PXENV_STATUS_BAD_FUNC 0x02
+#define PXENV_STATUS_UNSUPPORTED 0x03
+#define PXENV_STATUS_KEEP_UNDI 0x04
+#define PXENV_STATUS_KEEP_ALL 0x05
+#define PXENV_STATUS_OUT_OF_RESOURCES 0x06
+
+/* ARP errors (0x10 to 0x1f) */
+#define PXENV_STATUS_ARP_TIMEOUT 0x11
+
+/* Base-Code state errors */
+#define PXENV_STATUS_UDP_CLOSED 0x18
+#define PXENV_STATUS_UDP_OPEN 0x19
+#define PXENV_STATUS_TFTP_CLOSED 0x1a
+#define PXENV_STATUS_TFTP_OPEN 0x1b
+
+/* BIOS/system errors (0x20 to 0x2f) */
+#define PXENV_STATUS_MCOPY_PROBLEM 0x20
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22
+#define PXENV_STATUS_BIS_INIT_FAILURE 0x23
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24
+#define PXENV_STATUS_BIS_GBOA_FAILURE 0x25
+#define PXENV_STATUS_BIS_FREE_FAILURE 0x26
+#define PXENV_STATUS_BIS_GSI_FAILURE 0x27
+#define PXENV_STATUS_BIS_BAD_CKSUM 0x28
+
+/* TFTP/MTFTP errors (0x30 to 0x3f) */
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33
+#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35
+#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3d
+#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3f
+
+/* Reserved errors 0x40 to 0x4f) */
+
+/* DHCP/BOOTP errors (0x50 to 0x5f) */
+#define PXENV_STATUS_DHCP_TIMEOUT 0x51
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54
+
+/* Driver errors (0x60 to 0x6f) */
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
+#define PXENV_STATUS_UNDI_INVALID_STATE 0x6a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6c
+
+/* ROM and NBP bootstrap errors (0x70 to 0x7f) */
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76
+#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79
+
+/* Environment NBP errors (0x80 to 0x8f) */
+
+/* Reserved errors (0x90 to 0x9f) */
+
+/* Miscellaneous errors (0xa0 to 0xaf) */
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xa0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xa1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xa2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xa3
+
+/* BUSD errors (0xb0 to 0xbf) */
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xb0
+
+/* Loader errors (0xc0 to 0xcf) */
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xc0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xc1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xc2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xc3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xc4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xc5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xc6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xc8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xc9
+#define PXENV_STATUS_LOADER_UNDI_START 0xca
+#define PXENV_STATUS_LOADER_BC_START 0xcb
+
+/*
+ * The range 0xd0 to 0xff is defined as "Vendor errors" by the PXE
+ * spec. We place all our Etherboot-specific errors in this range.
+ * We also define some generic errors as aliases to the PXE errors.
+ *
+ */
+
+#define ENOERR 0x00
+#define ENOMEM PXENV_STATUS_OUT_OF_RESOURCES
+#define EBADIMG 0xd0
+#define EIMGRET 0xd1
+#define ETIMEDOUT 0xd2
+#define EINVAL 0xd3
+
+/* Data structures and declarations */
+
+#include "tables.h"
+
+extern int errno;
+
+extern const char * strerror ( int errno );
+
+struct errortab {
+ int errno;
+ const char *text;
+};
+
+#define __errortab __table(errortab,01)
+
+#endif /* ERRNO_H */
diff --git a/src/include/etherboot.h b/src/include/etherboot.h
index e61a711f..83f117ef 100644
--- a/src/include/etherboot.h
+++ b/src/include/etherboot.h
@@ -1,6 +1,26 @@
#ifndef ETHERBOOT_H
#define ETHERBOOT_H
+/*
+ * Standard includes that we always want
+ *
+ */
+
+#include "compiler.h"
+#include "stddef.h"
+#include "stdint.h"
+
+
+/*
+ * IMPORTANT!!!!!!!!!!!!!!
+ *
+ * Everything below this point is cruft left over from older versions
+ * of Etherboot. Do not add *anything* below this point. Things are
+ * gradually being moved to individual header files.
+ *
+ */
+
+
#include <stdarg.h>
#include "osdep.h"
@@ -110,16 +130,11 @@ enum {
MAX_ARP
};
-#define IGMP_SERVER 0
-#define MAX_IGMP IGMP_SERVER+1
-
#define RARP_REQUEST 3
#define RARP_REPLY 4
#include "in.h"
-#define MULTICAST_MASK 0xF0000000
-#define MULTICAST_NETWORK 0xE0000000
/* Helper macros used to identify when DHCP options are valid/invalid in/outside of encapsulation */
#define NON_ENCAP_OPT in_encapsulated_options == 0 &&
@@ -134,7 +149,6 @@ enum {
#include "udp.h"
#include "tcp.h"
#include "bootp.h"
-#include "tftp.h"
#include "igmp.h"
#include "nfs.h"
#include "console.h"
@@ -145,11 +159,6 @@ struct arptable_t {
uint8_t node[6];
} PACKED;
-struct igmptable_t {
- in_addr group;
- unsigned long time;
-} PACKED;
-
#define KERNEL_BUF (BOOTP_DATA_ADDR->bootp_reply.bp_file)
#define FLOPPY_BOOT_LOCATION 0x7c00
@@ -195,19 +204,13 @@ int tcp_reset(struct iphdr *ip);
typedef int (*reply_t)(int ival, void *ptr, unsigned short ptype, struct iphdr *ip, struct udphdr *udp, struct tcphdr *tcp);
extern int await_reply P((reply_t reply, int ival, void *ptr, long timeout));
extern int decode_rfc1533 P((unsigned char *, unsigned int, unsigned int, int));
-extern void join_group(int slot, unsigned long group);
-extern void leave_group(int slot);
#define RAND_MAX 2147483647L
extern uint16_t ipchksum P((const void *ip, unsigned long len));
extern uint16_t add_ipchksums P((unsigned long offset, uint16_t sum, uint16_t new));
extern int32_t random P((void));
extern long rfc2131_sleep_interval P((long base, int exp));
-extern long rfc1112_sleep_interval P((long base, int exp));
extern void cleanup P((void));
-/* config.c */
-extern void print_config(void);
-
/* osloader.c */
/* Be careful with sector_t it is an unsigned long long on x86 */
typedef uint64_t sector_t;
@@ -271,15 +274,6 @@ extern int elf_start(unsigned long machine, unsigned long entry, unsigned long p
extern unsigned long currticks P((void));
extern void exit P((int status));
-/* serial.c */
-extern int serial_getc P((void));
-extern void serial_putc P((int));
-extern int serial_ischar P((void));
-extern int serial_init P((void));
-extern void serial_fini P((void));
-
-/* floppy.c */
-extern int bootdisk P((int dev,int part));
/***************************************************************************
External variables
@@ -291,7 +285,6 @@ extern int hostnamelen;
extern jmp_buf restart_etherboot;
extern int url_port;
extern struct arptable_t arptable[MAX_ARP];
-extern struct igmptable_t igmptable[MAX_IGMP];
#ifdef IMAGE_MENU
extern int menutmo,menudefault;
extern unsigned char *defparams;
@@ -309,14 +302,6 @@ extern int freebsd_howto;
extern char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE];
#endif
-/* bootmenu.c */
-
-/* osloader.c */
-
-/* created by linker */
-extern char _virt_start[], _text[], _etext[], _text16[], _etext16[];
-extern char _data[], _edata[], _bss[], _ebss[], _end[];
-
/*
* Local variables:
diff --git a/src/include/igmp.h b/src/include/igmp.h
index 2235d6c6..48753eab 100644
--- a/src/include/igmp.h
+++ b/src/include/igmp.h
@@ -1,5 +1,8 @@
-#ifndef _IGMP_H
-#define _IGMP_H
+#ifndef IGMP_H
+#define IGMP_H
+
+#include "stdint.h"
+#include "in.h"
#define IGMP_QUERY 0x11
#define IGMPv1_REPORT 0x12
@@ -7,11 +10,19 @@
#define IGMP_LEAVE 0x17
#define GROUP_ALL_HOSTS 0xe0000001 /* 224.0.0.1 Host byte order */
+#define MULTICAST_MASK 0xf0000000
+#define MULTICAST_NETWORK 0xe0000000
+
+enum {
+ IGMP_SERVER,
+ MAX_IGMP
+};
+
struct igmp {
- uint8_t type;
- uint8_t response_time;
- uint16_t chksum;
- in_addr group;
+ uint8_t type;
+ uint8_t response_time;
+ uint16_t chksum;
+ struct in_addr group;
} PACKED;
struct igmp_ip_t { /* Format of an igmp ip packet */
@@ -20,4 +31,12 @@ struct igmp_ip_t { /* Format of an igmp ip packet */
struct igmp igmp;
} PACKED;
-#endif /* _IGMP_H */
+struct igmptable_t {
+ struct in_addr group;
+ unsigned long time;
+} PACKED;
+
+extern void join_group ( int slot, unsigned long group );
+extern void leave_group ( int slot );
+
+#endif /* IGMP_H */
diff --git a/src/include/nmb.h b/src/include/nmb.h
index 695f8e08..7948d9e4 100644
--- a/src/include/nmb.h
+++ b/src/include/nmb.h
@@ -14,7 +14,7 @@
#define NBNS_UDP_PORT 137
struct dns_rr_info_nb {
- struct dns_rr_info;
+ struct dns_rr_info info;
uint16_t nb_flags;
struct in_addr nb_address;
} __attribute__ (( packed ));
diff --git a/src/include/pci_ids.h b/src/include/pci_ids.h
index bcf4c3cd..a853d4dc 100644
--- a/src/include/pci_ids.h
+++ b/src/include/pci_ids.h
@@ -319,6 +319,7 @@
#define PCI_VENDOR_ID_MORETON 0x15aa
#define PCI_VENDOR_ID_ZOLTRIX 0x15b0
#define PCI_VENDOR_ID_PDC 0x15e9
+#define PCI_VENDOR_ID_FSC 0x1734
#define PCI_VENDOR_ID_SYMPHONY 0x1c1c
#define PCI_VENDOR_ID_TEKRAM 0x1de1
#define PCI_VENDOR_ID_3DLABS 0x3d3d
diff --git a/src/include/pxe.h b/src/include/pxe.h
index 48f55509..f8e2de79 100644
--- a/src/include/pxe.h
+++ b/src/include/pxe.h
@@ -1,895 +1,59 @@
-/*
- * pxe.h for Etherboot.
- *
- * PXE is technically specified only for i386, but there's no reason
- * why we shouldn't make the API available for other architectures,
- * provided that someone wants to write the shim that allows an
- * external program to call pxe_api_call().
- *
- * We stick with Intel's data structure definitions as far as possible
- * on other architectures. Generally the only i386-specific stuff is
- * related to addressing: real-mode segment:offset addresses, segment
- * selectors, segment descriptors etc. We allow an architecture-
- * specific header to define these types, then build the PXE
- * structures. Note that we retain the names from the PXE
- * specification document (e.g. SEGOFF16_t) even if the architecture
- * in question doesn't represent a SEGOFF16_t as anything resembling a
- * 16-bit segment:offset address. This is done in order to keep the
- * structure definitions as close as possible to those in the spec, to
- * minimise confusion.
- *
- * This file derives from several originals. One is pxe.h from
- * FreeBSD. Another is general.h86 from netboot. The original
- * copyright notices are reproduced below. This entire file is
- * licensed under the GPL; the netboot code is GPL anyway and the
- * FreeBSD code allows us to relicense under the GPL provided that we
- * retain the FreeBSD copyright notice. This is my understanding,
- * anyway. Other portions are my own and therefore 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.
- */
-
#ifndef PXE_H
#define PXE_H
-/* Include architecture-specific PXE data types
- *
- * May define SEGOFF16_t, SEGDESC_t and SEGSEL_t. These should be
- * #defines to underlying * types. May also define
- * IS_NULL_SEGOFF16(segoff16), SEGOFF16_TO_PTR(segoff16) and
- * PTR_TO_SEGOFF16(ptr,segoff16)
- */
-#ifndef PXE_TYPES_H
-#include <pxe_types.h>
-#endif
-
-/* Defaults in case pxe_types.h did not define a type. These are
- * placeholder structures just to make the code compile.
- */
-#ifndef SEGOFF16_t
-#define SEGOFF16_t void*
-#endif
-
-#ifndef IS_NULL_SEGOFF16
-#define IS_NULL_SEGOFF16(segoff16) ( (segoff16) == NULL )
-#endif
-
-#ifndef SEGOFF16_TO_PTR
-#define SEGOFF16_TO_PTR(segoff16) (segoff16)
-#endif
-
-#ifndef PTR_TO_SEGOFF16
-#define PTR_TO_SEGOFF16(ptr,segoff16) (segoff16) = (ptr);
-#endif
-
-#ifndef SEGDESC_t
-#define SEGDESC_t void
-#endif
-
-#ifndef SEGSEL_t
-#define SEGSEL_t void
-#endif
-
-/*****************************************************************************
- * The following portion of this file is derived from FreeBSD's pxe.h.
- * Do not remove the copyright notice below.
- *****************************************************************************
- */
-
-/*
- * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
- * All rights reserved.
- * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
- * All rights reserved.
- * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/boot/i386/libi386/pxe.h,v 1.4.2.2 2000/09/10 02:52:18 ps Exp $
- */
-
-/*
- * The typedefs and structures declared in this file
- * clearly violate style(9), the reason for this is to conform to the
- * typedefs/structure-names used in the Intel literature to avoid confusion.
- *
- * It's for your own good. :)
- */
-
-/* It seems that intel didn't think about ABI,
- * either that or 16bit ABI != 32bit ABI (which seems reasonable)
- * I have to thank Intel for the hair loss I incurred trying to figure
- * out why PXE was mis-reading structures I was passing it (at least
- * from my point of view)
- *
- * Solution: use gcc's '__attribute__ ((packed))' to correctly align
- * structures passed into PXE
- * Question: does this really work for PXE's expected ABI?
- */
-#ifndef PACKED
-#define PACKED __attribute__ ((packed))
-#endif
-
-#define S_SIZE(s) s, sizeof(s) - 1
-
-#define IP_STR "%d.%d.%d.%d"
-#define IP_ARGS(ip) \
- (int)(ip >> 24) & 0xff, (int)(ip >> 16) & 0xff, \
- (int)(ip >> 8) & 0xff, (int)ip & 0xff
-
-#define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARGS(mac) \
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
-
-typedef uint16_t PXENV_EXIT_t;
-typedef uint16_t PXENV_STATUS_t;
-typedef uint32_t IP4_t;
-typedef uint32_t ADDR32_t;
-/* It seems as though UDP_PORT_t is in network order, although I can't
- * find anything in the spec to back this up. (Michael Brown)
- */
-typedef uint16_t UDP_PORT_t;
-
-#define MAC_ADDR_LEN 16
-typedef uint8_t MAC_ADDR[MAC_ADDR_LEN];
-
-/* PXENV+ */
-typedef struct {
- uint8_t Signature[6]; /* 'PXENV+' */
- uint16_t Version; /* MSB = major, LSB = minor */
- uint8_t Length; /* structure length */
- uint8_t Checksum; /* checksum pad */
- SEGOFF16_t RMEntry; /* SEG:OFF to PXE entry point */
- /* don't use PMOffset and PMSelector (from the 2.1 PXE manual) */
- uint32_t PMOffset; /* Protected mode entry */
- SEGSEL_t PMSelector; /* Protected mode selector */
- SEGSEL_t StackSeg; /* Stack segment address */
- uint16_t StackSize; /* Stack segment size (bytes) */
- SEGSEL_t BC_CodeSeg; /* BC Code segment address */
- uint16_t BC_CodeSize; /* BC Code segment size (bytes) */
- SEGSEL_t BC_DataSeg; /* BC Data segment address */
- uint16_t BC_DataSize; /* BC Data segment size (bytes) */
- SEGSEL_t UNDIDataSeg; /* UNDI Data segment address */
- uint16_t UNDIDataSize; /* UNDI Data segment size (bytes) */
- SEGSEL_t UNDICodeSeg; /* UNDI Code segment address */
- uint16_t UNDICodeSize; /* UNDI Code segment size (bytes) */
- SEGOFF16_t PXEPtr; /* SEG:OFF to !PXE struct,
- only present when Version > 2.1 */
-} PACKED pxenv_t;
-
-/* !PXE */
-typedef struct {
- uint8_t Signature[4];
- uint8_t StructLength;
- uint8_t StructCksum;
- uint8_t StructRev;
- uint8_t reserved_1;
- SEGOFF16_t UNDIROMID;
- SEGOFF16_t BaseROMID;
- SEGOFF16_t EntryPointSP;
- SEGOFF16_t EntryPointESP;
- SEGOFF16_t StatusCallout;
- uint8_t reserved_2;
- uint8_t SegDescCn;
- SEGSEL_t FirstSelector;
- SEGDESC_t Stack;
- SEGDESC_t UNDIData;
- SEGDESC_t UNDICode;
- SEGDESC_t UNDICodeWrite;
- SEGDESC_t BC_Data;
- SEGDESC_t BC_Code;
- SEGDESC_t BC_CodeWrite;
-} PACKED pxe_t;
-
-#define PXENV_START_UNDI 0x0000
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t ax;
- uint16_t bx;
- uint16_t dx;
- uint16_t di;
- uint16_t es;
-} PACKED t_PXENV_START_UNDI;
-
-#define PXENV_UNDI_STARTUP 0x0001
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_STARTUP;
-
-#define PXENV_UNDI_CLEANUP 0x0002
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_CLEANUP;
-
-#define PXENV_UNDI_INITIALIZE 0x0003
-typedef struct {
- PXENV_STATUS_t Status;
- ADDR32_t ProtocolIni; /* Phys addr of a copy of the driver module */
- uint8_t reserved[8];
-} PACKED t_PXENV_UNDI_INITIALIZE;
-
-
-#define MAXNUM_MCADDR 8
-typedef struct {
- uint16_t MCastAddrCount;
- MAC_ADDR McastAddr[MAXNUM_MCADDR];
-} PACKED t_PXENV_UNDI_MCAST_ADDRESS;
-
-#define PXENV_UNDI_RESET_ADAPTER 0x0004
-typedef struct {
- PXENV_STATUS_t Status;
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} PACKED t_PXENV_UNDI_RESET_ADAPTER;
-
-#define PXENV_UNDI_SHUTDOWN 0x0005
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_SHUTDOWN;
-
-#define PXENV_UNDI_OPEN 0x0006
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t OpenFlag;
- uint16_t PktFilter;
-# define FLTR_DIRECTED 0x0001
-# define FLTR_BRDCST 0x0002
-# define FLTR_PRMSCS 0x0003
-# define FLTR_SRC_RTG 0x0004
-
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} PACKED t_PXENV_UNDI_OPEN;
-
-#define PXENV_UNDI_CLOSE 0x0007
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_CLOSE;
-
-#define PXENV_UNDI_TRANSMIT 0x0008
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t Protocol;
-# define P_UNKNOWN 0
-# define P_IP 1
-# define P_ARP 2
-# define P_RARP 3
-
- uint8_t XmitFlag;
-# define XMT_DESTADDR 0x0000
-# define XMT_BROADCAST 0x0001
-
- SEGOFF16_t DestAddr;
- SEGOFF16_t TBD;
- uint32_t Reserved[2];
-} PACKED t_PXENV_UNDI_TRANSMIT;
-
-#define MAX_DATA_BLKS 8
-typedef struct {
- uint16_t ImmedLength;
- SEGOFF16_t Xmit;
- uint16_t DataBlkCount;
- struct DataBlk {
- uint8_t TDPtrType;
- uint8_t TDRsvdByte;
- uint16_t TDDataLen;
- SEGOFF16_t TDDataPtr;
- } DataBlock[MAX_DATA_BLKS];
-} PACKED t_PXENV_UNDI_TBD;
-
-#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
-typedef struct {
- PXENV_STATUS_t Status;
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} PACKED t_PXENV_UNDI_SET_MCAST_ADDRESS;
-
-#define PXENV_UNDI_SET_STATION_ADDRESS 0x000A
-typedef struct {
- PXENV_STATUS_t Status;
- MAC_ADDR StationAddress; /* Temp MAC addres to use */
-} PACKED t_PXENV_UNDI_SET_STATION_ADDRESS;
-
-#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t filter; /* see UNDI_OPEN (0x0006) */
-} PACKED t_PXENV_UNDI_SET_PACKET_FILTER;
-
-#define PXENV_UNDI_GET_INFORMATION 0x000C
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t BaseIo; /* Adapter base I/O address */
- uint16_t IntNumber; /* Adapter IRQ number */
- uint16_t MaxTranUnit; /* Adapter maximum transmit unit */
- uint16_t HwType; /* Type of protocol at the hardware addr */
-# define ETHER_TYPE 1
-# define EXP_ETHER_TYPE 2
-# define IEEE_TYPE 6
-# define ARCNET_TYPE 7
-
- uint16_t HwAddrLen; /* Length of hardware address */
- MAC_ADDR CurrentNodeAddress; /* Current hardware address */
- MAC_ADDR PermNodeAddress; /* Permanent hardware address */
- SEGSEL_t ROMAddress; /* Real mode ROM segment address */
- uint16_t RxBufCt; /* Receive queue length */
- uint16_t TxBufCt; /* Transmit queue length */
-} PACKED t_PXENV_UNDI_GET_INFORMATION;
-
-#define PXENV_UNDI_GET_STATISTICS 0x000D
-typedef struct {
- PXENV_STATUS_t Status;
- uint32_t XmitGoodFrames; /* Number of successful transmissions */
- uint32_t RcvGoodFrames; /* Number of good frames received */
- uint32_t RcvCRCErrors; /* Number of frames with CRC errors */
- uint32_t RcvResourceErrors; /* Number of frames dropped */
-} PACKED t_PXENV_UNDI_GET_STATISTICS;
-
-#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_CLEAR_STATISTICS;
-
-#define PXENV_UNDI_INITIATE_DIAGS 0x000F
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_INITIATE_DIAGS;
-
-#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UNDI_FORCE_INTERRUPT;
-
-#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t InetAddr; /* IP mulicast address */
- MAC_ADDR MediaAddr; /* MAC multicast address */
-} PACKED t_PXENV_UNDI_GET_MCAST_ADDRESS;
-
-#define PXENV_UNDI_GET_NIC_TYPE 0x0012
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t NicType; /* Type of NIC */
-# define PCI_NIC 2
-# define PnP_NIC 3
-# define CardBus_NIC 4
-
- union {
- struct {
- uint16_t Vendor_ID;
- uint16_t Dev_ID;
- uint8_t Base_Class;
- uint8_t Sub_Class;
- uint8_t Prog_Intf;
- uint8_t Rev;
- uint16_t BusDevFunc;
- uint16_t SubVendor_ID;
- uint16_t SubDevice_ID;
- } pci, cardbus;
- struct {
- uint32_t EISA_Dev_ID;
- uint8_t Base_Class;
- uint8_t Sub_Class;
- uint8_t Prog_Intf;
- uint16_t CardSelNum;
- } pnp;
- } info;
-} PACKED t_PXENV_UNDI_GET_NIC_TYPE;
-
-#define PXENV_UNDI_GET_IFACE_INFO 0x0013
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t IfaceType[16]; /* Name of MAC type in ASCII. */
- uint32_t LinkSpeed; /* Defined in NDIS 2.0 spec */
- uint32_t ServiceFlags; /* Defined in NDIS 2.0 spec */
- uint32_t Reserved[4]; /* must be 0 */
-} PACKED t_PXENV_UNDI_GET_IFACE_INFO;
-
-#define PXENV_UNDI_ISR 0x0014
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t FuncFlag; /* PXENV_UNDI_ISR_OUT_xxx */
- uint16_t BufferLength; /* Length of Frame */
- uint16_t FrameLength; /* Total length of reciever frame */
- uint16_t FrameHeaderLength; /* Length of the media header in Frame */
- SEGOFF16_t Frame; /* receive buffer */
- uint8_t ProtType; /* Protocol type */
- uint8_t PktType; /* Packet Type */
-# define PXENV_UNDI_ISR_IN_START 1
-# define PXENV_UNDI_ISR_IN_PROCESS 2
-# define PXENV_UNDI_ISR_IN_GET_NEXT 3
-
- /* one of these will be returned for PXENV_UNDI_ISR_IN_START */
-# define PXENV_UNDI_ISR_OUT_OURS 0
-# define PXENV_UNDI_ISR_OUT_NOT_OURS 1
-
- /*
- * one of these will bre returnd for PXEND_UNDI_ISR_IN_PROCESS
- * and PXENV_UNDI_ISR_IN_GET_NEXT
- */
-# define PXENV_UNDI_ISR_OUT_DONE 0
-# define PXENV_UNDI_ISR_OUT_TRANSMIT 2
-# define PXENV_UNDI_ISR_OUT_RECEIVE 3
-# define PXENV_UNDI_ISR_OUT_BUSY 4
-} PACKED t_PXENV_UNDI_ISR;
-
-#define PXENV_STOP_UNDI 0x0015
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_STOP_UNDI;
-
-#define PXENV_TFTP_OPEN 0x0020
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t ServerIPAddress;
- IP4_t GatewayIPAddress;
- uint8_t FileName[128];
- UDP_PORT_t TFTPPort;
- uint16_t PacketSize;
-} PACKED t_PXENV_TFTP_OPEN;
-
-#define PXENV_TFTP_CLOSE 0x0021
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_TFTP_CLOSE;
-
-#define PXENV_TFTP_READ 0x0022
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t PacketNumber;
- uint16_t BufferSize;
- SEGOFF16_t Buffer;
-} PACKED t_PXENV_TFTP_READ;
-
-#define PXENV_TFTP_READ_FILE 0x0023
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t FileName[128];
- uint32_t BufferSize;
- ADDR32_t Buffer;
- IP4_t ServerIPAddress;
- IP4_t GatewayIPAdress;
- IP4_t McastIPAdress;
- UDP_PORT_t TFTPClntPort;
- UDP_PORT_t TFTPSrvPort;
- uint16_t TFTPOpenTimeOut;
- uint16_t TFTPReopenDelay;
-} PACKED t_PXENV_TFTP_READ_FILE;
-
-#define PXENV_TFTP_GET_FSIZE 0x0025
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t ServerIPAddress;
- IP4_t GatewayIPAdress;
- uint8_t FileName[128];
- uint32_t FileSize;
-} PACKED t_PXENV_TFTP_GET_FSIZE;
-
-#define PXENV_UDP_OPEN 0x0030
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t src_ip; /* IP address of this station */
-} PACKED t_PXENV_UDP_OPEN;
-
-#define PXENV_UDP_CLOSE 0x0031
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_UDP_CLOSE;
-
-#define PXENV_UDP_READ 0x0032
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t src_ip; /* IP of sender */
- IP4_t dest_ip; /* Only accept packets sent to this IP */
- UDP_PORT_t s_port; /* UDP source port of sender */
- UDP_PORT_t d_port; /* Only accept packets sent to this port */
- uint16_t buffer_size; /* Size of the packet buffer */
- SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
-} PACKED t_PXENV_UDP_READ;
-
-#define PXENV_UDP_WRITE 0x0033
-typedef struct {
- PXENV_STATUS_t Status;
- IP4_t ip; /* dest ip addr */
- IP4_t gw; /* ip gateway */
- UDP_PORT_t src_port; /* source udp port */
- UDP_PORT_t dst_port; /* destination udp port */
- uint16_t buffer_size; /* Size of the packet buffer */
- SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
-} PACKED t_PXENV_UDP_WRITE;
-
-#define PXENV_UNLOAD_STACK 0x0070
-typedef struct {
- PXENV_STATUS_t Status;
- uint8_t reserved[10];
-} PACKED t_PXENV_UNLOAD_STACK;
-
-
-#define PXENV_GET_CACHED_INFO 0x0071
-typedef struct {
- PXENV_STATUS_t Status;
- uint16_t PacketType; /* type (defined right here) */
-# define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
-# define PXENV_PACKET_TYPE_DHCP_ACK 2
-# define PXENV_PACKET_TYPE_BINL_REPLY 3
- uint16_t BufferSize; /* max to copy, leave at 0 for pointer */
- SEGOFF16_t Buffer; /* copy to, leave at 0 for pointer */
- uint16_t BufferLimit; /* max size of buffer in BC dataseg ? */
-} PACKED t_PXENV_GET_CACHED_INFO;
-
-
-/* structure filled in by PXENV_GET_CACHED_INFO
- * (how we determine which IP we downloaded the initial bootstrap from)
- * words can't describe...
- */
-typedef struct {
- uint8_t opcode;
-# define BOOTP_REQ 1
-# define BOOTP_REP 2
- uint8_t Hardware; /* hardware type */
- uint8_t Hardlen; /* hardware addr len */
- uint8_t Gatehops; /* zero it */
- uint32_t ident; /* random number chosen by client */
- uint16_t seconds; /* seconds since did initial bootstrap */
- uint16_t Flags; /* seconds since did initial bootstrap */
-# define BOOTP_BCAST 0x8000 /* ? */
- IP4_t cip; /* Client IP */
- IP4_t yip; /* Your IP */
- IP4_t sip; /* IP to use for next boot stage */
- IP4_t gip; /* Relay IP ? */
- MAC_ADDR CAddr; /* Client hardware address */
- uint8_t Sname[64]; /* Server's hostname (Optional) */
- uint8_t bootfile[128]; /* boot filename */
- union {
-# if 1
-# define BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size */
-# else
-# define BOOTP_DHCPVEND 312 /* DHCP standard vendor field size */
-# endif
- uint8_t d[BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options */
- struct {
- uint8_t magic[4]; /* DHCP magic cookie */
-# ifndef VM_RFC1048
-# define VM_RFC1048 0x63825363L /* ? */
-# endif
- uint32_t flags; /* bootp flags/opcodes */
- uint8_t pad[56]; /* I don't think intel knows what a
- union does... */
- } v;
- } vendor;
-} PACKED BOOTPLAYER;
-
-#define PXENV_RESTART_TFTP 0x0073
-#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE
-
-#define PXENV_START_BASE 0x0075
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_START_BASE;
-
-#define PXENV_STOP_BASE 0x0076
-typedef struct {
- PXENV_STATUS_t Status;
-} PACKED t_PXENV_STOP_BASE;
-
-/*****************************************************************************
- * The following portion of this file is derived from netboot's
- * general.h86. Do not remove the copyright notice below.
- *****************************************************************************
- */
-
-/*
- * general.h86 - Common PXE definitions
- *
- * Copyright (C) 2003 Gero Kuhlmann <gero@gkminix.han.de>
- *
- * 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.
- *
- * $Id$
- */
-
-/*
- **************************************************************************
- *
- * This file contains the Preboot API common definitions as
- * per Intels PXE specification version 2.0.
- *
- * Updated to comply with PXE specification version 2.1 by Michael Brown.
- *
- **************************************************************************
- *
- * Result codes returned in AX by a PXENV API service:
- */
-#define PXENV_EXIT_SUCCESS 0x0000
-#define PXENV_EXIT_FAILURE 0x0001
-
-
-
-/*
- **************************************************************************
- *
- * CPU types (defined in WfM 1.1):
- */
-#define PXENV_CPU_X86 0
-#define PXENV_CPU_ALPHA 1
-#define PXENV_CPU_PPC 2
-
-
-
-/*
- **************************************************************************
- *
- * Bus types (defined in WfM 1.1):
- */
-#define PXENV_BUS_ISA 0
-#define PXENV_BUS_EISA 1
-#define PXENV_BUS_MCA 2
-#define PXENV_BUS_PCI 3
-#define PXENV_BUS_VESA 4
-#define PXENV_BUS_PCMCIA 5
-
-
-
-/*
- **************************************************************************
- *
- * Status codes returned in the status word of the PXENV API parameter
- * structure. Some of these codes are also used to return status
- * information from a boot image loader back to the bootrom.
- */
-
-/* Generic API errors that are reported by the loader */
-#define PXENV_STATUS_SUCCESS 0x00
-#define PXENV_STATUS_FAILURE 0x01 /* general failure */
-#define PXENV_STATUS_BAD_FUNC 0x02 /* invalid function number */
-#define PXENV_STATUS_UNSUPPORTED 0x03 /* not yet supported */
-#define PXENV_STATUS_KEEP_UNDI 0x04 /* keep UNDI in memory */
-#define PXENV_STATUS_KEEP_ALL 0x05 /* keep everything in memory */
-#define PXENV_STATUS_OUT_OF_RESOURCES 0x06 /* also keep everything */
-
-/* ARP/UDP errors (0x10 to 0x1F) */
-#define PXENV_STATUS_ARP_CANCELED 0x10 /* ARP canceled by keystroke */
-#define PXENV_STATUS_ARP_TIMEOUT 0x11 /* ARP timeout */
-#define PXENV_STATUS_UDP_CLOSED 0x18 /* UDP closed */
-#define PXENV_STATUS_UDP_OPEN 0x19 /* UDP already open */
-#define PXENV_STATUS_TFTP_CLOSED 0x1A /* TFTP closed */
-#define PXENV_STATUS_TFTP_OPEN 0x1B /* TFTP already opened */
-
-/* BIOS/system errors (0x20 to 0x2F) */
-#define PXENV_STATUS_MCOPY_PROBLEM 0x20 /* can't copy into memory */
-
-/* TFP errors (0x30 to 0x3F) */
-#define PXENV_STATUS_TFTP_CANNOT_ARP 0x30 /* TFTP ARP problem */
-#define PXENV_STATUS_TFTP_OPEN_CANCELED 0x31 /* TFTP open canceled by key */
-#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32 /* timeout during TFTP open */
-#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33 /* unknown TFTP opcode */
-#define PXENV_STATUS_TFTP_READ_CANCELED 0x34 /* TFTP read canceled by key */
-#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35 /* timeout during TFTP read */
-#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36 /* bad TFTP opcode */
-#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION \
- 0x38 /* error during TFTP open */
-#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION \
- 0x39 /* error during TFTP read */
-#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES \
- 0x3A /* too many packages */
-#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3B /* file not found */
-#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3C /* access violation */
-#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3D /* no multicast address */
-#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3E /* unable to get file size */
-#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE \
- 0x3F /* invalid packet size */
-
-/* BOOTP errors (0x40 to 0x4F) */
-#define PXENV_STATUS_BOOTP_CANCELED 0x40 /* BOOTP canceled by key */
-#define PXENV_STATUS_BOOTP_TIMEOUT 0x41 /* timeout during BOOTP */
-#define PXENV_STATUS_BOOTP_NO_FILE 0x42 /* missing bootfile name */
-
-/* DHCP errors (0x50 to 0x5F) */
-#define PXENV_STATUS_DHCP_CANCELED 0x50 /* DHCP canceled by key */
-#define PXENV_STATUS_DHCP_TIMEOUT 0x51 /* timeout during DHCP */
-#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52 /* missing IP address */
-#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53 /* missing bootfile name */
-#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54 /* invalid IP address */
-
-/* Driver errors (0x60 to 0x6F) */
-#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60 /* invalid UNDI function */
-#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61 /* media test failed */
-#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST \
- 0x62 /* cannot init for multicast */
-#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC \
- 0x63 /* cannot init NIC */
-#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY \
- 0x64 /* cannot init hardware */
-#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA \
- 0x65 /* cannot read config data */
-#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA \
- 0x66 /* cannot read init data */
-#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67 /* invalid hardware address */
-#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM \
- 0x68 /* invalid EEPROM checksum */
-#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
-#define PXENV_STATUS_UNDI_INVALID_STATE 0x6a /* invalid UNDI state */
-#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b /* transmit error */
-#define PXENV_STATUS_UNDI_INVALID_PARAMETER \
- 0x6c /* almost anything */
-
-/* Bootstrap (.1) errors (0x70 to 0x7F) */
-#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74 /* invalid bootstrap menu */
-#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76 /* missing multicast address */
-#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77 /* missing file list */
-#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78 /* no response from server */
-#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79 /* next file too big */
-
-/* Environment (.2) errors (0x80 to 0x8F) */
-
-/* MTFTP errors (0x90 to 0x9F) */
-#define PXENV_STATUS_MTFTP_OPEN_CANCEL 0x91 /* MTFTP open canceled by key */
-#define PXENV_STATUS_MTFTP_OPEN_TIMEOUT 0x92 /* timeout during MTFTP open */
-#define PXENV_STATUS_MTFTP_UNKNOWN_OP 0x93 /* unknown TFTP opcode */
-#define PXENV_STATUS_MTFTP_READ_CANCEL 0x94 /* MTFTP read canceled by key */
-#define PXENV_STATUS_MTFTP_READ_TIMEOUT 0x95 /* timeout during MTFTP read */
-#define PXENV_STATUS_MTFTP_ERROR_OP 0x96 /* bad TFTP opcode */
-#define PXENV_STATUS_MTFTP_CANNOT_OPEN 0x98 /* error during MTFTP open */
-#define PXENV_STATUS_MTFTP_CANNOT_READ 0x99 /* error during MTFTP read */
-#define PXENV_STATUS_MTFTP_TOO_MANY 0x9A /* too many packages */
-#define PXENV_STATUS_MTFTP_PACK_SIZE 0x9B /* invalid package size */
-
-/* Misc. errors (0xA0 to 0xAF) */
-#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE \
- 0xA0 /* BINL canceled by key */
-#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xA1 /* no BINL server found */
-#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE \
- 0xA2 /* not avail. in prot mode */
-#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE \
- 0xA3 /* not avail. in real mode */
-
-/* BUSD errors (0xB0 to 0xBF) */
-#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED \
- 0xB0 /* BUSD services not enabled */
-#define PXENV_STATUS_BUSD_DEV_ENABLE 0xB1 /* BUSD device not enabled */
-
-/* Loader errors (0xC0 to 0xCF) */
-#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY \
- 0xC0 /* no free base memory */
-#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xC1 /* no base code rom ID */
-#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xC2 /* bad base code rom ID */
-#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE \
- 0xC3 /* bad base code image */
-#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xC4 /* no UNDI rom ID */
-#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xC5 /* bad UNDI rom ID */
-#define PXENV_STATUS_LOADER_UNDI_DRIVER_IMAGE \
- 0xC6 /* bad UNDI runtime image */
-#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xC8 /* missing !PXE struct */
-#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT \
- 0xC9 /* missing PXENV+ struct */
-#define PXENV_STATUS_LOADER_UNDI_START 0xCA /* UNDI not started */
-#define PXENV_STATUS_LOADER_BC_START 0xCB /* base code not started */
-
-/* Reserved errors (0xD0 to 0xFF) */
-#define PXENV_STATUS_IMAGE_INVALID 0xD0 /* invalid boot image */
-#define PXENV_STATUS_STOP_BASE 0xD1 /* error stopping base code */
-#define PXENV_STATUS_UNLOAD_BASE 0xD2 /* error unloading base code */
-#define PXENV_STATUS_STOP_UNDI 0xD3 /* error stopping UNDI */
-#define PXENV_STATUS_CLEANUP_UNDI 0xD4 /* error cleaning up UNDI */
-
-
-/*****************************************************************************
- * The remainder of this file is original to Etherboot.
- *****************************************************************************
- */
-
-/* Dummy PXE opcode for the loader routine. We do this to make the
- * API simpler
- */
-#define PXENV_UNDI_LOADER 0x104d /* 'load' */
-
-typedef struct undi_loader {
- union {
- struct {
- PXENV_STATUS_t Status;
- uint16_t ax;
- uint16_t bx;
- uint16_t dx;
- uint16_t di;
- uint16_t es;
- };
- t_PXENV_START_UNDI start_undi;
- };
- uint16_t undi_ds;
- uint16_t undi_cs;
- SEGOFF16_t pxe_ptr;
- SEGOFF16_t pxenv_ptr;
-} PACKED undi_loader_t;
+#include "pxe_types.h"
+#include "pxe_api.h"
+#include "etherboot.h"
+#include "tftp.h"
/* Union used for PXE API calls; we don't know the type of the
* structure until we interpret the opcode. Also, Status is available
* in the same location for any opcode, and it's convenient to have
* non-specific access to it.
*/
-typedef union {
- PXENV_STATUS_t Status; /* Make it easy to read status
- for any operation */
- t_PXENV_START_UNDI start_undi;
- t_PXENV_UNDI_STARTUP undi_startup;
- t_PXENV_UNDI_CLEANUP undi_cleanup;
- t_PXENV_UNDI_INITIALIZE undi_initialize;
- t_PXENV_UNDI_RESET_ADAPTER undi_reset_adapter;
- t_PXENV_UNDI_SHUTDOWN undi_shutdown;
- t_PXENV_UNDI_OPEN undi_open;
- t_PXENV_UNDI_CLOSE undi_close;
- t_PXENV_UNDI_TRANSMIT undi_transmit;
- t_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address;
- t_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address;
- t_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter;
- t_PXENV_UNDI_GET_INFORMATION undi_get_information;
- t_PXENV_UNDI_GET_STATISTICS undi_get_statistics;
- t_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics;
- t_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags;
- t_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt;
- t_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address;
- t_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type;
- t_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info;
- t_PXENV_UNDI_ISR undi_isr;
- t_PXENV_STOP_UNDI stop_undi;
- t_PXENV_TFTP_OPEN tftp_open;
- t_PXENV_TFTP_CLOSE tftp_close;
- t_PXENV_TFTP_READ tftp_read;
- t_PXENV_TFTP_READ_FILE tftp_read_file;
- t_PXENV_TFTP_GET_FSIZE tftp_get_fsize;
- t_PXENV_UDP_OPEN udp_open;
- t_PXENV_UDP_CLOSE udp_close;
- t_PXENV_UDP_READ udp_read;
- t_PXENV_UDP_WRITE udp_write;
- t_PXENV_UNLOAD_STACK unload_stack;
- t_PXENV_GET_CACHED_INFO get_cached_info;
- t_PXENV_RESTART_TFTP restart_tftp;
- t_PXENV_START_BASE start_base;
- t_PXENV_STOP_BASE stop_base;
- undi_loader_t loader;
-} t_PXENV_ANY;
+union u_PXENV_ANY {
+ /* Make it easy to read status for any operation */
+ PXENV_STATUS_t Status;
+ struct s_PXENV_UNLOAD_STACK unload_stack;
+ struct s_PXENV_GET_CACHED_INFO get_cached_info;
+ struct s_PXENV_TFTP_READ_FILE restart_tftp;
+ struct s_PXENV_START_UNDI start_undi;
+ struct s_PXENV_STOP_UNDI stop_undi;
+ struct s_PXENV_START_BASE start_base;
+ struct s_PXENV_STOP_BASE stop_base;
+ struct s_PXENV_TFTP_OPEN tftp_open;
+ struct s_PXENV_TFTP_CLOSE tftp_close;
+ struct s_PXENV_TFTP_READ tftp_read;
+ struct s_PXENV_TFTP_READ_FILE tftp_read_file;
+ struct s_PXENV_TFTP_GET_FSIZE tftp_get_fsize;
+ struct s_PXENV_UDP_OPEN udp_open;
+ struct s_PXENV_UDP_CLOSE udp_close;
+ struct s_PXENV_UDP_WRITE udp_write;
+ struct s_PXENV_UDP_READ udp_read;
+ struct s_PXENV_UNDI_STARTUP undi_startup;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_UNDI_INITIALIZE undi_initialize;
+ struct s_PXENV_UNDI_RESET undi_reset_adapter;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_OPEN undi_open;
+ struct s_PXENV_UNDI_CLOSE undi_close;
+ struct s_PXENV_UNDI_TRANSMIT undi_transmit;
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address;
+ struct s_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter;
+ struct s_PXENV_UNDI_GET_INFORMATION undi_get_information;
+ struct s_PXENV_UNDI_GET_STATISTICS undi_get_statistics;
+ struct s_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics;
+ struct s_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags;
+ struct s_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt;
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address;
+ struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type;
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info;
+ struct s_PXENV_UNDI_GET_STATE undi_get_state;
+ struct s_PXENV_UNDI_ISR undi_isr;
+};
+
+typedef union u_PXENV_ANY PXENV_ANY_t;
/* PXE stack status indicator. See pxe_export.c for further
* explanation.
@@ -900,22 +64,32 @@ typedef enum {
READY
} pxe_stack_state_t;
+#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+
/* Data structures installed as part of a PXE stack. Architectures
* will have extra information to append to the end of this.
*/
#define PXE_TFTP_MAGIC_COOKIE ( ( 'P'<<24 ) | ( 'x'<<16 ) | ( 'T'<<8 ) | 'f' )
-typedef struct {
- pxe_t pxe __attribute__ ((aligned(16)));
- pxenv_t pxenv __attribute__ ((aligned(16)));
+typedef struct pxe_stack {
+ struct s_PXE pxe __attribute__ ((aligned(16)));
+ struct s_PXENV pxenv __attribute__ ((aligned(16)));
pxe_stack_state_t state;
union {
- BOOTPLAYER cached_info;
+ BOOTPLAYER_t cached_info;
char packet[ETH_FRAME_LEN];
struct {
uint32_t magic_cookie;
unsigned int len;
int eof;
- char data[TFTP_MAX_PACKET];
+ char data[TFTP_MAX_BLKSIZE];
} tftpdata;
struct {
char *buffer;
@@ -926,4 +100,10 @@ typedef struct {
struct {} arch_data __attribute__ ((aligned(16)));
} pxe_stack_t;
+extern int ensure_pxe_state ( pxe_stack_state_t wanted );
+
+extern pxe_stack_t *pxe_stack;
+
+extern PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any );
+
#endif /* PXE_H */
diff --git a/src/include/pxe_api.h b/src/include/pxe_api.h
new file mode 100644
index 00000000..7256c57e
--- /dev/null
+++ b/src/include/pxe_api.h
@@ -0,0 +1,1696 @@
+#ifndef PXE_API_H
+#define PXE_API_H
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * Preboot eXecution Environment (PXE) API
+ *
+ */
+
+#include "pxe_types.h"
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_api_call PXE entry points
+ *
+ * PXE entry points and calling conventions
+ *
+ * @{
+ */
+
+/** The PXENV+ structure */
+struct s_PXENV {
+ /** Signature
+ *
+ * Contains the bytes 'P', 'X', 'E', 'N', 'V', '+'.
+ */
+ UINT8_t Signature[6];
+ /** PXE API version
+ *
+ * MSB is major version number, LSB is minor version number.
+ * If the API version number is 0x0201 or greater, the !PXE
+ * structure pointed to by #PXEPtr should be used instead of
+ * this data structure.
+ */
+ UINT16_t Version;
+ UINT8_t Length; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #Length) must be zero.
+ */
+ UINT8_t Checksum;
+ SEGOFF16_t RMEntry; /**< Real-mode PXENV+ entry point */
+ /** Protected-mode PXENV+ entry point offset
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ UINT32_t PMOffset;
+ /** Protected-mode PXENV+ entry point segment selector
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ SEGSEL_t PMSelector;
+ SEGSEL_t StackSeg; /**< Stack segment selector */
+ UINT16_t StackSize; /**< Stack segment size */
+ SEGSEL_t BC_CodeSeg; /**< Base-code code segment selector */
+ UINT16_t BC_CodeSize; /**< Base-code code segment size */
+ SEGSEL_t BC_DataSeg; /**< Base-code data segment selector */
+ UINT16_t BC_DataSize; /**< Base-code data segment size */
+ SEGSEL_t UNDIDataSeg; /**< UNDI data segment selector */
+ UINT16_t UNDIDataSize; /**< UNDI data segment size */
+ SEGSEL_t UNDICodeSeg; /**< UNDI code segment selector */
+ UINT16_t UNDICodeSize; /**< UNDI code segment size */
+ /** Address of the !PXE structure
+ *
+ * This field is present only if #Version is 0x0201 or
+ * greater. If present, it points to a struct s_PXE.
+ */
+ SEGOFF16_t PXEPtr;
+} PACKED;
+
+typedef struct s_PXENV PXENV_t;
+
+/** The !PXE structure */
+struct s_PXE {
+ /** Signature
+ *
+ * Contains the bytes '!', 'P', 'X', 'E'.
+ */
+ UINT8_t Signature[4];
+ UINT8_t StructLength; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #StructLength) must be zero.
+ */
+ UINT8_t StructCksum;
+ /** Revision of this structure
+ *
+ * For PXE version 2.1, this field must be zero.
+ */
+ UINT8_t StructRev;
+ UINT8_t reserved_1; /**< Must be zero */
+ /** Address of the UNDI ROM ID structure
+ *
+ * This is a pointer to a struct s_UNDI_ROM_ID.
+ */
+ SEGOFF16_t UNDIROMID;
+ /** Address of the Base Code ROM ID structure
+ *
+ * This is a pointer to a struct s_BC_ROM_ID.
+ */
+ SEGOFF16_t BaseROMID;
+ /** 16-bit !PXE entry point
+ *
+ * This is the entry point for either real mode, or protected
+ * mode with a 16-bit stack segment.
+ */
+ SEGOFF16_t EntryPointSP;
+ /** 32-bit !PXE entry point
+ *
+ * This is the entry point for protected mode with a 32-bit
+ * stack segment.
+ */
+ SEGOFF16_t EntryPointESP;
+ /** Status call-out function
+ *
+ * @v 0 (if in a time-out loop)
+ * @v n Number of a received TFTP packet
+ * @ret 0 Continue operation
+ * @ret 1 Cancel operation
+ *
+ * This function will be called whenever the PXE stack is in
+ * protected mode, is waiting for an event (e.g. a DHCP reply)
+ * and wishes to allow the user to cancel the operation.
+ * Parameters are passed in register %ax; the return value
+ * must also be placed in register %ax. All other registers
+ * and flags @b must be preserved.
+ *
+ * In real mode, an internal function (that checks for a
+ * keypress) will be used.
+ *
+ * If this field is set to -1, no status call-out function
+ * will be used and consequently the user will not be allowed
+ * to interrupt operations.
+ *
+ * @note The PXE specification version 2.1 defines the
+ * StatusCallout field, mentions it 11 times, but nowhere
+ * defines what it actually does or how it gets called.
+ * Fortunately, the WfM specification version 1.1a deigns to
+ * inform us of such petty details.
+ */
+ SEGOFF16_t StatusCallout;
+ UINT8_t reserved_2; /**< Must be zero */
+ /** Number of segment descriptors
+ *
+ * If this number is greater than 7, the remaining descriptors
+ * follow immediately after #BC_CodeWrite.
+ */
+ UINT8_t SegDescCnt;
+ /** First protected-mode selector
+ *
+ * This is the segment selector value for the first segment
+ * assigned to PXE. Protected-mode selectors must be
+ * consecutive, according to the PXE 2.1 specification, though
+ * no reason is given. Each #SEGDESC_t includes a field for
+ * the segment selector, so this information is entirely
+ * redundant.
+ */
+ SEGSEL_t FirstSelector;
+ /** Stack segment descriptor */
+ SEGDESC_t Stack;
+ /** UNDI data segment descriptor */
+ SEGDESC_t UNDIData;
+ /** UNDI code segment descriptor */
+ SEGDESC_t UNDICode;
+ /** UNDI writable code segment descriptor */
+ SEGDESC_t UNDICodeWrite;
+ /** Base-code data segment descriptor */
+ SEGDESC_t BC_Data;
+ /** Base-code code segment descriptor */
+ SEGDESC_t BC_Code;
+ /** Base-code writable code segment descriptor */
+ SEGDESC_t BC_CodeWrite;
+} PACKED;
+
+typedef struct s_PXE PXE_t;
+
+/** @} */ /* pxe_api_call */
+
+/** @defgroup pxe_preboot_api PXE Preboot API
+ *
+ * General high-level functions: #PXENV_UNLOAD_STACK, #PXENV_START_UNDI etc.
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_unload_stack PXENV_UNLOAD_STACK
+ *
+ * UNLOAD BASE CODE STACK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_unload_stack() */
+#define PXENV_UNLOAD_STACK 0x0070
+
+/** Parameter block for pxenv_unload_stack() */
+struct s_PXENV_UNLOAD_STACK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t reserved[10]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNLOAD_STACK PXENV_UNLOAD_STACK_t;
+
+extern PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK
+ *unload_stack );
+
+/** @} */ /* pxenv_unload_stack */
+
+/** @defgroup pxenv_get_cached_info PXENV_GET_CACHED_INFO
+ *
+ * GET CACHED INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_get_cached_info() */
+#define PXENV_GET_CACHED_INFO 0x0071
+
+/** The client's DHCPDISCOVER packet */
+#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+
+/** The DHCP server's DHCPACK packet */
+#define PXENV_PACKET_TYPE_DHCP_ACK 2
+
+/** The Boot Server's Discover Reply packet
+ *
+ * This packet contains DHCP option 60 set to "PXEClient", a valid
+ * boot file name, and may or may not contain MTFTP options.
+ */
+#define PXENV_PACKET_TYPE_CACHED_REPLY 3
+
+/** Parameter block for pxenv_get_cached_info() */
+struct s_PXENV_GET_CACHED_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Packet type.
+ *
+ * Valid values are #PXENV_PACKET_TYPE_DHCP_DISCOVER,
+ * #PXENV_PACKET_TYPE_DHCP_ACK or #PXENV_PACKET_TYPE_CACHED_REPLY
+ */
+ UINT16_t PacketType;
+ UINT16_t BufferSize; /**< Buffer size */
+ SEGOFF16_t Buffer; /**< Buffer address */
+ UINT16_t BufferLimit; /**< Maximum buffer size */
+} PACKED;
+
+typedef struct s_PXENV_GET_CACHED_INFO PXENV_GET_CACHED_INFO_t;
+
+#define BOOTP_REQ 1 /**< A BOOTP request packet */
+#define BOOTP_REP 2 /**< A BOOTP reply packet */
+
+/** DHCP broadcast flag
+ *
+ * Request a broadcast response (DHCPOFFER or DHCPACK) from the DHCP
+ * server.
+ */
+#define BOOTP_BCAST 0x8000
+
+#define VM_RFC1048 0x63825363L /**< DHCP magic cookie */
+
+/** Maximum length of DHCP options */
+#define BOOTP_DHCPVEND 1024
+
+/** Format of buffer filled in by pxenv_get_cached_info()
+ *
+ * This somewhat convoluted data structure simply describes the layout
+ * of a DHCP packet. Refer to RFC2131 section 2 for a full
+ * description.
+ */
+struct bootph {
+ /** Message opcode.
+ *
+ * Valid values are #BOOTP_REQ and #BOOTP_REP.
+ */
+ UINT8_t opcode;
+ /** NIC hardware type.
+ *
+ * Valid values are as for s_PXENV_UNDI_GET_INFORMATION::HwType.
+ */
+ UINT8_t Hardware;
+ UINT8_t Hardlen; /**< MAC address length */
+ /** Gateway hops
+ *
+ * Zero in packets sent by the client. May be non-zero in
+ * replies from the DHCP server, if the reply comes via a DHCP
+ * relay agent.
+ */
+ UINT8_t Gatehops;
+ UINT32_t ident; /**< DHCP transaction id (xid) */
+ /** Elapsed time
+ *
+ * Number of seconds since the client began the DHCP
+ * transaction.
+ */
+ UINT16_t seconds;
+ /** Flags
+ *
+ * This is the bitwise-OR of any of the following values:
+ * #BOOTP_BCAST.
+ */
+ UINT16_t Flags;
+ /** Client IP address
+ *
+ * Set only if the client already has an IP address.
+ */
+ IP4_t cip;
+ /** Your IP address
+ *
+ * This is the IP address that the server assigns to the
+ * client.
+ */
+ IP4_t yip;
+ /** Server IP address
+ *
+ * This is the IP address of the BOOTP/DHCP server.
+ */
+ IP4_t sip;
+ /** Gateway IP address
+ *
+ * This is the IP address of the BOOTP/DHCP relay agent, if
+ * any. It is @b not (necessarily) the address of the default
+ * gateway for routing purposes.
+ */
+ IP4_t gip;
+ MAC_ADDR_t CAddr; /**< Client MAC address */
+ UINT8_t Sname[64]; /**< Server host name */
+ UINT8_t bootfile[128]; /**< Boot file name */
+ /** DHCP options
+ *
+ * Don't ask. Just laugh. Then burn a copy of the PXE
+ * specification and send Intel an e-mail asking them if
+ * they've figured out what a "union" does in C yet.
+ */
+ union bootph_vendor {
+ UINT8_t d[BOOTP_DHCPVEND]; /**< DHCP options */
+ /** DHCP options */
+ struct bootph_vendor_v {
+ /** DHCP magic cookie
+ *
+ * Should have the value #VM_RFC1048.
+ */
+ UINT8_t magic[4];
+ UINT32_t flags; /**< BOOTP flags/opcodes */
+ /** "End of BOOTP vendor extensions"
+ *
+ * Abandon hope, all ye who consider the
+ * purpose of this field.
+ */
+ UINT8_t pad[56];
+ } v;
+ } vendor;
+} PACKED;
+
+typedef struct bootph BOOTPLAYER_t;
+
+extern PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
+ *get_cached_info );
+
+/** @} */ /* pxenv_get_cached_info */
+
+/** @defgroup pxenv_restart_tftp PXENV_RESTART_TFTP
+ *
+ * RESTART TFTP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_restart_tftp() */
+#define PXENV_RESTART_TFTP 0x0073
+
+/** Parameter block for pxenv_restart_tftp() */
+struct s_PXENV_TFTP_READ_FILE;
+
+typedef struct s_PXENV_RESTART_TFTP PXENV_RESTART_TFTP_t;
+
+extern PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
+ *restart_tftp );
+
+/** @} */ /* pxenv_restart_tftp */
+
+/** @defgroup pxenv_start_undi PXENV_START_UNDI
+ *
+ * START UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_undi() */
+#define PXENV_START_UNDI 0x0000
+
+/** Parameter block for pxenv_start_undi() */
+struct s_PXENV_START_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** %ax register as passed to the Option ROM initialisation routine.
+ *
+ * For a PCI device, this should contain the bus:dev:fn value
+ * that uniquely identifies the PCI device in the system. For
+ * a non-PCI device, this field is not defined.
+ */
+ UINT16_t AX;
+ /** %bx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the Card Select
+ * Number assigned to the ISAPnP card. For non-ISAPnP
+ * devices, this should contain 0xffff.
+ */
+ UINT16_t BX;
+ /** %dx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the ISAPnP Read
+ * Port address as currently set in all ISAPnP cards. If
+ * there are no ISAPnP cards, this should contain 0xffff. (If
+ * this is a non-ISAPnP device, but there are ISAPnP cards in
+ * the system, this value is not well defined.)
+ */
+ UINT16_t DX;
+ /** %di register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #OFF16_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #OFF16_t and #UINT16_t are
+ * equivalent anyway; for other architectures #OFF16_t makes
+ * more sense.
+ */
+ OFF16_t DI;
+ /** %es register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #SEGSEL_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t ES;
+} PACKED;
+
+typedef struct s_PXENV_START_UNDI PXENV_START_UNDI_t;
+
+extern PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi );
+
+/** @} */ /* pxenv_start_undi */
+
+/** @defgroup pxenv_stop_undi PXENV_STOP_UNDI
+ *
+ * STOP UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_undi() */
+#define PXENV_STOP_UNDI 0x0015
+
+/** Parameter block for pxenv_stop_undi() */
+struct s_PXENV_STOP_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_STOP_UNDI PXENV_STOP_UNDI_t;
+
+extern PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi );
+
+/** @} */ /* pxenv_stop_undi */
+
+/** @defgroup pxenv_start_base PXENV_START_BASE
+ *
+ * START BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_base() */
+#define PXENV_START_BASE 0x0075
+
+/** Parameter block for pxenv_start_base() */
+struct s_PXENV_START_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_START_BASE PXENV_START_BASE_t;
+
+extern PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base );
+
+/** @} */ /* pxenv_start_base */
+
+/** @defgroup pxenv_stop_base PXENV_STOP_BASE
+ *
+ * STOP BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_base() */
+#define PXENV_STOP_BASE 0x0076
+
+/** Parameter block for pxenv_stop_base() */
+struct s_PXENV_STOP_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_STOP_BASE PXENV_STOP_BASE_t;
+
+extern PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base );
+
+/** @} */ /* pxenv_stop_base */
+
+/** @} */ /* pxe_preboot_api */
+
+/** @defgroup pxe_tftp_api PXE TFTP API
+ *
+ * Download files via TFTP or MTFTP
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_tftp_open PXENV_TFTP_OPEN
+ *
+ * TFTP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_open() */
+#define PXENV_TFTP_OPEN 0x0020
+
+/** Parameter block for pxenv_tftp_open() */
+struct s_PXENV_TFTP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UDP_PORT_t TFTPPort; /**< TFTP server UDP port */
+ /** Requested size of TFTP packets
+ *
+ * This is the TFTP "blksize" option. This must be at least
+ * 512, since servers that do not support TFTP options cannot
+ * negotiate blocksizes smaller than this.
+ */
+ UINT16_t PacketSize;
+} PACKED;
+
+typedef struct s_PXENV_TFTP_OPEN PXENV_TFTP_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open );
+
+/** @} */ /* pxenv_tftp_open */
+
+/** @defgroup pxenv_tftp_close PXENV_TFTP_CLOSE
+ *
+ * TFTP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_close() */
+#define PXENV_TFTP_CLOSE 0x0021
+
+/** Parameter block for pxenv_tftp_close() */
+struct s_PXENV_TFTP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_CLOSE PXENV_TFTP_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close );
+
+/** @} */ /* pxenv_tftp_close */
+
+/** @defgroup pxenv_tftp_read PXENV_TFTP_READ
+ *
+ * TFTP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read() */
+#define PXENV_TFTP_READ 0x0022
+
+/** Parameter block for pxenv_tftp_read() */
+struct s_PXENV_TFTP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t PacketNumber; /**< TFTP packet number */
+ UINT16_t BufferSize; /**< Size of data buffer */
+ SEGOFF16_t Buffer; /**< Address of data buffer */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_READ PXENV_TFTP_READ_t;
+
+extern PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read );
+
+/** @} */ /* pxenv_tftp_read */
+
+/** @defgroup pxenv_tftp_read_file PXENV_TFTP_READ_FILE
+ *
+ * TFTP/MTFTP READ FILE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read_file() */
+#define PXENV_TFTP_READ_FILE 0x0023
+
+/** Parameter block for pxenv_tftp_read_file() */
+struct s_PXENV_TFTP_READ_FILE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t BufferSize; /**< Size of data buffer */
+ ADDR32_t Buffer; /**< Address of data buffer */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ /** File multicast IP address */
+ IP4_t McastIPAddress;
+ /** Client multicast listening port */
+ UDP_PORT_t TFTPClntPort;
+ /** Server multicast listening port */
+ UDP_PORT_t TFTPSrvPort;
+ /** TFTP open timeout.
+ *
+ * This is the timeout for receiving the first DATA or ACK
+ * packets during the MTFTP Listen phase.
+ */
+ UINT16_t TFTPOpenTimeOut;
+ /** TFTP reopen timeout.
+ *
+ * This is the timeout for receiving an ACK packet while in
+ * the MTFTP Listen phase (when at least one ACK packet has
+ * already been seen).
+ */
+ UINT16_t TFTPReopenDelay;
+} PACKED;
+
+typedef struct s_PXENV_TFTP_READ_FILE PXENV_TFTP_READ_FILE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file );
+
+/** @} */ /* pxenv_tftp_read_file */
+
+/** @defgroup pxenv_tftp_get_fsize PXENV_TFTP_GET_FSIZE
+ *
+ * TFTP GET FILE SIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_get_fsize() */
+#define PXENV_TFTP_GET_FSIZE 0x0025
+
+/** Parameter block for pxenv_tftp_get_fsize() */
+struct s_PXENV_TFTP_GET_FSIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t FileSize; /**< Size of the file */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_GET_FSIZE PXENV_TFTP_GET_FSIZE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+ *get_fsize );
+
+/** @} */ /* pxenv_tftp_get_fsize */
+
+/** @} */ /* pxe_tftp_api */
+
+/** @defgroup pxe_udp_api PXE UDP API
+ *
+ * Transmit and receive UDP packets
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_udp_open PXENV_UDP_OPEN
+ *
+ * UDP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_open() */
+#define PXENV_UDP_OPEN 0x0030
+
+/** Parameter block for pxenv_udp_open() */
+struct s_PXENV_UDP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< IP address of this station */
+} PACKED;
+
+typedef struct s_PXENV_UDP_OPEN PXENV_UDP_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open );
+
+/** @} */ /* pxenv_udp_open */
+
+/** @defgroup pxenv_udp_close PXENV_UDP_CLOSE
+ *
+ * UDP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_close() */
+#define PXENV_UDP_CLOSE 0x0031
+
+/** Parameter block for pxenv_udp_close() */
+struct s_PXENV_UDP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UDP_CLOSE PXENV_UDP_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close );
+
+/** @} */ /* pxenv_udp_close */
+
+/** @defgroup pxenv_udp_write PXENV_UDP_WRITE
+ *
+ * UDP WRITE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_write() */
+#define PXENV_UDP_WRITE 0x0033
+
+/** Parameter block for pxenv_udp_write() */
+struct s_PXENV_UDP_WRITE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ip; /**< Destination IP address */
+ IP4_t gw; /**< Relay agent IP address */
+ UDP_PORT_t src_port; /**< Source UDP port */
+ UDP_PORT_t dst_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} PACKED;
+
+typedef struct s_PXENV_UDP_WRITE PXENV_UDP_WRITE_t;
+
+extern PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write );
+
+/** @} */ /* pxenv_udp_write */
+
+/** @defgroup pxenv_udp_read PXENV_UDP_READ
+ *
+ * UDP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_read() */
+#define PXENV_UDP_READ 0x0032
+
+/** Parameter block for pxenv_udp_read() */
+struct s_PXENV_UDP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< Source IP address */
+ IP4_t dest_ip; /**< Destination IP address */
+ UDP_PORT_t s_port; /**< Source UDP port */
+ UDP_PORT_t d_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} PACKED;
+
+typedef struct s_PXENV_UDP_READ PXENV_UDP_READ_t;
+
+extern PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read );
+
+/** @} */ /* pxenv_udp_read */
+
+/** @} */ /* pxe_udp_api */
+
+/** @defgroup pxe_undi_api PXE UNDI API
+ *
+ * Direct control of the network interface card
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_undi_startup PXENV_UNDI_STARTUP
+ *
+ * UNDI STARTUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_startup() */
+#define PXENV_UNDI_STARTUP 0x0001
+
+#define PXENV_BUS_ISA 0 /**< ISA bus type */
+#define PXENV_BUS_EISA 1 /**< EISA bus type */
+#define PXENV_BUS_MCA 2 /**< MCA bus type */
+#define PXENV_BUS_PCI 3 /**< PCI bus type */
+#define PXENV_BUS_VESA 4 /**< VESA bus type */
+#define PXENV_BUS_PCMCIA 5 /**< PCMCIA bus type */
+
+/** Parameter block for pxenv_undi_startup() */
+struct s_PXENV_UNDI_STARTUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_STARTUP PXENV_UNDI_STARTUP_t;
+
+extern PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP
+ *undi_startup );
+
+/** @} */ /* pxenv_undi_startup */
+
+/** @defgroup pxenv_undi_cleanup PXENV_UNDI_CLEANUP
+ *
+ * UNDI CLEANUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_cleanup() */
+#define PXENV_UNDI_CLEANUP 0x0002
+
+/** Parameter block for pxenv_undi_cleanup() */
+struct s_PXENV_UNDI_CLEANUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLEANUP PXENV_UNDI_CLEANUP_t;
+
+extern PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP
+ *undi_cleanup );
+
+/** @} */ /* pxenv_undi_cleanup */
+
+/** @defgroup pxenv_undi_initialize PXENV_UNDI_INITIALIZE
+ *
+ * UNDI INITIALIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initialize() */
+#define PXENV_UNDI_INITIALIZE 0x0003
+
+/** Parameter block for pxenv_undi_initialize() */
+struct s_PXENV_UNDI_INITIALIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NDIS 2.0 configuration information, or NULL
+ *
+ * This is a pointer to the data structure returned by the
+ * NDIS 2.0 GetProtocolManagerInfo() API call. The data
+ * structure is documented, in a rather haphazard way, in
+ * section 4-17 of the NDIS 2.0 specification.
+ */
+ ADDR32_t ProtocolIni;
+ UINT8_t reserved[8]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_INITIALIZE PXENV_UNDI_INITIALIZE_t;
+
+extern PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
+ *undi_initialize );
+
+/** @} */ /* pxenv_undi_initialize */
+
+/** @defgroup pxenv_undi_reset_adapter PXENV_UNDI_RESET_ADAPTER
+ *
+ * UNDI RESET ADAPTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_reset_adapter() */
+#define PXENV_UNDI_RESET_ADAPTER 0x0004
+
+/** Maximum number of multicast MAC addresses */
+#define MAXNUM_MCADDR 8
+
+/** List of multicast MAC addresses */
+struct s_PXENV_UNDI_MCAST_ADDRESS {
+ /** Number of multicast MAC addresses */
+ UINT16_t MCastAddrCount;
+ /** List of up to #MAXNUM_MCADDR multicast MAC addresses */
+ MAC_ADDR_t McastAddr[MAXNUM_MCADDR];
+} PACKED;
+
+typedef struct s_PXENV_UNDI_MCAST_ADDRESS PXENV_UNDI_MCAST_ADDRESS_t;
+
+/** Parameter block for pxenv_undi_reset_adapter() */
+struct s_PXENV_UNDI_RESET {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_RESET PXENV_UNDI_RESET_t;
+
+extern PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
+ *undi_reset_adapter );
+
+/** @} */ /* pxenv_undi_reset_adapter */
+
+/** @defgroup pxenv_undi_shutdown PXENV_UNDI_SHUTDOWN
+ *
+ * UNDI SHUTDOWN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_shutdown() */
+#define PXENV_UNDI_SHUTDOWN 0x0005
+
+/** Parameter block for pxenv_undi_shutdown() */
+struct s_PXENV_UNDI_SHUTDOWN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SHUTDOWN PXENV_UNDI_SHUTDOWN_t;
+
+extern PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
+ *undi_shutdown );
+
+/** @} */ /* pxenv_undi_shutdown */
+
+/** @defgroup pxenv_undi_open PXENV_UNDI_OPEN
+ *
+ * UNDI OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_open() */
+#define PXENV_UNDI_OPEN 0x0006
+
+/** Accept "directed" packets
+ *
+ * These are packets addresses to either this adapter's MAC address or
+ * to any of the configured multicast MAC addresses (see
+ * #s_PXENV_UNDI_MCAST_ADDRESS).
+ */
+#define FLTR_DIRECTED 0x0001
+/** Accept broadcast packets */
+#define FLTR_BRDCST 0x0002
+/** Accept all packets; listen in promiscuous mode */
+#define FLTR_PRMSCS 0x0004
+/** Accept source-routed packets */
+#define FLTR_SRC_RTG 0x0008
+
+/** Parameter block for pxenv_undi_open() */
+struct s_PXENV_UNDI_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Open flags as defined in NDIS 2.0
+ *
+ * This is the OpenOptions field as passed to the NDIS 2.0
+ * OpenAdapter() API call. It is defined to be "adapter
+ * specific", though 0 is guaranteed to be a valid value.
+ */
+ UINT16_t OpenFlag;
+ /** Receive packet filter
+ *
+ * This is the bitwise-OR of any of the following flags:
+ * #FLTR_DIRECTED, #FLTR_BRDCST, #FLTR_PRMSCS and
+ * #FLTR_SRC_RTG.
+ */
+ UINT16_t PktFilter;
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_OPEN PXENV_UNDI_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open );
+
+/** @} */ /* pxenv_undi_open */
+
+/** @defgroup pxenv_undi_close PXENV_UNDI_CLOSE
+ *
+ * UNDI CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_close() */
+#define PXENV_UNDI_CLOSE 0x0007
+
+/** Parameter block for pxenv_undi_close() */
+struct s_PXENV_UNDI_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLOSE PXENV_UNDI_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close );
+
+/** @} */ /* pxenv_undi_close */
+
+/** @defgroup pxenv_undi_transmit PXENV_UNDI_TRANSMIT
+ *
+ * UNDI TRANSMIT PACKET
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_transmit() */
+#define PXENV_UNDI_TRANSMIT 0x0008
+
+#define P_UNKNOWN 0 /**< Media header already filled in */
+#define P_IP 1 /**< IP protocol */
+#define P_ARP 2 /**< ARP protocol */
+#define P_RARP 3 /**< RARP protocol */
+#define P_OTHER 4 /**< Other protocol */
+
+#define XMT_DESTADDR 0x0000 /**< Unicast packet */
+#define XMT_BROADCAST 0x0001 /**< Broadcast packet */
+
+/** Maximum number of data blocks in a transmit buffer descriptor */
+#define MAX_DATA_BLKS 8
+
+/** A transmit buffer descriptor, as pointed to by s_PXENV_UNDI_TRANSMIT::TBD
+ */
+struct s_PXENV_UNDI_TBD {
+ UINT16_t ImmedLength; /**< Length of the transmit buffer */
+ SEGOFF16_t Xmit; /**< Address of the transmit buffer */
+ UINT16_t DataBlkCount;
+ /** Array of up to #MAX_DATA_BLKS additional transmit buffers */
+ struct DataBlk {
+ /** Always 1
+ *
+ * A value of 0 would indicate that #TDDataPtr were an
+ * #ADDR32_t rather than a #SEGOFF16_t. The PXE
+ * specification version 2.1 explicitly states that
+ * this is not supported; #TDDataPtr will always be a
+ * #SEGOFF16_t.
+ */
+ UINT8_t TDPtrType;
+ UINT8_t TDRsvdByte; /**< Must be zero */
+ UINT16_t TDDataLen; /**< Length of this transmit buffer */
+ SEGOFF16_t TDDataPtr; /**< Address of this transmit buffer */
+ } DataBlock[MAX_DATA_BLKS];
+} PACKED;
+
+typedef struct s_PXENV_UNDI_TBD PXENV_UNDI_TBD_t;
+
+/** Parameter block for pxenv_undi_transmit() */
+struct s_PXENV_UNDI_TRANSMIT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Protocol
+ *
+ * Valid values are #P_UNKNOWN, #P_IP, #P_ARP or #P_RARP. If
+ * the caller has already filled in the media header, this
+ * field must be set to #P_UNKNOWN.
+ */
+ UINT8_t Protocol;
+ /** Unicast/broadcast flag
+ *
+ * Valid values are #XMT_DESTADDR or #XMT_BROADCAST.
+ */
+ UINT8_t XmitFlag;
+ SEGOFF16_t DestAddr; /**< Destination MAC address */
+ /** Address of the Transmit Buffer Descriptor
+ *
+ * This is a pointer to a struct s_PXENV_UNDI_TBD.
+ */
+ SEGOFF16_t TBD;
+ UINT32_t Reserved[2]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_TRANSMIT PXENV_UNDI_TRANSMIT_t;
+
+extern PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
+ *undi_transmit );
+
+/** @} */ /* pxenv_undi_transmit */
+
+/** @defgroup pxenv_undi_set_mcast_address PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * UNDI SET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_mcast_address() */
+#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
+
+/** Parameter block for pxenv_undi_set_mcast_address() */
+struct s_PXENV_UNDI_SET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** List of multicast addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS PXENV_UNDI_SET_MCAST_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_mcast_address (
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS *undi_set_mcast_address );
+
+/** @} */ /* pxenv_undi_set_mcast_address */
+
+/** @defgroup pxenv_undi_set_station_address PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * UNDI SET STATION ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_station_address() */
+#define PXENV_UNDI_SET_STATION_ADDRESS 0x000a
+
+/** Parameter block for pxenv_undi_set_station_address() */
+struct s_PXENV_UNDI_SET_STATION_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ MAC_ADDR_t StationAddress; /**< Station MAC address */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS PXENV_UNDI_SET_STATION_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_station_address (
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS *undi_set_station_address );
+
+/** @} */ /* pxenv_undi_set_station_address */
+
+/** @defgroup pxenv_undi_set_packet_filter PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * UNDI SET PACKET FILTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_packet_filter() */
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000b
+
+/** Parameter block for pxenv_undi_set_packet_filter() */
+struct s_PXENV_UNDI_SET_PACKET_FILTER {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Receive packet filter
+ *
+ * This field takes the same values as
+ * s_PXENV_UNDI_OPEN::PktFilter.
+ *
+ * @note Yes, this field is a different size to
+ * s_PXENV_UNDI_OPEN::PktFilter. Blame "the managers at Intel
+ * who apparently let a consultant come up with the spec
+ * without any kind of adult supervision" (quote from hpa).
+ */
+ UINT8_t filter;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_PACKET_FILTER PXENV_UNDI_SET_PACKET_FILTER_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_packet_filter (
+ struct s_PXENV_UNDI_SET_PACKET_FILTER *undi_set_packet_filter );
+
+/** @} */ /* pxenv_undi_set_packet_filter */
+
+/** @defgroup pxenv_undi_get_information PXENV_UNDI_GET_INFORMATION
+ *
+ * UNDI GET INFORMATION
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_information() */
+#define PXENV_UNDI_GET_INFORMATION 0x000c
+
+#define ETHER_TYPE 1 /**< Ethernet (10Mb) */
+#define EXP_ETHER_TYPE 2 /**< Experimental Ethernet (3Mb) */
+#define AX25_TYPE 3 /**< Amateur Radio AX.25 */
+#define TOKEN_RING_TYPE 4 /**< Proteon ProNET Token Ring */
+#define CHAOS_TYPE 5 /**< Chaos */
+#define IEEE_TYPE 6 /**< IEEE 802 Networks */
+#define ARCNET_TYPE 7 /**< ARCNET */
+
+/** Parameter block for pxenv_undi_get_information() */
+struct s_PXENV_UNDI_GET_INFORMATION {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t BaseIo; /**< I/O base address */
+ UINT16_t IntNumber; /**< IRQ number */
+ UINT16_t MaxTranUnit; /**< Adapter MTU */
+ /** Hardware type
+ *
+ * Valid values are defined in RFC1010 ("Assigned numbers"),
+ * and are #ETHER_TYPE, #EXP_ETHER_TYPE, #AX25_TYPE,
+ * #TOKEN_RING_TYPE, #CHAOS_TYPE, #IEEE_TYPE or #ARCNET_TYPE.
+ */
+ UINT16_t HwType;
+ UINT16_t HwAddrLen; /**< MAC address length */
+ MAC_ADDR_t CurrentNodeAddress; /**< Current MAC address */
+ MAC_ADDR_t PermNodeAddress; /**< Permanent (EEPROM) MAC address */
+ SEGSEL_t ROMAddress; /**< Real-mode ROM segment address */
+ UINT16_t RxBufCt; /**< Receive queue length */
+ UINT16_t TxBufCt; /**< Transmit queue length */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_INFORMATION PXENV_UNDI_GET_INFORMATION_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_information (
+ struct s_PXENV_UNDI_GET_INFORMATION *undi_get_information );
+
+/** @} */ /* pxenv_undi_get_information */
+
+/** @defgroup pxenv_undi_get_statistics PXENV_UNDI_GET_STATISTICS
+ *
+ * UNDI GET STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_statistics() */
+#define PXENV_UNDI_GET_STATISTICS 0x000d
+
+/** Parameter block for pxenv_undi_get_statistics() */
+struct s_PXENV_UNDI_GET_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT32_t XmtGoodFrames; /**< Successful transmission count */
+ UINT32_t RcvGoodFrames; /**< Successful reception count */
+ UINT32_t RcvCRCErrors; /**< Receive CRC error count */
+ UINT32_t RcvResourceErrors; /**< Receive queue overflow count */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_STATISTICS PXENV_UNDI_GET_STATISTICS_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_statistics (
+ struct s_PXENV_UNDI_GET_STATISTICS *undi_get_statistics );
+
+/** @} */ /* pxenv_undi_get_statistics */
+
+/** @defgroup pxenv_undi_clear_statistics PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * UNDI CLEAR STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_clear_statistics() */
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000e
+
+/** Parameter block for pxenv_undi_clear_statistics() */
+struct s_PXENV_UNDI_CLEAR_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLEAR_STATISTICS PXENV_UNDI_CLEAR_STATISTICS_t;
+
+extern PXENV_EXIT_t pxenv_undi_clear_statistics (
+ struct s_PXENV_UNDI_CLEAR_STATISTICS *undi_clear_statistics );
+
+/** @} */ /* pxenv_undi_clear_statistics */
+
+/** @defgroup pxenv_undi_initiate_diags PXENV_UNDI_INITIATE_DIAGS
+ *
+ * UNDI INITIATE DIAGS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initiate_diags() */
+#define PXENV_UNDI_INITIATE_DIAGS 0x000f
+
+/** Parameter block for pxenv_undi_initiate_diags() */
+struct s_PXENV_UNDI_INITIATE_DIAGS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_INITIATE_DIAGS PXENV_UNDI_INITIATE_DIAGS_t;
+
+extern PXENV_EXIT_t pxenv_undi_initiate_diags (
+ struct s_PXENV_UNDI_INITIATE_DIAGS *undi_initiate_diags );
+
+/** @} */ /* pxenv_undi_initiate_diags */
+
+/** @defgroup pxenv_undi_force_interrupt PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * UNDI FORCE INTERRUPT
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_force_interrupt() */
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+
+/** Parameter block for pxenv_undi_force_interrupt() */
+struct s_PXENV_UNDI_FORCE_INTERRUPT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_FORCE_INTERRUPT PXENV_UNDI_FORCE_INTERRUPT_t;
+
+extern PXENV_EXIT_t pxenv_undi_force_interrupt (
+ struct s_PXENV_UNDI_FORCE_INTERRUPT *undi_force_interrupt );
+
+/** @} */ /* pxenv_undi_force_interrupt */
+
+/** @defgroup pxenv_undi_get_mcast_address PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * UNDI GET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_mcast_address() */
+#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
+
+/** Parameter block for pxenv_undi_get_mcast_address() */
+struct s_PXENV_UNDI_GET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t InetAddr; /**< Multicast IP address */
+ MAC_ADDR_t MediaAddr; /**< Multicast MAC address */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS PXENV_UNDI_GET_MCAST_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_mcast_address (
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS *undi_get_mcast_address );
+
+/** @} */ /* pxenv_undi_get_mcast_address */
+
+/** @defgroup pxenv_undi_get_nic_type PXENV_UNDI_GET_NIC_TYPE
+ *
+ * UNDI GET NIC TYPE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_nic_type() */
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+
+#define PCI_NIC 2 /**< PCI network card */
+#define PnP_NIC 3 /**< ISAPnP network card */
+#define CardBus_NIC 4 /**< CardBus network card */
+
+/** Information for a PCI or equivalent NIC */
+struct pci_nic_info {
+ UINT16_t Vendor_ID; /**< PCI vendor ID */
+ UINT16_t Dev_ID; /**< PCI device ID */
+ UINT8_t Base_Class; /**< PCI base class */
+ UINT8_t Sub_Class; /**< PCI sub class */
+ UINT8_t Prog_Intf; /**< PCI programming interface */
+ UINT8_t Rev; /**< PCI revision */
+ UINT16_t BusDevFunc; /**< PCI bus:dev:fn address */
+ UINT16_t SubVendor_ID; /**< PCI subvendor ID */
+ UINT16_t SubDevice_ID; /**< PCI subdevice ID */
+} PACKED;
+
+/** Information for an ISAPnP or equivalent NIC */
+struct pnp_nic_info {
+ UINT32_t EISA_Dev_ID; /**< EISA device ID */
+ UINT8_t Base_Class; /**< Base class */
+ UINT8_t Sub_Class; /**< Sub class */
+ UINT8_t Prog_Intf; /**< Programming interface */
+ /** Card Select Number assigned to card */
+ UINT16_t CardSelNum;
+} PACKED;
+
+/** Parameter block for pxenv_undi_get_nic_type() */
+struct s_PXENV_UNDI_GET_NIC_TYPE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NIC type
+ *
+ * Valid values are #PCI_NIC, #PnP_NIC or #CardBus_NIC.
+ */
+ UINT8_t NicType;
+ /** NIC information */
+ union nic_type_info {
+ /** NIC information (if #NicType==#PCI_NIC) */
+ struct pci_nic_info pci;
+ /** NIC information (if #NicType==#CardBus_NIC) */
+ struct pci_nic_info cardbus;
+ /** NIC information (if #NicType==#PnP_NIC) */
+ struct pnp_nic_info pnp;
+ } info;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_NIC_TYPE PXENV_UNDI_GET_NIC_TYPE_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_nic_type (
+ struct s_PXENV_UNDI_GET_NIC_TYPE *undi_get_nic_type );
+
+/** @} */ /* pxenv_undi_get_nic_type */
+
+/** @defgroup pxenv_undi_get_iface_info PXENV_UNDI_GET_IFACE_INFO
+ *
+ * UNDI GET IFACE INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_iface_info() */
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+
+/** Parameter block for pxenv_undi_get_iface_info() */
+struct s_PXENV_UNDI_GET_IFACE_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Interface type
+ *
+ * This is defined in the NDIS 2.0 specification to be one of
+ * the strings "802.3", "802.4", "802.5", "802.6", "DIX",
+ * "DIX+802.3", "APPLETALK", "ARCNET", "FDDI", "SDLC", "BSC",
+ * "HDLC", or "ISDN".
+ *
+ * "Normal" Ethernet, for various historical reasons, is
+ * "DIX+802.3".
+ */
+ UINT8_t IfaceType[16];
+ UINT32_t LinkSpeed; /**< Link speed, in bits per second */
+ /** Service flags
+ *
+ * These are the "service flags" defined in the "MAC
+ * Service-Specific Characteristics" table in the NDIS 2.0
+ * specification. Almost all of them are irrelevant to PXE.
+ */
+ UINT32_t ServiceFlags;
+ UINT32_t Reserved[4]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_IFACE_INFO PXENV_UNDI_GET_IFACE_INFO_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_iface_info (
+ struct s_PXENV_UNDI_GET_IFACE_INFO *undi_get_iface_info );
+
+/** @} */ /* pxenv_undi_get_iface_info */
+
+/** @defgroup pxenv_undi_get_state PXENV_UNDI_GET_STATE
+ *
+ * UNDI GET STATE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_state() */
+#define PXENV_UNDI_GET_STATE 0x0015
+
+/** pxenv_start_undi() has been called */
+#define PXE_UNDI_GET_STATE_STARTED 1
+/** pxenv_undi_initialize() has been called */
+#define PXE_UNDI_GET_STATE_INITIALIZED 2
+/** pxenv_undi_open() has been called */
+#define PXE_UNDI_GET_STATE_OPENED 3
+
+/** Parameter block for pxenv_undi_get_state() */
+struct s_PXENV_UNDI_GET_STATE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Current state of the UNDI driver
+ *
+ * Valid values are #PXE_UNDI_GET_STATE_STARTED,
+ * #PXE_UNDI_GET_STATE_INITIALIZED or
+ * #PXE_UNDI_GET_STATE_OPENED.
+ */
+ UINT8_t UNDIstate;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_STATE PXENV_UNDI_GET_STATE_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
+ *undi_get_state );
+
+/** @} */ /* pxenv_undi_get_state */
+
+/** @defgroup pxenv_undi_isr PXENV_UNDI_ISR
+ *
+ * UNDI ISR
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_isr() */
+#define PXENV_UNDI_ISR 0x0014
+
+/** Determine whether or not this is our interrupt */
+#define PXENV_UNDI_ISR_IN_START 1
+/** Start processing interrupt */
+#define PXENV_UNDI_ISR_IN_PROCESS 2
+/** Continue processing interrupt */
+#define PXENV_UNDI_ISR_IN_GET_NEXT 3
+/** This interrupt was ours */
+#define PXENV_UNDI_ISR_OUT_OURS 0
+/** This interrupt was not ours */
+#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+/** Finished processing interrupt */
+#define PXENV_UNDI_ISR_OUT_DONE 0
+/** A packet transmission has completed */
+#define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+/** A packet has been received */
+#define PXENV_UNDI_ISR_OUT_RECEIVE 3
+/** We are already in the middle of processing an interrupt */
+#define PXENV_UNDI_ISR_OUT_BUSY 4
+
+/** Unicast packet (or packet captured in promiscuous mode) */
+#define P_DIRECTED 0
+/** Broadcast packet */
+#define P_BROADCAST 1
+/** Multicast packet */
+#define P_MULTICAST 2
+
+/** Parameter block for pxenv_undi_isr() */
+struct s_PXENV_UNDI_ISR {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Function flag
+ *
+ * Valid values are #PXENV_UNDI_ISR_IN_START,
+ * #PXENV_UNDI_ISR_IN_PROCESS, #PXENV_UNDI_ISR_IN_GET_NEXT,
+ * #PXENV_UNDI_ISR_OUT_OURS, #PXENV_UNDI_ISR_OUT_NOT_OURS,
+ * #PXENV_UNDI_ISR_OUT_DONE, #PXENV_UNDI_ISR_OUT_TRANSMIT,
+ * #PXENV_UNDI_ISR_OUT_RECEIVE or #PXENV_UNDI_ISR_OUT_BUSY.
+ */
+ UINT16_t FuncFlag;
+ UINT16_t BufferLength; /**< Data buffer length */
+ UINT16_t FrameLength; /**< Total frame length */
+ UINT16_t FrameHeaderLength; /**< Frame header length */
+ SEGOFF16_t Frame; /**< Data buffer address */
+ /** Protocol type
+ *
+ * Valid values are #P_IP, #P_ARP, #P_RARP or #P_OTHER.
+ */
+ UINT8_t ProtType;
+ /** Packet type
+ *
+ * Valid values are #P_DIRECTED, #P_BROADCAST or #P_MULTICAST.
+ */
+ UINT8_t PktType;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_ISR PXENV_UNDI_ISR_t;
+
+extern PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr );
+
+/** @} */ /* pxenv_undi_isr */
+
+/** @} */ /* pxe_undi_api */
+
+/** @defgroup pxe_loader_api PXE Loader API
+ *
+ * The UNDI ROM loader API
+ *
+ * @{
+ */
+
+/** The UNDI ROM ID structure */
+struct s_UNDI_ROM_ID {
+ /** Signature
+ *
+ * Contains the bytes 'U', 'N', 'D', 'I'.
+ */
+ UINT32_t Signature;
+ UINT8_t StructLength; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #StructLength) must be zero.
+ */
+ UINT8_t StructCksum;
+ /** Revision of this structure
+ *
+ * For PXE version 2.1, this field must be zero.
+ */
+ UINT8_t StructRev;
+ /** UNDI revision
+ *
+ * UNDI revision, least significant byte first. For UNDI
+ * version 2.1.0, this field will contain { 0x00, 0x01, 0x02 }.
+ */
+ UINT8_t UNDIRev[3];
+ /** UNDI loader routine entry point
+ *
+ * This is the entry point for calling undi_loader().
+ */
+ UINT16_t UNDILoader;
+ /** Minimum required stack segment size */
+ UINT16_t StackSize;
+ /** Minimum required data segment size */
+ UINT16_t DataSize;
+ /** Minimum required code segment size */
+ UINT16_t CodeSize;
+} PACKED;
+
+typedef struct s_UNDI_ROM_ID UNDI_ROM_ID_t;
+
+/** Parameter block for undi_loader() */
+struct s_UNDI_LOADER {
+ /** struct s_UNDI_LOADER starts with a struct s_PXENV_START_UNDI */
+ union undi_loader_start_undi {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Parameters to pass to pxenv_start_undi() */
+ struct s_PXENV_START_UNDI start_undi;
+ } u;
+ /** UNDI data segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t undi_ds;
+ /** UNDI code segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t undi_cs;
+ /** Address of the !PXE structure (a struct s_PXE) */
+ SEGOFF16_t pxe_ptr;
+ /** Address of the PXENV+ structure (a struct s_PXENV) */
+ SEGOFF16_t pxenv_ptr;
+} PACKED;
+
+typedef struct s_UNDI_LOADER UNDI_LOADER_t;
+
+extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
+
+/** @} */ /* pxe_loader_api */
+
+/** @} */ /* pxe */
+
+/** @page pxe_notes Etherboot PXE implementation notes
+
+@section pxe_routing IP routing
+
+Several PXE API calls (e.g. pxenv_tftp_open() and pxenv_udp_write())
+allow for the caller to specify a "relay agent IP address", often in a
+field called "gateway" or similar. The PXE specification states that
+"The IP layer should provide space for a minimum of four routing
+entries obtained from the default router and static route DHCP option
+tags in the DHCPACK message, plus any non-zero giaddr field from the
+DHCPOFFER message(s) accepted by the client".
+
+The DHCP static route option ("option static-routes" in dhcpd.conf)
+works only for classed IP routing (i.e. it provides no way to specify
+a subnet mask). Since virtually everything now uses classless IP
+routing, the DHCP static route option is almost totally useless, and
+is (according to the dhcp-options man page) not implemented by any of
+the popular DHCP clients.
+
+This leaves the caller-specified "relay agent IP address", the giaddr
+field from the DHCPOFFER message(s) and the default gateway(s)
+provided via the routers option ("option routers" in dhcpd.conf) in
+the DHCPACK message. Each of these is a default gateway address.
+It's a fair bet that the routers option should take priority over the
+giaddr field, since the routers option has to be explicitly specified
+by the DHCP server operator. Similarly, it's fair to assume that the
+caller-specified "relay agent IP address", if present, should take
+priority over any other routing table entries.
+
+@bug Etherboot currently ignores all potential sources of routing
+information other than the first router provided to it by a DHCP
+routers option.
+
+@section pxe_x86_modes x86 processor mode restrictions
+
+On the x86 platform, different PXE API calls have different
+restrictions on the processor modes (real or protected) that can be
+used. See the individual API call descriptions for the restrictions
+that apply to any particular call.
+
+@subsection pxe_x86_pmode16 Real mode, or protected-mode with 16-bit stack
+
+The PXE specification states that the API function can be called in
+protected mode only if the s_PXE::StatusCallout field is set to a
+non-zero value, and that the API function cannot be called with a
+32-bit stack segment.
+
+Etherboot does not enforce either of these restrictions; they seem (as
+with so much of the PXE specification) to be artifacts of the Intel
+implementation.
+
+*/
+
+#endif /* PXE_API_H */
diff --git a/src/include/pxe_export.h b/src/include/pxe_export.h
deleted file mode 100644
index 3d39e73c..00000000
--- a/src/include/pxe_export.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Header for pxe_export.c
- */
-
-#ifndef PXE_EXPORT_H
-#define PXE_EXPORT_H
-
-#include "pxe.h"
-
-/* Function prototypes */
-extern int ensure_pxe_state ( pxe_stack_state_t wanted );
-
-extern PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI * );
-extern PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP * );
-extern PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP * );
-extern PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE * );
-extern PXENV_EXIT_t pxenv_undi_reset_adapter ( t_PXENV_UNDI_RESET_ADAPTER * );
-extern PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN * );
-extern PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN * );
-extern PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE * );
-extern PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT * );
-extern PXENV_EXIT_t pxenv_undi_set_mcast_address (
- t_PXENV_UNDI_SET_MCAST_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_set_station_address (
- t_PXENV_UNDI_SET_STATION_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_set_packet_filter (
- t_PXENV_UNDI_SET_PACKET_FILTER * );
-extern PXENV_EXIT_t pxenv_undi_get_information (
- t_PXENV_UNDI_GET_INFORMATION * );
-extern PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS* );
-extern PXENV_EXIT_t pxenv_undi_clear_statistics (
- t_PXENV_UNDI_CLEAR_STATISTICS * );
-extern PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS* );
-extern PXENV_EXIT_t pxenv_undi_force_interrupt (
- t_PXENV_UNDI_FORCE_INTERRUPT * );
-extern PXENV_EXIT_t pxenv_undi_get_mcast_address (
- t_PXENV_UNDI_GET_MCAST_ADDRESS * );
-extern PXENV_EXIT_t pxenv_undi_get_nic_type ( t_PXENV_UNDI_GET_NIC_TYPE * );
-extern PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO *);
-extern PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR * );
-extern PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI * );
-extern PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN * );
-extern PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE * );
-extern PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ * );
-extern PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE * );
-extern PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE * );
-extern PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN * );
-extern PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE * );
-extern PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ * );
-extern PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE * );
-extern PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK * );
-extern PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO * );
-extern PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP * );
-extern PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE * );
-extern PXENV_EXIT_t pxenv_stop_base ( t_PXENV_STOP_BASE * );
-
-extern PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params );
-
-/* Static variables */
-extern pxe_stack_t *pxe_stack;
-
-#endif /* PXE_EXPORT_H */
diff --git a/src/include/pxe_types.h b/src/include/pxe_types.h
new file mode 100644
index 00000000..80e6aa17
--- /dev/null
+++ b/src/include/pxe_types.h
@@ -0,0 +1,126 @@
+#ifndef PXE_TYPES_H
+#define PXE_TYPES_H
+
+/** @file
+ *
+ * PXE data types
+ *
+ */
+
+#include "stdint.h"
+#include "pxe_addr.h" /* Architecture-specific PXE definitions */
+#include "errno.h" /* PXE status codes */
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_types PXE data types
+ *
+ * Basic PXE data types such as #UINT16_t, #ADDR32_t, #SEGSEL_t etc.
+ *
+ * These definitions are based on Table 1-1 ("Data Type Definitions")
+ * in the Intel PXE specification version 2.1. They have been
+ * generalised to non-x86 architectures where possible.
+ *
+ * @{
+ */
+
+/** An 8-bit unsigned integer */
+typedef uint8_t UINT8_t;
+
+/** A 16-bit unsigned integer */
+typedef uint16_t UINT16_t;
+
+/** A 32-bit unsigned integer */
+typedef uint32_t UINT32_t;
+
+/** A PXE exit code.
+ *
+ * Permitted values are #PXENV_EXIT_SUCCESS and #PXENV_EXIT_FAILURE.
+ *
+ */
+typedef UINT16_t PXENV_EXIT_t;
+#define PXENV_EXIT_SUCCESS 0x0000 /**< No error occurred */
+#define PXENV_EXIT_FAILURE 0x0001 /**< An error occurred */
+
+/** A PXE status code.
+ *
+ * Status codes are defined in errno.h.
+ *
+ */
+typedef UINT16_t PXENV_STATUS_t;
+
+/** An IPv4 address.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT32_t IP4_t;
+
+/** A UDP port.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT16_t UDP_PORT_t;
+
+/** Maximum length of a MAC address */
+#define MAC_ADDR_LEN 16
+
+/** A MAC address */
+typedef UINT8_t MAC_ADDR_t[MAC_ADDR_LEN];
+
+#ifndef HAVE_ARCH_ADDR32
+/** A physical address.
+ *
+ * For x86, this is a 32-bit physical address, and is therefore
+ * limited to the low 4GB.
+ *
+ */
+typedef UINT32_t ADDR32_t;
+#endif
+
+#ifndef HAVE_ARCH_SEGSEL
+/** A segment selector.
+ *
+ * For x86, this is a real mode segment (0x0000-0xffff), or a
+ * protected-mode segment selector, such as could be loaded into a
+ * segment register.
+ *
+ */
+typedef UINT16_t SEGSEL_t;
+#endif
+
+#ifndef HAVE_ARCH_OFF16
+/** An offset within a segment identified by #SEGSEL
+ *
+ * For x86, this is a 16-bit offset.
+ *
+ */
+typedef UINT16_t OFF16_t;
+#endif
+
+/** A segment:offset address
+ *
+ * For x86, this is a 16-bit real-mode or protected-mode
+ * segment:offset address.
+ *
+ */
+typedef struct s_SEGOFF16 {
+ OFF16_t offset; /**< Offset within the segment */
+ SEGSEL_t segment; /**< Segment selector */
+} PACKED SEGOFF16_t;
+
+/** A segment descriptor */
+typedef struct s_SEGDESC {
+ SEGSEL_t segment_address; /**< Segment selector */
+ ADDR32_t physical_address; /**< Segment base address */
+ OFF16_t seg_size; /**< Size of the segment */
+} PACKED SEGDESC_t;
+
+/** @} */ /* pxe_types */
+
+/** @} */ /* pxe */
+
+#endif /* PXE_TYPES_H */
diff --git a/src/include/tables.h b/src/include/tables.h
index 26ad61db..c3e05e55 100644
--- a/src/include/tables.h
+++ b/src/include/tables.h
@@ -1,47 +1,171 @@
#ifndef TABLES_H
#define TABLES_H
-/*
- * Macros for dealing with linker-generated tables of fixed-size
- * symbols. We make fairly extensive use of these in order to avoid
- * ifdef spaghetti and/or linker symbol pollution. For example,
- * instead of having code such as
+/** @page ifdef_harmful #ifdef considered harmful
+ *
+ * Overuse of @c #ifdef has long been a problem in Etherboot.
+ * Etherboot provides a rich array of features, but all these features
+ * take up valuable space in a ROM image. The traditional solution to
+ * this problem has been for each feature to have its own @c #ifdef
+ * option, allowing the feature to be compiled in only if desired.
+ *
+ * The problem with this is that it becomes impossible to compile, let
+ * alone test, all possible versions of Etherboot. Code that is not
+ * typically used tends to suffer from bit-rot over time. It becomes
+ * extremely difficult to predict which combinations of compile-time
+ * options will result in code that can even compile and link
+ * correctly.
+ *
+ * To solve this problem, we have adopted a new approach from
+ * Etherboot 5.5 onwards. @c #ifdef is now "considered harmful", and
+ * its use should be minimised. Separate features should be
+ * implemented in separate @c .c files, and should \b always be
+ * compiled (i.e. they should \b not be guarded with a @c #ifdef @c
+ * MY_PET_FEATURE statement). By making (almost) all code always
+ * compile, we avoid the problem of bit-rot in rarely-used code.
+ *
+ * The file config.h, in combination with the @c make command line,
+ * specifies the objects that will be included in any particular build
+ * of Etherboot. For example, suppose that config.h includes the line
+ *
+ * @code
+ *
+ * #define CONSOLE_SERIAL
+ * #define DOWNLOAD_PROTO_TFTP
+ *
+ * @endcode
+ *
+ * When a particular Etherboot image (e.g. @c bin/rtl8139.zdsk) is
+ * built, the options specified in config.h are used to drag in the
+ * relevant objects at link-time. For the above example, serial.o and
+ * tftp.o would be linked in.
+ *
+ * There remains one problem to solve: how do these objects get used?
+ * Traditionally, we had code such as
+ *
+ * @code
+ *
+ * #ifdef CONSOLE_SERIAL
+ * serial_init();
+ * #endif
+ *
+ * @endcode
+ *
+ * in main.c, but this reintroduces @c #ifdef and so is a Bad Idea.
+ * We cannot simply remove the @c #ifdef and make it
+ *
+ * @code
*
- * #ifdef CONSOLE_SERIAL
* serial_init();
- * #endif
+ *
+ * @endcode
+ *
+ * because then serial.o would end up always being linked in.
+ *
+ * The solution is to use @link tables.h linker tables @endlink.
+ *
+ */
+
+/** @file
+ *
+ * Linker tables
+ *
+ * Read @ref ifdef_harmful first for some background on the motivation
+ * for using linker tables.
+ *
+ * This file provides macros for dealing with linker-generated tables
+ * of fixed-size symbols. We make fairly extensive use of these in
+ * order to avoid @c #ifdef spaghetti and/or linker symbol pollution.
+ * For example, instead of having code such as
+ *
+ * @code
+ *
+ * #ifdef CONSOLE_SERIAL
+ * serial_init();
+ * #endif
+ *
+ * @endcode
*
* we make serial.c generate an entry in the initialisation function
* table, and then have a function call_init_fns() that simply calls
* all functions present in this table. If and only if serial.o gets
* linked in, then its initialisation function will be called. We
* avoid linker symbol pollution (i.e. always dragging in serial.o
- * just because of a call to serial_init()) and we also avoid ifdef
- * spaghetti (having to conditionalise every reference to functions in
- * serial.c).
+ * just because of a call to serial_init()) and we also avoid @c
+ * #ifdef spaghetti (having to conditionalise every reference to
+ * functions in serial.c).
*
* The linker script takes care of assembling the tables for us. All
- * our table sections have names of the format ".tbl.NAME.NN" where
- * NAME designates the data structure stored in the table
- * (e.g. "init_fn") and NN is a two-digit decimal number used to
- * impose an ordering upon the tables if required. NN=00 is reserved
- * for the symbol indicating "table start", and NN=99 is reserved for
- * the symbol indicating "table end".
+ * our table sections have names of the format @c .tbl.NAME.NN where
+ * @c NAME designates the data structure stored in the table (e.g. @c
+ * init_fn) and @c NN is a two-digit decimal number used to impose an
+ * ordering upon the tables if required. @c NN=00 is reserved for the
+ * symbol indicating "table start", and @c NN=99 is reserved for the
+ * symbol indicating "table end".
+ *
+ * As an example, suppose that we want to create a "frobnicator"
+ * feature framework, and allow for several independent modules to
+ * provide frobnicating services. Then we would create a frob.h
+ * header file containing e.g.
+ *
+ * @code
+ *
+ * struct frobnicator {
+ * const char *name; // Name of the frobnicator
+ * void ( *frob ) ( void ); // The frobnicating function itself
+ * };
+ *
+ * #define __frobnicator __table ( frobnicators, 01 )
+ *
+ * @endcode
*
- * To define an entry in the "xxx" table:
+ * Any module providing frobnicating services would look something
+ * like
*
- * static struct xxx my_xxx __table(xxx,01) = { ... };
+ * @code
*
- * To access start and end markers for the "xxx" table:
+ * #include "frob.h"
*
- * static struct xxx xxx_start[0] __table_start(xxx);
- * static struct xxx xxx_end[0] __table_end(xxx);
+ * static void my_frob ( void ) {
+ * // Do my frobnicating
+ * ...
+ * }
*
- * See init.h and init.c for an example of how these macros are used
- * in practice.
+ * static struct frob my_frobnicator __frobnicator = {
+ * .name = "my_frob",
+ * .frob = my_frob,
+ * };
+ *
+ * @endcode
+ *
+ * The central frobnicator code (frob.c) would use the frobnicating
+ * modules as follows
+ *
+ * @code
+ *
+ * #include "frob.h"
+ *
+ * static struct frob frob_start[0] __table_start ( frobnicators );
+ * static struct frob frob_end[0] __table_end ( frobnicators );
+ *
+ * // Call all linked-in frobnicators
+ * void frob_all ( void ) {
+ * struct frob *frob;
+ *
+ * for ( frob = frob_start ; frob < frob_end ; frob++ ) {
+ * printf ( "Calling frobnicator \"%s\"\n", frob->name );
+ * frob->frob ();
+ * }
+ * }
+ *
+ * @endcode
+ *
+ * See init.h and init.c for a real-life example.
*
*/
+#include "compiler.h" /* for doxygen */
+
#define __table_str(x) #x
#define __table_section(table,idx) \
__section__ ( ".tbl." __table_str(table) "." __table_str(idx) )
@@ -49,10 +173,54 @@
#define __table_section_start(table) __table_section(table,00)
#define __table_section_end(table) __table_section(table,99)
+
+/**
+ * Linker table entry.
+ *
+ * Declares a data structure to be part of a linker table. Use as
+ * e.g.
+ *
+ * @code
+ *
+ * static struct my_foo __table ( foo, 01 ) = {
+ * ...
+ * };
+ *
+ * @endcode
+ *
+ */
#define __table(table,idx) \
__attribute__ (( unused, __table_section(table,idx) ))
+
+/**
+ * Linker table start marker.
+ *
+ * Declares a data structure (usually an empty data structure) to be
+ * the start of a linker table. Use as e.g.
+ *
+ * @code
+ *
+ * static struct foo_start[0] __table_start ( foo );
+ *
+ * @endcode
+ *
+ */
#define __table_start(table) \
__attribute__ (( unused, __table_section_start(table) ))
+
+/**
+ * Linker table end marker.
+ *
+ * Declares a data structure (usually an empty data structure) to be
+ * the end of a linker table. Use as e.g.
+ *
+ * @code
+ *
+ * static struct foo_end[0] __table_end ( foo );
+ *
+ * @endcode
+ *
+ */
#define __table_end(table) \
__attribute__ (( unused, __table_section_end(table) ))
diff --git a/src/include/tftp.h b/src/include/tftp.h
index 70192067..2a086eb2 100644
--- a/src/include/tftp.h
+++ b/src/include/tftp.h
@@ -1,13 +1,17 @@
#ifndef TFTP_H
#define TFTP_H
+/** @file */
+
#include "in.h"
#include "buffer.h"
#include "nic.h"
+#include "ip.h"
+#include "udp.h"
-#define TFTP_PORT 69
-#define TFTP_DEFAULTSIZE_PACKET 512
-#define TFTP_MAX_PACKET 1432 /* 512 */
+#define TFTP_PORT 69 /**< Default TFTP server port */
+#define TFTP_DEFAULT_BLKSIZE 512
+#define TFTP_MAX_BLKSIZE 1432 /* 512 */
#define TFTP_RRQ 1
#define TFTP_WRQ 2
@@ -16,53 +20,131 @@
#define TFTP_ERROR 5
#define TFTP_OACK 6
-#define TFTP_CODE_EOF 1
-#define TFTP_CODE_MORE 2
-#define TFTP_CODE_ERROR 3
-#define TFTP_CODE_BOOT 4
-#define TFTP_CODE_CFG 5
+#define TFTP_ERR_FILE_NOT_FOUND 1 /**< File not found */
+#define TFTP_ERR_ACCESS_DENIED 2 /**< Access violation */
+#define TFTP_ERR_DISK_FULL 3 /**< Disk full or allocation exceeded */
+#define TFTP_ERR_ILLEGAL_OP 4 /**< Illegal TFTP operation */
+#define TFTP_ERR_UNKNOWN_TID 5 /**< Unknown transfer ID */
+#define TFTP_ERR_FILE_EXISTS 6 /**< File already exists */
+#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */
+#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */
+
+/** A TFTP request (RRQ) packet */
+struct tftp_rrq {
+ struct iphdr ip;
+ struct udphdr udp;
+ uint16_t opcode;
+ char data[TFTP_DEFAULT_BLKSIZE];
+} PACKED;
+
+/** A TFTP data (DATA) packet */
+struct tftp_data {
+ struct iphdr ip;
+ struct udphdr udp;
+ uint16_t opcode;
+ uint16_t block;
+ uint8_t data[TFTP_MAX_BLKSIZE];
+} PACKED;
+
+/** A TFTP acknowledgement (ACK) packet */
+struct tftp_ack {
+ struct iphdr ip;
+ struct udphdr udp;
+ uint16_t opcode;
+ uint16_t block;
+} PACKED;
+
+/** A TFTP error (ERROR) packet */
+struct tftp_error {
+ struct iphdr ip;
+ struct udphdr udp;
+ uint16_t opcode;
+ uint16_t errcode;
+ char errmsg[TFTP_DEFAULT_BLKSIZE];
+} PACKED;
-struct tftp_t {
+/** A TFTP options acknowledgement (OACK) packet */
+struct tftp_oack {
struct iphdr ip;
struct udphdr udp;
uint16_t opcode;
- union {
- uint8_t rrq[TFTP_DEFAULTSIZE_PACKET];
- struct {
- uint16_t block;
- uint8_t download[TFTP_MAX_PACKET];
- } data;
- struct {
- uint16_t block;
- } ack;
- struct {
- uint16_t errcode;
- uint8_t errmsg[TFTP_DEFAULTSIZE_PACKET];
- } err;
- struct {
- uint8_t data[TFTP_DEFAULTSIZE_PACKET+2];
- } oack;
- } u;
+ uint8_t data[TFTP_DEFAULT_BLKSIZE];
} PACKED;
-/* define a smaller tftp packet solely for making requests to conserve stack
- 512 bytes should be enough */
-struct tftpreq_t {
+/** The common header of all TFTP packets */
+struct tftp_common {
struct iphdr ip;
struct udphdr udp;
uint16_t opcode;
- union {
- uint8_t rrq[512];
- struct {
- uint16_t block;
- } ack;
- struct {
- uint16_t errcode;
- uint8_t errmsg[512-2];
- } err;
- } u;
} PACKED;
+/** A union encapsulating all TFTP packet types */
+union tftp_any {
+ struct tftp_common common;
+ struct tftp_rrq rrq;
+ struct tftp_data data;
+ struct tftp_ack ack;
+ struct tftp_error error;
+ struct tftp_oack oack;
+};
+
+/**
+ * TFTP state
+ *
+ * This data structure holds the state for an ongoing TFTP transfer.
+ */
+struct tftp_state {
+ /** TFTP server address
+ *
+ * This is the IP address and UDP port from which data packets
+ * will be sent, and to which ACK packets should be sent.
+ */
+ struct sockaddr_in server;
+ /** TFTP client port
+ *
+ * This is the UDP port from which the open request will be
+ * sent, and to which any unicast data packets will be sent.
+ */
+ in_port_t lport;
+ /** TFTP multicast address
+ *
+ * This is the IP address and UDP port to which multicast data
+ * packets, if any, will be sent.
+ */
+ struct sockaddr_in multicast;
+ /** Master client
+ *
+ * This will be true if the client is the master client for a
+ * multicast protocol (i.e. MTFTP or TFTM). (It will always
+ * be true for a non-multicast protocol, i.e. plain old TFTP).
+ */
+ int master;
+ /** Data block size
+ *
+ * This is the "blksize" option negotiated with the TFTP
+ * server. (If the TFTP server does not support TFTP options,
+ * this will default to 512).
+ */
+ unsigned int blksize;
+ /** File size
+ *
+ * This is the value returned in the "tsize" option from the
+ * TFTP server. If the TFTP server does not support the
+ * "tsize" option, this value will be zero.
+ */
+ off_t tsize;
+ /** Last received block
+ *
+ * The block number of the most recent block received from the
+ * TFTP server. Note that the first data block is block 1; a
+ * value of 0 indicates that no data blocks have yet been
+ * received.
+ */
+ unsigned int block;
+};
+
+
+
struct tftpreq_info_t {
struct sockaddr_in *server;
const char *name;
@@ -78,13 +160,4 @@ struct tftpblk_info_t {
#define TFTP_MIN_PACKET (sizeof(struct iphdr) + sizeof(struct udphdr) + 4)
-/*
- * Functions in tftp.c. Needed for pxe_export.c
- *
- */
-extern int tftp_block ( struct tftpreq_info_t *request,
- struct tftpblk_info_t *block );
-extern int tftp ( char *url, struct sockaddr_in *server, char *file,
- struct buffer *buffer );
-
#endif /* TFTP_H */
diff --git a/src/include/tftpcore.h b/src/include/tftpcore.h
new file mode 100644
index 00000000..52f63bd9
--- /dev/null
+++ b/src/include/tftpcore.h
@@ -0,0 +1,33 @@
+#ifndef TFTPCORE_H
+#define TFTPCORE_H
+
+/** @file
+ *
+ * TFTP core functions
+ *
+ * This file provides functions that are common to the TFTP (rfc1350),
+ * TFTM (rfc2090) and MTFTP (PXE) protocols.
+ *
+ */
+
+#include "tftp.h"
+
+extern int tftp_open ( struct tftp_state *state, const char *filename,
+ union tftp_any **reply, int multicast );
+
+extern int tftp_process_opts ( struct tftp_state *state,
+ struct tftp_oack *oack );
+
+extern int tftp_ack_nowait ( struct tftp_state *state );
+
+extern int tftp_get ( struct tftp_state *state, long timeout,
+ union tftp_any **reply );
+
+extern int tftp_ack ( struct tftp_state *state, union tftp_any **reply );
+
+extern int tftp_error ( struct tftp_state *state, int errcode,
+ const char *errmsg );
+
+extern void tftp_set_errno ( struct tftp_error *error );
+
+#endif /* TFTPCORE_H */
diff --git a/src/include/vsprintf.h b/src/include/vsprintf.h
index 2bf20085..553ebeb5 100644
--- a/src/include/vsprintf.h
+++ b/src/include/vsprintf.h
@@ -1,10 +1,46 @@
#ifndef VSPRINTF_H
#define VSPRINTF_H
-/*
- * Note that we cannot use __attribute__ (( format ( printf, ... ) ))
- * to get automatic type checking on arguments, because we use
- * non-standard format characters such as "%!" and "%@".
+/** @file
+ *
+ * printf and friends.
+ *
+ * Etherboot's printf() functions understand the following format
+ * specifiers:
+ *
+ * - Hexadecimal integers
+ * - @c %[#]x - 4 bytes int (8 hex digits, lower case)
+ * - @c %[#]X - 4 bytes int (8 hex digits, upper case)
+ * - @c %[#]lx - 8 bytes long (16 hex digits, lower case)
+ * - @c %[#]lX - 8 bytes long (16 hex digits, upper case)
+ * - @c %[#]hx - 2 bytes int (4 hex digits, lower case)
+ * - @c %[#]hX - 2 bytes int (4 hex digits, upper case)
+ * - @c %[#]hhx - 1 byte int (2 hex digits, lower case)
+ * - @c %[#]hhX - 1 byte int (2 hex digits, upper case)
+ * .
+ * If the optional # prefix is specified, the output will
+ * be prefixed with 0x (or 0X).
+ *
+ * - Other integers
+ * - @c %d - decimal int
+ * .
+ * Note that any width specification (e.g. the @c 02 in @c %02x)
+ * will be accepted but ignored.
+ *
+ * - Strings and characters
+ * - @c %c - char
+ * - @c %s - string
+ * - @c %m - error message text (i.e. strerror(errno))
+ *
+ * - Etherboot-specific specifiers
+ * - @c %@ - IP address in ddd.ddd.ddd.ddd notation
+ * - @c %! - MAC address in xx:xx:xx:xx:xx:xx notation
+ *
+ *
+ * @note Unfortunately, we cannot use <tt> __attribute__ (( format (
+ * printf, ... ) )) </tt> to get automatic type checking on arguments,
+ * because we use non-standard format characters such as @c %! and
+ * @c %@.
*
*/
diff --git a/src/interface/pxe/pxe.c b/src/interface/pxe/pxe.c
new file mode 100644
index 00000000..06e6121f
--- /dev/null
+++ b/src/interface/pxe/pxe.c
@@ -0,0 +1,311 @@
+/** @file
+ *
+ *
+ *
+ */
+
+/*
+ * 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 "pxe.h"
+
+/* Global pointer to currently installed PXE stack */
+pxe_stack_t *pxe_stack = NULL;
+
+/* Various startup/shutdown routines. The startup/shutdown call
+ * sequence is incredibly badly defined in the Intel PXE spec, for
+ * example:
+ *
+ * PXENV_UNDI_INITIALIZE says that the parameters used to initialize
+ * the adaptor should be those supplied to the most recent
+ * PXENV_UNDI_STARTUP call. PXENV_UNDI_STARTUP takes no parameters.
+ *
+ * PXENV_UNDI_CLEANUP says that the rest of the API will not be
+ * available after making this call. Figure 3-3 ("Early UNDI API
+ * usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
+ * call to the supposedly now unavailable PXENV_STOP_UNDI.
+ *
+ * PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
+ * occupied by the PXE stack. Figure 4-3 ("PXE IPL") shows a call
+ * to PXENV_STOP_UNDI being made after the call to
+ * PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
+ * should have been freed (and, potentially, zeroed).
+ *
+ * Nothing, anywhere, seems to mention who's responsible for freeing
+ * up the base memory allocated for the stack segment. It's not
+ * even clear whether or not this is expected to be in free base
+ * memory rather than claimed base memory.
+ *
+ * Consequently, we adopt a rather defensive strategy, designed to
+ * work with any conceivable sequence of initialisation or shutdown
+ * calls. We have only two things that we care about:
+ *
+ * 1. Have we hooked INT 1A and INT 15,E820(etc.)?
+ * 2. Is the NIC initialised?
+ *
+ * The NIC should never be initialised without the vectors being
+ * hooked, similarly the vectors should never be unhooked with the NIC
+ * still initialised. We do, however, want to be able to have the
+ * vectors hooked with the NIC shutdown. We therefore have three
+ * possible states:
+ *
+ * 1. Ready to unload: interrupts unhooked, NIC shutdown.
+ * 2. Midway: interrupts hooked, NIC shutdown.
+ * 3. Fully ready: interrupts hooked, NIC initialised.
+ *
+ * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
+ * these, and the call pxe_ensure_state() to ensure that the stack is
+ * in the specified state. All our PXE API call implementations
+ * should use this call to ensure that the state is as required for
+ * that PXE API call. This enables us to cope with whatever the
+ * end-user's interpretation of the PXE spec may be. It even allows
+ * for someone calling e.g. PXENV_START_UNDI followed by
+ * PXENV_UDP_WRITE, without bothering with any of the intervening
+ * calls.
+ *
+ * pxe_ensure_state() returns 1 for success, 0 for failure. In the
+ * event of failure (which can arise from e.g. asking for state READY
+ * when we don't know where our NIC is), the error code
+ * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
+ * The macros ENSURE_XXX() can be used to achieve this without lots of
+ * duplicated code.
+ */
+
+/* pxe_[un]hook_stack are architecture-specific and provided in
+ * pxe_callbacks.c
+ */
+
+int pxe_initialise_nic ( void ) {
+ if ( pxe_stack->state >= READY ) return 1;
+
+#warning "device probing mechanism has completely changed"
+#if 0
+
+ /* Check if NIC is initialised. dev.disable is set to 0
+ * when disable() is called, so we use this.
+ */
+ if ( dev.disable ) {
+ /* NIC may have been initialised independently
+ * (e.g. when we set up the stack prior to calling the
+ * NBP).
+ */
+ pxe_stack->state = READY;
+ return 1;
+ }
+
+ /* If we already have a NIC defined, reuse that one with
+ * PROBE_AWAKE. If one was specifed via PXENV_START_UNDI, try
+ * that one first. Otherwise, set PROBE_FIRST.
+ */
+
+ if ( dev.state.pci.dev.use_specified == 1 ) {
+ dev.how_probe = PROBE_NEXT;
+ DBG ( " initialising NIC specified via START_UNDI" );
+ } else if ( dev.state.pci.dev.driver ) {
+ DBG ( " reinitialising NIC" );
+ dev.how_probe = PROBE_AWAKE;
+ } else {
+ DBG ( " probing for any NIC" );
+ dev.how_probe = PROBE_FIRST;
+ }
+
+ /* Call probe routine to bring up the NIC */
+ if ( eth_probe ( &dev ) != PROBE_WORKED ) {
+ DBG ( " failed" );
+ return 0;
+ }
+#endif
+
+
+ pxe_stack->state = READY;
+ return 1;
+}
+
+int pxe_shutdown_nic ( void ) {
+ if ( pxe_stack->state <= MIDWAY ) return 1;
+
+ eth_irq ( DISABLE );
+ disable ( &dev );
+ pxe_stack->state = MIDWAY;
+ return 1;
+}
+
+int ensure_pxe_state ( pxe_stack_state_t wanted ) {
+ int success = 1;
+
+ if ( ! pxe_stack ) return 0;
+ if ( wanted >= MIDWAY )
+ success = success & hook_pxe_stack();
+ if ( wanted > MIDWAY ) {
+ success = success & pxe_initialise_nic();
+ } else {
+ success = success & pxe_shutdown_nic();
+ }
+ if ( wanted < MIDWAY )
+ success = success & unhook_pxe_stack();
+ return success;
+}
+
+/* API call dispatcher
+ *
+ * Status: complete
+ */
+PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any ) {
+ PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
+
+ /* Set default status in case child routine fails to do so */
+ any->Status = PXENV_STATUS_FAILURE;
+
+ DBG ( "[" );
+
+ /* Hand off to relevant API routine */
+ switch ( opcode ) {
+ case PXENV_START_UNDI:
+ ret = pxenv_start_undi ( &any->start_undi );
+ break;
+ case PXENV_UNDI_STARTUP:
+ ret = pxenv_undi_startup ( &any->undi_startup );
+ break;
+ case PXENV_UNDI_CLEANUP:
+ ret = pxenv_undi_cleanup ( &any->undi_cleanup );
+ break;
+ case PXENV_UNDI_INITIALIZE:
+ ret = pxenv_undi_initialize ( &any->undi_initialize );
+ break;
+ case PXENV_UNDI_RESET_ADAPTER:
+ ret = pxenv_undi_reset_adapter ( &any->undi_reset_adapter );
+ break;
+ case PXENV_UNDI_SHUTDOWN:
+ ret = pxenv_undi_shutdown ( &any->undi_shutdown );
+ break;
+ case PXENV_UNDI_OPEN:
+ ret = pxenv_undi_open ( &any->undi_open );
+ break;
+ case PXENV_UNDI_CLOSE:
+ ret = pxenv_undi_close ( &any->undi_close );
+ break;
+ case PXENV_UNDI_TRANSMIT:
+ ret = pxenv_undi_transmit ( &any->undi_transmit );
+ break;
+ case PXENV_UNDI_SET_MCAST_ADDRESS:
+ ret = pxenv_undi_set_mcast_address (
+ &any->undi_set_mcast_address );
+ break;
+ case PXENV_UNDI_SET_STATION_ADDRESS:
+ ret = pxenv_undi_set_station_address (
+ &any->undi_set_station_address );
+ break;
+ case PXENV_UNDI_SET_PACKET_FILTER:
+ ret = pxenv_undi_set_packet_filter (
+ &any->undi_set_packet_filter );
+ break;
+ case PXENV_UNDI_GET_INFORMATION:
+ ret = pxenv_undi_get_information (
+ &any->undi_get_information );
+ break;
+ case PXENV_UNDI_GET_STATISTICS:
+ ret = pxenv_undi_get_statistics ( &any->undi_get_statistics );
+ break;
+ case PXENV_UNDI_CLEAR_STATISTICS:
+ ret = pxenv_undi_clear_statistics (
+ &any->undi_clear_statistics );
+ break;
+ case PXENV_UNDI_INITIATE_DIAGS:
+ ret = pxenv_undi_initiate_diags ( &any->undi_initiate_diags );
+
+ break;
+ case PXENV_UNDI_FORCE_INTERRUPT:
+ ret = pxenv_undi_force_interrupt (
+ &any->undi_force_interrupt );
+ break;
+ case PXENV_UNDI_GET_MCAST_ADDRESS:
+ ret = pxenv_undi_get_mcast_address (
+ &any->undi_get_mcast_address );
+ break;
+ case PXENV_UNDI_GET_NIC_TYPE:
+ ret = pxenv_undi_get_nic_type ( &any->undi_get_nic_type );
+ break;
+ case PXENV_UNDI_GET_IFACE_INFO:
+ ret = pxenv_undi_get_iface_info ( &any->undi_get_iface_info );
+ break;
+ case PXENV_UNDI_ISR:
+ ret = pxenv_undi_isr ( &any->undi_isr );
+ break;
+ case PXENV_STOP_UNDI:
+ ret = pxenv_stop_undi ( &any->stop_undi );
+ break;
+ case PXENV_TFTP_OPEN:
+ ret = pxenv_tftp_open ( &any->tftp_open );
+ break;
+ case PXENV_TFTP_CLOSE:
+ ret = pxenv_tftp_close ( &any->tftp_close );
+ break;
+ case PXENV_TFTP_READ:
+ ret = pxenv_tftp_read ( &any->tftp_read );
+ break;
+ case PXENV_TFTP_READ_FILE:
+ ret = pxenv_tftp_read_file ( &any->tftp_read_file );
+ break;
+ case PXENV_TFTP_GET_FSIZE:
+ ret = pxenv_tftp_get_fsize ( &any->tftp_get_fsize );
+ break;
+ case PXENV_UDP_OPEN:
+ ret = pxenv_udp_open ( &any->udp_open );
+ break;
+ case PXENV_UDP_CLOSE:
+ ret = pxenv_udp_close ( &any->udp_close );
+ break;
+ case PXENV_UDP_READ:
+ ret = pxenv_udp_read ( &any->udp_read );
+ break;
+ case PXENV_UDP_WRITE:
+ ret = pxenv_udp_write ( &any->udp_write );
+ break;
+ case PXENV_UNLOAD_STACK:
+ ret = pxenv_unload_stack ( &any->unload_stack );
+ break;
+ case PXENV_GET_CACHED_INFO:
+ ret = pxenv_get_cached_info ( &any->get_cached_info );
+ break;
+ case PXENV_RESTART_TFTP:
+ ret = pxenv_restart_tftp ( &any->restart_tftp );
+ break;
+ case PXENV_START_BASE:
+ ret = pxenv_start_base ( &any->start_base );
+ break;
+ case PXENV_STOP_BASE:
+ ret = pxenv_stop_base ( &any->stop_base );
+ break;
+
+ default:
+ DBG ( "PXENV_UNKNOWN_%hx", opcode );
+ any->Status = PXENV_STATUS_UNSUPPORTED;
+ ret = PXENV_EXIT_FAILURE;
+ break;
+ }
+
+ if ( any->Status != PXENV_STATUS_SUCCESS ) {
+ DBG ( " %hx", any->Status );
+ }
+ if ( ret != PXENV_EXIT_SUCCESS ) {
+ DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
+ }
+ DBG ( "]" );
+
+ return ret;
+}
diff --git a/src/interface/pxe/pxe_errors.c b/src/interface/pxe/pxe_errors.c
new file mode 100644
index 00000000..581393e8
--- /dev/null
+++ b/src/interface/pxe/pxe_errors.c
@@ -0,0 +1,102 @@
+#include "errno.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.
+ *
+ */
+static 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/src/interface/pxe/pxe_loader.c b/src/interface/pxe/pxe_loader.c
new file mode 100644
index 00000000..2539aaeb
--- /dev/null
+++ b/src/interface/pxe/pxe_loader.c
@@ -0,0 +1,83 @@
+/** @file
+ *
+ * PXE UNDI loader
+ *
+ */
+
+/*
+ * 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 "pxe.h"
+
+/* PXENV_UNDI_LOADER
+ *
+ * Status: working
+ *
+ * NOTE: This is not a genuine PXE API call; the loader has a separate
+ * entry point. However, to simplify the mapping of the PXE API to
+ * the internal Etherboot API, both are directed through the same
+ * interface.
+ */
+PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
+ uint32_t loader_phys = virt_to_phys ( undi_loader );
+
+ DBG ( "PXENV_UNDI_LOADER" );
+
+ /* Set UNDI DS as our real-mode stack */
+ use_undi_ds_for_rm_stack ( undi_loader->undi_ds );
+
+ /* FIXME: These lines are borrowed from main.c. There should
+ * probably be a single initialise() function that does all
+ * this, but it's currently split interestingly between main()
+ * and main_loop()...
+ */
+
+
+ /* CHECKME: Our init functions have probably already been
+ called by the ROM prefix's call to setup(), haven't
+ they? */
+
+
+
+ /* We have relocated; the loader pointer is now invalid */
+ undi_loader = phys_to_virt ( loader_phys );
+
+ /* Install PXE stack to area specified by NBP */
+ install_pxe_stack ( VIRTUAL ( undi_loader->undi_cs, 0 ) );
+
+ /* Call pxenv_start_undi to set parameters. Why the hell PXE
+ * requires these parameters to be provided twice is beyond
+ * the wit of any sane man. Don't worry if it fails; the NBP
+ * should call PXENV_START_UNDI separately anyway.
+ */
+ pxenv_start_undi ( &undi_loader->u.start_undi );
+ /* Unhook stack; the loader is not meant to hook int 1a etc,
+ * but the call the pxenv_start_undi will cause it to happen.
+ */
+
+ /* FIXME: can't use ENSURE_CAN_UNLOAD() thanks to newer gcc's
+ * barfing on unnamed struct/unions. */
+ /* ENSURE_CAN_UNLOAD ( undi_loader ); */
+
+ /* Fill in addresses of !PXE and PXENV+ structures */
+ PTR_TO_SEGOFF16 ( &pxe_stack->pxe, undi_loader->pxe_ptr );
+ PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, undi_loader->pxenv_ptr );
+
+ undi_loader->u.Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/src/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c
new file mode 100644
index 00000000..bed32042
--- /dev/null
+++ b/src/interface/pxe/pxe_preboot.c
@@ -0,0 +1,249 @@
+/** @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 "pxe.h"
+#include "pxe_callbacks.h"
+
+/**
+ * UNLOAD BASE CODE STACK
+ *
+ * @v None -
+ * @ret ...
+ *
+ */
+PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
+ int success;
+
+ DBG ( "PXENV_UNLOAD_STACK" );
+ success = ensure_pxe_state ( CAN_UNLOAD );
+
+ /* We need to call cleanup() at some point. The network card
+ * has already been disabled by ENSURE_CAN_UNLOAD(), but for
+ * the sake of completeness we should call the console_fini()
+ * etc. that are part of cleanup().
+ *
+ * There seems to be a lack of consensus on which is the final
+ * PXE API call to make, but it's a fairly safe bet that all
+ * the potential shutdown sequences will include a call to
+ * PXENV_UNLOAD_STACK at some point, so we may as well do it
+ * here.
+ */
+ cleanup();
+
+ if ( ! success ) {
+ unload_stack->Status = PXENV_STATUS_KEEP_ALL;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ 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 ) {
+ BOOTPLAYER_t *cached_info = &pxe_stack->cached_info;
+ DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+ ENSURE_READY ( get_cached_info );
+
+ /* Fill in cached_info structure in our pxe_stack */
+
+ /* I don't think there's actually any way we can be called in
+ * the middle of a DHCP request...
+ */
+ cached_info->opcode = BOOTP_REP;
+ /* We only have Ethernet drivers */
+ cached_info->Hardware = ETHER_TYPE;
+ cached_info->Hardlen = ETH_ALEN;
+ /* PXESPEC: "Client sets" says the spec, but who's filling in
+ * this structure? It ain't the client.
+ */
+ cached_info->Gatehops = 0;
+ cached_info->ident = 0;
+ cached_info->seconds = 0;
+ cached_info->Flags = BOOTP_BCAST;
+ /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
+ cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
+ cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
+ cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
+ /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
+ cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+ memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
+ /* Nullify server name */
+ cached_info->Sname[0] = '\0';
+ memcpy ( cached_info->bootfile, KERNEL_BUF,
+ sizeof(cached_info->bootfile) );
+ /* Copy DHCP vendor options */
+ memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+ sizeof(cached_info->vendor.d) );
+
+ /* Copy to user-specified buffer, or set pointer to our buffer */
+ get_cached_info->BufferLimit = sizeof(*cached_info);
+ /* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
+ * 0, but what are we supposed to do with a null buffer of
+ * non-zero size?!
+ */
+ if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
+ /* Point back to our buffer */
+ PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
+ get_cached_info->BufferSize = sizeof(*cached_info);
+ } else {
+ /* Copy to user buffer */
+ size_t size = sizeof(*cached_info);
+ void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
+ if ( get_cached_info->BufferSize < size )
+ size = get_cached_info->BufferSize;
+ DBG ( " to %x", virt_to_phys ( buffer ) );
+ memcpy ( buffer, cached_info, size );
+ /* PXESPEC: Should we return an error if the user
+ * buffer is too small? We do return the actual size
+ * of the buffer via BufferLimit, so the user does
+ * have a way to detect this already.
+ */
+ }
+
+ get_cached_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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" );
+ ENSURE_READY ( restart_tftp );
+
+ /* Words cannot describe the complete mismatch between the PXE
+ * specification and any possible version of reality...
+ */
+ restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
+ restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
+ DBG ( "(" );
+ tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (union u_PXENV_ANY*)restart_tftp );
+ DBG ( ")" );
+ if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
+
+ /* Fire up the new NBP */
+ restart_tftp->Status = xstartpxe();
+
+ /* 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 char bus, devfn;
+
+ DBG ( "PXENV_START_UNDI" );
+ ENSURE_MIDWAY(start_undi);
+
+ /* Record PCI bus & devfn passed by caller, so we know which
+ * NIC they want to use.
+ *
+ * If they don't match our already-existing NIC structure, set
+ * values to ensure that the specified NIC is used at the next
+ * call to pxe_intialise_nic().
+ */
+ bus = ( start_undi->AX >> 8 ) & 0xff;
+ devfn = start_undi->AX & 0xff;
+
+#warning "device probing mechanism has completely changed"
+#if 0
+ if ( ( pci->dev.driver == NULL ) ||
+ ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
+ /* This is quite a bit of a hack and relies on
+ * knowledge of the internal operation of Etherboot's
+ * probe mechanism.
+ */
+ DBG ( " set PCI %hhx:%hhx.%hhx",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
+ dev->type = BOOT_NIC;
+ dev->to_probe = PROBE_PCI;
+ memset ( &dev->state, 0, sizeof(dev->state) );
+ pci->advance = 1;
+ pci->dev.use_specified = 1;
+ pci->dev.bus = bus;
+ pci->dev.devfn = devfn;
+ }
+#endif
+
+ 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" );
+
+ if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
+ stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ stop_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_BASE
+ *
+ * Status: won't implement (requires major structural changes)
+ */
+PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
+ DBG ( "PXENV_START_BASE" );
+ /* ENSURE_READY ( 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/src/interface/pxe/pxe_tftp.c b/src/interface/pxe/pxe_tftp.c
new file mode 100644
index 00000000..2d824e11
--- /dev/null
+++ b/src/interface/pxe/pxe_tftp.c
@@ -0,0 +1,623 @@
+/** @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 "pxe.h"
+
+static int pxe_tftp_read_block ( unsigned char *data, unsigned int block,
+ unsigned int len, int eof );
+
+/**
+ * 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.
+ *
+ * The blksize negotiated with the TFTP server will be returned in
+ * s_PXENV_TFTP_OPEN::PacketSize, and will be the size of data blocks
+ * returned by subsequent calls to pxenv_tftp_read(). The TFTP server
+ * may negotiate a smaller blksize than the caller requested.
+ *
+ * Some TFTP servers do not support TFTP options, and will therefore
+ * not be able to use anything other than a fixed 512-byte blksize.
+ * The PXE specification version 2.1 requires that the caller must
+ * pass in s_PXENV_TFTP_OPEN::PacketSize with a value of 512 or
+ * greater.
+ *
+ * You can only have one TFTP connection open at a time, because the
+ * PXE API requires the PXE stack to keep state (e.g. local and remote
+ * port numbers, data block index) about the open TFTP connection,
+ * rather than letting the caller do so.
+ *
+ * It is unclear precisely what constitutes a "TFTP open" operation.
+ * Clearly, we must send the TFTP open request to the server. Since
+ * we must know whether or not the open succeeded, we must wait for
+ * the first reply packet from the TFTP server. If the TFTP server
+ * supports options, the first reply packet will be an OACK; otherwise
+ * it will be a DATA packet. In other words, we may only get to
+ * discover whether or not the open succeeded when we receive the
+ * first block of data. However, the pxenv_tftp_open() API provides
+ * no way for us to return this block of data at this time. See the
+ * relevant @ref pxe_note_tftp "implementation note" for Etherboot's
+ * solution to this problem.
+ *
+ * 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 If you pass in a value less than 512 for
+ * s_PXENV_TFTP_OPEN::PacketSize, Etherboot will attempt to negotiate
+ * this blksize with the TFTP server, even though such a value is not
+ * permitted according to the PXE specification. If the TFTP server
+ * ends up dictating a blksize larger than the value requested by the
+ * caller (which is very probable in the case of a requested blksize
+ * less than 512), then Etherboot will return the error
+ * #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE.
+ *
+ * @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 ) {
+ struct sockaddr_in tftp_server;
+ struct tftpreq_info_t request;
+ struct tftpblk_info_t block;
+
+ DBG ( "PXENV_TFTP_OPEN" );
+ ENSURE_READY ( tftp_open );
+
+ /* Set server address and port */
+ tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
+ ? tftp_open->ServerIPAddress
+ : arptable[ARP_SERVER].ipaddr.s_addr;
+ tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
+#ifdef WORK_AROUND_BPBATCH_BUG
+ /* Force use of port 69; BpBatch tries to use port 4 for some
+ * bizarre reason. */
+ tftp_server.sin_port = TFTP_PORT;
+#endif
+ /* Ignore gateway address; we can route properly */
+ /* Fill in request structure */
+ request.server = &tftp_server;
+ request.name = tftp_open->FileName;
+ request.blksize = tftp_open->PacketSize;
+ DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
+ tftp_open->TFTPPort, request.name, request.blksize );
+ if ( !request.blksize ) request.blksize = TFTP_DEFAULT_BLKSIZE;
+ /* Make request and get first packet */
+ if ( !tftp_block ( &request, &block ) ) {
+ tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+ return PXENV_EXIT_FAILURE;
+ }
+ /* Fill in PacketSize */
+ tftp_open->PacketSize = request.blksize;
+ /* Store first block for later retrieval by TFTP_READ */
+ pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
+ pxe_stack->tftpdata.len = block.len;
+ pxe_stack->tftpdata.eof = block.eof;
+ memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
+
+ tftp_open->Status = PXENV_STATUS_SUCCESS;
+ return 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.)
+ *
+ * @note Since TFTP runs over UDP, which is a connectionless protocol,
+ * the concept of closing a file is somewhat meaningless. This call
+ * is a no-op for Etherboot.
+ */
+PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
+ DBG ( "PXENV_TFTP_CLOSE" );
+ ENSURE_READY ( tftp_close );
+ 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()).
+ *
+ * Nothing in the PXE specification indicates when the TFTP
+ * acknowledgement packets will be sent back to the server. See the
+ * relevant @ref pxe_note_tftp "implementation note" for details on
+ * when Etherboot chooses to send these packets.
+ *
+ * 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 ) {
+ struct tftpblk_info_t block;
+
+ DBG ( "PXENV_TFTP_READ" );
+ ENSURE_READY ( tftp_read );
+
+ /* Do we have a block pending */
+ if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
+ block.data = pxe_stack->tftpdata.data;
+ block.len = pxe_stack->tftpdata.len;
+ block.eof = pxe_stack->tftpdata.eof;
+ block.block = 1; /* Will be the first block */
+ pxe_stack->tftpdata.magic_cookie = 0;
+ } else {
+ if ( !tftp_block ( NULL, &block ) ) {
+ tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ /* Return data */
+ tftp_read->PacketNumber = block.block;
+ tftp_read->BufferSize = block.len;
+ memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
+ DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
+ tftp_read->Buffer.offset );
+
+ tftp_read->Status = PXENV_STATUS_SUCCESS;
+ return 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 ) {
+ struct sockaddr_in tftp_server;
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
+ tftp_read_file->Buffer,
+ tftp_read_file->Buffer + tftp_read_file->BufferSize );
+ ENSURE_READY ( tftp_read_file );
+
+ /* inserted by Klaus Wittemeier */
+ /* KERNEL_BUF stores the name of the last required file */
+ /* This is a fix to make Microsoft Remote Install Services work (RIS) */
+ memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
+ /* end of insertion */
+
+ /* Set server address and port */
+ tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
+ ? tftp_read_file->ServerIPAddress
+ : arptable[ARP_SERVER].ipaddr.s_addr;
+ tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
+
+ pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
+ pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
+ pxe_stack->readfile.offset = 0;
+
+ rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
+ pxe_tftp_read_block );
+ if ( rc ) {
+ tftp_read_file->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+ tftp_read_file->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+static int pxe_tftp_read_block ( unsigned char *data,
+ unsigned int block __unused,
+ unsigned int len, int eof ) {
+ if ( pxe_stack->readfile.buffer ) {
+ if ( pxe_stack->readfile.offset + len >=
+ pxe_stack->readfile.bufferlen ) return -1;
+ memcpy ( pxe_stack->readfile.buffer +
+ pxe_stack->readfile.offset, data, len );
+ }
+ pxe_stack->readfile.offset += len;
+ return eof ? 0 : 1;
+}
+
+/**
+ * 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" );
+ ENSURE_READY ( tftp_get_fsize );
+
+ pxe_stack->readfile.buffer = NULL;
+ pxe_stack->readfile.bufferlen = 0;
+ pxe_stack->readfile.offset = 0;
+
+#warning "Rewrite pxenv_tftp_get_fsize, please"
+ if ( rc ) {
+ tftp_get_fsize->FileSize = 0;
+ tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+ tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
+ tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** @page pxe_notes Etherboot PXE implementation notes
+
+@section pxe_note_tftp Welding together the TFTP protocol and the PXE TFTP API
+
+The PXE TFTP API is fundamentally poorly designed; the TFTP protocol
+simply does not map well into "open file", "read file block", "close
+file" operations. The problem is the unreliable nature of UDP
+transmissions and the lock-step mechanism employed by TFTP to
+guarantee file transfer. The lock-step mechanism requires that if we
+time out waiting for a packet to arrive, we must trigger its
+retransmission by retransmitting our own previously transmitted
+packet.
+
+For example, suppose that pxenv_tftp_read() is called to read the
+first data block of a file from a server that does not support TFTP
+options, and that no data block is received within the timeout period.
+In order to trigger the retransmission of this data block,
+pxenv_tftp_read() must retransmit the TFTP open request. However, the
+information used to build the TFTP open request is not available at
+this time; it was provided only to the pxenv_tftp_open() call. Even
+if we were able to retransmit a TFTP open request, we would have to
+allocate a new local port number (and be prepared for data to arrive
+from a new remote port number) in order to avoid violating the TFTP
+protocol specification.
+
+The question of when to transmit the ACK packets is also awkward. At
+a first glance, it would seem to be fairly simple: acknowledge a
+packet immediately after receiving it. However, since the ACK packet
+may itself be lost, the next call to pxenv_tftp_read() must be
+prepared to retransmit the acknowledgement.
+
+Another problem to consider is that the pxenv_tftp_open() API call
+must return an indication of whether or not the TFTP open request
+succeeded. In the case of a TFTP server that doesn't support TFTP
+options, the only indication of a successful open is the reception of
+the first data block. However, the pxenv_tftp_open() API provides no
+way to return this data block at this time.
+
+At least some PXE stacks (e.g. NILO) solve this problem by violating
+the TFTP protocol and never bothering with retransmissions, relying on
+the TFTP server to retransmit when it times out waiting for an ACK.
+This approach is dubious at best; if, for example, the initial TFTP
+open request is lost then NILO will believe that it has opened the
+file and will eventually time out and give up while waiting for the
+first packet to arrive.
+
+The only viable solution seems to be to allocate a buffer for the
+storage of the first data packet returned by the TFTP server, since we
+may receive this packet during the pxenv_tftp_open() call but have to
+return it from the subsequent pxenv_tftp_read() call. This buffer
+must be statically allocated and must be dedicated to providing a
+temporary home for TFTP packets. There is nothing in the PXE
+specification that prevents a caller from calling
+e.g. pxenv_undi_transmit() between calls to the TFTP API, so we cannot
+use the normal transmit/receive buffer for this purpose.
+
+Having paid the storage penalty for this buffer, we can then gain some
+simplicity by exploiting it in full. There is at least one
+circumstance (pxenv_tftp_open() called to open a file on a server that
+does not support TFTP options) in which we will have to enter
+pxenv_tftp_read() knowing that our previous transmission (the open
+request, in this situation) has already been acknowledged.
+Implementation of pxenv_tftp_read() can be made simpler by making this
+condition an invariant. Specifically, on each call to
+pxenv_tftp_read(), we shall ensure that the following are true:
+
+ - Our previous transmission has already been acknowledged. We
+ therefore do not need to keep state about our previous
+ transmission.
+
+ - The next packet to read is already in a buffer in memory.
+
+In order to maintain these two conditions, pxenv_tftp_read() must do
+the following:
+
+ - Copy the data packet from our buffer to the caller's buffer.
+
+ - Acknowledge the data packet that we have just copied. This will
+ trigger transmission of the next packet from the server.
+
+ - Retransmit this acknowledgement packet until the next packet
+ arrives.
+
+ - Copy the packet into our internal buffer, ready for the next call
+ to pxenv_tftp_read().
+
+It can be verified that this preserves the invariant condition, and it
+is clear that the resulting implementation of pxenv_tftp_read() can be
+relatively simple. (For the special case of the last data packet,
+pxenv_tftp_read() should return immediately after sending a single
+acknowledgement packet.)
+
+In order to set up this invariant condition for the first call to
+pxenv_tftp_read(), pxenv_tftp_open() must do the following:
+
+ - Construct and transmit the TFTP open request.
+
+ - Retransmit the TFTP open request (using a new local port number as
+ necessary) until a response (DATA, OACK, or ERROR) is received.
+
+ - If the response is an OACK, acknowledge the OACK and retransmit
+ the acknowledgement until the first DATA packet arrives.
+
+ - If we have a DATA packet, store it in a buffer ready for the first
+ call to pxenv_tftp_read().
+
+This approach has the advantage of being fully compliant with both
+RFC1350 (TFTP) and RFC2347 (TFTP options). It avoids unnecessary
+retransmissions. The cost is approximately 1500 bytes of
+uninitialised storage. Since there is demonstrably no way to avoid
+paying this cost without either violating the protocol specifications
+or introducing unnecessary retransmissions, we deem this to be a cost
+worth paying.
+
+A small performance gain may be obtained by adding a single extra
+"send ACK" in both pxenv_tftp_open() and pxenv_tftp_read() immediately
+after receiving the DATA packet and copying it into the internal
+buffer. The sequence of events for pxenv_tftp_read() then becomes:
+
+ - Copy the data packet from our buffer to the caller's buffer.
+
+ - If this was the last data packet, return immediately.
+
+ - Check to see if a TFTP data packet is waiting. If not, send an
+ ACK for the data packet that we have just copied, and retransmit
+ this ACK until the next data packet arrives.
+
+ - Copy the packet into our internal buffer, ready for the next call
+ to pxenv_tftp_read().
+
+ - Send a single ACK for this data packet.
+
+Sending the ACK at this point allows the server to transmit the next
+data block while our caller is processing the current packet. If this
+ACK is lost, or the DATA packet it triggers is lost or is consumed by
+something other than pxenv_tftp_read() (e.g. by calls to
+pxenv_undi_isr()), then the next call to pxenv_tftp_read() will not
+find a TFTP data packet waiting and will retransmit the ACK anyway.
+
+Note to future API designers at Intel: try to understand the
+underlying network protocol first!
+
+*/
diff --git a/src/interface/pxe/pxe_udp.c b/src/interface/pxe/pxe_udp.c
new file mode 100644
index 00000000..db5981b4
--- /dev/null
+++ b/src/interface/pxe/pxe_udp.c
@@ -0,0 +1,332 @@
+/** @file
+ *
+ * PXE UDP API
+ *
+ */
+
+#include "pxe.h"
+#include "io.h"
+#include "string.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.
+ */
+
+/**
+ * UDP OPEN
+ *
+ * @v 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_UNDI_INVALID_STATE NIC could not be initialised
+ *
+ * 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. You cannot
+ * have a UDP connection open at the same time as a TFTP connection.
+ * (This is not strictly true for Etherboot; see the relevant @ref
+ * pxe_note_udp "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 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.
+ *
+ */
+PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ) {
+ DBG ( "PXENV_UDP_OPEN" );
+ ENSURE_READY ( udp_open );
+
+ if ( udp_open->src_ip &&
+ udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
+ /* Overwrite our IP address */
+ DBG ( " with new IP %@", udp_open->src_ip );
+ arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
+ }
+
+ udp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP CLOSE
+ *
+ * @v 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. (This is not strictly true for
+ * Etherboot; see the relevant @ref pxe_note_udp "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.)
+ *
+ */
+PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close __unused ) {
+ DBG ( "PXENV_UDP_CLOSE" );
+ udp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP WRITE
+ *
+ * @v 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_UNDI_INVALID_STATE NIC could not be initialised
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Packet was too large to transmit
+ * @err other Any error from pxenv_undi_transmit()
+ *
+ * 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(). (This is not strictly true for
+ * Etherboot; see the relevant @ref pxe_note_udp "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.)
+ *
+ */
+PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write ) {
+ uint16_t src_port;
+ uint16_t dst_port;
+ struct udppacket *packet = (struct udppacket *)nic.packet;
+ int packet_size;
+
+ DBG ( "PXENV_UDP_WRITE" );
+ ENSURE_READY ( udp_write );
+
+ /* PXE spec says source port is 2069 if not specified */
+ src_port = ntohs(udp_write->src_port);
+ if ( src_port == 0 ) src_port = 2069;
+ dst_port = ntohs(udp_write->dst_port);
+ DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
+ udp_write->buffer_size );
+
+ /* 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.
+ */
+
+ /* Copy payload to packet buffer */
+ packet_size = ( (void*)&packet->payload - (void*)packet )
+ + udp_write->buffer_size;
+ if ( packet_size > ETH_FRAME_LEN ) {
+ udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
+ udp_write->buffer_size );
+
+ /* Transmit packet */
+ if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
+ packet_size, packet ) ) {
+ udp_write->Status = errno;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ udp_write->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* Utility function for pxenv_udp_read() */
+static int await_pxe_udp ( int ival __unused, void *ptr,
+ unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp,
+ struct tcphdr *tcp __unused ) {
+ struct s_PXENV_UDP_READ *udp_read = (struct s_PXENV_UDP_READ*)ptr;
+ uint16_t d_port;
+ size_t size;
+
+ /* Ignore non-UDP packets */
+ if ( !udp ) {
+ DBG ( " non-UDP" );
+ return 0;
+ }
+
+ /* Check dest_ip */
+ if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
+ DBG ( " wrong dest IP (got %@, wanted %@)",
+ ip->dest.s_addr, udp_read->dest_ip );
+ return 0;
+ }
+
+ /* Check dest_port */
+ d_port = ntohs ( udp_read->d_port );
+ if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
+ DBG ( " wrong dest port (got %d, wanted %d)",
+ ntohs(udp->dest), d_port );
+ return 0;
+ }
+
+ /* Copy packet to buffer and fill in information */
+ udp_read->src_ip = ip->src.s_addr;
+ udp_read->s_port = udp->src; /* Both in network order */
+ size = ntohs(udp->len) - sizeof(*udp);
+ /* Workaround: NTLDR expects us to fill these in, even though
+ * PXESPEC clearly defines them as input parameters.
+ */
+ udp_read->dest_ip = ip->dest.s_addr;
+ udp_read->d_port = udp->dest;
+ DBG ( " %@:%d->%@:%d (%d)",
+ udp_read->src_ip, ntohs(udp_read->s_port),
+ udp_read->dest_ip, ntohs(udp_read->d_port), size );
+ if ( udp_read->buffer_size < size ) {
+ /* PXESPEC: what error code should we actually return? */
+ DBG ( " buffer too small (%d)", udp_read->buffer_size );
+ udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return 0;
+ }
+ memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
+ udp_read->buffer_size = size;
+
+ return 1;
+}
+
+/**
+ * UDP READ
+ *
+ * @v 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_UNDI_INVALID_STATE NIC could not be initialised
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Buffer was too small for payload
+ * @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(). (This is not strictly true for
+ * Etherboot; see the relevant @ref pxe_note_udp "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 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 *udp_read ) {
+ DBG ( "PXENV_UDP_READ" );
+ ENSURE_READY ( udp_read );
+
+ /* Use await_reply with a timeout of zero */
+ /* Allow await_reply to change Status if necessary */
+ udp_read->Status = PXENV_STATUS_FAILURE;
+ if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
+ return PXENV_EXIT_FAILURE;
+ }
+
+ udp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/** @page pxe_notes Etherboot PXE implementation notes
+
+@section pxe_note_udp The connectionless nature of UDP
+
+The PXE specification states that it is possible to have only one open
+UDP or TFTP connection at any one time. Etherboot does not
+rigourously enforce this restriction, on the UNIX principle that the
+code should not prevent the user from doing stupid things, because
+that would also prevent the user from doing clever things. Since UDP
+is a connectionless protocol, it is perfectly possible to have
+multiple concurrent UDP "connections" open, provided that you take the
+multiplicity of connections into account when calling
+pxenv_udp_read(). Similarly, there is no technical reason that
+prevents you from calling pxenv_udp_write() in the middle of a TFTP
+download.
+
+Etherboot will therefore never return error codes indicating "a
+connection is already open", such as #PXENV_STATUS_UDP_OPEN. If you
+want to have multiple concurrent connections, go for it (but don't
+expect your perfectly sensible code to work with any other PXE stack).
+
+Since Etherboot treats UDP as the connectionless protocol that it
+really is, pxenv_udp_close() is actually a no-op, and there is no need
+to call pxenv_udp_open() before using pxenv_udp_write() or
+pxenv_udp_read().
+
+*/
diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c
new file mode 100644
index 00000000..3919915b
--- /dev/null
+++ b/src/interface/pxe/pxe_undi.c
@@ -0,0 +1,538 @@
+/** @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 "pxe.h"
+
+typedef struct {
+ char dest[ETH_ALEN];
+ char source[ETH_ALEN];
+ uint16_t nstype;
+} media_header_t;
+
+static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
+ DBG ( "PXENV_UNDI_STARTUP" );
+ ENSURE_MIDWAY(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" );
+ ENSURE_CAN_UNLOAD ( undi_cleanup );
+
+ 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" );
+ ENSURE_MIDWAY ( 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 ) {
+ DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+
+ ENSURE_MIDWAY ( undi_reset_adapter );
+ ENSURE_READY ( undi_reset_adapter );
+
+ 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" );
+ ENSURE_MIDWAY ( undi_shutdown );
+
+ 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 ) {
+ DBG ( "PXENV_UNDI_OPEN" );
+ ENSURE_READY ( undi_open );
+
+ /* PXESPEC: This is where we choose to enable interrupts.
+ * Can't actually find where we're meant to in the PXE spec,
+ * but this should work.
+ */
+ eth_irq ( ENABLE );
+
+ 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" );
+ ENSURE_MIDWAY ( undi_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;
+ const char *dest;
+ unsigned int type;
+ unsigned int length;
+ const char *data;
+ media_header_t *media_header;
+
+ DBG ( "PXENV_UNDI_TRANSMIT" );
+ ENSURE_READY ( undi_transmit );
+
+ /* We support only the "immediate" portion of the TBD. Who
+ * knows what Intel's "engineers" were smoking when they came
+ * up with the array of transmit data blocks...
+ */
+ tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
+ if ( tbd->DataBlkCount > 0 ) {
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+ data = SEGOFF16_TO_PTR ( tbd->Xmit );
+ length = tbd->ImmedLength;
+
+ /* If destination is broadcast, we need to supply the MAC address */
+ if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
+ dest = broadcast_mac;
+ } else {
+ dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
+ }
+
+ /* We can't properly support P_UNKNOWN without rewriting all
+ * the driver transmit() methods, so we cheat: if P_UNKNOWN is
+ * specified we rip the destination address and type out of
+ * the pre-assembled packet, then skip over the header.
+ */
+ switch ( undi_transmit->Protocol ) {
+ case P_IP: type = IP; break;
+ case P_ARP: type = ARP; break;
+ case P_RARP: type = RARP; break;
+ case P_UNKNOWN:
+ media_header = (media_header_t*)data;
+ dest = media_header->dest;
+ type = ntohs ( media_header->nstype );
+ data += ETH_HLEN;
+ length -= ETH_HLEN;
+ break;
+ default:
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Send the packet */
+ eth_transmit ( dest, type, length, data );
+
+ 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" );
+ /* ENSURE_READY ( undi_set_mcast_address ); */
+ undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working (deliberately incomplete)
+ */
+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" );
+ ENSURE_READY ( undi_set_station_address );
+
+ /* We don't offer a facility to set the MAC address; this
+ * would require adding extra code to all the Etherboot
+ * drivers, for very little benefit. If we're setting it to
+ * the current value anyway then return success, otherwise
+ * return UNSUPPORTED.
+ */
+ if ( memcmp ( nic.node_addr,
+ &undi_set_station_address->StationAddress,
+ ETH_ALEN ) == 0 ) {
+ undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+ }
+ undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* 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" );
+ /* ENSURE_READY ( 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 ) {
+ DBG ( "PXENV_UNDI_GET_INFORMATION" );
+ ENSURE_READY ( undi_get_information );
+
+ undi_get_information->BaseIo = nic.ioaddr;
+ undi_get_information->IntNumber = nic.irqno;
+ /* Cheat: assume all cards can cope with this */
+ undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+ /* Cheat: we only ever have Ethernet cards */
+ undi_get_information->HwType = ETHER_TYPE;
+ undi_get_information->HwAddrLen = ETH_ALEN;
+ /* 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, nic.node_addr,
+ ETH_ALEN );
+ memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
+ ETH_ALEN );
+ 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: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
+ *undi_get_statistics ) {
+ DBG ( "PXENV_UNDI_GET_STATISTICS" );
+ /* ENSURE_READY ( undi_get_statistics ); */
+ undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
+ *undi_clear_statistics ) {
+ DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
+ /* ENSURE_READY ( undi_clear_statistics ); */
+ undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* 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" );
+ /* ENSURE_READY ( undi_initiate_diags ); */
+ undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
+ *undi_force_interrupt ) {
+ DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
+ ENSURE_READY ( undi_force_interrupt );
+
+ eth_irq ( FORCE );
+ undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* 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" );
+ /* ENSURE_READY ( 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 ) {
+#warning "device probing mechanism has changed completely"
+
+#if 0
+ struct dev *dev = &dev;
+
+ DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
+ ENSURE_READY ( undi_get_nic_type );
+
+ if ( dev->to_probe == PROBE_PCI ) {
+ struct pci_device *pci = &dev->state.pci.dev;
+
+ undi_get_nic_type->NicType = PCI_NIC;
+ undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
+ undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
+ undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
+ undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
+ undi_get_nic_type->info.pci.BusDevFunc =
+ ( pci->bus << 8 ) | pci->devfn;
+ /* Cheat: these fields are probably unnecessary, and
+ * would require adding extra code to pci.c.
+ */
+ undi_get_nic_type->info.pci.Prog_Intf = 0;
+ undi_get_nic_type->info.pci.Rev = 0;
+ undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
+ undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+ } else if ( dev->to_probe == PROBE_ISA ) {
+ /* const struct isa_driver *isa = dev->state.isa.driver; */
+
+ undi_get_nic_type->NicType = PnP_NIC;
+ /* Don't think anything fills these fields in, and
+ * probably no-one will ever be interested in them.
+ */
+ undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
+ undi_get_nic_type->info.pnp.Base_Class = 0;
+ undi_get_nic_type->info.pnp.Sub_Class = 0;
+ undi_get_nic_type->info.pnp.Prog_Intf = 0;
+ undi_get_nic_type->info.pnp.CardSelNum = 0;
+ } else {
+ /* PXESPEC: There doesn't seem to be an "unknown type"
+ * defined.
+ */
+ undi_get_nic_type->NicType = 0;
+ }
+ undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+#endif
+}
+
+/* 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" );
+ ENSURE_READY ( undi_get_iface_info );
+
+ /* Just hand back some info, doesn't really matter what it is.
+ * Most PXE stacks seem to take this approach.
+ */
+ sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
+ 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 ) {
+ 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 ) {
+ media_header_t *media_header = (media_header_t*)nic.packet;
+
+ DBG ( "PXENV_UNDI_ISR" );
+ /* We can't call ENSURE_READY, because this could be being
+ * called as part of an interrupt service routine. Instead,
+ * we should simply die if we're not READY.
+ */
+ if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* 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 :
+ /* Is there a packet waiting? If so, disable
+ * interrupts on the NIC and return "it's ours". Do
+ * *not* necessarily acknowledge the interrupt; this
+ * can happen later when eth_poll(1) is called. As
+ * long as the interrupt is masked off so that it
+ * doesn't immediately retrigger the 8259A then all
+ * should be well.
+ */
+ DBG ( " START" );
+ if ( eth_poll ( 0 ) ) {
+ DBG ( " OURS" );
+ eth_irq ( DISABLE );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+ } else {
+ DBG ( " NOT_OURS" );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
+ }
+ break;
+ case PXENV_UNDI_ISR_IN_PROCESS :
+ /* Call poll(), return packet. If no packet, return "done".
+ */
+ DBG ( " PROCESS" );
+ if ( eth_poll ( 1 ) ) {
+ DBG ( " RECEIVE %d", nic.packetlen );
+ if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status =
+ PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+ undi_isr->BufferLength = nic.packetlen;
+ undi_isr->FrameLength = nic.packetlen;
+ undi_isr->FrameHeaderLength = ETH_HLEN;
+ memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
+ PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
+ switch ( ntohs(media_header->nstype) ) {
+ case IP : undi_isr->ProtType = P_IP; break;
+ case ARP : undi_isr->ProtType = P_ARP; break;
+ case RARP : undi_isr->ProtType = P_RARP; break;
+ default : undi_isr->ProtType = P_UNKNOWN;
+ }
+ if ( memcmp ( media_header->dest, broadcast_mac,
+ sizeof(broadcast_mac) ) ) {
+ undi_isr->PktType = XMT_BROADCAST;
+ } else {
+ undi_isr->PktType = XMT_DESTADDR;
+ }
+ break;
+ } else {
+ /* No break - fall through to IN_GET_NEXT */
+ }
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
+ /* We only ever return one frame at a time */
+ DBG ( " GET_NEXT DONE" );
+ /* Re-enable interrupts */
+ eth_irq ( ENABLE );
+ /* Force an interrupt if there's a packet still
+ * waiting, since we only handle one packet per
+ * interrupt.
+ */
+ if ( eth_poll ( 0 ) ) {
+ DBG ( " (RETRIGGER)" );
+ eth_irq ( FORCE );
+ }
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ break;
+ default :
+ /* 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/src/proto/fsp.c b/src/proto/fsp.c
new file mode 100644
index 00000000..6aba9376
--- /dev/null
+++ b/src/proto/fsp.c
@@ -0,0 +1,245 @@
+ /*********************************************************************\
+ * Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz) *
+ * *
+ * You may copy or modify this file in any manner you wish, provided *
+ * that this notice is always included, and that you hold the author *
+ * harmless for any loss or damage resulting from the installation or *
+ * use of this software. *
+ * *
+ * This file provides support for FSP v2 protocol written from scratch *
+ * by Radim Kolar, FSP project leader. *
+ * *
+ * ABOUT FSP *
+ * FSP is a lightweight file transfer protocol and is being used for *
+ * booting, Internet firmware updates, embedded devices and in *
+ * wireless applications. FSP is very easy to implement; contact Radim *
+ * Kolar if you need hand optimized assembler FSP stacks for various *
+ * microcontrollers, CPUs or consultations. *
+ * http://fsp.sourceforge.net/ *
+ * *
+ * REVISION HISTORY *
+ * 1.0 2005-03-17 rkolar Initial coding *
+ * 1.1 2005-03-24 rkolar We really need to send CC_BYE to the server *
+ * at end of transfer, because next stage boot *
+ * loader is unable to contact FSP server *
+ * until session timeouts. *
+ * 1.2 2005-03-26 rkolar We need to query filesize in advance, *
+ * because NBI loader do not reads file until *
+ * eof is reached.
+ * REMARKS *
+ * there is no support for selecting port number of fsp server, maybe *
+ * we should parse fsp:// URLs in boot image filename. *
+ * this implementation has filename limit 255 chars. *
+ \*********************************************************************/
+
+#ifdef DOWNLOAD_PROTO_FSP
+#include "etherboot.h"
+#include "nic.h"
+
+#define FSP_PORT 21
+
+/* FSP commands */
+#define CC_GET_FILE 0x42
+#define CC_BYE 0x4A
+#define CC_ERR 0x40
+#define CC_STAT 0x4D
+
+/* etherboot limits */
+#define FSP_MAXFILENAME 255
+
+struct fsp_info {
+ in_addr server_ip;
+ uint16_t server_port;
+ uint16_t local_port;
+ const char *filename;
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
+};
+
+struct fsp_header {
+ uint8_t cmd;
+ uint8_t sum;
+ uint16_t key;
+ uint16_t seq;
+ uint16_t len;
+ uint32_t pos;
+} PACKED;
+
+#define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
+ (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
+
+static struct fsp_request {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct fsp_header fsp;
+ unsigned char data[FSP_MAXFILENAME + 1 + 2];
+} request;
+
+struct fsp_reply {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct fsp_header fsp;
+ unsigned char data[FSP_MAXPAYLOAD];
+} PACKED;
+
+
+static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp)
+{
+ if(!udp)
+ return 0;
+ if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+ if (ntohs(udp->len) < 12+sizeof(struct udphdr))
+ return 0;
+ return 1;
+}
+
+static int proto_fsp(struct fsp_info *info)
+{
+ uint32_t filepos;
+ uint32_t filelength=0;
+ int i,retry;
+ uint16_t reqlen;
+ struct fsp_reply *reply;
+ int block=1;
+
+ /* prepare FSP request packet */
+ filepos=0;
+ i=strlen(info->filename);
+ if(i>FSP_MAXFILENAME)
+ {
+ printf("Boot filename is too long.\n");
+ return 0;
+ }
+ strcpy(request.data,info->filename);
+ *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
+ request.fsp.len=htons(i+1);
+ reqlen=i+3+12;
+
+ rx_qdrain();
+ retry=0;
+
+ /* main loop */
+ for(;;) {
+ int sum;
+ long timeout;
+
+ /* query filelength if not known */
+ if(filelength == 0)
+ request.fsp.cmd=CC_STAT;
+
+ /* prepare request packet */
+ request.fsp.pos=htonl(filepos);
+ request.fsp.seq=random();
+ request.fsp.sum=0;
+ for(i=0,sum=reqlen;i<reqlen;i++)
+ {
+ sum += ((uint8_t *)&request.fsp)[i];
+ }
+ request.fsp.sum= sum + (sum >> 8);
+ /* send request */
+ if (!udp_transmit(info->server_ip.s_addr, info->local_port,
+ info->server_port, sizeof(request.ip) +
+ sizeof(request.udp) + reqlen, &request))
+ return (0);
+ /* wait for retry */
+#ifdef CONGESTED
+ timeout =
+ rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
+#else
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+#endif
+ retry++;
+ if (!await_reply(await_fsp, info->local_port, NULL, timeout))
+ continue;
+ reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];
+ /* check received packet */
+ if (reply->fsp.seq != request.fsp.seq)
+ continue;
+ reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
+ if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
+ continue;
+ sum=-reply->fsp.sum;
+ for(i=0;i<reply->udp.len;i++)
+ {
+ sum += ((uint8_t *)&(reply->fsp))[i];
+ }
+ sum = (sum + (sum >> 8)) & 0xff;
+ if(sum != reply->fsp.sum)
+ {
+ printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
+ continue;
+ }
+ if(reply->fsp.cmd == CC_ERR)
+ {
+ printf("\nFSP error: %s",info->filename);
+ if(reply->fsp.len)
+ printf(" [%s]",reply->data);
+ printf("\n");
+ return 0;
+ }
+ if(reply->fsp.cmd == CC_BYE && filelength == 1)
+ {
+ info->fnc(request.data,block,1,1);
+ return 1;
+ }
+ if(reply->fsp.cmd == CC_STAT)
+ {
+ if(reply->data[8] == 0)
+ {
+ /* file not found, etc. */
+ filelength=0xffffffff;
+ } else
+ {
+ filelength= ntohl(*((uint32_t *)&reply->data[4]));
+ }
+ request.fsp.cmd = CC_GET_FILE;
+ request.fsp.key = reply->fsp.key;
+ retry=0;
+ continue;
+ }
+
+ if(reply->fsp.cmd == CC_GET_FILE)
+ {
+ if(ntohl(reply->fsp.pos) != filepos)
+ continue;
+ request.fsp.key = reply->fsp.key;
+ retry=0;
+ i=ntohs(reply->fsp.len);
+ if(i == 1)
+ {
+ request.fsp.cmd=CC_BYE;
+ request.data[0]=reply->data[0];
+ continue;
+ }
+ /* let last byte alone */
+ if(i >= filelength)
+ i = filelength - 1;
+ if(!info->fnc(reply->data,block++,i,0))
+ return 0;
+ filepos += i;
+ filelength -= i;
+ }
+ }
+
+ return 0;
+}
+
+int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
+{
+ struct fsp_info info;
+ /* Set the defaults */
+ info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
+ info.server_port = FSP_PORT;
+ info.local_port = 1024 + random() & 0xfbff;
+ info.fnc = fnc;
+
+ /* Now parse the url */
+ /* printf("fsp-URI: [%s]\n", name); */
+ /* quick hack for now */
+ info.filename=name;
+ return proto_fsp(&info);
+}
+#endif
diff --git a/src/proto/http.c b/src/proto/http.c
index 6f47fc14..4fa594f6 100644
--- a/src/proto/http.c
+++ b/src/proto/http.c
@@ -40,18 +40,19 @@ static int send_tcp_request(int length, void *buffer, void *ptr) {
/**************************************************************************
RECV_TCP_CALLBACK - Receive data using TCP
**************************************************************************/
-static int recv_tcp_request(int length, const void *buffer, void *ptr) {
+static int recv_tcp_request(int length, const void *data, void *ptr) {
struct send_recv_state *state = (struct send_recv_state *)ptr;
+ const char *buffer = data;
/* Assume that the lines in an HTTP header do not straddle a packet */
/* boundary. This is probably a reasonable assumption */
if (state->recv_state == RESULT_CODE) {
while (length > 0) {
/* Find HTTP result code */
- if (*(const char *)buffer == ' ') {
- const char *ptr = ((const char *)buffer) + 1;
+ if (*buffer == ' ') {
+ const char *ptr = buffer + 1;
int rc = strtoul(ptr, &ptr, 10);
- if (ptr >= (const char *)buffer + length) {
+ if (ptr >= buffer + length) {
state->recv_state = ERROR;
DBG ( "HTTP got bad result code\n" );
return 0;
@@ -61,7 +62,7 @@ static int recv_tcp_request(int length, const void *buffer, void *ptr) {
DBG ( "HTTP got result code %d\n", rc );
goto header;
}
- ++(const char *)buffer;
+ ++buffer;
length--;
}
state->recv_state = ERROR;
@@ -88,7 +89,7 @@ static int recv_tcp_request(int length, const void *buffer, void *ptr) {
/* Find beginning of line */
while (length > 0) {
length--;
- if (*((const char *)buffer)++ == '\n')
+ if (*buffer++ == '\n')
break;
}
/* Check for end of header */
@@ -140,7 +141,7 @@ static int http ( char *url, struct sockaddr_in *server __unused,
tcp_transaction ( server->sin_addr.s_addr,
server->sin_port, &state,
- send_tcp_request, recv_tcp_request );
+ send_tcp_request, (int (*)(int, const void *, void *))recv_tcp_request );
}
if ( state.recv_state == MOVED ) {
diff --git a/src/proto/igmp.c b/src/proto/igmp.c
new file mode 100644
index 00000000..aad530f7
--- /dev/null
+++ b/src/proto/igmp.c
@@ -0,0 +1,166 @@
+/*
+ * Eric Biederman wrote this code originally.
+ *
+ */
+
+#include "ip.h"
+#include "igmp.h"
+#include "background.h"
+#include "nic.h"
+#include "etherboot.h"
+
+static unsigned long last_igmpv1 = 0;
+static struct igmptable_t igmptable[MAX_IGMP];
+
+static long rfc1112_sleep_interval ( long base, int exp ) {
+ unsigned long divisor, tmo;
+
+ if ( exp > BACKOFF_LIMIT )
+ exp = BACKOFF_LIMIT;
+ divisor = RAND_MAX / ( base << exp );
+ tmo = random() / divisor;
+ return tmo;
+}
+
+static void send_igmp_reports ( unsigned long now ) {
+ struct igmp_ip_t igmp;
+ int i;
+
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ if ( ! igmptable[i].time )
+ continue;
+ if ( now < igmptable[i].time )
+ continue;
+
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP,
+ sizeof ( igmp.router_alert ),
+ sizeof ( igmp ), &igmp );
+ igmp.igmp.type = IGMPv2_REPORT;
+ if ( last_igmpv1 &&
+ ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) {
+ igmp.igmp.type = IGMPv1_REPORT;
+ }
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
+ igmp.igmp.chksum = ipchksum ( &igmp.igmp,
+ sizeof ( igmp.igmp ) );
+ ip_transmit ( sizeof ( igmp ), &igmp );
+ DBG ( "IGMP sent report to %@\n",
+ igmp.igmp.group.s_addr );
+ /* Don't send another igmp report until asked */
+ igmptable[i].time = 0;
+ }
+}
+
+static void process_igmp ( unsigned long now, unsigned short ptype __unused,
+ struct iphdr *ip ) {
+ struct igmp *igmp;
+ int i;
+ unsigned iplen;
+
+ if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) ||
+ ( nic.packetlen < ( sizeof ( struct iphdr ) +
+ sizeof ( struct igmp ) ) ) ) {
+ return;
+ }
+
+ iplen = ( ip->verhdrlen & 0xf ) * 4;
+ igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ];
+ if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 )
+ return;
+
+ if ( ( igmp->type == IGMP_QUERY ) &&
+ ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) {
+ unsigned long interval = IGMP_INTERVAL;
+
+ if ( igmp->response_time == 0 ) {
+ last_igmpv1 = now;
+ } else {
+ interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
+ }
+
+ DBG ( "IGMP received query for %@\n", igmp->group.s_addr );
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ uint32_t group = igmptable[i].group.s_addr;
+ if ( ( group == 0 ) ||
+ ( group == igmp->group.s_addr ) ) {
+ unsigned long time;
+ time = currticks() +
+ rfc1112_sleep_interval ( interval, 0 );
+ if ( time < igmptable[i].time ) {
+ igmptable[i].time = time;
+ }
+ }
+ }
+ }
+ if ( ( ( igmp->type == IGMPv1_REPORT ) ||
+ ( igmp->type == IGMPv2_REPORT ) ) &&
+ ( ip->dest.s_addr == igmp->group.s_addr ) ) {
+ DBG ( "IGMP received report for %@\n", igmp->group.s_addr);
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ if ( ( igmptable[i].group.s_addr ==
+ igmp->group.s_addr ) &&
+ ( igmptable[i].time != 0 ) ) {
+ igmptable[i].time = 0;
+ }
+ }
+ }
+}
+
+static struct background igmp_background __background = {
+ .send = send_igmp_reports,
+ .process = process_igmp,
+};
+
+void leave_group ( int slot ) {
+ /* Be very stupid and always send a leave group message if
+ * I have subscribed. Imperfect but it is standards
+ * compliant, easy and reliable to implement.
+ *
+ * The optimal group leave method is to only send leave when,
+ * we were the last host to respond to a query on this group,
+ * and igmpv1 compatibility is not enabled.
+ */
+ if ( igmptable[slot].group.s_addr ) {
+ struct igmp_ip_t igmp;
+
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP,
+ sizeof ( igmp.router_alert ), sizeof ( igmp ),
+ &igmp);
+ igmp.igmp.type = IGMP_LEAVE;
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
+ igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
+ ip_transmit ( sizeof ( igmp ), &igmp );
+ DBG ( "IGMP left group %@\n", igmp.igmp.group.s_addr );
+ }
+ memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
+}
+
+void join_group ( int slot, unsigned long group ) {
+ /* I have already joined */
+ if ( igmptable[slot].group.s_addr == group )
+ return;
+ if ( igmptable[slot].group.s_addr ) {
+ leave_group ( slot );
+ }
+ /* Only join a group if we are given a multicast ip, this way
+ * code can be given a non-multicast (broadcast or unicast ip)
+ * and still work...
+ */
+ if ( ( group & htonl ( MULTICAST_MASK ) ) ==
+ htonl ( MULTICAST_NETWORK ) ) {
+ igmptable[slot].group.s_addr = group;
+ igmptable[slot].time = currticks();
+ }
+}
diff --git a/src/proto/nmb.c b/src/proto/nmb.c
index b395dabe..d44c0685 100644
--- a/src/proto/nmb.c
+++ b/src/proto/nmb.c
@@ -15,6 +15,7 @@ static inline char * nbns_make_name ( char *dest, const char *name ) {
char nb_name[16];
char c;
int i;
+ uint16_t *d;
*(dest++) = 32; /* Length is always 32 */
@@ -26,11 +27,13 @@ static inline char * nbns_make_name ( char *dest, const char *name ) {
memset ( nb_name, ' ', 15 );
nb_name[15] = '\0';
memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */
+
+ d = ( uint16_t * ) dest;
for ( i = 0 ; i < 16 ; i++ ) {
c = nb_name[i];
- *( ( ( uint16_t * ) dest ) ++ ) =
- htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 );
+ *( d++ ) = htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 );
}
+ dest = ( char * ) d;
*(dest++) = 0; /* Terminating 0-length name component */
return dest;
diff --git a/src/proto/tcp.c b/src/proto/tcp.c
index 197bfce2..1e0531d5 100644
--- a/src/proto/tcp.c
+++ b/src/proto/tcp.c
@@ -1,7 +1,7 @@
#include "etherboot.h"
#include "ip.h"
#include "tcp.h"
-
+#include "nic.h"
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
diff --git a/src/proto/tftm.c b/src/proto/tftm.c
index 426d0dda..fb011c62 100644
--- a/src/proto/tftm.c
+++ b/src/proto/tftm.c
@@ -1,483 +1,208 @@
-/**************************************************************************
-*
-* proto_tftm.c -- Etherboot Multicast TFTP
-* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* This code is based on the DOWNLOAD_PROTO_TFTM section of
-* Etherboot 5.3 core/nic.c and:
-*
-* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
-* Eric Biederman's proto_slam.c
-*
-* $Revision$
-* $Author$
-* $Date$
-*
-* REVISION HISTORY:
-* ================
-* 09-07-2003 timlegge Release Version, Capable of Multicast Booting
-* 08-30-2003 timlegge Initial version, Assumes consecutive blocks
-*
-* Indent Options: indent -kr -i8
-***************************************************************************/
+#include "etherboot.h"
+#include "proto.h"
+#include "errno.h"
+#include "tftp.h"
+#include "tftpcore.h"
-/*
- * IMPORTANT
+/** @file
*
- * This file should be rewritten to avoid the use of a bitmap. Our
- * buffer routines can cope with being handed blocks in an arbitrary
- * order, duplicate blocks, etc. This code could be substantially
- * simplified by taking advantage of these features.
+ * TFTM protocol
*
+ * TFTM is a protocol defined in RFC2090 as a multicast extension to
+ * TFTP.
*/
-#include "etherboot.h"
-#include "proto.h"
-#include "nic.h"
+static inline int tftm_process_opts ( struct tftp_state *state,
+ struct tftp_oack *oack ) {
+ struct in_addr old_mcast_addr = state->multicast.sin_addr;
-struct tftm_info {
- struct sockaddr_in server;
- struct sockaddr_in local;
- struct sockaddr_in multicast;
- int sent_nack;
- const char *name; /* Filename */
-};
+ if ( ! tftp_process_opts ( state, oack ) )
+ return 0;
-struct tftm_state {
- unsigned long block_size;
- unsigned long total_bytes;
- unsigned long total_packets;
- char ismaster;
- unsigned long received_packets;
- struct buffer *buffer;
- unsigned char *image;
- unsigned char *bitmap;
- char recvd_oack;
-} state;
+ if ( old_mcast_addr.s_addr != state->multicast.sin_addr.s_addr ) {
+ if ( old_mcast_addr.s_addr ) {
+ DBG ( "TFTM: Leaving multicast group %@\n",
+ old_mcast_addr.s_addr );
+ leave_group ( IGMP_SERVER );
+ }
+ DBG ( "TFTM: Joining multicast group %@\n",
+ state->multicast.sin_addr.s_addr );
+ join_group ( IGMP_SERVER, state->multicast.sin_addr.s_addr );
+ }
-#define TFTM_PORT 1758
-#define TFTM_MIN_PACKET 1024
+ DBG ( "TFTM: I am a %s client\n",
+ ( state->master ? "master" : "slave" ) );
+ return 1;
+}
-static int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
- unsigned long *filesize, struct tftm_info *info);
-static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
- struct iphdr *ip, struct udphdr *udp,
- struct tcphdr *tcp __unused)
-{
- struct tftm_info *info = ptr;
+static inline int tftm_process_data ( struct tftp_state *state,
+ struct tftp_data *data,
+ struct buffer *buffer ) {
+ unsigned int blksize;
+ off_t offset;
- /* Check for Unicast data being received */
- if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
- if (!udp) {
- return 0;
- }
- if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
- return 0;
- if (ntohs(udp->dest) != ival)
- return 0;
+ /* Calculate block size and offset within file */
+ blksize = ( ntohs ( data->udp.len )
+ + offsetof ( typeof ( *data ), udp )
+ - offsetof ( typeof ( *data ), data ) );
+ offset = ( ntohs ( data->block ) - 1 ) * state->blksize;
- return 1; /* Unicast Data Received */
+ /* Check for oversized block */
+ if ( blksize > state->blksize ) {
+ DBG ( "TFTM: oversized block size %d (max %d)\n",
+ blksize, state->blksize );
+ errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
+ return 0;
}
- /* Also check for Multicast data being received */
- if ((ip->dest.s_addr == info->multicast.sin_addr.s_addr) &&
- (ntohs(udp->dest) == info->multicast.sin_port) &&
- (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
- sizeof(struct udphdr))) {
- return 1; /* Multicast data received */
+ /* Place block in the buffer */
+ if ( ! fill_buffer ( buffer, data->data, offset, blksize ) ) {
+ DBG ( "TFTM: could not place data in buffer: %m\n" );
+ return 0;
}
- return 0;
-}
-static int proto_tftm(struct tftm_info *info)
-{
- int retry = 0;
- static unsigned short iport = 2000;
- unsigned short oport = 0;
- unsigned short len, block = 0, prevblock = 0;
- struct tftp_t *tr;
- struct tftpreq_t tp;
- unsigned long filesize = 0;
+ /* If this is the last block, record the filesize (in case the
+ * server didn't supply a tsize option.
+ */
+ if ( blksize < state->blksize ) {
+ state->tsize = offset + blksize;
+ }
- state.image = 0;
- state.bitmap = 0;
+ /* Record the last received block */
+ state->block = ntohs ( data->block );
- rx_qdrain();
+ return 1;
+}
- /* Warning: the following assumes the layout of bootp_t.
- But that's fixed by the IP, UDP and BOOTP specs. */
- /* Send a tftm-request to the server */
- tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */
- len =
- sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
- sprintf((char *) tp.u.rrq,
- "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
- info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
+static inline int tftm_next ( struct tftp_state *state,
+ union tftp_any **reply,
+ struct buffer *buffer ) {
+ long listen_timeout;
- if (!udp_transmit(info->server.sin_addr.s_addr, ++iport,
- info->server.sin_port, len, &tp))
- return (0);
+ listen_timeout = rfc2131_sleep_interval ( TIMEOUT, MAX_TFTP_RETRIES );
- /* loop to listen for packets and to receive the file */
- for (;;) {
- long timeout;
-#ifdef CONGESTED
- timeout =
- rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
- retry);
-#else
- timeout = rfc2131_sleep_interval(TIMEOUT, retry);
-#endif
- /* Calls the await_reply function in nic.c which in turn calls
- await_tftm (1st parameter) as above */
- if (!await_reply(await_tftm, iport, info, timeout)) {
- if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */
- if (!udp_transmit
- (info->server.sin_addr.s_addr, ++iport,
- info->server.sin_port, len, &tp))
- return (0);
- continue;
- }
-#ifdef CONGESTED
- if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */
- DBG("Timed out receiving file");
- len =
- sizeof(tp.ip) + sizeof(tp.udp) +
- sizeof(tp.opcode) +
- sprintf((char *) tp.u.rrq,
- "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
- info->name, 0, 0, 0, 0, 0,
- TFTM_MIN_PACKET, 0, 0) + 1;
-
- udp_transmit
- (info->server.sin_addr.s_addr,
- ++iport, info->server.sin_port,
- len, &tp);
- continue;
- }
-#endif
- break; /* timeout */
+ /* If we are not the master client, just listen for the next
+ * packet
+ */
+ if ( ! state->master ) {
+ if ( tftp_get ( state, listen_timeout, reply ) ) {
+ /* Heard a non-error packet */
+ return 1;
}
-
- tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
-
- if (tr->opcode == ntohs(TFTP_ERROR)) {
- printf("TFTP error %d (%s)\n",
- ntohs(tr->u.err.errcode), tr->u.err.errmsg);
- break;
+ if ( *reply ) {
+ /* Received an error packet */
+ return 0;
}
+ /* Didn't hear anything; try prodding the server */
+ state->master = 1;
+ }
+ /* We are the master client; trigger the next packet
+ * that we want
+ */
+ state->block = buffer->fill / state->blksize;
+ return tftp_ack ( state, reply );
+}
- if (tr->opcode == ntohs(TFTP_OACK)) {
- int i =
- opt_get_multicast(tr, &len, &filesize, info);
-
- if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */
- /* Transmit an error message to the server to end the transmission */
- printf
- ("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
- tp.opcode = htons(TFTP_ERROR);
- tp.u.err.errcode = 8;
- /*
- * Warning: the following assumes the layout of bootp_t.
- * But that's fixed by the IP, UDP and BOOTP specs.
- */
- len =
- sizeof(tp.ip) + sizeof(tp.udp) +
- sizeof(tp.opcode) +
- sizeof(tp.u.err.errcode) +
- /*
- * Normally bad form to omit the format string, but in this case
- * the string we are copying from is fixed. sprintf is just being
- * used as a strcpy and strlen.
- */
- sprintf((char *) tp.u.err.errmsg,
- "RFC2090 error") + 1;
- udp_transmit(info->server.sin_addr.s_addr,
- iport, ntohs(tr->udp.src),
- len, &tp);
- block = tp.u.ack.block = 0; /* this ensures, that */
- /* the packet does not get */
- /* processed as data! */
- return (0);
- } else {
- unsigned long bitmap_len;
- /* */
- if (!state.recvd_oack) {
-
- state.total_packets =
- 1 + (filesize -
- (filesize %
- state.block_size)) /
- state.block_size;
- bitmap_len =
- (state.total_packets + 7) / 8;
- if (!state.image) {
- state.image = phys_to_virt ( state.buffer->start );
- state.bitmap = state.image + filesize;
- /* We don't yet use the buffer routines; fake it */
- state.buffer->fill = filesize;
-
- memset(state.bitmap, 0,
- bitmap_len);
- }
- /* If I'm running over multicast join the multicast group */
- join_group(IGMP_SERVER,
- info->multicast.sin_addr.s_addr);
- }
- state.recvd_oack = 1;
- }
-
-
-
- } else if (tr->opcode == htons(TFTP_DATA)) {
- unsigned long data_len;
- unsigned char *data;
- struct udphdr *udp;
- udp =
- (struct udphdr *) &nic.packet[ETH_HLEN +
- sizeof(struct
- iphdr)];
- len =
- ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
- data =
- nic.packet + ETH_HLEN + sizeof(struct iphdr) +
- sizeof(struct udphdr) + 4;
-
- if (len > TFTM_MIN_PACKET) /* shouldn't happen */
- continue; /* ignore it */
-
- block = ntohs(tp.u.ack.block = tr->u.data.block);
-
- if (block > state.total_packets) {
- printf("ALERT: Invalid packet number\n");
- continue;
- }
-
- /* Compute the expected data length */
- if (block != state.total_packets) {
- data_len = state.block_size;
- } else {
- data_len = filesize % state.block_size;
- }
- /* If the packet size is wrong drop the packet and then continue */
- if (ntohs(udp->len) !=
- (data_len + (data - (unsigned char *) udp))) {
- printf
- ("ALERT: udp packet is not the correct size: %d\n",
- block);
- continue;
- }
- if (nic.packetlen < data_len + (data - nic.packet)) {
- printf
- ("ALERT: Ethernet packet shorter than data_len: %d\n",
- block);
- continue;
- }
-
- if (data_len > state.block_size) {
- data_len = state.block_size;
- }
- if (((state.
- bitmap[block >> 3] >> (block & 7)) & 1) ==
- 0) {
- /* Non duplicate packet */
- state.bitmap[block >> 3] |=
- (1 << (block & 7));
- memcpy(state.image +
- ((block - 1) * state.block_size),
- data, data_len);
- state.received_packets++;
- } else {
+/**
+ * Download a file via TFTM
+ *
+ * @v server TFTP server
+ * @v file File name
+ * @v buffer Buffer into which to load file
+ * @ret True File was downloaded successfully
+ * @ret False File was not downloaded successfully
+ * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
+ * @err other As returned by tftp_open()
+ * @err other As returned by tftp_process_opts()
+ * @err other As returned by tftp_ack()
+ * @err other As returned by tftp_process_data()
+ *
+ * Download a file from a TFTP server into the specified buffer using
+ * the TFTM protocol.
+ */
+static int tftm ( char *url __unused, struct sockaddr_in *server, char *file,
+ struct buffer *buffer ) {
+ struct tftp_state state;
+ union tftp_any *reply;
+ int rc = 0;
+
+ /* Initialise TFTP state */
+ memset ( &state, 0, sizeof ( state ) );
+ state.server = *server;
+
+ /* Start as the master. This means that if the TFTP server
+ * doesn't actually support multicast, we'll still ACK the
+ * packets and it should all proceed as for a normal TFTP
+ * connection.
+ */
+ state.master = 1;
+
+ /* Open the file */
+ if ( ! tftp_open ( &state, file, &reply, 1 ) ) {
+ DBG ( "TFTM: could not open %@:%d/%s : %m\n",
+ server->sin_addr.s_addr, server->sin_port, file );
+ return 0;
+ }
-/* printf("<DUP>\n"); */
+ /* Fetch file, a block at a time */
+ while ( 1 ) {
+ twiddle();
+ /* Process the current packet */
+ switch ( ntohs ( reply->common.opcode ) ) {
+ case TFTP_OACK:
+ /* Options can be received at any time */
+ if ( ! tftm_process_opts ( &state, &reply->oack ) ) {
+ DBG ( "TFTM: failed to process OACK: %m\n" );
+ tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
+ goto out;
}
- }
-
- else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
break;
- }
-
- if (state.received_packets <= state.total_packets) {
- unsigned long b;
- unsigned long len;
- unsigned long max;
- int value;
- int last;
-
- /* Compute the last bit and store an inverted trailer */
- max = state.total_packets + 1;
- value =
- ((state.
- bitmap[(max - 1) >> 3] >> ((max -
- 1) & 7)) & 1);
- value = !value;
- state.bitmap[max >> 3] &= ~(1 << (max & 7));
- state.bitmap[max >> 3] |= value << (max & 7);
-
- len = 0;
- last = 0; /* Start with the received packets */
- for (b = 1; b <= max; b++) {
- value =
- (state.bitmap[b >> 3] >> (b & 7)) & 1;
-
- if (value == 0) {
- tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */
- break;
- }
- }
- }
- if (state.ismaster) {
- tp.opcode = htons(TFTP_ACK);
- oport = ntohs(tr->udp.src);
- udp_transmit(info->server.sin_addr.s_addr, iport,
- oport, TFTP_MIN_PACKET, &tp); /* ack */
- }
- if (state.received_packets == state.total_packets) {
- /* If the client is finished and not the master,
- * ack the last packet */
- if (!state.ismaster) {
- tp.opcode = htons(TFTP_ACK);
- /* Ack Last packet to end xfer */
- tp.u.ack.block = htons(state.total_packets);
- oport = ntohs(tr->udp.src);
- udp_transmit(info->server.sin_addr.s_addr,
- iport, oport,
- TFTP_MIN_PACKET, &tp); /* ack */
+ case TFTP_DATA:
+ if ( ! tftm_process_data ( &state, &reply->data,
+ buffer ) ) {
+ DBG ( "TFTM: failed to process DATA: %m\n" );
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
+ NULL );
+ goto out;
}
- /* We are done get out */
break;
+ default:
+ DBG ( "TFTM: unexpected packet type %d\n",
+ ntohs ( reply->common.opcode ) );
+ errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
+ goto out;
}
-
- if ((unsigned short) (block - prevblock) != 1) {
- /* Retransmission or OACK, don't process via callback
- * and don't change the value of prevblock. */
- continue;
+ /* If we know the filesize, and we have all the data, stop */
+ if ( state.tsize && ( buffer->fill == state.tsize ) )
+ break;
+ /* Fetch the next packet */
+ if ( ! tftm_next ( &state, &reply, buffer ) ) {
+ DBG ( "TFTM: could not get next block: %m\n" );
+ if ( ! reply ) {
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
+ NULL );
+ }
+ goto out;
}
-
- prevblock = block;
- retry = 0; /* It's the right place to zero the timer? */
-
- }
- /* Leave the multicast group */
- leave_group(IGMP_SERVER);
- return 1;
-}
-
-static int url_tftm ( char *url __unused, struct sockaddr_in *server,
- char *file, struct buffer *buffer ) {
-
- int ret;
- struct tftm_info info;
-
- /* Set the defaults */
- info.server = *server;
- info.local.sin_addr.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
- info.local.sin_port = TFTM_PORT; /* Does not matter. */
- info.multicast = info.local;
- state.ismaster = 0;
- info.name = file;
-
- state.block_size = 0;
- state.total_bytes = 0;
- state.total_packets = 0;
- state.received_packets = 0;
- state.buffer = buffer;
- state.image = 0;
- state.bitmap = 0;
- state.recvd_oack = 0;
-
- if (file[0] != '/') {
- printf("Bad tftm-URI: [%s]\n", file);
- return 0;
}
- ret = proto_tftm(&info);
-
- return ret;
-}
-
-/******************************
-* Parse the multicast options
-*******************************/
-static int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
- unsigned long *filesize, struct tftm_info *info)
-{
- const char *p = tr->u.oack.data, *e = 0;
- int i = 0;
- *len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
- if (*len > TFTM_MIN_PACKET)
- return -1;
- e = p + *len;
+ /* ACK the final packet, as a courtesy to the server */
+ tftp_ack_nowait ( &state );
- while (*p != '\0' && p < e) {
- if (!strcasecmp("tsize", p)) {
- p += 6;
- if ((*filesize = strtoul(p, &p, 10)) > 0)
- i |= 4;
- DBG("\n");
- DBG("tsize=%d\n", *filesize);
- while (p < e && *p)
- p++;
- if (p < e)
- p++;
- } else if (!strcasecmp("blksize", p)) {
- i |= 2;
- p += 8;
- state.block_size = strtoul(p, &p, 10);
- if (state.block_size != TFTM_MIN_PACKET) {
- printf
- ("TFTM-Server rejected required transfer blocksize %d\n",
- TFTM_MIN_PACKET);
- return 0;
- }
- DBG("blksize=%d\n", state.block_size);
- while (p < e && *p)
- p++;
- if (p < e)
- p++;
- } else if (!strncmp(p, "multicast", 10)) {
- i |= 1;
- p += 10;
- DBG("multicast options: %s\n", p);
- p += 1 + inet_aton(p, &info->multicast.sin_addr);
- DBG("multicast ip = %@\n", info->multicast_ip);
- info->multicast.sin_port = strtoul(p, &p, 10);
- ++p;
- DBG("multicast port = %d\n",
- info->multicast.sin_port);
- state.ismaster = (*p == '1' ? 1 : 0);
- DBG("multicast ismaster = %d\n",
- state.ismaster);
- while (p < e && *p)
- p++;
- if (p < e)
- p++;
- }
+ rc = 1;
+ out:
+ if ( state.multicast.sin_addr.s_addr ) {
+ leave_group ( IGMP_SERVER );
}
- if (p > e)
- return 0;
- return i;
+ return rc;
}
static struct protocol tftm_protocol __protocol = {
.name = "x-tftm",
- .default_port = TFTM_PORT,
- .load = url_tftm,
+ .default_port = TFTP_PORT,
+ .load = tftm,
};
diff --git a/src/proto/tftp.c b/src/proto/tftp.c
index b154f575..3f6bf35a 100644
--- a/src/proto/tftp.c
+++ b/src/proto/tftp.c
@@ -1,169 +1,159 @@
#include "etherboot.h"
-#include "in.h"
-#include "nic.h"
#include "proto.h"
+#include "errno.h"
#include "tftp.h"
+#include "tftpcore.h"
-/* Utility function for tftp_block() */
-static int await_tftp ( int ival, void *ptr __unused,
- unsigned short ptype __unused, struct iphdr *ip,
- struct udphdr *udp, struct tcphdr *tcp __unused ) {
- if ( ! udp ) {
- return 0;
+/** @file
+ *
+ * TFTP protocol
+ */
+
+/**
+ * Process a TFTP block
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::block Last received data block
+ * @v tftp_state::blksize Transfer block size
+ * @v data The data block to process
+ * @v buffer The buffer to fill with the data
+ * @ret True Block processed successfully
+ * @ret False Block not processed successfully
+ * @ret tftp_state::block Incremented if applicable
+ * @ret *eof End-of-file marker
+ * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Packet is too large
+ * @err other As returned by fill_buffer()
+ *
+ * Process a TFTP DATA packet that has been received. If the data
+ * packet is the next data packet in the stream, its contents will be
+ * placed in the #buffer and tftp_state::block will be incremented.
+ * If the packet is the final packet, end-of-file will be indicated
+ * via #eof.
+ *
+ * If the data packet is a duplicate, then process_tftp_data() will
+ * still return True, though nothing will be done with the packet. A
+ * False return value always indicates an error that should abort the
+ * transfer.
+ */
+static inline int tftp_process_data ( struct tftp_state *state,
+ struct tftp_data *data,
+ struct buffer *buffer,
+ int *eof ) {
+ unsigned int blksize;
+
+ /* Check it's the correct DATA block */
+ if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
+ DBG ( "TFTP: got block %d, wanted block %d\n",
+ ntohs ( data->block ), state->block + 1 );
+ return 1;
}
- if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr )
+ /* Check it's an acceptable size */
+ blksize = ( ntohs ( data->udp.len )
+ + offsetof ( typeof ( *data ), udp )
+ - offsetof ( typeof ( *data ), data ) );
+ if ( blksize > state->blksize ) {
+ DBG ( "TFTP: oversized block size %d (max %d)\n",
+ blksize, state->blksize );
+ errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
return 0;
- if ( ntohs ( udp->dest ) != ival )
+ }
+ /* Place block in the buffer */
+ if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
+ blksize ) ) {
+ DBG ( "TFTP: could not place data in buffer: %m\n" );
return 0;
+ }
+ /* Increment block counter */
+ state->block++;
+ /* Set EOF marker */
+ *eof = ( blksize < state->blksize );
return 1;
}
-/*
- * Download a single block via TFTP. This function is non-static so
- * that pxe_export.c can call it.
+/**
+ * Download a file via TFTP
+ *
+ * @v server TFTP server
+ * @v file File name
+ * @v buffer Buffer into which to load file
+ * @ret True File was downloaded successfully
+ * @ret False File was not downloaded successfully
+ * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
+ * @err other As returned by tftp_open()
+ * @err other As returned by tftp_process_opts()
+ * @err other As returned by tftp_ack()
+ * @err other As returned by tftp_process_data()
*
+ * Download a file from a TFTP server into the specified buffer.
*/
-int tftp_block ( struct tftpreq_info_t *request,
- struct tftpblk_info_t *block ) {
- static struct sockaddr_in server;
- static unsigned short lport = 2000; /* local port */
- struct tftp_t *rcvd = NULL;
- static struct tftpreq_t xmit;
- static unsigned short xmitlen = 0;
- static unsigned short blockidx = 0; /* Last block received */
- static unsigned short retry = 0; /* Retry attempts on last block */
- static int blksize = 0;
- unsigned short recvlen = 0;
+static int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
+ struct buffer *buffer ) {
+ struct tftp_state state;
+ union tftp_any *reply;
+ int eof = 0;
- /* If this is a new request (i.e. if name is set), fill in
- * transmit block with RRQ and send it.
- */
- if ( request ) {
- rx_qdrain(); /* Flush receive queue */
- xmit.opcode = htons(TFTP_RRQ);
- xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
- sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
- request->name, 0, 0, 0, request->blksize)
- + 1; /* null terminator */
- blockidx = 0; /* Reset counters */
- retry = 0;
- blksize = TFTP_DEFAULTSIZE_PACKET;
- lport++; /* Use new local port */
- server = *(request->server);
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit) )
- return (0);
+ /* Initialise TFTP state */
+ memset ( &state, 0, sizeof ( state ) );
+ state.server = *server;
+
+ /* Open the file */
+ if ( ! tftp_open ( &state, file, &reply, 0 ) ) {
+ DBG ( "TFTP: could not open %@:%d/%s : %m\n",
+ server->sin_addr.s_addr, server->sin_port, file );
+ return 0;
}
- /* Exit if no transfer in progress */
- if ( !blksize ) return (0);
- /* Loop to wait until we get a packet we're interested in */
- block->data = NULL; /* Used as flag */
- while ( block->data == NULL ) {
- long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
- TIMEOUT, retry );
- if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
- /* No packet received */
- if ( retry++ > MAX_TFTP_RETRIES ) break;
- /* Retransmit last packet */
- if ( !blockidx ) lport++; /* New lport if new RRQ */
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit) )
- return (0);
- continue; /* Back to waiting for packet */
- }
- /* Packet has been received */
- rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
- recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
- - sizeof(rcvd->opcode);
- server.sin_port = ntohs(rcvd->udp.src);
- retry = 0; /* Reset retry counter */
- switch ( htons(rcvd->opcode) ) {
- case TFTP_ERROR : {
- printf ( "TFTP error %d (%s)\n",
- ntohs(rcvd->u.err.errcode),
- rcvd->u.err.errmsg );
- return (0); /* abort */
- }
- case TFTP_OACK : {
- const char *p = rcvd->u.oack.data;
- const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
-
- *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
- if ( blockidx || !request ) break; /* Too late */
- if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
- /* Check for blksize option honoured */
- while ( p < e ) {
- if ( strcasecmp("blksize",p) == 0 &&
- p[7] == '\0' ) {
- blksize = strtoul(p+8,&p,10);
- p++; /* skip null */
- }
- while ( *(p++) ) {};
- }
+
+ /* Fetch file, a block at a time */
+ while ( 1 ) {
+ twiddle();
+ switch ( ntohs ( reply->common.opcode ) ) {
+ case TFTP_DATA:
+ if ( ! tftp_process_data ( &state, &reply->data,
+ buffer, &eof ) ) {
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
+ NULL );
+ return 0;
+ }
+ break;
+ case TFTP_OACK:
+ if ( state.block ) {
+ /* OACK must be first block, if present */
+ DBG ( "TFTP: OACK after block %d\n",
+ state.block );
+ errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
+ NULL );
+ return 0;
}
- if ( blksize < TFTP_DEFAULTSIZE_PACKET ||
- blksize > request->blksize ) {
- /* Incorrect blksize - error and abort */
- xmit.opcode = htons(TFTP_ERROR);
- xmit.u.err.errcode = 8;
- xmitlen = (void*)&xmit.u.err.errmsg
- - (void*)&xmit
- + sprintf((char*)xmit.u.err.errmsg,
- "RFC1782 error")
- + 1;
- udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit);
- return (0);
+ if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
+ DBG ( "TFTP: option processing failed: %m\n" );
+ tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
+ return 0;
}
- } break;
- case TFTP_DATA :
- if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
- break; /* Re-ACK last block sent */
- if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
- break; /* Too large; ignore */
- block->data = rcvd->u.data.download;
- block->block = ++blockidx;
- block->len = recvlen - sizeof(rcvd->u.data.block);
- block->eof = ( (unsigned short)block->len < blksize );
- /* If EOF, zero blksize to indicate transfer done */
- if ( block->eof ) blksize = 0;
break;
- default: break; /* Do nothing */
+ default:
+ DBG ( "TFTP: unexpected opcode %d\n",
+ ntohs ( reply->common.opcode ) );
+ errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
+ return 0;
+ }
+ /* If we have reached EOF, stop here */
+ if ( eof )
+ break;
+ /* Fetch the next data block */
+ if ( ! tftp_ack ( &state, &reply ) ) {
+ DBG ( "TFTP: could not get next block: %m\n" );
+ if ( ! reply ) {
+ tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
+ NULL );
+ }
+ return 0;
}
- /* Send ACK */
- xmit.opcode = htons(TFTP_ACK);
- xmit.u.ack.block = htons(blockidx);
- xmitlen = TFTP_MIN_PACKET;
- udp_transmit ( server.sin_addr.s_addr, lport, server.sin_port,
- xmitlen, &xmit );
}
- return ( block->data ? 1 : 0 );
-}
-
-/*
- * Download a file via TFTP
- *
- */
-int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
- struct buffer *buffer ) {
- struct tftpreq_info_t request_data = {
- .server = server,
- .name = file,
- .blksize = TFTP_MAX_PACKET,
- };
- struct tftpreq_info_t *request = &request_data;
- struct tftpblk_info_t block;
- off_t offset = 0;
- do {
- if ( ! tftp_block ( request, &block ) )
- return 0;
- if ( ! fill_buffer ( buffer, block.data, offset, block.len ) )
- return 0;
- twiddle();
- offset += block.len;
- request = NULL; /* Send request only once */
- } while ( ! block.eof );
+ /* ACK the final packet, as a courtesy to the server */
+ tftp_ack_nowait ( &state );
return 1;
}
diff --git a/src/proto/tftpcore.c b/src/proto/tftpcore.c
new file mode 100644
index 00000000..511c6859
--- /dev/null
+++ b/src/proto/tftpcore.c
@@ -0,0 +1,541 @@
+#include "tftp.h"
+#include "tcp.h" /* for struct tcphdr */
+#include "errno.h"
+#include "etherboot.h"
+#include "tftpcore.h"
+
+/** @file */
+
+/**
+ * await_reply() filter for TFTP packets
+ *
+ * @v ptr Pointer to a struct tftp_state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::lport Client UDP port
+ * @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
+ * @v tftp_state::multicast::sin_port Multicast UDP port, or 0
+ * @v ip IP header
+ * @v udp UDP header
+ * @ret True This is our TFTP packet
+ * @ret False This is not one of our TFTP packets
+ *
+ * Wait for a TFTP packet that is part of the current connection
+ * (i.e. comes from the TFTP server, has the correct destination port,
+ * and is addressed either to our IP address and UDP port, or to our
+ * multicast listening address and UDP port).
+ *
+ * Use await_tftp() in code such as
+ *
+ * @code
+ *
+ * if ( await_reply ( await_tftp, 0, &tftp_state, timeout ) ) {
+ * ...
+ * }
+ *
+ * @endcode
+ */
+static int await_tftp ( int ival __unused, void *ptr,
+ unsigned short ptype __unused, struct iphdr *ip,
+ struct udphdr *udp, struct tcphdr *tcp __unused ) {
+ struct tftp_state *state = ptr;
+
+ /* Must have valid UDP (and, therefore, also IP) headers */
+ if ( ! udp ) {
+ DBG2 ( "TFTPCORE: not UDP\n" );
+ return 0;
+ }
+ /* Packet must come from the TFTP server */
+ if ( ip->src.s_addr != state->server.sin_addr.s_addr ) {
+ DBG2 ( "TFTPCORE: from %@, not from TFTP server %@\n",
+ ip->src.s_addr, state->server.sin_addr.s_addr );
+ return 0;
+ }
+ /* Packet may be addressed to our IP address and unicast UDP
+ * port
+ */
+ if ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) &&
+ ( ntohs ( udp->dest ) == state->lport ) ) {
+ return 1;
+ }
+ /* Packet may be addressed to our multicast IP address and UDP
+ * port, if we have one
+ */
+ if ( ( state->multicast.sin_addr.s_addr ) &&
+ ( ip->dest.s_addr == state->multicast.sin_addr.s_addr ) &&
+ ( ntohs ( udp->dest ) == state->multicast.sin_port ) ) {
+ return 1;
+ }
+ DBG2 ( "TFTPCORE: to %@:%d, not to %@:%d (or %@:%d)\n",
+ ip->dest.s_addr, ntohs ( udp->dest ),
+ arptable[ARP_CLIENT].ipaddr.s_addr, state->lport,
+ state->multicast.sin_addr.s_addr, state->multicast.sin_port );
+ return 0;
+}
+
+/**
+ * Retrieve a TFTP packet
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::lport Client UDP port
+ * @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
+ * @v tftp_state::multicast::sin_port Multicast UDP port, or 0
+ * @v timeout Time to wait for a response
+ * @ret True Received a non-error response
+ * @ret False Received error response / no response
+ * @ret *reply The server's response, if any
+ * @err #PXENV_STATUS_TFTP_READ_TIMEOUT No response received in time
+ * @err other As set by tftp_set_errno()
+ *
+ * Retrieve the next packet sent by the TFTP server, if any is sent
+ * within the specified timeout period. The packet is returned via
+ * #reply. If no packet is received within the timeout period, a NULL
+ * value will be stored in #reply.
+ *
+ * If the response from the server is a TFTP ERROR packet, tftp_get()
+ * will return False and #errno will be set accordingly.
+ *
+ * You can differentiate between "received no response" and "received
+ * an error response" by checking #reply; if #reply is NULL then no
+ * response was received.
+ */
+int tftp_get ( struct tftp_state *state, long timeout,
+ union tftp_any **reply ) {
+
+ *reply = NULL;
+
+ if ( ! await_reply ( await_tftp, 0, state, timeout ) ) {
+ errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
+ return 0;
+ }
+
+ *reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
+ DBG ( "TFTPCORE: got reply (type %d)\n",
+ ntohs ( (*reply)->common.opcode ) );
+ if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
+ tftp_set_errno ( &(*reply)->error );
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Issue a TFTP open request (RRQ)
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::server::sin_port TFTP server UDP port, or 0
+ * @v tftp_state::lport Client UDP port, or 0
+ * @v tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
+ * @v tftp_state::multicast::sin_port Multicast UDP port, or 0
+ * @v tftp_state::blksize Requested blksize, or 0
+ * @v filename File name
+ * @v multicast Enable/disable rfc2090 multicast TFTP
+ * @ret True Received a non-error response
+ * @ret False Received error response / no response
+ * @ret tftp_state::server::sin_port TFTP server UDP port
+ * @ret tftp_state::lport Client UDP port
+ * @ret tftp_state::blksize Always #TFTP_DEFAULT_BLKSIZE
+ * @ret *reply The server's response, if any
+ * @err #PXENV_STATUS_TFTP_OPEN_TIMEOUT TFTP open timed out
+ * @err other As returned by udp_transmit()
+ * @err other As set by tftp_set_errno()
+ *
+ * Send a TFTP/TFTM/MTFTP RRQ (read request) to a TFTP server, and
+ * return the server's reply (which may be an OACK, DATA or ERROR
+ * packet). The server's reply will not be acknowledged, or processed
+ * in any way.
+ *
+ * If tftp_state::server::sin_port is 0, the standard TFTP server port
+ * (#TFTP_PORT) will be used.
+ *
+ * If tftp_state::lport is 0, the standard mechanism of
+ * using a new, unique port number for each TFTP request will be used.
+ *
+ * If tftp_state::multicast::sin_addr is not 0.0.0.0, it (and
+ * tftp_state::multicast::sin_port) will be used as a multicast
+ * listening address for replies from the TFTP server.
+ *
+ * For the various different types of TFTP server, you should treat
+ * tftp_state::lport and tftp_state::multicast as follows:
+ *
+ * - Standard TFTP server: set tftp_state::lport to 0,
+ * tftp_state::multicast::sin_addr to 0.0.0.0 and
+ * tftp_state::multicast::sin_port to 0. tftp_open() will set
+ * tftp_state::lport to the assigned local UDP port.
+ *
+ * - TFTM server: set tftp_state::lport to 0,
+ * tftp_state::multicast::sin_addr to 0.0.0.0 and
+ * tftp_state::multicast::sin_port to 0. tftp_open() will set
+ * tftp_state::lport to the assigned local UDP port. (Your call
+ * to tftp_process_opts() will then overwrite both
+ * tftp_state::multicast::sin_addr and
+ * tftp_state::multicast::sin_port with the values specified in
+ * the OACK packet.)
+ *
+ * - MTFTP server: set tftp_state::multicast::sin_addr to the
+ * multicast address and both tftp_state::lport and
+ * tftp_state::multicast::sin_port to the multicast port (both of
+ * which must be previously known, e.g. provided by a DHCP
+ * server). tftp_open() will not alter these values.
+ *
+ * If tftp_state::blksize is 0, the maximum blocksize
+ * (#TFTP_MAX_BLKSIZE) will be requested.
+ *
+ * On exit, tftp_state::blksize will always contain
+ * #TFTP_DEFAULT_BLKSIZE, since this is the blocksize value that must
+ * be assumed until the OACK packet is processed (by a subsequent call
+ * to tftp_process_opts()).
+ *
+ * tftp_state::server::sin_port will be set to the UDP port from which
+ * the server's response originated. This may or may not be the port
+ * to which the open request was sent.
+ *
+ * The options "blksize" and "tsize" will always be appended to a TFTP
+ * open request. The option "multicast" will be appended to the
+ * request if #multicast is True. Servers that do not understand any
+ * of these options should simply ignore them.
+ *
+ * tftp_open() will not automatically join or leave multicast groups;
+ * the caller is responsible for calling join_group() and
+ * leave_group() at appropriate times.
+ *
+ * If the response from the server is a TFTP ERROR packet, tftp_open()
+ * will return False and #errno will be set accordingly.
+ */
+int tftp_open ( struct tftp_state *state, const char *filename,
+ union tftp_any **reply, int multicast ) {
+ static unsigned short lport = 2000; /* local port */
+ int fixed_lport;
+ struct tftp_rrq rrq;
+ char *p;
+ unsigned int rrqlen;
+ int retry;
+
+ /* Flush receive queue */
+ rx_qdrain();
+
+ /* Default to blksize of TFTP_MAX_BLKSIZE if none specified */
+ if ( ! state->blksize )
+ state->blksize = TFTP_MAX_BLKSIZE;
+
+ /* Use default TFTP server port if none specified */
+ if ( ! state->server.sin_port )
+ state->server.sin_port = TFTP_PORT;
+
+ /* Determine whether or not to use lport */
+ fixed_lport = state->lport;
+
+ /* Set up RRQ */
+ rrq.opcode = htons ( TFTP_RRQ );
+ p = rrq.data;
+ p += sprintf ( p, "%s%coctet%cblksize%c%d%ctsize%c0",
+ filename, 0, 0, 0, state->blksize, 0, 0 ) + 1;
+ if ( multicast ) {
+ p += sprintf ( p, "multicast%c", 0 ) + 1;
+ }
+ rrqlen = ( p - ( char * ) &rrq );
+
+ /* Set negotiated blksize to default value */
+ state->blksize = TFTP_DEFAULT_BLKSIZE;
+
+ /* Nullify received packet pointer */
+ *reply = NULL;
+
+ /* Transmit RRQ until we get a response */
+ for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
+ long timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
+
+ /* Set client UDP port, if not already fixed */
+ if ( ! fixed_lport )
+ state->lport = ++lport;
+
+ /* Send the RRQ */
+ DBG ( "TFTPCORE: requesting %@:%d/%s from port %d\n",
+ state->server.sin_addr.s_addr, state->server.sin_port,
+ rrq.data, state->lport );
+ if ( ! udp_transmit ( state->server.sin_addr.s_addr,
+ state->lport, state->server.sin_port,
+ rrqlen, &rrq ) )
+ return 0;
+
+ /* Wait for response */
+ if ( tftp_get ( state, timeout, reply ) ) {
+ /* We got a non-error response */
+ state->server.sin_port
+ = ntohs ( (*reply)->common.udp.src );
+ DBG ( "TFTP server is at %@:%d\n",
+ state->server.sin_addr.s_addr,
+ state->server.sin_port );
+ return 1;
+ }
+ if ( *reply ) {
+ /* We got an error response; abort */
+ return 0;
+ }
+ }
+
+ DBG ( "TFTPCORE: open request timed out\n" );
+ errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
+ return 0;
+}
+
+/**
+ * Process a TFTP OACK packet
+ *
+ * @v state TFTP transfer state
+ * @v oack The TFTP OACK packet
+ * @ret True Options were processed successfully
+ * @ret False Options were not processed successfully
+ * @ret tftp_state::blksize Negotiated blksize
+ * @ret tftp_state::tsize File size (if known), or 0
+ * @ret tftp_state::multicast::sin_addr Multicast IP address, or 0.0.0.0
+ * @ret tftp_state::multicast::sin_port Multicast UDP port, or 0
+ * @ret tftp_state::master Client is master
+ * @err EINVAL An invalid option value was encountered
+ *
+ * Process the options returned by the TFTP server in an rfc2347 OACK
+ * packet. The options "blksize" (rfc2348), "tsize" (rfc2349) and
+ * "multicast" (rfc2090) are recognised and processed; any other
+ * options are silently ignored.
+ *
+ * Where an option is not present in the OACK packet, the
+ * corresponding field(s) in #state will be left unaltered.
+ *
+ * Calling tftp_process_opts() does not send an acknowledgement for
+ * the OACK packet; this is the responsibility of the caller.
+ *
+ * @note If the "blksize" option is not present, tftp_state::blksize
+ * will @b not be implicitly set to #TFTP_DEFAULT_BLKSIZE. However,
+ * since tftp_open() always sets tftp_state::blksize to
+ * #TFTP_DEFAULT_BLKSIZE before returning, you probably don't need to
+ * worry about this.
+ */
+int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
+ const char *p;
+ const char *end;
+
+ DBG ( "TFTPCORE: processing OACK\n" );
+
+ /* End of options */
+ end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
+
+ /* Only possible error */
+ errno = EINVAL;
+
+ for ( p = oack->data ; p < end ; ) {
+ if ( strcasecmp ( "blksize", p ) == 0 ) {
+ p += 8;
+ state->blksize = strtoul ( p, &p, 10 );
+ if ( *p ) {
+ DBG ( "TFTPCORE: garbage \"%s\" "
+ "after blksize\n", p );
+ return 0;
+ }
+ p++;
+ DBG ( "TFTPCORE: got blksize %d\n", state->blksize );
+ } else if ( strcasecmp ( "tsize", p ) == 0 ) {
+ p += 6;
+ state->tsize = strtoul ( p, &p, 10 );
+ if ( *p ) {
+ DBG ( "TFTPCORE: garbage \"%s\" "
+ "after tsize\n", p );
+ return 0;
+ }
+ p++;
+ DBG ( "TFTPCORE: got tsize %d\n", state->tsize );
+ } else if ( strcasecmp ( "multicast", p ) == 0 ) {
+ p += 10;
+ char *e = strchr ( p, ',' );
+ if ( ( ! e ) || ( e >= end ) ) {
+ DBG ( "TFTPCORE: malformed multicast field "
+ "\"%s\"\n", p );
+ return 0;
+ }
+ /* IP address may be missing, in which case we
+ * should leave state->multicast.sin_addr
+ * unaltered.
+ */
+ if ( e != p ) {
+ int rc;
+ *e = '\0';
+ rc = inet_aton ( p,
+ &state->multicast.sin_addr );
+ *e = ',';
+ if ( ! rc ) {
+ DBG ( "TFTPCORE: malformed multicast "
+ "IP address \"%s\"\n", p );
+ return 0;
+ }
+ }
+ p = e + 1;
+ /* UDP port may also be missing */
+ if ( *p != ',' ) {
+ state->multicast.sin_port
+ = strtoul ( p, &p, 10 );
+ if ( *p != ',' ) {
+ DBG ( "TFTPCORE: garbage \"%s\" "
+ "after multicast port\n", p );
+ return 0;
+ }
+ }
+ p++;
+ /* "Master Client" must always be present */
+ state->master = strtoul ( p, &p, 10 );
+ if ( *p ) {
+ DBG ( "TFTPCORE: garbage \"%s\" "
+ "after multicast mc\n", p );
+ return 0;
+ }
+ p++;
+ DBG ( "TFTPCORE: got multicast %@:%d (%s)\n",
+ state->multicast.sin_addr.s_addr,
+ state->multicast.sin_port,
+ ( state->master ? "master" : "not master" ) );
+ } else {
+ DBG ( "TFTPCORE: unknown option \"%s\"\n", p );
+ p += strlen ( p ) + 1; /* skip option name */
+ p += strlen ( p ) + 1; /* skip option value */
+ }
+ }
+
+ if ( p > end ) {
+ DBG ( "TFTPCORE: overran options in OACK\n" );
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Acknowledge a TFTP packet
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::server::sin_port TFTP server UDP port
+ * @v tftp_state::lport Client UDP port
+ * @v tftp_state::block Most recently received block number
+ * @ret True Acknowledgement packet was sent
+ * @ret False Acknowledgement packet was not sent
+ * @err other As returned by udp_transmit()
+ *
+ * Send a TFTP ACK packet for the most recently received block.
+ *
+ * This sends only a single ACK packet; it does not wait for the
+ * server's response.
+ */
+int tftp_ack_nowait ( struct tftp_state *state ) {
+ struct tftp_ack ack;
+
+ DBG ( "TFTPCORE: acknowledging data block %d\n", state->block );
+ ack.opcode = htons ( TFTP_ACK );
+ ack.block = htons ( state->block );
+ return udp_transmit ( state->server.sin_addr.s_addr,
+ state->lport, state->server.sin_port,
+ sizeof ( ack ), &ack );
+}
+
+/**
+ * Acknowledge a TFTP packet and wait for a response
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::server::sin_port TFTP server UDP port
+ * @v tftp_state::lport Client UDP port
+ * @v tftp_state::block Most recently received block number
+ * @ret True Received a non-error response
+ * @ret False Received error response / no response
+ * @ret *reply The server's response, if any
+ * @err #PXENV_STATUS_TFTP_READ_TIMEOUT Timed out waiting for a response
+ * @err other As returned by tftp_ack_nowait()
+ * @err other As set by tftp_set_errno()
+ *
+ * Send a TFTP ACK packet for the most recently received data block,
+ * and keep transmitting this ACK until we get a response from the
+ * server (e.g. a new data block).
+ *
+ * If the response is a TFTP DATA packet, no processing is done.
+ * Specifically, the block number is not checked to ensure that this
+ * is indeed the next data block in the sequence, nor is
+ * tftp_state::block updated with the new block number.
+ *
+ * If the response from the server is a TFTP ERROR packet, tftp_open()
+ * will return False and #errno will be set accordingly.
+ */
+int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
+ int retry;
+
+ *reply = NULL;
+ for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
+ long timeout = rfc2131_sleep_interval ( TFTP_REXMT, retry );
+ /* ACK the last data block */
+ if ( ! tftp_ack_nowait ( state ) ) {
+ DBG ( "TFTP: could not send ACK: %m\n" );
+ return 0;
+ }
+ if ( tftp_get ( state, timeout, reply ) ) {
+ /* We got a non-error response */
+ return 1;
+ }
+ if ( *reply ) {
+ /* We got an error response */
+ return 0;
+ }
+ }
+ DBG ( "TFTP: timed out during read\n" );
+ errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
+ return 0;
+}
+
+/**
+ * Send a TFTP error
+ *
+ * @v state TFTP transfer state
+ * @v tftp_state::server::sin_addr TFTP server IP address
+ * @v tftp_state::server::sin_port TFTP server UDP port
+ * @v tftp_state::lport Client UDP port
+ * @v errcode TFTP error code
+ * @v errmsg Descriptive error string, or NULL
+ * @ret True Error packet was sent
+ * @ret False Error packet was not sent
+ *
+ * Send a TFTP ERROR packet back to the server to terminate the
+ * transfer.
+ *
+ * If #errmsg is NULL, the current error message string as returned by
+ * strerror(errno) will be used as the error text.
+ */
+int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
+ struct tftp_error error;
+
+ DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
+ error.opcode = htons ( TFTP_ERROR );
+ error.errcode = htons ( errcode );
+ strncpy ( error.errmsg, errmsg ? errmsg : strerror ( errno ),
+ sizeof ( error.errmsg ) );
+ return udp_transmit ( state->server.sin_addr.s_addr,
+ state->lport, state->server.sin_port,
+ sizeof ( error ), &error );
+}
+
+/**
+ * Interpret a TFTP error
+ *
+ * @v error Pointer to a struct tftp_error
+ *
+ * Sets #errno based on the error code in a TFTP ERROR packet.
+ */
+void tftp_set_errno ( struct tftp_error *error ) {
+ static int errmap[] = {
+ [TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND,
+ [TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION,
+ [TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE,
+ };
+ unsigned int errcode = ntohs ( error->errcode );
+
+ errno = 0;
+ if ( errcode < ( sizeof(errmap) / sizeof(errmap[0]) ) )
+ errno = errmap[errcode];
+ if ( ! errno )
+ errno = PXENV_STATUS_TFTP_ERROR_OPCODE;
+}
diff --git a/src/util/makerom.pl b/src/util/makerom.pl
index 695468c2..9d751e19 100755
--- a/src/util/makerom.pl
+++ b/src/util/makerom.pl
@@ -157,7 +157,7 @@ sub makerom () {
# If PXE image, just fill the length field and write it out
if ($opts{'x'}) {
substr($rom, 2, 1) = chr((length($rom) + 511) / 512);
- &writerom($ARGV[0], \$rom);
+ writerom($ARGV[0], \$rom);
return;
}
# Size specified with -s overrides value in 3rd byte in image
@@ -168,7 +168,7 @@ sub makerom () {
$romsize = ($filesize + 511) & ~511
}
} else {
- $romsize = &getromsize(\$rom);
+ $romsize = getromsize(\$rom);
# 0 put there by *loader.S means makerom should pick the size
if ($romsize == 0) {
# Shrink romsize down to the smallest power of two that will do
@@ -190,14 +190,16 @@ sub makerom () {
}
substr($rom, 2, 1) = chr(($romsize / 512) % 256);
print "ROM size is $romsize\n" if $opts{'v'};
- my $identoffset = &addident(\$rom);
- &pcipnpheaders(\$rom, $identoffset);
- &undiheaders(\$rom);
+ # set the product string only if we don't have one yet
+ my $pnp_hdr_offset = unpack('v', substr($rom, PNP_PTR_LOC, 2));
+ my $identoffset = substr($rom, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) eq "\0\0" ? addident(\$rom) : undef;
+ pcipnpheaders(\$rom, $identoffset);
+ undiheaders(\$rom);
# 3c503 requires last two bytes to be 0x80
substr($rom, MINROMSIZE-2, 2) = "\x80\x80"
if ($opts{'3'} and $romsize == MINROMSIZE);
- &checksum(\$rom);
- &writerom($ARGV[0], \$rom);
+ checksum(\$rom);
+ writerom($ARGV[0], \$rom);
}
sub modrom () {
@@ -211,16 +213,16 @@ sub modrom () {
close(R);
defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n";
print "$filesize bytes read\n" if $opts{'v'};
- &pcipnpheaders(\$rom);
- &undiheaders(\$rom);
- &checksum(\$rom);
- &writerom($ARGV[0], \$rom);
+ pcipnpheaders(\$rom, undef);
+ undiheaders(\$rom);
+ checksum(\$rom);
+ writerom($ARGV[0], \$rom);
}
# Main routine. See how we were called and behave accordingly
if ($0 =~ m:modrom(\.pl)?$:) {
- &modrom();
+ modrom();
} else {
- &makerom();
+ makerom();
}
exit(0);