1*1b8adde7SWilliam Kucharski /************************************************************************** 2*1b8adde7SWilliam Kucharski Etherboot - BOOTP/TFTP Bootstrap Program 3*1b8adde7SWilliam Kucharski Bochs Pseudo NIC driver for Etherboot 4*1b8adde7SWilliam Kucharski ***************************************************************************/ 5*1b8adde7SWilliam Kucharski 6*1b8adde7SWilliam Kucharski /* 7*1b8adde7SWilliam Kucharski * This program is free software; you can redistribute it and/or 8*1b8adde7SWilliam Kucharski * modify it under the terms of the GNU General Public License as 9*1b8adde7SWilliam Kucharski * published by the Free Software Foundation; either version 2, or (at 10*1b8adde7SWilliam Kucharski * your option) any later version. 11*1b8adde7SWilliam Kucharski * 12*1b8adde7SWilliam Kucharski * See pnic_api.h for an explanation of the Bochs Pseudo NIC. 13*1b8adde7SWilliam Kucharski */ 14*1b8adde7SWilliam Kucharski 15*1b8adde7SWilliam Kucharski /* to get some global routines like printf */ 16*1b8adde7SWilliam Kucharski #include "etherboot.h" 17*1b8adde7SWilliam Kucharski /* to get the interface to the body of the program */ 18*1b8adde7SWilliam Kucharski #include "nic.h" 19*1b8adde7SWilliam Kucharski /* to get the PCI support functions, if this is a PCI NIC */ 20*1b8adde7SWilliam Kucharski #include "pci.h" 21*1b8adde7SWilliam Kucharski 22*1b8adde7SWilliam Kucharski /* PNIC API */ 23*1b8adde7SWilliam Kucharski #include "pnic_api.h" 24*1b8adde7SWilliam Kucharski 25*1b8adde7SWilliam Kucharski /* Private data structure */ 26*1b8adde7SWilliam Kucharski typedef struct { 27*1b8adde7SWilliam Kucharski uint16_t api_version; 28*1b8adde7SWilliam Kucharski } pnic_priv_data_t; 29*1b8adde7SWilliam Kucharski 30*1b8adde7SWilliam Kucharski /* Function prototypes */ 31*1b8adde7SWilliam Kucharski static int pnic_api_check ( uint16_t api_version ); 32*1b8adde7SWilliam Kucharski 33*1b8adde7SWilliam Kucharski /* NIC specific static variables go here */ 34*1b8adde7SWilliam Kucharski static uint8_t tx_buffer[ETH_FRAME_LEN]; 35*1b8adde7SWilliam Kucharski 36*1b8adde7SWilliam Kucharski /* 37*1b8adde7SWilliam Kucharski * Utility functions: issue a PNIC command, retrieve result. Use 38*1b8adde7SWilliam Kucharski * pnic_command_quiet if you don't want failure codes to be 39*1b8adde7SWilliam Kucharski * automatically printed. Returns the PNIC status code. 40*1b8adde7SWilliam Kucharski * 41*1b8adde7SWilliam Kucharski * Set output_length to NULL only if you expect to receive exactly 42*1b8adde7SWilliam Kucharski * output_max_length bytes, otherwise it'll complain that you didn't 43*1b8adde7SWilliam Kucharski * get enough data (on the assumption that if you not interested in 44*1b8adde7SWilliam Kucharski * discovering the output length then you're expecting a fixed amount 45*1b8adde7SWilliam Kucharski * of data). 46*1b8adde7SWilliam Kucharski */ 47*1b8adde7SWilliam Kucharski 48*1b8adde7SWilliam Kucharski static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command, 49*1b8adde7SWilliam Kucharski void *input, uint16_t input_length, 50*1b8adde7SWilliam Kucharski void *output, uint16_t output_max_length, 51*1b8adde7SWilliam Kucharski uint16_t *output_length ) { 52*1b8adde7SWilliam Kucharski int i; 53*1b8adde7SWilliam Kucharski uint16_t status; 54*1b8adde7SWilliam Kucharski uint16_t _output_length; 55*1b8adde7SWilliam Kucharski 56*1b8adde7SWilliam Kucharski if ( input != NULL ) { 57*1b8adde7SWilliam Kucharski /* Write input length */ 58*1b8adde7SWilliam Kucharski outw ( input_length, nic->ioaddr + PNIC_REG_LEN ); 59*1b8adde7SWilliam Kucharski /* Write input data */ 60*1b8adde7SWilliam Kucharski for ( i = 0; i < input_length; i++ ) { 61*1b8adde7SWilliam Kucharski outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA ); 62*1b8adde7SWilliam Kucharski } 63*1b8adde7SWilliam Kucharski } 64*1b8adde7SWilliam Kucharski /* Write command */ 65*1b8adde7SWilliam Kucharski outw ( command, nic->ioaddr + PNIC_REG_CMD ); 66*1b8adde7SWilliam Kucharski /* Retrieve status */ 67*1b8adde7SWilliam Kucharski status = inw ( nic->ioaddr + PNIC_REG_STAT ); 68*1b8adde7SWilliam Kucharski /* Retrieve output length */ 69*1b8adde7SWilliam Kucharski _output_length = inw ( nic->ioaddr + PNIC_REG_LEN ); 70*1b8adde7SWilliam Kucharski if ( output_length == NULL ) { 71*1b8adde7SWilliam Kucharski if ( _output_length != output_max_length ) { 72*1b8adde7SWilliam Kucharski printf ( "pnic_command %#hx: wrong data length " 73*1b8adde7SWilliam Kucharski "returned (expected %d, got %d)\n", command, 74*1b8adde7SWilliam Kucharski output_max_length, _output_length ); 75*1b8adde7SWilliam Kucharski } 76*1b8adde7SWilliam Kucharski } else { 77*1b8adde7SWilliam Kucharski *output_length = _output_length; 78*1b8adde7SWilliam Kucharski } 79*1b8adde7SWilliam Kucharski if ( output != NULL ) { 80*1b8adde7SWilliam Kucharski if ( _output_length > output_max_length ) { 81*1b8adde7SWilliam Kucharski printf ( "pnic_command %#hx: output buffer too small " 82*1b8adde7SWilliam Kucharski "(have %d, need %d)\n", command, 83*1b8adde7SWilliam Kucharski output_max_length, _output_length ); 84*1b8adde7SWilliam Kucharski _output_length = output_max_length; 85*1b8adde7SWilliam Kucharski } 86*1b8adde7SWilliam Kucharski /* Retrieve output data */ 87*1b8adde7SWilliam Kucharski for ( i = 0; i < _output_length; i++ ) { 88*1b8adde7SWilliam Kucharski ((char*)output)[i] = 89*1b8adde7SWilliam Kucharski inb ( nic->ioaddr + PNIC_REG_DATA ); 90*1b8adde7SWilliam Kucharski } 91*1b8adde7SWilliam Kucharski } 92*1b8adde7SWilliam Kucharski return status; 93*1b8adde7SWilliam Kucharski } 94*1b8adde7SWilliam Kucharski 95*1b8adde7SWilliam Kucharski static uint16_t pnic_command ( struct nic *nic, uint16_t command, 96*1b8adde7SWilliam Kucharski void *input, uint16_t input_length, 97*1b8adde7SWilliam Kucharski void *output, uint16_t output_max_length, 98*1b8adde7SWilliam Kucharski uint16_t *output_length ) { 99*1b8adde7SWilliam Kucharski pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data; 100*1b8adde7SWilliam Kucharski uint16_t status = pnic_command_quiet ( nic, command, 101*1b8adde7SWilliam Kucharski input, input_length, 102*1b8adde7SWilliam Kucharski output, output_max_length, 103*1b8adde7SWilliam Kucharski output_length ); 104*1b8adde7SWilliam Kucharski if ( status == PNIC_STATUS_OK ) return status; 105*1b8adde7SWilliam Kucharski printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n", 106*1b8adde7SWilliam Kucharski command, input_length, status ); 107*1b8adde7SWilliam Kucharski if ( priv->api_version ) pnic_api_check(priv->api_version); 108*1b8adde7SWilliam Kucharski return status; 109*1b8adde7SWilliam Kucharski } 110*1b8adde7SWilliam Kucharski 111*1b8adde7SWilliam Kucharski /* Check API version matches that of NIC */ 112*1b8adde7SWilliam Kucharski static int pnic_api_check ( uint16_t api_version ) { 113*1b8adde7SWilliam Kucharski if ( api_version != PNIC_API_VERSION ) { 114*1b8adde7SWilliam Kucharski printf ( "Warning: API version mismatch! " 115*1b8adde7SWilliam Kucharski "(NIC's is %d.%d, ours is %d.%d)\n", 116*1b8adde7SWilliam Kucharski api_version >> 8, api_version & 0xff, 117*1b8adde7SWilliam Kucharski PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff ); 118*1b8adde7SWilliam Kucharski } 119*1b8adde7SWilliam Kucharski if ( api_version < PNIC_API_VERSION ) { 120*1b8adde7SWilliam Kucharski printf ( "*** You may need to update your copy of Bochs ***\n" ); 121*1b8adde7SWilliam Kucharski } 122*1b8adde7SWilliam Kucharski return ( api_version == PNIC_API_VERSION ); 123*1b8adde7SWilliam Kucharski } 124*1b8adde7SWilliam Kucharski 125*1b8adde7SWilliam Kucharski /************************************************************************** 126*1b8adde7SWilliam Kucharski POLL - Wait for a frame 127*1b8adde7SWilliam Kucharski ***************************************************************************/ 128*1b8adde7SWilliam Kucharski static int pnic_poll(struct nic *nic, int retrieve) 129*1b8adde7SWilliam Kucharski { 130*1b8adde7SWilliam Kucharski uint16_t length; 131*1b8adde7SWilliam Kucharski uint16_t qlen; 132*1b8adde7SWilliam Kucharski 133*1b8adde7SWilliam Kucharski /* Check receive queue length to see if there's anything to 134*1b8adde7SWilliam Kucharski * get. Necessary since once we've called PNIC_CMD_RECV we 135*1b8adde7SWilliam Kucharski * have to read out the packet, otherwise it's lost forever. 136*1b8adde7SWilliam Kucharski */ 137*1b8adde7SWilliam Kucharski if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0, 138*1b8adde7SWilliam Kucharski &qlen, sizeof(qlen), NULL ) 139*1b8adde7SWilliam Kucharski != PNIC_STATUS_OK ) return ( 0 ); 140*1b8adde7SWilliam Kucharski if ( qlen == 0 ) return ( 0 ); 141*1b8adde7SWilliam Kucharski 142*1b8adde7SWilliam Kucharski /* There is a packet ready. Return 1 if we're only checking. */ 143*1b8adde7SWilliam Kucharski if ( ! retrieve ) return ( 1 ); 144*1b8adde7SWilliam Kucharski 145*1b8adde7SWilliam Kucharski /* Retrieve the packet */ 146*1b8adde7SWilliam Kucharski if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0, 147*1b8adde7SWilliam Kucharski nic->packet, ETH_FRAME_LEN, &length ) 148*1b8adde7SWilliam Kucharski != PNIC_STATUS_OK ) return ( 0 ); 149*1b8adde7SWilliam Kucharski nic->packetlen = length; 150*1b8adde7SWilliam Kucharski return ( 1 ); 151*1b8adde7SWilliam Kucharski } 152*1b8adde7SWilliam Kucharski 153*1b8adde7SWilliam Kucharski /************************************************************************** 154*1b8adde7SWilliam Kucharski TRANSMIT - Transmit a frame 155*1b8adde7SWilliam Kucharski ***************************************************************************/ 156*1b8adde7SWilliam Kucharski static void pnic_transmit( 157*1b8adde7SWilliam Kucharski struct nic *nic, 158*1b8adde7SWilliam Kucharski const char *dest, /* Destination */ 159*1b8adde7SWilliam Kucharski unsigned int type, /* Type */ 160*1b8adde7SWilliam Kucharski unsigned int size, /* size */ 161*1b8adde7SWilliam Kucharski const char *data) /* Packet */ 162*1b8adde7SWilliam Kucharski { 163*1b8adde7SWilliam Kucharski unsigned int nstype = htons ( type ); 164*1b8adde7SWilliam Kucharski 165*1b8adde7SWilliam Kucharski if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) { 166*1b8adde7SWilliam Kucharski printf ( "pnic_transmit: packet too large\n" ); 167*1b8adde7SWilliam Kucharski return; 168*1b8adde7SWilliam Kucharski } 169*1b8adde7SWilliam Kucharski 170*1b8adde7SWilliam Kucharski /* Assemble packet */ 171*1b8adde7SWilliam Kucharski memcpy ( tx_buffer, dest, ETH_ALEN ); 172*1b8adde7SWilliam Kucharski memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN ); 173*1b8adde7SWilliam Kucharski memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 ); 174*1b8adde7SWilliam Kucharski memcpy ( tx_buffer + ETH_HLEN, data, size ); 175*1b8adde7SWilliam Kucharski 176*1b8adde7SWilliam Kucharski pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size, 177*1b8adde7SWilliam Kucharski NULL, 0, NULL ); 178*1b8adde7SWilliam Kucharski } 179*1b8adde7SWilliam Kucharski 180*1b8adde7SWilliam Kucharski /************************************************************************** 181*1b8adde7SWilliam Kucharski DISABLE - Turn off ethernet interface 182*1b8adde7SWilliam Kucharski ***************************************************************************/ 183*1b8adde7SWilliam Kucharski static void pnic_disable(struct dev *dev) 184*1b8adde7SWilliam Kucharski { 185*1b8adde7SWilliam Kucharski struct nic *nic = (struct nic *)dev; 186*1b8adde7SWilliam Kucharski pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL ); 187*1b8adde7SWilliam Kucharski } 188*1b8adde7SWilliam Kucharski 189*1b8adde7SWilliam Kucharski /************************************************************************** 190*1b8adde7SWilliam Kucharski IRQ - Handle card interrupt status 191*1b8adde7SWilliam Kucharski ***************************************************************************/ 192*1b8adde7SWilliam Kucharski static void pnic_irq ( struct nic *nic, irq_action_t action ) 193*1b8adde7SWilliam Kucharski { 194*1b8adde7SWilliam Kucharski uint8_t enabled; 195*1b8adde7SWilliam Kucharski 196*1b8adde7SWilliam Kucharski switch ( action ) { 197*1b8adde7SWilliam Kucharski case DISABLE : 198*1b8adde7SWilliam Kucharski case ENABLE : 199*1b8adde7SWilliam Kucharski enabled = ( action == ENABLE ? 1 : 0 ); 200*1b8adde7SWilliam Kucharski pnic_command ( nic, PNIC_CMD_MASK_IRQ, 201*1b8adde7SWilliam Kucharski &enabled, sizeof(enabled), NULL, 0, NULL ); 202*1b8adde7SWilliam Kucharski break; 203*1b8adde7SWilliam Kucharski case FORCE : 204*1b8adde7SWilliam Kucharski pnic_command ( nic, PNIC_CMD_FORCE_IRQ, 205*1b8adde7SWilliam Kucharski NULL, 0, NULL, 0, NULL ); 206*1b8adde7SWilliam Kucharski break; 207*1b8adde7SWilliam Kucharski } 208*1b8adde7SWilliam Kucharski } 209*1b8adde7SWilliam Kucharski 210*1b8adde7SWilliam Kucharski /************************************************************************** 211*1b8adde7SWilliam Kucharski PROBE - Look for an adapter, this routine's visible to the outside 212*1b8adde7SWilliam Kucharski ***************************************************************************/ 213*1b8adde7SWilliam Kucharski 214*1b8adde7SWilliam Kucharski static int pnic_probe(struct dev *dev, struct pci_device *pci) 215*1b8adde7SWilliam Kucharski { 216*1b8adde7SWilliam Kucharski struct nic *nic = (struct nic *)dev; 217*1b8adde7SWilliam Kucharski static pnic_priv_data_t priv; 218*1b8adde7SWilliam Kucharski uint16_t status; 219*1b8adde7SWilliam Kucharski 220*1b8adde7SWilliam Kucharski printf(" - "); 221*1b8adde7SWilliam Kucharski 222*1b8adde7SWilliam Kucharski /* Clear private data structure and chain it in */ 223*1b8adde7SWilliam Kucharski memset ( &priv, 0, sizeof(priv) ); 224*1b8adde7SWilliam Kucharski nic->priv_data = &priv; 225*1b8adde7SWilliam Kucharski 226*1b8adde7SWilliam Kucharski /* Mask the bit that says "this is an io addr" */ 227*1b8adde7SWilliam Kucharski nic->ioaddr = pci->ioaddr & ~3; 228*1b8adde7SWilliam Kucharski nic->irqno = pci->irq; 229*1b8adde7SWilliam Kucharski /* Not sure what this does, but the rtl8139 driver does it */ 230*1b8adde7SWilliam Kucharski adjust_pci_device(pci); 231*1b8adde7SWilliam Kucharski 232*1b8adde7SWilliam Kucharski status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0, 233*1b8adde7SWilliam Kucharski &priv.api_version, 234*1b8adde7SWilliam Kucharski sizeof(priv.api_version), NULL ); 235*1b8adde7SWilliam Kucharski if ( status != PNIC_STATUS_OK ) { 236*1b8adde7SWilliam Kucharski printf ( "PNIC failed installation check, code %#hx\n", 237*1b8adde7SWilliam Kucharski status ); 238*1b8adde7SWilliam Kucharski return 0; 239*1b8adde7SWilliam Kucharski } 240*1b8adde7SWilliam Kucharski pnic_api_check(priv.api_version); 241*1b8adde7SWilliam Kucharski status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0, 242*1b8adde7SWilliam Kucharski nic->node_addr, ETH_ALEN, NULL ); 243*1b8adde7SWilliam Kucharski printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n", 244*1b8adde7SWilliam Kucharski nic->node_addr, priv.api_version>>8, priv.api_version&0xff, 245*1b8adde7SWilliam Kucharski nic->ioaddr ); 246*1b8adde7SWilliam Kucharski 247*1b8adde7SWilliam Kucharski /* point to NIC specific routines */ 248*1b8adde7SWilliam Kucharski dev->disable = pnic_disable; 249*1b8adde7SWilliam Kucharski nic->poll = pnic_poll; 250*1b8adde7SWilliam Kucharski nic->transmit = pnic_transmit; 251*1b8adde7SWilliam Kucharski nic->irq = pnic_irq; 252*1b8adde7SWilliam Kucharski return 1; 253*1b8adde7SWilliam Kucharski } 254*1b8adde7SWilliam Kucharski 255*1b8adde7SWilliam Kucharski static struct pci_id pnic_nics[] = { 256*1b8adde7SWilliam Kucharski /* genrules.pl doesn't let us use macros for PCI IDs...*/ 257*1b8adde7SWilliam Kucharski PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"), 258*1b8adde7SWilliam Kucharski }; 259*1b8adde7SWilliam Kucharski 260*1b8adde7SWilliam Kucharski struct pci_driver pnic_driver = { 261*1b8adde7SWilliam Kucharski .type = NIC_DRIVER, 262*1b8adde7SWilliam Kucharski .name = "PNIC", 263*1b8adde7SWilliam Kucharski .probe = pnic_probe, 264*1b8adde7SWilliam Kucharski .ids = pnic_nics, 265*1b8adde7SWilliam Kucharski .id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]), 266*1b8adde7SWilliam Kucharski .class = 0, 267*1b8adde7SWilliam Kucharski }; 268