diff options
Diffstat (limited to 'src/interface')
| -rw-r--r-- | src/interface/pxe/pxe.c | 16 | ||||
| -rw-r--r-- | src/interface/pxe/pxe_preboot.c | 6 | ||||
| -rw-r--r-- | src/interface/pxe/pxe_undi.c | 299 |
3 files changed, 151 insertions, 170 deletions
diff --git a/src/interface/pxe/pxe.c b/src/interface/pxe/pxe.c index 8aa7df0d7..5e67758bc 100644 --- a/src/interface/pxe/pxe.c +++ b/src/interface/pxe/pxe.c @@ -22,12 +22,24 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "dev.h" +#include <gpxe/netdevice.h> #include "pxe.h" -#warning "pxe_netdev should hold a persistent reference to the net device" struct net_device *pxe_netdev = NULL; +/** + * Set network device as current PXE network device + * + * @v netdev Network device, or NULL + */ +void pxe_set_netdev ( struct net_device *netdev ) { + if ( pxe_netdev ) + netdev_put ( pxe_netdev ); + pxe_netdev = NULL; + if ( netdev ) + pxe_netdev = netdev_get ( netdev ); +} + #if 0 /* Global pointer to currently installed PXE stack */ diff --git a/src/interface/pxe/pxe_preboot.c b/src/interface/pxe/pxe_preboot.c index 3cbbf2260..15752b231 100644 --- a/src/interface/pxe/pxe_preboot.c +++ b/src/interface/pxe/pxe_preboot.c @@ -28,7 +28,7 @@ #include <stdlib.h> #include <gpxe/uaccess.h> #include <gpxe/dhcp.h> -#include <dhcp_basemem.h> +#include <basemem_packet.h> #include "pxe.h" #include "pxe_call.h" @@ -99,10 +99,10 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO */ len = get_cached_info->BufferSize; if ( len == 0 ) { - len = sizeof ( dhcp_basemem ); + len = sizeof ( basemem_packet ); get_cached_info->Buffer.segment = rm_ds; get_cached_info->Buffer.offset = - ( unsigned int ) ( & __from_data16 ( dhcp_basemem ) ); + ( unsigned int ) ( & __from_data16 ( basemem_packet ) ); get_cached_info->BufferLimit = len; } diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c index 4a9b3390b..9488d9f40 100644 --- a/src/interface/pxe/pxe_undi.c +++ b/src/interface/pxe/pxe_undi.c @@ -23,12 +23,18 @@ */ #include <stdint.h> +#include <stdio.h> #include <string.h> +#include <byteswap.h> +#include <basemem_packet.h> #include <gpxe/netdevice.h> +#include <gpxe/iobuf.h> #include <gpxe/device.h> #include <gpxe/pci.h> -#include <gpxe/isapnp.h> #include <gpxe/if_ether.h> +#include <gpxe/ip.h> +#include <gpxe/arp.h> +#include <gpxe/rarp.h> #include <gpxe/shutdown.h> #include "pxe.h" @@ -128,61 +134,76 @@ PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) { */ 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; + struct s_PXENV_UNDI_TBD tbd; + struct DataBlk *datablk; + struct io_buffer *iobuf; + struct net_protocol *net_protocol; + char destaddr[MAX_LL_ADDR_LEN]; + const void *ll_dest; + size_t len; + unsigned int i; + int rc; DBG ( "PXENV_UNDI_TRANSMIT" ); -#if 0 - /* 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 ) { + /* Identify network-layer protocol */ + switch ( undi_transmit->Protocol ) { + case P_IP: net_protocol = &ipv4_protocol; break; + case P_ARP: net_protocol = &arp_protocol; break; + case P_RARP: net_protocol = &rarp_protocol; break; + case P_UNKNOWN: net_protocol = NULL; break; + default: 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 ); + /* Calculate total packet length */ + copy_from_real ( &tbd, undi_transmit->TBD.segment, + undi_transmit->TBD.offset, sizeof ( tbd ) ); + len = tbd.ImmedLength; + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + len += datablk->TDDataLen; } - /* 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 = ETH_P_IP; break; - case P_ARP: type = ETH_P_ARP; break; - case P_RARP: type = ETH_P_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; + /* Allocate and fill I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES; return PXENV_EXIT_FAILURE; } + copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment, + tbd.Xmit.offset, tbd.ImmedLength ); + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ), + datablk->TDDataPtr.segment, + datablk->TDDataPtr.offset, + datablk->TDDataLen ); + } - /* Send the packet */ - eth_transmit ( dest, type, length, data ); -#endif - - undi_transmit->Status = PXENV_STATUS_SUCCESS; - return PXENV_EXIT_SUCCESS; + /* Transmit packet */ + if ( net_protocol == NULL ) { + /* Link-layer header already present */ + rc = netdev_tx ( pxe_netdev, iobuf ); + } else { + /* Calculate destination address */ + if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { + copy_from_real ( destaddr, + undi_transmit->DestAddr.segment, + undi_transmit->DestAddr.offset, + pxe_netdev->ll_protocol->ll_addr_len ); + ll_dest = destaddr; + } else { + ll_dest = pxe_netdev->ll_protocol->ll_broadcast; + } + rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest ); + } + +#warning "TX completion?" + + undi_transmit->Status = PXENV_STATUS ( rc ); + return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE ); } /* PXENV_UNDI_SET_MCAST_ADDRESS @@ -200,30 +221,30 @@ pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS /* PXENV_UNDI_SET_STATION_ADDRESS * - * Status: working (deliberately incomplete) + * Status: working */ PXENV_EXIT_t pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS *undi_set_station_address ) { + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" ); -#if 0 - /* 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 adapter is open, the change will have no effect; return + * an error */ - 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; + if ( pxe_netdev->state & NETDEV_OPEN ) { + undi_set_station_address->Status = + PXENV_STATUS_UNDI_INVALID_STATE; + return PXENV_EXIT_FAILURE; } -#endif - undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED; - return PXENV_EXIT_FAILURE; + /* Update MAC address */ + memcpy ( pxe_netdev->ll_addr, + &undi_set_station_address->StationAddress, + pxe_netdev->ll_protocol->ll_addr_len ); + + undi_set_station_address = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; } /* PXENV_UNDI_SET_PACKET_FILTER @@ -248,33 +269,11 @@ PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION *undi_get_information ) { struct device *dev = pxe_netdev->dev; struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; - unsigned int ioaddr; - unsigned int irqno; DBG ( "PXENV_UNDI_GET_INFORMATION" ); - switch ( dev->desc.bus_type ) { - case BUS_TYPE_PCI: { - struct pci_device *pci = - container_of ( dev, struct pci_device, dev ); - - ioaddr = pci->ioaddr; - irqno = pci->irq; - break; } - case BUS_TYPE_ISAPNP: { - struct isapnp_device *isapnp = - container_of ( dev, struct isapnp_device, dev ); - - ioaddr = isapnp->ioaddr; - irqno = isapnp->irqno; - break; } - default: - undi_get_information->Status = PXENV_STATUS_FAILURE; - return PXENV_EXIT_FAILURE; - } - - undi_get_information->BaseIo = ioaddr; - undi_get_information->IntNumber = irqno; + undi_get_information->BaseIo = dev->desc.ioaddr; + undi_get_information->IntNumber = dev->desc.irq; /* Cheat: assume all cards can cope with this */ undi_get_information->MaxTranUnit = ETH_MAX_MTU; undi_get_information->HwType = ntohs ( ll_protocol->ll_proto ); @@ -384,17 +383,15 @@ PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE switch ( dev->desc.bus_type ) { case BUS_TYPE_PCI: { - struct pci_device *pci = - container_of ( dev, struct pci_device, dev ); struct pci_nic_info *info = &undi_get_nic_type->info.pci; undi_get_nic_type->NicType = PCI_NIC; - info->Vendor_ID = pci->vendor; - info->Dev_ID = pci->device; - info->Base_Class = PCI_BASE_CLASS ( pci->class ); - info->Sub_Class = PCI_SUB_CLASS ( pci->class ); - info->Prog_Intf = PCI_PROG_INTF ( pci->class ); - info->BusDevFunc = PCI_BUSDEVFN ( pci->bus, pci->devfn ); + info->Vendor_ID = dev->desc.vendor; + info->Dev_ID = dev->desc.device; + info->Base_Class = PCI_BASE_CLASS ( dev->desc.class ); + info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class ); + info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class ); + info->BusDevFunc = dev->desc.location; /* Cheat: remaining fields are probably unnecessary, * and would require adding extra code to pci.c. */ @@ -402,14 +399,12 @@ PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type->info.pci.SubDevice_ID = 0xffff; break; } case BUS_TYPE_ISAPNP: { - struct isapnp_device *isapnp = - container_of ( dev, struct isapnp_device, dev ); struct pnp_nic_info *info = &undi_get_nic_type->info.pnp; undi_get_nic_type->NicType = PnP_NIC; - info->EISA_Dev_ID = ( ( isapnp->vendor_id << 16 ) | - isapnp->prod_id ); - info->CardSelNum = isapnp->csn; + info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) | + dev->desc.device ); + info->CardSelNum = dev->desc.location; /* Cheat: remaining fields are probably unnecessary, * and would require adding extra code to isapnp.c. */ @@ -431,16 +426,15 @@ 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" ); -#if 0 /* 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" ); + snprintf ( ( char * ) undi_get_iface_info->IfaceType, + sizeof ( 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) ); -#endif undi_get_iface_info->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; @@ -463,18 +457,11 @@ PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE * Status: working */ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { + struct io_buffer *iobuf; + size_t len; + DBG ( "PXENV_UNDI_ISR" ); -#if 0 - /* 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... */ @@ -486,18 +473,14 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { 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 ) ) { + + /* Call poll(). This should acknowledge the device + * interrupt and queue up any received packet. + */ + if ( netdev_poll ( pxe_netdev, -1U ) ) { + /* Packet waiting in queue */ DBG ( " OURS" ); - eth_irq ( DISABLE ); undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; } else { DBG ( " NOT_OURS" ); @@ -505,62 +488,48 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { } 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 ETH_P_IP: undi_isr->ProtType = P_IP; break; - case ETH_P_ARP: undi_isr->ProtType = P_ARP; break; - case ETH_P_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; - } + case PXENV_UNDI_ISR_IN_GET_NEXT : + DBG ( " PROCESS/GET_NEXT" ); + + /* Remove first packet from netdev RX queue */ + iobuf = netdev_rx_dequeue ( pxe_netdev ); + if ( ! iobuf ) { + /* No more packets remaining */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; 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 ); + + /* Copy packet to base memory buffer */ + len = iob_len ( iobuf ); + DBG ( " RECEIVE %zd", len ); + if ( len > sizeof ( basemem_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_DONE; + memcpy ( basemem_packet, iobuf->data, len ); + + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; + undi_isr->BufferLength = len; + undi_isr->FrameLength = len; + undi_isr->FrameHeaderLength = + pxe_netdev->ll_protocol->ll_header_len; + undi_isr->Frame.segment = rm_ds; + undi_isr->Frame.offset = + ( ( unsigned ) & __from_data16 ( basemem_packet ) ); + /* Probably ought to fill in packet type */ + undi_isr->ProtType = P_UNKNOWN; + undi_isr->PktType = XMT_DESTADDR; break; default : + DBG ( " INVALID(%04x)", undi_isr->FuncFlag ); + /* Should never happen */ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; return PXENV_EXIT_FAILURE; } -#endif undi_isr->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; |
