17c478bd9Sstevel@tonic-gate /**************************************************************************
27c478bd9Sstevel@tonic-gate Etherboot - BOOTP/TFTP Bootstrap Program
37c478bd9Sstevel@tonic-gate Bochs Pseudo NIC driver for Etherboot
47c478bd9Sstevel@tonic-gate ***************************************************************************/
57c478bd9Sstevel@tonic-gate
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate * This program is free software; you can redistribute it and/or
87c478bd9Sstevel@tonic-gate * modify it under the terms of the GNU General Public License as
97c478bd9Sstevel@tonic-gate * published by the Free Software Foundation; either version 2, or (at
107c478bd9Sstevel@tonic-gate * your option) any later version.
117c478bd9Sstevel@tonic-gate *
127c478bd9Sstevel@tonic-gate * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
137c478bd9Sstevel@tonic-gate */
147c478bd9Sstevel@tonic-gate
157c478bd9Sstevel@tonic-gate /* to get some global routines like printf */
167c478bd9Sstevel@tonic-gate #include "etherboot.h"
177c478bd9Sstevel@tonic-gate /* to get the interface to the body of the program */
187c478bd9Sstevel@tonic-gate #include "nic.h"
197c478bd9Sstevel@tonic-gate /* to get the PCI support functions, if this is a PCI NIC */
207c478bd9Sstevel@tonic-gate #include "pci.h"
217c478bd9Sstevel@tonic-gate
227c478bd9Sstevel@tonic-gate /* PNIC API */
237c478bd9Sstevel@tonic-gate #include "pnic_api.h"
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate /* Private data structure */
267c478bd9Sstevel@tonic-gate typedef struct {
277c478bd9Sstevel@tonic-gate uint16_t api_version;
287c478bd9Sstevel@tonic-gate } pnic_priv_data_t;
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate /* Function prototypes */
317c478bd9Sstevel@tonic-gate static int pnic_api_check ( uint16_t api_version );
327c478bd9Sstevel@tonic-gate
337c478bd9Sstevel@tonic-gate /* NIC specific static variables go here */
347c478bd9Sstevel@tonic-gate static uint8_t tx_buffer[ETH_FRAME_LEN];
357c478bd9Sstevel@tonic-gate
367c478bd9Sstevel@tonic-gate /*
377c478bd9Sstevel@tonic-gate * Utility functions: issue a PNIC command, retrieve result. Use
387c478bd9Sstevel@tonic-gate * pnic_command_quiet if you don't want failure codes to be
397c478bd9Sstevel@tonic-gate * automatically printed. Returns the PNIC status code.
407c478bd9Sstevel@tonic-gate *
417c478bd9Sstevel@tonic-gate * Set output_length to NULL only if you expect to receive exactly
427c478bd9Sstevel@tonic-gate * output_max_length bytes, otherwise it'll complain that you didn't
437c478bd9Sstevel@tonic-gate * get enough data (on the assumption that if you not interested in
447c478bd9Sstevel@tonic-gate * discovering the output length then you're expecting a fixed amount
457c478bd9Sstevel@tonic-gate * of data).
467c478bd9Sstevel@tonic-gate */
477c478bd9Sstevel@tonic-gate
pnic_command_quiet(struct nic * nic,uint16_t command,void * input,uint16_t input_length,void * output,uint16_t output_max_length,uint16_t * output_length)487c478bd9Sstevel@tonic-gate static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
497c478bd9Sstevel@tonic-gate void *input, uint16_t input_length,
507c478bd9Sstevel@tonic-gate void *output, uint16_t output_max_length,
517c478bd9Sstevel@tonic-gate uint16_t *output_length ) {
527c478bd9Sstevel@tonic-gate int i;
537c478bd9Sstevel@tonic-gate uint16_t status;
547c478bd9Sstevel@tonic-gate uint16_t _output_length;
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate if ( input != NULL ) {
577c478bd9Sstevel@tonic-gate /* Write input length */
587c478bd9Sstevel@tonic-gate outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
597c478bd9Sstevel@tonic-gate /* Write input data */
607c478bd9Sstevel@tonic-gate for ( i = 0; i < input_length; i++ ) {
617c478bd9Sstevel@tonic-gate outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
627c478bd9Sstevel@tonic-gate }
637c478bd9Sstevel@tonic-gate }
647c478bd9Sstevel@tonic-gate /* Write command */
657c478bd9Sstevel@tonic-gate outw ( command, nic->ioaddr + PNIC_REG_CMD );
667c478bd9Sstevel@tonic-gate /* Retrieve status */
677c478bd9Sstevel@tonic-gate status = inw ( nic->ioaddr + PNIC_REG_STAT );
687c478bd9Sstevel@tonic-gate /* Retrieve output length */
697c478bd9Sstevel@tonic-gate _output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
707c478bd9Sstevel@tonic-gate if ( output_length == NULL ) {
717c478bd9Sstevel@tonic-gate if ( _output_length != output_max_length ) {
727c478bd9Sstevel@tonic-gate printf ( "pnic_command %#hx: wrong data length "
737c478bd9Sstevel@tonic-gate "returned (expected %d, got %d)\n", command,
747c478bd9Sstevel@tonic-gate output_max_length, _output_length );
757c478bd9Sstevel@tonic-gate }
767c478bd9Sstevel@tonic-gate } else {
777c478bd9Sstevel@tonic-gate *output_length = _output_length;
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate if ( output != NULL ) {
807c478bd9Sstevel@tonic-gate if ( _output_length > output_max_length ) {
817c478bd9Sstevel@tonic-gate printf ( "pnic_command %#hx: output buffer too small "
827c478bd9Sstevel@tonic-gate "(have %d, need %d)\n", command,
837c478bd9Sstevel@tonic-gate output_max_length, _output_length );
847c478bd9Sstevel@tonic-gate _output_length = output_max_length;
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate /* Retrieve output data */
877c478bd9Sstevel@tonic-gate for ( i = 0; i < _output_length; i++ ) {
887c478bd9Sstevel@tonic-gate ((char*)output)[i] =
897c478bd9Sstevel@tonic-gate inb ( nic->ioaddr + PNIC_REG_DATA );
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate return status;
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate
pnic_command(struct nic * nic,uint16_t command,void * input,uint16_t input_length,void * output,uint16_t output_max_length,uint16_t * output_length)957c478bd9Sstevel@tonic-gate static uint16_t pnic_command ( struct nic *nic, uint16_t command,
967c478bd9Sstevel@tonic-gate void *input, uint16_t input_length,
977c478bd9Sstevel@tonic-gate void *output, uint16_t output_max_length,
987c478bd9Sstevel@tonic-gate uint16_t *output_length ) {
997c478bd9Sstevel@tonic-gate pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data;
1007c478bd9Sstevel@tonic-gate uint16_t status = pnic_command_quiet ( nic, command,
1017c478bd9Sstevel@tonic-gate input, input_length,
1027c478bd9Sstevel@tonic-gate output, output_max_length,
1037c478bd9Sstevel@tonic-gate output_length );
1047c478bd9Sstevel@tonic-gate if ( status == PNIC_STATUS_OK ) return status;
1057c478bd9Sstevel@tonic-gate printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
1067c478bd9Sstevel@tonic-gate command, input_length, status );
1077c478bd9Sstevel@tonic-gate if ( priv->api_version ) pnic_api_check(priv->api_version);
1087c478bd9Sstevel@tonic-gate return status;
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate /* Check API version matches that of NIC */
pnic_api_check(uint16_t api_version)1127c478bd9Sstevel@tonic-gate static int pnic_api_check ( uint16_t api_version ) {
1137c478bd9Sstevel@tonic-gate if ( api_version != PNIC_API_VERSION ) {
1147c478bd9Sstevel@tonic-gate printf ( "Warning: API version mismatch! "
1157c478bd9Sstevel@tonic-gate "(NIC's is %d.%d, ours is %d.%d)\n",
1167c478bd9Sstevel@tonic-gate api_version >> 8, api_version & 0xff,
1177c478bd9Sstevel@tonic-gate PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate if ( api_version < PNIC_API_VERSION ) {
1207c478bd9Sstevel@tonic-gate printf ( "*** You may need to update your copy of Bochs ***\n" );
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate return ( api_version == PNIC_API_VERSION );
1237c478bd9Sstevel@tonic-gate }
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate /**************************************************************************
1267c478bd9Sstevel@tonic-gate POLL - Wait for a frame
1277c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_poll(struct nic * nic,int retrieve)1287c478bd9Sstevel@tonic-gate static int pnic_poll(struct nic *nic, int retrieve)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate uint16_t length;
1317c478bd9Sstevel@tonic-gate uint16_t qlen;
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate /* Check receive queue length to see if there's anything to
1347c478bd9Sstevel@tonic-gate * get. Necessary since once we've called PNIC_CMD_RECV we
1357c478bd9Sstevel@tonic-gate * have to read out the packet, otherwise it's lost forever.
1367c478bd9Sstevel@tonic-gate */
1377c478bd9Sstevel@tonic-gate if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
1387c478bd9Sstevel@tonic-gate &qlen, sizeof(qlen), NULL )
1397c478bd9Sstevel@tonic-gate != PNIC_STATUS_OK ) return ( 0 );
1407c478bd9Sstevel@tonic-gate if ( qlen == 0 ) return ( 0 );
1417c478bd9Sstevel@tonic-gate
1427c478bd9Sstevel@tonic-gate /* There is a packet ready. Return 1 if we're only checking. */
1437c478bd9Sstevel@tonic-gate if ( ! retrieve ) return ( 1 );
1447c478bd9Sstevel@tonic-gate
1457c478bd9Sstevel@tonic-gate /* Retrieve the packet */
1467c478bd9Sstevel@tonic-gate if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
1477c478bd9Sstevel@tonic-gate nic->packet, ETH_FRAME_LEN, &length )
1487c478bd9Sstevel@tonic-gate != PNIC_STATUS_OK ) return ( 0 );
1497c478bd9Sstevel@tonic-gate nic->packetlen = length;
1507c478bd9Sstevel@tonic-gate return ( 1 );
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate
1537c478bd9Sstevel@tonic-gate /**************************************************************************
1547c478bd9Sstevel@tonic-gate TRANSMIT - Transmit a frame
1557c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_transmit(struct nic * nic,const char * dest,unsigned int type,unsigned int size,const char * data)1567c478bd9Sstevel@tonic-gate static void pnic_transmit(
1577c478bd9Sstevel@tonic-gate struct nic *nic,
1587c478bd9Sstevel@tonic-gate const char *dest, /* Destination */
1597c478bd9Sstevel@tonic-gate unsigned int type, /* Type */
1607c478bd9Sstevel@tonic-gate unsigned int size, /* size */
1617c478bd9Sstevel@tonic-gate const char *data) /* Packet */
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate unsigned int nstype = htons ( type );
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
1667c478bd9Sstevel@tonic-gate printf ( "pnic_transmit: packet too large\n" );
1677c478bd9Sstevel@tonic-gate return;
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate
1707c478bd9Sstevel@tonic-gate /* Assemble packet */
1717c478bd9Sstevel@tonic-gate memcpy ( tx_buffer, dest, ETH_ALEN );
1727c478bd9Sstevel@tonic-gate memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
1737c478bd9Sstevel@tonic-gate memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
1747c478bd9Sstevel@tonic-gate memcpy ( tx_buffer + ETH_HLEN, data, size );
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
1777c478bd9Sstevel@tonic-gate NULL, 0, NULL );
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate
1807c478bd9Sstevel@tonic-gate /**************************************************************************
1817c478bd9Sstevel@tonic-gate DISABLE - Turn off ethernet interface
1827c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_disable(struct dev * dev)1837c478bd9Sstevel@tonic-gate static void pnic_disable(struct dev *dev)
1847c478bd9Sstevel@tonic-gate {
1857c478bd9Sstevel@tonic-gate struct nic *nic = (struct nic *)dev;
1867c478bd9Sstevel@tonic-gate pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
1877c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate
1897c478bd9Sstevel@tonic-gate /**************************************************************************
1907c478bd9Sstevel@tonic-gate IRQ - Handle card interrupt status
1917c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_irq(struct nic * nic,irq_action_t action)1927c478bd9Sstevel@tonic-gate static void pnic_irq ( struct nic *nic, irq_action_t action )
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate uint8_t enabled;
1957c478bd9Sstevel@tonic-gate
1967c478bd9Sstevel@tonic-gate switch ( action ) {
1977c478bd9Sstevel@tonic-gate case DISABLE :
1987c478bd9Sstevel@tonic-gate case ENABLE :
1997c478bd9Sstevel@tonic-gate enabled = ( action == ENABLE ? 1 : 0 );
2007c478bd9Sstevel@tonic-gate pnic_command ( nic, PNIC_CMD_MASK_IRQ,
2017c478bd9Sstevel@tonic-gate &enabled, sizeof(enabled), NULL, 0, NULL );
2027c478bd9Sstevel@tonic-gate break;
2037c478bd9Sstevel@tonic-gate case FORCE :
2047c478bd9Sstevel@tonic-gate pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
2057c478bd9Sstevel@tonic-gate NULL, 0, NULL, 0, NULL );
2067c478bd9Sstevel@tonic-gate break;
2077c478bd9Sstevel@tonic-gate }
2087c478bd9Sstevel@tonic-gate }
2097c478bd9Sstevel@tonic-gate
2107c478bd9Sstevel@tonic-gate /**************************************************************************
2117c478bd9Sstevel@tonic-gate PROBE - Look for an adapter, this routine's visible to the outside
2127c478bd9Sstevel@tonic-gate ***************************************************************************/
2137c478bd9Sstevel@tonic-gate
pnic_probe(struct dev * dev,struct pci_device * pci)2147c478bd9Sstevel@tonic-gate static int pnic_probe(struct dev *dev, struct pci_device *pci)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate struct nic *nic = (struct nic *)dev;
2177c478bd9Sstevel@tonic-gate static pnic_priv_data_t priv;
2187c478bd9Sstevel@tonic-gate uint16_t status;
2197c478bd9Sstevel@tonic-gate
2207c478bd9Sstevel@tonic-gate printf(" - ");
2217c478bd9Sstevel@tonic-gate
2227c478bd9Sstevel@tonic-gate /* Clear private data structure and chain it in */
2237c478bd9Sstevel@tonic-gate memset ( &priv, 0, sizeof(priv) );
2247c478bd9Sstevel@tonic-gate nic->priv_data = &priv;
2257c478bd9Sstevel@tonic-gate
2267c478bd9Sstevel@tonic-gate /* Mask the bit that says "this is an io addr" */
2277c478bd9Sstevel@tonic-gate nic->ioaddr = pci->ioaddr & ~3;
2287c478bd9Sstevel@tonic-gate nic->irqno = pci->irq;
2297c478bd9Sstevel@tonic-gate /* Not sure what this does, but the rtl8139 driver does it */
2307c478bd9Sstevel@tonic-gate adjust_pci_device(pci);
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0,
2337c478bd9Sstevel@tonic-gate &priv.api_version,
2347c478bd9Sstevel@tonic-gate sizeof(priv.api_version), NULL );
2357c478bd9Sstevel@tonic-gate if ( status != PNIC_STATUS_OK ) {
2367c478bd9Sstevel@tonic-gate printf ( "PNIC failed installation check, code %#hx\n",
2377c478bd9Sstevel@tonic-gate status );
2387c478bd9Sstevel@tonic-gate return 0;
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate pnic_api_check(priv.api_version);
2417c478bd9Sstevel@tonic-gate status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
2427c478bd9Sstevel@tonic-gate nic->node_addr, ETH_ALEN, NULL );
2437c478bd9Sstevel@tonic-gate printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n",
2447c478bd9Sstevel@tonic-gate nic->node_addr, priv.api_version>>8, priv.api_version&0xff,
2457c478bd9Sstevel@tonic-gate nic->ioaddr );
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate /* point to NIC specific routines */
2487c478bd9Sstevel@tonic-gate dev->disable = pnic_disable;
2497c478bd9Sstevel@tonic-gate nic->poll = pnic_poll;
2507c478bd9Sstevel@tonic-gate nic->transmit = pnic_transmit;
2517c478bd9Sstevel@tonic-gate nic->irq = pnic_irq;
2527c478bd9Sstevel@tonic-gate return 1;
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate static struct pci_id pnic_nics[] = {
2567c478bd9Sstevel@tonic-gate /* genrules.pl doesn't let us use macros for PCI IDs...*/
2577c478bd9Sstevel@tonic-gate PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"),
2587c478bd9Sstevel@tonic-gate };
2597c478bd9Sstevel@tonic-gate
2607c478bd9Sstevel@tonic-gate struct pci_driver pnic_driver = {
2617c478bd9Sstevel@tonic-gate .type = NIC_DRIVER,
2627c478bd9Sstevel@tonic-gate .name = "PNIC",
2637c478bd9Sstevel@tonic-gate .probe = pnic_probe,
2647c478bd9Sstevel@tonic-gate .ids = pnic_nics,
2657c478bd9Sstevel@tonic-gate .id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]),
2667c478bd9Sstevel@tonic-gate .class = 0,
2677c478bd9Sstevel@tonic-gate };
268