diff --git a/src/include/gpxe/arp.h b/src/include/gpxe/arp.h index 1c702b00..6464ce0c 100644 --- a/src/include/gpxe/arp.h +++ b/src/include/gpxe/arp.h @@ -30,6 +30,8 @@ struct arp_net_protocol { #define __arp_net_protocol \ __table ( struct arp_net_protocol, arp_net_protocols, 01 ) +extern struct net_protocol arp_protocol; + extern int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol, const void *dest_net_addr, diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index 9800ef56..0060e7d5 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -122,6 +122,8 @@ struct ll_protocol { uint16_t ll_proto; /** Link-layer address length */ uint8_t ll_addr_len; + /** Link-layer header length */ + uint8_t ll_header_len; /** Link-layer broadcast address */ const uint8_t *ll_broadcast; }; diff --git a/src/include/gpxe/rarp.h b/src/include/gpxe/rarp.h new file mode 100644 index 00000000..81e03bde --- /dev/null +++ b/src/include/gpxe/rarp.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_RARP_H +#define _GPXE_RARP_H + +/** @file + * + * Reverse Address Resolution Protocol + * + */ + +struct net_protocol; + +extern struct net_protocol rarp_protocol; + +#endif /* _GPXE_RARP_H */ diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c index e108764c..9488d9f4 100644 --- a/src/interface/pxe/pxe_undi.c +++ b/src/interface/pxe/pxe_undi.c @@ -23,12 +23,18 @@ */ #include +#include #include #include +#include #include +#include #include #include #include +#include +#include +#include #include #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 ) { - 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. - */ + /* Identify network-layer protocol */ 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; + 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; } - /* Send the packet */ - eth_transmit ( dest, type, length, data ); -#endif - - undi_transmit->Status = PXENV_STATUS_SUCCESS; - return PXENV_EXIT_SUCCESS; + /* 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; + } + + /* 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 ); + } + + /* 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 @@ -405,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; @@ -437,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... */ @@ -460,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" ); @@ -479,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; - } - 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 ); + 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; } - undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + + /* 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; + } + 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; diff --git a/src/net/arp.c b/src/net/arp.c index 383dc842..011d4fef 100644 --- a/src/net/arp.c +++ b/src/net/arp.c @@ -281,8 +281,7 @@ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, * * This operation is meaningless for the ARP protocol. */ -static const char * -arp_ntoa ( const void *net_addr __attribute__ (( unused )) ) { +static const char * arp_ntoa ( const void *net_addr __unused ) { return ""; } diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 7108beff..8f259fb3 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -108,6 +108,7 @@ struct ll_protocol ethernet_protocol __ll_protocol = { .name = "Ethernet", .ll_proto = htons ( ARPHRD_ETHER ), .ll_addr_len = ETH_ALEN, + .ll_header_len = ETH_HLEN, .ll_broadcast = eth_broadcast, .tx = eth_tx, .rx = eth_rx, diff --git a/src/net/rarp.c b/src/net/rarp.c new file mode 100644 index 00000000..bb5e6ad7 --- /dev/null +++ b/src/net/rarp.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/** @file + * + * Reverse Address Resolution Protocol + * + */ + +/** + * Process incoming ARP packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + * + * This is a dummy method which simply discards RARP packets. + */ +static int rarp_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_source __unused ) { + free_iob ( iobuf ); + return 0; +} + + +/** + * Transcribe RARP address + * + * @v net_addr RARP address + * @ret string "" + * + * This operation is meaningless for the RARP protocol. + */ +static const char * rarp_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** RARP protocol */ +struct net_protocol rarp_protocol __net_protocol = { + .name = "RARP", + .net_proto = htons ( ETH_P_RARP ), + .rx = rarp_rx, + .ntoa = rarp_ntoa, +};