1/************************************************************************** 2Etherboot - BOOTP/TFTP Bootstrap Program 3Bochs Pseudo NIC driver for Etherboot 4***************************************************************************/ 5 6/* 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2, or (at 10 * your option) any later version. 11 * 12 * See pnic_api.h for an explanation of the Bochs Pseudo NIC. 13 */ 14 15FILE_LICENCE ( GPL2_OR_LATER ); 16 17#include <stdint.h> 18#include <stdio.h> 19#include <gpxe/io.h> 20#include <errno.h> 21#include <gpxe/pci.h> 22#include <gpxe/if_ether.h> 23#include <gpxe/ethernet.h> 24#include <gpxe/iobuf.h> 25#include <gpxe/netdevice.h> 26 27#include "pnic_api.h" 28 29struct pnic { 30 unsigned short ioaddr; 31}; 32 33/* 34 * Utility functions: issue a PNIC command, retrieve result. Use 35 * pnic_command_quiet if you don't want failure codes to be 36 * automatically printed. Returns the PNIC status code. 37 * 38 * Set output_length to NULL only if you expect to receive exactly 39 * output_max_length bytes, otherwise it'll complain that you didn't 40 * get enough data (on the assumption that if you not interested in 41 * discovering the output length then you're expecting a fixed amount 42 * of data). 43 */ 44 45static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command, 46 const void *input, uint16_t input_length, 47 void *output, uint16_t output_max_length, 48 uint16_t *output_length ) { 49 uint16_t status; 50 uint16_t _output_length; 51 52 if ( input != NULL ) { 53 /* Write input length */ 54 outw ( input_length, pnic->ioaddr + PNIC_REG_LEN ); 55 /* Write input data */ 56 outsb ( pnic->ioaddr + PNIC_REG_DATA, input, input_length ); 57 } 58 /* Write command */ 59 outw ( command, pnic->ioaddr + PNIC_REG_CMD ); 60 /* Retrieve status */ 61 status = inw ( pnic->ioaddr + PNIC_REG_STAT ); 62 /* Retrieve output length */ 63 _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN ); 64 if ( output_length == NULL ) { 65 if ( _output_length != output_max_length ) { 66 printf ( "pnic_command %#hx: wrong data length " 67 "returned (expected %d, got %d)\n", command, 68 output_max_length, _output_length ); 69 } 70 } else { 71 *output_length = _output_length; 72 } 73 if ( output != NULL ) { 74 if ( _output_length > output_max_length ) { 75 printf ( "pnic_command %#hx: output buffer too small " 76 "(have %d, need %d)\n", command, 77 output_max_length, _output_length ); 78 _output_length = output_max_length; 79 } 80 /* Retrieve output data */ 81 insb ( pnic->ioaddr + PNIC_REG_DATA, output, _output_length ); 82 } 83 return status; 84} 85 86static uint16_t pnic_command ( struct pnic *pnic, uint16_t command, 87 const void *input, uint16_t input_length, 88 void *output, uint16_t output_max_length, 89 uint16_t *output_length ) { 90 uint16_t status = pnic_command_quiet ( pnic, command, 91 input, input_length, 92 output, output_max_length, 93 output_length ); 94 if ( status == PNIC_STATUS_OK ) return status; 95 printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n", 96 command, input_length, status ); 97 return status; 98} 99 100/* Check API version matches that of NIC */ 101static int pnic_api_check ( uint16_t api_version ) { 102 if ( api_version != PNIC_API_VERSION ) { 103 printf ( "Warning: API version mismatch! " 104 "(NIC's is %d.%d, ours is %d.%d)\n", 105 api_version >> 8, api_version & 0xff, 106 PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff ); 107 } 108 if ( api_version < PNIC_API_VERSION ) { 109 printf ( "** You may need to update your copy of Bochs **\n" ); 110 } 111 return ( api_version == PNIC_API_VERSION ); 112} 113 114/************************************************************************** 115POLL - Wait for a frame 116***************************************************************************/ 117static void pnic_poll ( struct net_device *netdev ) { 118 struct pnic *pnic = netdev->priv; 119 struct io_buffer *iobuf; 120 uint16_t length; 121 uint16_t qlen; 122 123 /* Fetch all available packets */ 124 while ( 1 ) { 125 if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0, 126 &qlen, sizeof ( qlen ), NULL ) 127 != PNIC_STATUS_OK ) 128 return; 129 if ( qlen == 0 ) 130 return; 131 iobuf = alloc_iob ( ETH_FRAME_LEN ); 132 if ( ! iobuf ) { 133 DBG ( "could not allocate buffer\n" ); 134 netdev_rx_err ( netdev, NULL, -ENOMEM ); 135 return; 136 } 137 if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0, 138 iobuf->data, ETH_FRAME_LEN, &length ) 139 != PNIC_STATUS_OK ) { 140 netdev_rx_err ( netdev, iobuf, -EIO ); 141 return; 142 } 143 iob_put ( iobuf, length ); 144 netdev_rx ( netdev, iobuf ); 145 } 146} 147 148/************************************************************************** 149TRANSMIT - Transmit a frame 150***************************************************************************/ 151static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { 152 struct pnic *pnic = netdev->priv; 153 154 /* Pad the packet */ 155 iob_pad ( iobuf, ETH_ZLEN ); 156 157 /* Send packet */ 158 pnic_command ( pnic, PNIC_CMD_XMIT, iobuf->data, iob_len ( iobuf ), 159 NULL, 0, NULL ); 160 161 netdev_tx_complete ( netdev, iobuf ); 162 return 0; 163} 164 165/************************************************************************** 166OPEN - Open network device 167***************************************************************************/ 168static int pnic_open ( struct net_device *netdev __unused ) { 169 /* Nothing to do */ 170 return 0; 171} 172 173/************************************************************************** 174CLOSE - Close network device 175***************************************************************************/ 176static void pnic_close ( struct net_device *netdev __unused ) { 177 /* Nothing to do */ 178} 179 180/************************************************************************** 181IRQ - Enable/disable interrupts 182***************************************************************************/ 183static void pnic_irq ( struct net_device *netdev, int enable ) { 184 struct pnic *pnic = netdev->priv; 185 uint8_t mask = ( enable ? 1 : 0 ); 186 187 pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ), 188 NULL, 0, NULL ); 189} 190 191/************************************************************************** 192OPERATIONS TABLE 193***************************************************************************/ 194static struct net_device_operations pnic_operations = { 195 .open = pnic_open, 196 .close = pnic_close, 197 .transmit = pnic_transmit, 198 .poll = pnic_poll, 199 .irq = pnic_irq, 200}; 201 202/************************************************************************** 203DISABLE - Turn off ethernet interface 204***************************************************************************/ 205static void pnic_remove ( struct pci_device *pci ) { 206 struct net_device *netdev = pci_get_drvdata ( pci ); 207 struct pnic *pnic = netdev->priv; 208 209 unregister_netdev ( netdev ); 210 pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL ); 211 netdev_nullify ( netdev ); 212 netdev_put ( netdev ); 213} 214 215/************************************************************************** 216PROBE - Look for an adapter, this routine's visible to the outside 217***************************************************************************/ 218static int pnic_probe ( struct pci_device *pci, 219 const struct pci_device_id *id __unused ) { 220 struct net_device *netdev; 221 struct pnic *pnic; 222 uint16_t api_version; 223 uint16_t status; 224 int rc; 225 226 /* Allocate net device */ 227 netdev = alloc_etherdev ( sizeof ( *pnic ) ); 228 if ( ! netdev ) 229 return -ENOMEM; 230 netdev_init ( netdev, &pnic_operations ); 231 pnic = netdev->priv; 232 pci_set_drvdata ( pci, netdev ); 233 netdev->dev = &pci->dev; 234 pnic->ioaddr = pci->ioaddr; 235 236 /* Fix up PCI device */ 237 adjust_pci_device ( pci ); 238 239 /* API version check */ 240 status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0, 241 &api_version, 242 sizeof ( api_version ), NULL ); 243 if ( status != PNIC_STATUS_OK ) { 244 printf ( "PNIC failed installation check, code %#hx\n", 245 status ); 246 rc = -EIO; 247 goto err; 248 } 249 pnic_api_check ( api_version ); 250 251 /* Get MAC address */ 252 status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0, 253 netdev->hw_addr, ETH_ALEN, NULL ); 254 255 /* Mark as link up; PNIC has no concept of link state */ 256 netdev_link_up ( netdev ); 257 258 /* Register network device */ 259 if ( ( rc = register_netdev ( netdev ) ) != 0 ) 260 goto err; 261 262 return 0; 263 264 err: 265 /* Free net device */ 266 netdev_nullify ( netdev ); 267 netdev_put ( netdev ); 268 return rc; 269} 270 271static struct pci_device_id pnic_nics[] = { 272/* genrules.pl doesn't let us use macros for PCI IDs...*/ 273PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor", 0 ), 274}; 275 276struct pci_driver pnic_driver __pci_driver = { 277 .ids = pnic_nics, 278 .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ), 279 .probe = pnic_probe, 280 .remove = pnic_remove, 281}; 282