diff --git a/src/arch/i386/prefix/select_isapnp.c b/src/arch/i386/prefix/select_isapnp.c index ac1af217c..14e3db032 100644 --- a/src/arch/i386/prefix/select_isapnp.c +++ b/src/arch/i386/prefix/select_isapnp.c @@ -1,3 +1,4 @@ +#include "dev.h" #include "isapnp.h" #include "registers.h" @@ -16,5 +17,16 @@ void i386_select_isapnp_device ( struct i386_all_regs *regs ) { * address in %dx. * */ - select_isapnp_device ( regs->dx, regs->bx ); + union { + struct bus_loc bus_loc; + struct isapnp_loc isapnp_loc; + } u; + + /* Set ISAPnP read port */ + isapnp_set_read_port ( regs->dx ); + + /* Select ISAPnP bus and specified CSN as first boot device */ + memset ( &u, 0, sizeof ( u ) ); + u.isapnp_loc.csn = regs->bx; + select_device ( &dev, &isapnp_driver, &u.bus_loc ); } diff --git a/src/arch/i386/prefix/select_pci.c b/src/arch/i386/prefix/select_pci.c index 046c59f42..c9a62d526 100644 --- a/src/arch/i386/prefix/select_pci.c +++ b/src/arch/i386/prefix/select_pci.c @@ -1,3 +1,4 @@ +#include "dev.h" #include "pci.h" #include "registers.h" @@ -15,5 +16,13 @@ void i386_select_pci_device ( struct i386_all_regs *regs ) { * PCI BIOS passes busdevfn in %ax * */ - select_pci_device ( regs->ax ); + union { + struct bus_loc bus_loc; + struct pci_loc pci_loc; + } u; + + /* Select PCI bus and specified busdevfn as first boot device */ + memset ( &u, 0, sizeof ( u ) ); + u.pci_loc.busdevfn = regs->ax; + select_device ( &dev, &pci_driver, &u.bus_loc ); } diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index fc44683a0..0ed9955d0 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -142,9 +142,15 @@ SECTIONS { *(.data.*) /* Various tables */ - boot_drivers = .; - *(.boot_drivers) - boot_drivers_end = .; + device_drivers = .; + *(.drivers.device) + device_drivers_end = .; + bus_drivers = .; + *(.drivers.bus) + bus_drivers_end = .; + type_drivers = .; + *(.drivers.type) + type_drivers_end = .; console_drivers = .; *(.drivers.console) console_drivers_end = .; diff --git a/src/core/btext.c b/src/core/btext.c index 663fd4a9e..86bbba36e 100644 --- a/src/core/btext.c +++ b/src/core/btext.c @@ -408,7 +408,7 @@ static void btext_init(void) #warning "pci_find_device_x no longer exists; use find_pci_device instead" /* pci_find_device_x(0x1002, 0x4752, 0, &dev); */ - if(dev.vendor==0) return; // no fb + if(dev.vendor_id==0) return; // no fb frame_buffer = (uint32_t)dev.membase; #else diff --git a/src/core/dev.c b/src/core/dev.c index 9c5368875..5d13f7013 100644 --- a/src/core/dev.c +++ b/src/core/dev.c @@ -16,51 +16,154 @@ * function (probe). */ -/* Defined by linker */ -extern struct boot_driver boot_drivers[]; -extern struct boot_driver boot_drivers_end[]; +/* Current attempted boot device */ +struct dev dev = { + .bus_driver = bus_drivers, + .device_driver = device_drivers, +}; -/* Current attempted boot driver */ -static struct boot_driver *boot_driver = boot_drivers; - -/* Print all drivers */ +/* + * Print all drivers + * + */ void print_drivers ( void ) { - struct boot_driver *driver; + struct device_driver *driver; - for ( driver = boot_drivers ; driver < boot_drivers_end ; driver++ ) { + for ( driver = device_drivers ; + driver < device_drivers_end ; + driver++ ) { printf ( "%s ", driver->name ); } } -/* Get the next available boot device */ -int find_boot_device ( struct dev *dev ) { - for ( ; boot_driver < boot_drivers_end ; boot_driver++ ) { - dev->driver = boot_driver; - dev->name = boot_driver->name; - DBG ( "Probing driver %s...\n", dev->name ); - if ( boot_driver->find_bus_boot_device ( dev, - boot_driver->bus_driver ) ) { - DBG ( "Found device %s (ID %hhx:%hx:%hx)\n", - dev->name, dev->devid.bus_type, - dev->devid.vendor_id, dev->devid.device_id ); - return 1; - } - } +/* + * Move to the next location on any bus + * + */ +static inline int next_location ( struct bus_driver **bus_driver, + struct bus_loc *bus_loc ) { + /* Move to next location on this bus, if any */ + if ( (*bus_driver)->next_location ( bus_loc ) ) + return 1; - /* No more boot devices found */ - boot_driver = boot_drivers; + /* Move to first (zeroed) location on next bus, if any */ + if ( ++(*bus_driver) < bus_drivers_end ) + return 1; + + /* Reset to first bus, return "no more locations" */ + *bus_driver = bus_drivers; return 0; } -/* Probe the boot device */ -int probe ( struct dev *dev ) { - return dev->driver->probe ( dev, dev->bus ); +/* + * Find the next available device on any bus + * + * Set skip=1 to skip over the current device + * + */ +int find_any ( struct bus_driver **bus_driver, struct bus_loc *bus_loc, + struct bus_dev *bus_dev, signed int skip ) { + DBG ( "searching for any device\n" ); + do { + if ( --skip >= 0 ) + continue; + if ( ! (*bus_driver)->fill_device ( bus_dev, bus_loc ) ) + continue; + DBG ( "found device %s\n", + (*bus_driver)->describe ( bus_dev ) ); + return 1; + } while ( next_location ( bus_driver, bus_loc ) ); + + DBG ( "found no device\n" ); + return 0; } -/* Disable a device */ -void disable ( struct dev *dev ) { - if ( dev->dev_op ) { - dev->dev_op->disable ( dev ); - dev->dev_op = NULL; - } +/* + * Find a driver by specified device. + * + * Set skip=1 to skip over the current driver + * + */ +int find_by_device ( struct device_driver **device_driver, + struct bus_driver *bus_driver, struct bus_dev *bus_dev, + signed int skip ) { + DBG ( "searching for a driver for device %s\n", + bus_driver->describe ( bus_dev ) ); + do { + if ( --skip >= 0 ) + continue; + if ( (*device_driver)->bus_driver != bus_driver ) + continue; + if ( ! bus_driver->check_driver ( bus_dev, *device_driver )) + continue; + DBG ( "found driver %s\n", (*device_driver)->name ); + return 1; + } while ( ++(*device_driver) < device_drivers_end ); + + /* Reset to first driver, return "not found" */ + DBG ( "found no driver for device %s\n", + bus_driver->describe ( bus_dev ) ); + *device_driver = device_drivers; + return 0; +} + +/* + * Find a device by specified driver. + * + * Set skip=1 to skip over the current device + * + */ +int find_by_driver ( struct bus_loc *bus_loc, struct bus_dev *bus_dev, + struct device_driver *device_driver, + signed int skip ) { + struct bus_driver *bus_driver = device_driver->bus_driver; + + DBG ( "searching for a device for driver %s\n", device_driver->name ); + do { + if ( --skip >= 0 ) + continue; + if ( ! bus_driver->fill_device ( bus_dev, bus_loc ) ) + continue; + if ( ! bus_driver->check_driver ( bus_dev, device_driver ) ) + continue; + DBG ( "found device %s\n", bus_driver->describe ( bus_dev ) ); + return 1; + } while ( bus_driver->next_location ( bus_loc ) ); + + DBG ( "found no device for driver %s\n" ); + return 0; +} + +/* + * Find the next available (device,driver) combination + * + * Set skip=1 to skip over the current (device,driver) + * + * Note that the struct dev may not have been previously used, and so + * may not contain a valid (device,driver) combination. + * + */ +int find_any_with_driver ( struct dev *dev, signed int skip ) { + signed int skip_device = 0; + signed int skip_driver = skip; + + while ( find_any ( &dev->bus_driver, &dev->bus_loc, &dev->bus_dev, + skip_device ) ) { + if ( find_by_device ( &dev->device_driver, dev->bus_driver, + &dev->bus_dev, skip_driver ) ) { + /* Set type_driver to be that of the device + * driver + */ + dev->type_driver = dev->device_driver->type_driver; + /* Set type device instance to be the single + * instance provided by the type driver + */ + dev->type_dev = dev->type_driver->type_dev; + return 1; + } + skip_driver = 0; + skip_device = 1; + } + + return 0; } diff --git a/src/core/main.c b/src/core/main.c index 01e616c83..bcfdd5c0a 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -143,12 +143,6 @@ static int exit_status; static int initialized; -/* Global instance of the current boot device */ -DEV_BUS(struct bus_device, dev_bus); -struct dev dev = { - .bus = &dev_bus, -}; - /************************************************************************** * initialise() - perform any C-level initialisation * @@ -169,6 +163,7 @@ void initialise ( void ) { MAIN - Kick off routine **************************************************************************/ int main ( void ) { + int skip = 0; /* Print out configuration */ print_config(); @@ -181,36 +176,34 @@ int main ( void ) { for ( ; ; disable ( &dev ), call_reset_fns() ) { /* Get next boot device */ - if ( ! find_boot_device ( &dev ) ) { + if ( ! find_any_with_driver ( &dev, skip ) ) { /* Reached end of device list */ printf ( "No more boot devices\n" ); + skip = 0; sleep ( 2 ); continue; } + /* Skip this device the next time we encounter it */ + skip = 1; + + /* Print out what we're doing */ + printf ( "Booting from %s %s at %s " + "using the %s driver\n", + dev.bus_driver->name ( &dev.bus_dev ), + dev.type_driver->name, + dev.bus_driver->describe ( &dev.bus_dev ), + dev.device_driver->name ); + /* Probe boot device */ if ( ! probe ( &dev ) ) { /* Device found on bus, but probe failed */ - printf ( "Probe failed on %s, trying next device\n", - dev.name ); + printf ( "...probe failed on %s\n" ); continue; } - /* Print device info */ - print_info ( &dev ); - - /* Load configuration (e.g. DHCP) */ - if ( ! load_configuration ( &dev ) ) { - /* DHCP failed */ - printf ( "Could not configure device %s\n", dev.name ); - continue; - } - - /* Load image */ - if ( ! load ( &dev ) ) - /* Load failed */ - printf ( "Could not boot from device %s\n", dev.name ); - continue; + printf ( "%s: %s\n", dev.bus_driver->name ( &dev.bus_dev ), + dev.type_driver->describe ( dev.type_dev ) ); } /* Call registered per-object exit functions */ @@ -463,8 +456,7 @@ void cleanup(void) nfs_umountall(ARP_SERVER); #endif /* Stop receiving packets */ - eth_disable(); - disk_disable(); + disable ( &dev ); initialized = 0; } diff --git a/src/core/nic.c b/src/core/nic.c index 50ca4e81b..166eedd70 100644 --- a/src/core/nic.c +++ b/src/core/nic.c @@ -52,7 +52,7 @@ static unsigned char dhcp_machine_info[] = { /* Our enclosing DHCP tag */ RFC1533_VENDOR_ETHERBOOT_ENCAP, 11, /* Our boot device */ - RFC1533_VENDOR_NIC_DEV_ID, 5, PCI_BUS_TYPE, 0, 0, 0, 0, + RFC1533_VENDOR_NIC_DEV_ID, 5, 0, 0, 0, 0, 0, /* Our current architecture */ RFC1533_VENDOR_ARCH, 2, EM_CURRENT & 0xff, (EM_CURRENT >> 8) & 0xff, #ifdef EM_CURRENT_64 @@ -231,13 +231,10 @@ static int bootp(void); static unsigned short tcpudpchksum(struct iphdr *ip); -struct nic *nic = &dev.nic; - /* * Find out what our boot parameters are */ -static int nic_load_configuration ( struct dev *dev ) { - struct nic *nic = &dev->nic; +static int nic_load_configuration ( struct nic *nic ) { int server_found; if ( ! nic->nic_op->connect ( nic ) ) { @@ -321,35 +318,31 @@ static int nic_load(struct dev *dev __unused) return 0; } - -static void nic_disable ( struct dev *dev ) { - struct nic *nic = &dev->nic; - +void nic_disable ( struct nic *nic __unused ) { #ifdef MULTICAST_LEVEL2 int i; for(i = 0; i < MAX_IGMP; i++) { leave_group(i); } #endif - - nic->nic_op->disable ( nic ); } -static void nic_print_info ( struct dev *dev ) { - struct nic *nic = &dev->nic; - - printf ( "Found %s NIC (MAC %!)\n", dev->name, nic->node_addr ); +static char * nic_describe ( struct type_dev *type_dev ) { + struct nic *nic = ( struct nic * ) type_dev; + static char nic_description[] = "MAC 00:00:00:00:00:00"; + + sprintf ( nic_description + 4, "%!", nic->node_addr ); + return nic_description; } /* * Device operations tables * */ -static struct dev_operations nic_operations = { - .disable = nic_disable, - .print_info = nic_print_info, - .load_configuration = nic_load_configuration, - .load = nic_load, +struct type_driver nic_driver = { + .name = "NIC", + .type_dev = ( struct type_dev * ) &nic, + .describe = nic_describe, }; /* Careful. We need an aligned buffer to avoid problems on machines @@ -360,19 +353,10 @@ static struct dev_operations nic_operations = { */ static char packet[ETH_FRAME_LEN + ETH_DATA_ALIGN] __aligned; -/* - * Set up a struct dev to operate as a NIC, return the struct nic * - * - */ -struct nic * nic_device ( struct dev *dev ) { - struct nic *nic = &dev->nic; - - memset ( nic, 0, sizeof ( *nic ) ); - nic->node_addr = arptable[ARP_CLIENT].node; - nic->packet = packet + ETH_DATA_ALIGN; - dev->dev_op = &nic_operations; - return nic; -} +struct nic nic = { + .node_addr = arptable[ARP_CLIENT].node, + .packet = packet + ETH_DATA_ALIGN, +}; @@ -408,9 +392,9 @@ static int await_arp(int ival, void *ptr, struct arprequest *arpreply; if (ptype != ETH_P_ARP) return 0; - if (nic->packetlen < ETH_HLEN + sizeof(struct arprequest)) + if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest)) return 0; - arpreply = (struct arprequest *)&nic->packet[ETH_HLEN]; + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; if (arpreply->opcode != htons(ARP_REPLY)) return 0; @@ -697,7 +681,7 @@ int tftp_block ( struct tftpreq_info_t *request, struct tftpblk_info_t *block ) continue; /* Back to waiting for packet */ } /* Packet has been received */ - rcvd = (struct tftp_t *)&nic->packet[ETH_HLEN]; + rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN]; recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr) - sizeof(rcvd->opcode); rport = ntohs(rcvd->udp.src); @@ -777,9 +761,9 @@ static int await_rarp(int ival, void *ptr, struct arprequest *arpreply; if (ptype != ETH_P_RARP) return 0; - if (nic->packetlen < ETH_HLEN + sizeof(struct arprequest)) + if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest)) return 0; - arpreply = (struct arprequest *)&nic->packet[ETH_HLEN]; + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; if (arpreply->opcode != htons(RARP_REPLY)) return 0; if ((arpreply->opcode == htons(RARP_REPLY)) && @@ -841,9 +825,9 @@ static int await_bootp(int ival __unused, void *ptr __unused, if (!udp) { return 0; } - bootpreply = (struct bootp_t *)&nic->packet[ETH_HLEN + + bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr)]; - if (nic->packetlen < ETH_HLEN + sizeof(struct iphdr) + + if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + #ifdef NO_DHCP_SUPPORT sizeof(struct bootp_t) @@ -916,14 +900,7 @@ static int bootp(void) unsigned char *bp_vend; #ifndef NO_DHCP_SUPPORT - struct { - uint8_t bus_type; - uint16_t vendor_id; - uint16_t device_id; - } __attribute__((packed)) *dhcp_dev_id = (void*)&dhcp_machine_info[4]; - dhcp_dev_id->bus_type = dev.devid.bus_type; - dhcp_dev_id->vendor_id = htons ( dev.devid.vendor_id ); - dhcp_dev_id->device_id = htons ( dev.devid.device_id ); + * ( ( struct dhcp_dev_id * ) &dhcp_machine_info[4] ) = nic.dhcp_dev_id; #endif /* NO_DHCP_SUPPORT */ memset(&ip, 0, sizeof(struct bootpip_t)); ip.bp.bp_op = BOOTP_REQUEST; @@ -1089,11 +1066,11 @@ static void process_igmp(struct iphdr *ip, unsigned long now) int i; unsigned iplen; if (!ip || (ip->protocol == IP_IGMP) || - (nic->packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) { + (nic.packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) { return; } iplen = (ip->verhdrlen & 0xf)*4; - igmp = (struct igmp *)&nic->packet[sizeof(struct iphdr)]; + igmp = (struct igmp *)&nic.packet[sizeof(struct iphdr)]; if (ipchksum(igmp, ntohs(ip->len) - iplen) != 0) return; if ((igmp->type == IGMP_QUERY) && @@ -1300,7 +1277,7 @@ int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr, syn_ack = state == CLOSED || state == SYN_RCVD; consumed = ntohl(tcp->ack) - send_seq - syn_ack; if (consumed < 0 || consumed > can_send) { - tcp_reset((struct iphdr *)&nic->packet[ETH_HLEN]); + tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]); goto recv_data; } @@ -1342,7 +1319,7 @@ int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr, } consume_data: - ip = (struct iphdr *)&nic->packet[ETH_HLEN]; + ip = (struct iphdr *)&nic.packet[ETH_HLEN]; header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C); payload = ntohs(ip->len) - header_size; if (payload > 0 && state == ESTABLISHED) { @@ -1351,7 +1328,7 @@ int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr, recv_seq += payload - old_bytes; if (state != FIN_WAIT_1 && state != FIN_WAIT_2 && !recv(payload - old_bytes, - &nic->packet[ETH_HLEN+header_size+old_bytes], + &nic.packet[ETH_HLEN+header_size+old_bytes], ptr)) { goto close; } @@ -1463,15 +1440,15 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout) /* We have something! */ /* Find the Ethernet packet type */ - if (nic->packetlen >= ETH_HLEN) { - ptype = ((unsigned short) nic->packet[12]) << 8 - | ((unsigned short) nic->packet[13]); + if (nic.packetlen >= ETH_HLEN) { + ptype = ((unsigned short) nic.packet[12]) << 8 + | ((unsigned short) nic.packet[13]); } else continue; /* what else could we do with it? */ /* Verify an IP header */ ip = 0; - if ((ptype == ETH_P_IP) && (nic->packetlen >= ETH_HLEN + sizeof(struct iphdr))) { + if ((ptype == ETH_P_IP) && (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr))) { unsigned ipoptlen; - ip = (struct iphdr *)&nic->packet[ETH_HLEN]; + ip = (struct iphdr *)&nic.packet[ETH_HLEN]; if ((ip->verhdrlen < 0x45) || (ip->verhdrlen > 0x4F)) continue; iplen = (ip->verhdrlen & 0xf) * 4; @@ -1493,17 +1470,17 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout) /* Delete the ip options, to guarantee * good alignment, and make etherboot simpler. */ - memmove(&nic->packet[ETH_HLEN + sizeof(struct iphdr)], - &nic->packet[ETH_HLEN + iplen], - nic->packetlen - ipoptlen); - nic->packetlen -= ipoptlen; + memmove(&nic.packet[ETH_HLEN + sizeof(struct iphdr)], + &nic.packet[ETH_HLEN + iplen], + nic.packetlen - ipoptlen); + nic.packetlen -= ipoptlen; } } udp = 0; if (ip && (ip->protocol == IP_UDP) && - (nic->packetlen >= + (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))) { - udp = (struct udphdr *)&nic->packet[ETH_HLEN + sizeof(struct iphdr)]; + udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)]; /* Make certain we have a reasonable packet length */ if (ntohs(udp->len) > (ntohs(ip->len) - iplen)) @@ -1517,9 +1494,9 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout) tcp = 0; #ifdef DOWNLOAD_PROTO_HTTP if (ip && (ip->protocol == IP_TCP) && - (nic->packetlen >= + (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){ - tcp = (struct tcphdr *)&nic->packet[ETH_HLEN + + tcp = (struct tcphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)]; /* Make certain we have a reasonable packet length */ if (((ntohs(tcp->ctrl) >> 10) & 0x3C) > @@ -1541,11 +1518,11 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout) * action. This allows us reply to arp, igmp, and lacp queries. */ if ((ptype == ETH_P_ARP) && - (nic->packetlen >= ETH_HLEN + sizeof(struct arprequest))) { + (nic.packetlen >= ETH_HLEN + sizeof(struct arprequest))) { struct arprequest *arpreply; unsigned long tmp; - arpreply = (struct arprequest *)&nic->packet[ETH_HLEN]; + arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); if ((arpreply->opcode == htons(ARP_REQUEST)) && (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) { diff --git a/src/core/pxe_export.c b/src/core/pxe_export.c index f1f7c5e36..3fb173b6c 100644 --- a/src/core/pxe_export.c +++ b/src/core/pxe_export.c @@ -169,7 +169,7 @@ int pxe_shutdown_nic ( void ) { if ( pxe_stack->state <= MIDWAY ) return 1; eth_irq ( DISABLE ); - eth_disable(); + disable ( &dev ); pxe_stack->state = MIDWAY; return 1; } @@ -433,7 +433,7 @@ PXENV_EXIT_t pxenv_undi_set_station_address ( t_PXENV_UNDI_SET_STATION_ADDRESS * the current value anyway then return success, otherwise * return UNSUPPORTED. */ - if ( memcmp ( nic->node_addr, + if ( memcmp ( nic.node_addr, &undi_set_station_address->StationAddress, ETH_ALEN ) == 0 ) { undi_set_station_address->Status = PXENV_STATUS_SUCCESS; @@ -465,8 +465,8 @@ PXENV_EXIT_t pxenv_undi_get_information ( t_PXENV_UNDI_GET_INFORMATION DBG ( "PXENV_UNDI_GET_INFORMATION" ); ENSURE_READY ( undi_get_information ); - undi_get_information->BaseIo = nic->ioaddr; - undi_get_information->IntNumber = nic->irqno; + undi_get_information->BaseIo = nic.ioaddr; + undi_get_information->IntNumber = nic.irqno; /* Cheat: assume all cards can cope with this */ undi_get_information->MaxTranUnit = ETH_MAX_MTU; /* Cheat: we only ever have Ethernet cards */ @@ -476,12 +476,12 @@ PXENV_EXIT_t pxenv_undi_get_information ( t_PXENV_UNDI_GET_INFORMATION * node address. This is a valid assumption within Etherboot * at the time of writing. */ - memcpy ( &undi_get_information->CurrentNodeAddress, nic->node_addr, + memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr, ETH_ALEN ); - memcpy ( &undi_get_information->PermNodeAddress, nic->node_addr, + memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr, ETH_ALEN ); undi_get_information->ROMAddress = 0; - /* nic->rom_info->rom_segment; */ + /* nic.rom_info->rom_segment; */ /* We only provide the ability to receive or transmit a single * packet at a time. This is a bootloader, not an OS. */ @@ -637,7 +637,7 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO * Status: working */ PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) { - media_header_t *media_header = (media_header_t*)nic->packet; + media_header_t *media_header = (media_header_t*)nic.packet; DBG ( "PXENV_UNDI_ISR" ); /* We can't call ENSURE_READY, because this could be being @@ -683,8 +683,8 @@ PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) { */ DBG ( " PROCESS" ); if ( eth_poll ( 1 ) ) { - DBG ( " RECEIVE %d", nic->packetlen ); - if ( nic->packetlen > sizeof(pxe_stack->packet) ) { + 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 = @@ -692,10 +692,10 @@ PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) { return PXENV_EXIT_FAILURE; } undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; - undi_isr->BufferLength = nic->packetlen; - undi_isr->FrameLength = nic->packetlen; + undi_isr->BufferLength = nic.packetlen; + undi_isr->FrameLength = nic.packetlen; undi_isr->FrameHeaderLength = ETH_HLEN; - memcpy ( pxe_stack->packet, nic->packet, nic->packetlen); + memcpy ( pxe_stack->packet, nic.packet, nic.packetlen); PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame ); switch ( ntohs(media_header->nstype) ) { case IP : undi_isr->ProtType = P_IP; break; @@ -1026,7 +1026,7 @@ PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ *udp_read ) { PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE *udp_write ) { uint16_t src_port; uint16_t dst_port; - struct udppacket *packet = (struct udppacket *)nic->packet; + struct udppacket *packet = (struct udppacket *)nic.packet; int packet_size; DBG ( "PXENV_UDP_WRITE" ); diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index e109d0306..3cbe67e5e 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -1,16 +1,9 @@ #include "stdint.h" #include "string.h" #include "console.h" +#include "nic.h" #include "pci.h" -/* - * Ensure that there is sufficient space in the shared dev_bus - * structure for a struct pci_device. - * - */ -DEV_BUS( struct pci_device, pci_dev ); -static char pci_magic[0]; /* guaranteed unique symbol */ - /* * pci_io.c may know how many buses we have, in which case it can * overwrite this value. @@ -18,13 +11,39 @@ static char pci_magic[0]; /* guaranteed unique symbol */ */ unsigned int pci_max_bus = 0xff; +/* + * Increment a bus_loc structure to the next possible PCI location. + * Leave the structure zeroed and return 0 if there are no more valid + * locations. + * + */ +static int pci_next_location ( struct bus_loc *bus_loc ) { + struct pci_loc *pci_loc = ( struct pci_loc * ) bus_loc; + + /* + * Ensure that there is sufficient space in the shared bus + * structures for a struct pci_loc and a struct + * pci_dev, as mandated by bus.h. + * + */ + BUS_LOC_CHECK ( struct pci_loc ); + BUS_DEV_CHECK ( struct pci_device ); + + return ( ++pci_loc->busdevfn ); +} + /* * Fill in parameters (vendor & device ids, class, membase etc.) for a * PCI device based on bus & devfn. * * Returns 1 if a device was found, 0 for no device present. + * */ -static int fill_pci_device ( struct pci_device *pci ) { +static int pci_fill_device ( struct bus_dev *bus_dev, + struct bus_loc *bus_loc ) { + struct pci_loc *pci_loc = ( struct pci_loc * ) bus_loc; + struct pci_device *pci = ( struct pci_device * ) bus_dev; + uint16_t busdevfn = pci_loc->busdevfn; static struct { uint16_t devfn0; int is_present; @@ -32,8 +51,12 @@ static int fill_pci_device ( struct pci_device *pci ) { uint32_t l; int reg; + /* Store busdevfn in struct pci_device and set default values */ + pci->busdevfn = busdevfn; + pci->name = "?"; + /* Check bus is within range */ - if ( PCI_BUS ( pci->busdevfn ) > pci_max_bus ) { + if ( PCI_BUS ( busdevfn ) > pci_max_bus ) { return 0; } @@ -41,8 +64,8 @@ static int fill_pci_device ( struct pci_device *pci ) { * non-zero function on a non-existent card. This is done to * increase scan speed by a factor of 8. */ - if ( ( PCI_FUNC ( pci->busdevfn ) != 0 ) && - ( PCI_FN0 ( pci->busdevfn ) == cache.devfn0 ) && + if ( ( PCI_FUNC ( busdevfn ) != 0 ) && + ( PCI_FN0 ( busdevfn ) == cache.devfn0 ) && ( ! cache.is_present ) ) { return 0; } @@ -52,28 +75,27 @@ static int fill_pci_device ( struct pci_device *pci ) { pci_read_config_dword ( pci, PCI_VENDOR_ID, &l ); /* some broken boards return 0 if a slot is empty: */ if ( ( l == 0xffffffff ) || ( l == 0x00000000 ) ) { - if ( PCI_FUNC ( pci->busdevfn ) == 0 ) { + if ( PCI_FUNC ( busdevfn ) == 0 ) { /* Don't look for subsequent functions if the * card itself is not present. */ - cache.devfn0 = pci->busdevfn; + cache.devfn0 = busdevfn; cache.is_present = 0; } return 0; } - pci->vendor = l & 0xffff; - pci->dev_id = ( l >> 16 ) & 0xffff; + pci->vendor_id = l & 0xffff; + pci->device_id = ( l >> 16 ) & 0xffff; /* Check that we're not a duplicate function on a * non-multifunction device. */ - if ( PCI_FUNC ( pci->busdevfn ) != 0 ) { - uint16_t save_busdevfn = pci->busdevfn; + if ( PCI_FUNC ( busdevfn ) != 0 ) { uint8_t header_type; - pci->busdevfn &= PCI_FN0 ( pci->busdevfn ); + pci->busdevfn &= PCI_FN0 ( busdevfn ); pci_read_config_byte ( pci, PCI_HEADER_TYPE, &header_type ); - pci->busdevfn = save_busdevfn; + pci->busdevfn = busdevfn; if ( ! ( header_type & 0x80 ) ) { return 0; @@ -108,14 +130,87 @@ static int fill_pci_device ( struct pci_device *pci ) { pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq ); } - DBG ( "PCI found device %hhx:%hhx.%d Class %hx: %hx:%hx (rev %hhx)\n", + DBG ( "found device %hhx:%hhx.%d Class %hx: %hx:%hx (rev %hhx)\n", PCI_BUS ( pci->busdevfn ), PCI_DEV ( pci->busdevfn ), - PCI_FUNC ( pci->busdevfn ), pci->class, pci->vendor, pci->dev_id, - pci->revision ); + PCI_FUNC ( pci->busdevfn ), pci->class, pci->vendor_id, + pci->device_id, pci->revision ); return 1; } +/* + * Test whether or not a driver is capable of driving the device. + * + */ +static int pci_check_driver ( struct bus_dev *bus_dev, + struct device_driver *device_driver ) { + struct pci_device *pci = ( struct pci_device * ) bus_dev; + struct pci_driver_info *pci_driver_info + = ( struct pci_driver_info * ) device_driver->bus_driver_info; + unsigned int i; + + /* If driver has a class, and class matches, use it */ + if ( pci_driver_info->class && + ( pci_driver_info->class == pci->class ) ) { + DBG ( "driver %s matches class %hx\n", + device_driver->name, pci_driver_info->class ); + pci->name = device_driver->name; + return 1; + } + + /* If any of driver's IDs match, use it */ + for ( i = 0 ; i < pci_driver_info->id_count; i++ ) { + struct pci_id *id = &pci_driver_info->ids[i]; + + if ( ( pci->vendor_id == id->vendor_id ) && + ( pci->device_id == id->device_id ) ) { + DBG ( "driver %s device %s matches ID %hx:%hx\n", + device_driver->name, id->name, + id->vendor_id, id->device_id ); + pci->name = id->name; + return 1; + } + } + + return 0; +} + +/* + * Describe a PCI device + * + */ +static char * pci_describe ( struct bus_dev *bus_dev ) { + struct pci_device *pci = ( struct pci_device * ) bus_dev; + static char pci_description[] = "PCI 00:00.0"; + + sprintf ( pci_description + 4, "%hhx:%hhx.%d", + PCI_BUS ( pci->busdevfn ), PCI_DEV ( pci->busdevfn ), + PCI_FUNC ( pci->busdevfn ) ); + return pci_description; +} + +/* + * Name a PCI device + * + */ +static char * pci_name ( struct bus_dev *bus_dev ) { + struct pci_device *pci = ( struct pci_device * ) bus_dev; + + return pci->name; +} + +/* + * PCI bus operations table + * + */ +struct bus_driver pci_driver __bus_driver = { + .next_location = pci_next_location, + .fill_device = pci_fill_device, + .check_driver = pci_check_driver, + .describe = pci_describe, + .name = pci_name, +}; + /* * Set device to be a busmaster in case BIOS neglected to do so. Also * adjust PCI latency timer to a reasonable value, 32. @@ -127,7 +222,7 @@ void adjust_pci_device ( struct pci_device *pci ) { pci_read_config_word ( pci, PCI_COMMAND, &pci_command ); new_command = pci_command | PCI_COMMAND_MASTER | PCI_COMMAND_IO; if ( pci_command != new_command ) { - DBG ( "PCI BIOS has not enabled device %hhx:%hhx.%d! " + DBG ( "BIOS has not enabled device %hhx:%hhx.%d! " "Updating PCI command %hX->%hX\n", PCI_BUS ( pci->busdevfn ), PCI_DEV ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), pci_command, new_command ); @@ -135,7 +230,7 @@ void adjust_pci_device ( struct pci_device *pci ) { } pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency); if ( pci_latency < 32 ) { - DBG ( "PCI device %hhx:%hhx.%d latency timer is " + DBG ( "device %hhx:%hhx.%d latency timer is " "unreasonably low at %d. Setting to 32.\n", PCI_BUS ( pci->busdevfn ), PCI_DEV ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), pci_latency ); @@ -143,100 +238,6 @@ void adjust_pci_device ( struct pci_device *pci ) { } } -/* - * Set PCI device to use. - * - * This routine can be called by e.g. the ROM prefix to specify that - * the first device to be tried should be the device on which the ROM - * was physically located. - * - */ -void set_pci_device ( uint16_t busdevfn ) { - pci_dev.magic = pci_magic; - pci_dev.busdevfn = busdevfn; - pci_dev.already_tried = 0; -} - -/* - * Find a PCI device matching the specified driver - * - */ -int find_pci_device ( struct pci_device *pci, - struct pci_driver *driver ) { - int i; - - /* Initialise struct pci if it's the first time it's been used. */ - if ( pci->magic != pci_magic ) { - memset ( pci, 0, sizeof ( *pci ) ); - pci->magic = pci_magic; - } - - /* Iterate through all possible PCI bus:dev.fn combinations, - * starting where we left off. - */ - DBG ( "PCI searching for device matching driver %s\n", driver->name ); - do { - /* If we've already used this device, skip it */ - if ( pci->already_tried ) { - pci->already_tried = 0; - continue; - } - - /* Fill in device parameters, if device present */ - if ( ! fill_pci_device ( pci ) ) { - continue; - } - - /* If driver has a class, and class matches, use it */ - if ( driver->class && - ( driver->class == pci->class ) ) { - DBG ( "PCI found class %hx matching driver %s\n", - driver->class, driver->name ); - pci->name = driver->name; - pci->already_tried = 1; - return 1; - } - - /* If any of driver's IDs match, use it */ - for ( i = 0 ; i < driver->id_count; i++ ) { - struct pci_id *id = &driver->ids[i]; - - if ( ( pci->vendor == id->vendor ) && - ( pci->dev_id == id->dev_id ) ) { - DBG ( "PCI found ID %hx:%hx (device %s) " - "matching driver %s\n", id->vendor, - id->dev_id, id->name, driver->name ); - pci->name = id->name; - pci->already_tried = 1; - return 1; - } - } - } while ( ++pci->busdevfn ); - - /* No device found */ - DBG ( "PCI found no device matching driver %s\n", driver->name ); - return 0; -} - -/* - * Find the next PCI device that can be used to boot using the - * specified driver. - * - */ -int find_pci_boot_device ( struct dev *dev, struct pci_driver *driver ) { - struct pci_device *pci = ( struct pci_device * )dev->bus; - - if ( ! find_pci_device ( pci, driver ) ) - return 0; - - dev->name = pci->name; - dev->devid.bus_type = PCI_BUS_TYPE; - dev->devid.vendor_id = pci->vendor; - dev->devid.device_id = pci->dev_id; - - return 1; -} - /* * Find the start of a pci resource. */ @@ -346,3 +347,19 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { } return 0; } + +/* + * Fill in a DHCP device ID structure + * + */ +void pci_fill_nic ( struct nic *nic, struct pci_device *pci ) { + + /* Fill in ioaddr and irqno */ + nic->ioaddr = pci->ioaddr; + nic->irqno = pci->irq; + + /* Fill in DHCP device ID structure */ + nic->dhcp_dev_id.bus_type = PCI_BUS_TYPE; + nic->dhcp_dev_id.vendor_id = htons ( pci->vendor_id ); + nic->dhcp_dev_id.device_id = htons ( pci->device_id ); +} diff --git a/src/include/bus.h b/src/include/bus.h deleted file mode 100644 index 80faa0e89..000000000 --- a/src/include/bus.h +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef BUS_H -#define BUS_H - -#include "stdint.h" - -/* - * When looking at the following data structures, mentally substitute - * "_" in place of "bus_" and everything will become clear. - * "struct bus_location" becomes "struct _location", which means - * "the location of a device on a bus", where is a - * particular type of bus such as "pci" or "isapnp". - * - */ - -/* - * A physical device location. - * - */ -#define BUS_LOCATION_SIZE 4 -struct bus_location { - char bytes[BUS_LOCATION_SIZE]; -}; - -/* - * A structure fully describing a physical device. - * - */ -#define BUS_DEVICE_SIZE 32 -struct bus_device { - char bytes[BUS_DEVICE_SIZE]; -}; - -/* - * Individual buses will have different sizes for their _location - * and _device structures. We need to be able to allocate static - * storage that's large enough to contain these structures for any - * bus type that's being used in the current binary. - * - * We can't just create a union of all the various types, because some - * may be architecture-dependent (and some are even embedded in - * specific drivers, e.g. 3c509), so this would quickly get messy. - * - * We could use the magic of common symbols. Each bus could declare a - * common symbol with the name "_bus_device" of the correct size; this - * is easily done using code like - * struct pci_device _bus_device; - * The linker would then use the largest size of the "_bus_device" - * symbol in any included object, thus giving us a single _bus_device - * symbol of *exactly* the required size. However, there's no way to - * extract the size of this symbol, either directly as a linker symbol - * ("_bus_device_size = SIZEOF(_bus_device)"; the linker language just - * doesn't provide this construct) or via any linker trickery I can - * think of (such as creating a special common symbol section just for - * this symbol then using SIZE(section) to read the size of the - * section; ld recognises only a single common symbol section called - * "COMMON"). - * - * Since there's no way to get the size of the symbol, this - * effectively limits us to just one instance of the symbol. This is - * all very well for the simple case of "just boot from any single - * device you can", but becomes limiting when you want to do things - * like introducing PCMCIA buses (which must instantiate other devices - * such as PCMCIA controllers). - * - * So, we declare the maximum sizes of these constructions to be - * compile-time constants. Each individual bus driver should define - * its own struct _location and struct _device however it - * likes, and can freely cast pointers from struct bus_location to - * struct _location (and similarly for bus_device). To guard - * against bounding errors, each bus driver *MUST* use the macros - * BUS_LOCATION_CHECK() and BUS_DEVICE_CHECK(), as in: - * - * BUS_LOCATION_CHECK ( struct pci_location ); - * BUS_DEVICE_CHECK ( struct pci_device ); - * - * These macros will generate a link-time error if the size of the - * structure exceeds the declared maximum size. - * - * The macros will generate no binary object code, but must be placed - * inside a function (in order to generate syntactically valid C). - * The easiest wy to do this is to place them in the - * _next_location() function. - * - * If anyone can think of a better way of doing this that avoids *ALL* - * of the problems described above, please implement it! - * - */ - -#define LINKER_ASSERT(test,error_symbol) \ - if ( ! (test) ) { \ - extern void error_symbol ( void ); \ - error_symbol(); \ - } - -#define BUS_LOCATION_CHECK(datatype) \ - LINKER_ASSERT( ( sizeof (datatype) < sizeof (struct bus_location) ), - __BUS_LOCATION_SIZE_is_too_small__see_dev_h ) -#define BUS_DEVICE_CHECK(datatype) \ - LINKER_ASSERT( ( sizeof (datatype) < sizeof (struct bus_device) ), - __BUS_DEVICE_SIZE_is_too_small__see_dev_h ) - -/* - * A description of a device. This is used to send information about - * the device to a DHCP server, and to provide a text string to - * describe the device to the user. - * - * Note that "text" is allowed to be NULL, in which case the - * describe_device() method will print the information directly to the - * console rather than writing it into a buffer. (This happens - * transparently because sprintf(NULL,...) is exactly equivalent to - * printf(...) in our vsprintf.c). - * - */ -struct bus_description { - char *text; - uint16_t vendor_id; - uint16_t device_id; - uint8_t bus_type; -}; - -/* - * A driver definition - * - */ -struct bus_driver; - -/* - * Bus-level operations. - * - * int next_location ( struct bus_location * bus_location ) - * - * Increment bus_location to point to the next possible device on - * the bus (e.g. the next PCI busdevfn, or the next ISAPnP CSN). - * If there are no more valid locations, return 0 and leave - * struct bus_location zeroed, otherwise return true. - * - * int fill_device ( struct bus_location *bus_location, - * struct bus_device *bus_device ) - * - * Fill out a bus_device structure with the parameters for the - * device at bus_location. (For example, fill in the PCI vendor - * and device IDs). Return true if there is a device physically - * present at this location, otherwise 0. - * - * int check_driver ( ) - * - */ -struct bus_operations { - int ( *next_location ) ( struct bus_location * bus_location ); - int ( *fill_device ) ( struct bus_location * bus_location, - struct bus_device * bus_device ); - int ( *check_driver ) ( struct bus_device * bus_device, - struct bus_driver * bus_driver ); - void ( *describe_device ) ( struct bus_device * bus_device, - struct bus_driver * bus_driver, - struct bus_description * bus_description ); -}; - - - -#endif /* BUS_H */ diff --git a/src/include/dev.h b/src/include/dev.h index 555712aa0..327a27143 100644 --- a/src/include/dev.h +++ b/src/include/dev.h @@ -2,91 +2,276 @@ #define DEV_H #include "stdint.h" +#include "string.h" +#include "dhcp.h" /* for dhcp_dev_id */ -/* Device types */ -#include "nic.h" +/* + * Forward declarations + * + */ +struct type_dev; +struct type_driver; +struct bus_driver; +struct bus_dev; +struct device_driver; -/* Need to check the packing of this struct if Etherboot is ported */ -struct dev_id { - uint16_t vendor_id; - uint16_t device_id; - uint8_t bus_type; -#define PCI_BUS_TYPE 1 -#define ISA_BUS_TYPE 2 -#define MCA_BUS_TYPE 3 -} __attribute__ ((packed)); +/* + * When looking at the following data structures, mentally substitute + * "_" in place of "bus_" and everything will become clear. + * "struct bus_location" becomes "struct _location", which means + * "the location of a device on a bus", where is a + * particular type of bus such as "pci" or "isapnp". + * + */ -/* Dont use sizeof, that will include the padding */ -#define DEV_ID_SIZE 8 +/* + * A physical device location on a bus. + * + */ +#define BUS_LOC_SIZE 4 +struct bus_loc { + char bytes[BUS_LOC_SIZE]; +}; -struct dev { - struct dev_operations *dev_op; - const char *name; - struct dev_id devid; /* device ID string (sent to DHCP server) */ - struct boot_driver *driver; /* driver being used for boot */ - /* Pointer to bus information for device. Whatever sets up - * the struct dev must make sure that this points to a buffer - * large enough for the required struct _device. - */ - struct bus_device *bus; - /* All possible device types */ - union { - struct nic nic; - }; +/* + * A structure fully describing a physical device on a bus. + * + */ +#define BUS_DEV_SIZE 32 +struct bus_dev { + char bytes[BUS_DEV_SIZE]; }; /* - * Macro to help create a common symbol with enough space for any - * struct _device. + * Individual buses will have different sizes for their _location + * and _device structures. We need to be able to allocate static + * storage that's large enough to contain these structures for any + * bus type that's being used in the current binary. + * + * We can't just create a union of all the various types, because some + * may be architecture-dependent (and some are even embedded in + * specific drivers, e.g. 3c509), so this would quickly get messy. + * + * We could use the magic of common symbols. Each bus could declare a + * common symbol with the name "_bus_dev" of the correct size; this + * is easily done using code like + * struct pci_device _bus_dev; + * The linker would then use the largest size of the "_bus_dev" symbol + * in any included object, thus giving us a single _bus_dev symbol of + * *exactly* the required size. However, there's no way to extract + * the size of this symbol, either directly as a linker symbol + * ("_bus_dev_size = SIZEOF(_bus_dev)"; the linker language just + * doesn't provide this construct) or via any linker trickery I can + * think of (such as creating a special common symbol section just for + * this symbol then using SIZE(section) to read the size of the + * section; ld recognises only a single common symbol section called + * "COMMON"). + * + * Since there's no way to get the size of the symbol, this + * effectively limits us to just one instance of the symbol. This is + * all very well for the simple case of "just boot from any single + * device you can", but becomes limiting when you want to do things + * like introducing PCMCIA buses (which must instantiate other devices + * such as PCMCIA controllers). + * + * So, we declare the maximum sizes of these constructions to be + * compile-time constants. Each individual bus driver should define + * its own struct _location and struct _device however it + * likes, and can freely cast pointers from struct bus_loc to + * struct _location (and similarly for bus_dev). To guard + * against bounding errors, each bus driver *MUST* use the macros + * BUS_LOC_CHECK() and BUS_DEV_CHECK(), as in: + * + * BUS_LOC_CHECK ( struct pci_location ); + * BUS_DEV_CHECK ( struct pci_device ); + * + * These macros will generate a link-time error if the size of the + * structure exceeds the declared maximum size. + * + * The macros will generate no binary object code, but must be placed + * inside a function (in order to generate syntactically valid C). + * The easiest wy to do this is to place them in the + * _next_location() function. + * + * If anyone can think of a better way of doing this that avoids *ALL* + * of the problems described above, please implement it! * - * Use as e.g. DEV_BUS(struct pci_device); */ -#define DEV_BUS(datatype,symbol) datatype symbol __asm__ ( "_dev_bus" ); -struct dev_operations { - void ( *disable ) ( struct dev * ); - void ( *print_info ) ( struct dev * ); - int ( *load_configuration ) ( struct dev * ); - int ( *load ) ( struct dev * ); -}; +#define LINKER_ASSERT(test,error_symbol) \ + if ( ! (test) ) { \ + extern void error_symbol ( void ); \ + error_symbol(); \ + } + +#define BUS_LOC_CHECK(datatype) \ + LINKER_ASSERT( ( sizeof (datatype) < sizeof (struct bus_loc) ), \ + __BUS_LOC_SIZE_is_too_small__see_dev_h ) +#define BUS_DEV_CHECK(datatype) \ + LINKER_ASSERT( ( sizeof (datatype) < sizeof (struct bus_dev) ), \ + __BUS_DEV_SIZE_is_too_small__see_dev_h ) /* - * Table to describe a bootable device driver. See comments in dev.c - * for an explanation. + * Bus-level operations. + * + * int next_location ( struct bus_loc * bus_loc ) + * + * Increment bus_loc to point to the next possible device on + * the bus (e.g. the next PCI busdevfn, or the next ISAPnP CSN). + * If there are no more valid locations, return 0 and leave + * struct bus_loc zeroed, otherwise return true. + * + * int fill_device ( struct bus_dev *bus_dev, + * struct bus_loc *bus_loc ) + * + * Fill out a bus_dev structure with the parameters for the + * device at bus_loc. (For example, fill in the PCI vendor + * and device IDs). Return true if there is a device physically + * present at this location, otherwise 0. + * + * int check_driver ( struct bus_dev *bus_dev, + * struct device_driver *device_driver ) + * + * Test whether or not the specified driver is capable of driving + * the specified device by, for example, comparing the device's + * PCI IDs against the list of PCI IDs claimed by the driver. + * + * char * describe ( struct bus_dev *bus_dev ) + * + * Return a text string describing the bus device bus_dev + * (e.g. "PCI 00:01.2") + * + * char * name ( struct bus_dev *bus_dev ) + * + * Return a text string describing the bus device bus_dev + * (e.g. "dfe538") * */ -struct bus_device {}; -struct bus_driver {}; -struct boot_driver { +struct bus_driver { + int ( *next_location ) ( struct bus_loc *bus_loc ); + int ( *fill_device ) ( struct bus_dev *bus_dev, + struct bus_loc *bus_loc ); + int ( *check_driver ) ( struct bus_dev *bus_dev, + struct device_driver *device_driver ); + char * ( *describe ) ( struct bus_dev *bus_dev ); + char * ( *name ) ( struct bus_dev *bus_dev ); +}; + +#define __bus_driver __attribute__ (( used, __section__ ( ".drivers.bus" ) )) + +/* + * A structure fully describing the bus-independent parts of a + * particular type (e.g. nic or disk) of device. + * + * Unlike struct bus_dev, e can limit ourselves to having no more than + * one instance of this data structure. We therefore place an + * instance in each type driver file (e.g. nic.c), and simply use a + * pointer to the struct type_dev in the struct dev. + * + */ +struct type_dev; + +/* + * A type driver (e.g. nic, disk) + * + */ +struct type_driver { char *name; - struct bus_device * ( *find_bus_boot_device ) ( struct dev *dev, - struct bus_driver *driver ); - struct bus_driver *bus_driver; - int ( *probe ) ( struct dev *dev, struct bus_device *bus_device ); + struct type_dev *type_dev; /* single instance */ + char * ( * describe ) ( struct type_dev *type_dev ); }; -#define BOOT_DRIVER( _name, _find_bus_boot_device, _bus_driver, _probe ) \ - static struct boot_driver boot_ ## _bus_driver \ - __attribute__ ((used,__section__(".boot_drivers"))) = { \ - .name = _name, \ - .find_bus_boot_device = ( void * ) _find_bus_boot_device, \ - .bus_driver = ( void * ) &_bus_driver, \ - .probe = ( void * ) _probe, \ +#define __type_driver __attribute__ (( used, __section__ ( ".drivers.type" ) )) + +/* + * A driver for a device. + * + */ +struct device_driver { + const char *name; + struct type_driver *type_driver; + struct bus_driver *bus_driver; + struct bus_driver_info *bus_driver_info; + int ( * probe ) ( struct type_dev *type_dev, + struct bus_dev *bus_dev ); + void ( * disable ) ( struct type_dev *type_dev, + struct bus_dev *bus_dev ); +}; + +#define __device_driver \ + __attribute__ (( used, __section__ ( ".drivers.device" ) )) + +#define DRIVER(_name,_name_string,_type_driver,_bus_driver,_bus_info, \ + _probe,_disable) \ + static struct device_driver _name __device_driver = { \ + .name = _name_string, \ + .type_driver = &_type_driver, \ + .bus_driver = &_bus_driver, \ + .bus_driver_info = ( struct bus_driver_info * ) &_bus_info, \ + .probe = ( int (*) () ) _probe, \ + .disable = ( void (*) () ) _disable, \ }; -/* Functions in dev.c */ +/* + * A bootable device, comprising a physical device on a bus, a driver + * for that device, and a type device + * + */ +struct dev { + struct bus_driver *bus_driver; + struct bus_loc bus_loc; + struct bus_dev bus_dev; + struct device_driver *device_driver; + struct type_driver *type_driver; + struct type_dev *type_dev; +}; + +/* The current boot device */ +extern struct dev dev; + +/* + * Functions in dev.c + * + */ extern void print_drivers ( void ); -extern int find_boot_device ( struct dev *dev ); -extern int probe ( struct dev *dev ); -extern void disable ( struct dev *dev ); -static inline void print_info ( struct dev *dev ) { - dev->dev_op->print_info ( dev ); +extern int find_any ( struct bus_driver **bus_driver, struct bus_loc *bus_loc, + struct bus_dev *bus_dev, signed int skip ); +extern int find_by_device ( struct device_driver **device_driver, + struct bus_driver *bus_driver, + struct bus_dev *bus_dev, + signed int skip ); +extern int find_by_driver ( struct bus_loc *bus_loc, struct bus_dev *bus_dev, + struct device_driver *device_driver, + signed int skip ); +extern int find_any_with_driver ( struct dev *dev, signed int skip ); + +/* + * Functions inlined to save space + * + */ + +/* Probe a device */ +static inline int probe ( struct dev *dev ) { + return dev->device_driver->probe ( dev->type_dev, &dev->bus_dev ); } -static inline int load_configuration ( struct dev *dev ) { - return dev->dev_op->load_configuration ( dev ); +/* Disable a device */ +static inline void disable ( struct dev *dev ) { + dev->device_driver->disable ( dev->type_dev, &dev->bus_dev ); } -static inline int load ( struct dev *dev ) { - return dev->dev_op->load ( dev ); +/* Set the default boot device */ +static inline void select_device ( struct dev *dev, + struct bus_driver *bus_driver, + struct bus_loc *bus_loc ) { + dev->bus_driver = bus_driver; + memcpy ( &dev->bus_loc, bus_loc, sizeof ( dev->bus_loc ) ); } +/* Linker symbols for the various tables */ +extern struct bus_driver bus_drivers[]; +extern struct bus_driver bus_drivers_end[]; +extern struct type_driver type_drivers[]; +extern struct type_driver type_drivers_end[]; +extern struct device_driver device_drivers[]; +extern struct device_driver device_drivers_end[]; + #endif /* DEV_H */ diff --git a/src/include/dhcp.h b/src/include/dhcp.h new file mode 100644 index 000000000..deba219bc --- /dev/null +++ b/src/include/dhcp.h @@ -0,0 +1,12 @@ +#ifndef DHCP_H +#define DHCP_H + +#include "stdint.h" + +struct dhcp_dev_id { + uint8_t bus_type; + uint16_t vendor_id; + uint16_t device_id; +} __attribute__ (( packed )); + +#endif /* DHCP_H */ diff --git a/src/include/isa_ids.h b/src/include/isa_ids.h index f21a093be..7d77f0d86 100644 --- a/src/include/isa_ids.h +++ b/src/include/isa_ids.h @@ -19,6 +19,8 @@ #include "stdint.h" +#define ISA_BUS_TYPE 2 + /* * Construct a vendor ID from three ASCII characters * diff --git a/src/include/mca.h b/src/include/mca.h index e5daf7156..3b4de21f8 100644 --- a/src/include/mca.h +++ b/src/include/mca.h @@ -11,6 +11,8 @@ #include "isa_ids.h" #include "dev.h" +#define MCA_BUS_TYPE 3 + /* * MCA constants * diff --git a/src/include/nic.h b/src/include/nic.h index 02fcafc1b..7b12c5be1 100644 --- a/src/include/nic.h +++ b/src/include/nic.h @@ -8,6 +8,10 @@ #ifndef NIC_H #define NIC_H +#include "dev.h" +#include "byteswap.h" +#include "dhcp.h" + typedef enum { DISABLE = 0, ENABLE, @@ -24,16 +28,17 @@ typedef enum duplex { * functions. */ struct nic { - struct nic_operations *nic_op; - int flags; /* driver specific flags */ - unsigned char *node_addr; - unsigned char *packet; - unsigned int packetlen; - unsigned int ioaddr; - unsigned char irqno; - unsigned int mbps; - duplex_t duplex; - void *priv_data; /* driver can hang private data here */ + struct nic_operations *nic_op; + int flags; /* driver specific flags */ + unsigned char *node_addr; + unsigned char *packet; + unsigned int packetlen; + unsigned int ioaddr; + unsigned char irqno; + unsigned int mbps; + duplex_t duplex; + struct dhcp_dev_id dhcp_dev_id; + void *priv_data; /* driver private data */ }; struct nic_operations { @@ -42,52 +47,43 @@ struct nic_operations { void ( *transmit ) ( struct nic *, const char *, unsigned int, unsigned int, const char * ); void ( *irq ) ( struct nic *, irq_action_t ); - void ( *disable ) ( struct nic * ); }; +extern struct type_driver nic_driver; + /* * Function prototypes * */ -struct dev; -extern struct nic * nic_device ( struct dev * dev ); extern int dummy_connect ( struct nic *nic ); extern void dummy_irq ( struct nic *nic, irq_action_t irq_action ); +extern void nic_disable ( struct nic *nic ); /* * Functions that implicitly operate on the current boot device * - * "nic" always points to &dev.nic */ -extern struct nic *nic; +extern struct nic nic; static inline int eth_connect ( void ) { - return nic->nic_op->connect ( nic ); + return nic.nic_op->connect ( &nic ); } static inline int eth_poll ( int retrieve ) { - return nic->nic_op->poll ( nic, retrieve ); + return nic.nic_op->poll ( &nic, retrieve ); } static inline void eth_transmit ( const char *dest, unsigned int type, unsigned int size, const void *packet ) { - nic->nic_op->transmit ( nic, dest, type, size, packet ); + nic.nic_op->transmit ( &nic, dest, type, size, packet ); } static inline void eth_irq ( irq_action_t action ) { - nic->nic_op->irq ( nic, action ); + nic.nic_op->irq ( &nic, action ); } /* Should be using disable() rather than eth_disable() */ -static inline void eth_disable ( void ) __attribute__ (( deprecated )); -static inline void eth_disable ( void ) { - nic->nic_op->disable ( nic ); -} - -/* dev.h needs declarations from nic.h */ -#include "dev.h" -/* to get global "dev" */ -#include "main.h" +extern void eth_disable ( void ) __attribute__ (( deprecated )); #endif /* NIC_H */ diff --git a/src/include/pci.h b/src/include/pci.h index fd6ac9ba6..f904504f0 100644 --- a/src/include/pci.h +++ b/src/include/pci.h @@ -22,8 +22,10 @@ */ #include "stdint.h" +#include "nic.h" #include "pci_ids.h" -#include "dev.h" + +#define PCI_BUS_TYPE 1 /* * PCI constants @@ -233,33 +235,45 @@ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +/* + * A location on a PCI bus + * + */ +struct pci_loc { + uint16_t busdevfn; +}; + /* * A physical PCI device * */ struct pci_device { - char * magic; /* must be first */ - const char * name; - uint32_t membase; /* BAR 1 */ - uint32_t ioaddr; /* first IO BAR */ - uint16_t vendor, dev_id; - uint16_t class; - uint16_t busdevfn; - uint8_t revision; - uint8_t irq; - uint8_t already_tried; -}; + const char * name; + uint32_t membase; /* BAR 1 */ + uint32_t ioaddr; /* first IO BAR */ + uint16_t vendor_id, device_id; + uint16_t class; + uint16_t busdevfn; + uint8_t revision; + uint8_t irq; +} __attribute__ (( packed )); + +/* + * Useful busdevfn calculations + * + */ #define PCI_BUS(busdevfn) ( ( uint8_t ) ( ( (busdevfn) >> 8 ) & 0xff ) ) #define PCI_DEV(busdevfn) ( ( uint8_t ) ( ( (busdevfn) >> 3 ) & 0x1f ) ) #define PCI_FUNC(busdevfn) ( ( uint8_t ) ( (busdevfn) & 0x07 ) ) #define PCI_FN0(busdevfn) ( ( uint16_t ) ( (busdevfn) & 0xfff8 ) ) +#define PCI_MAX_BUSDEVFN 0xffff /* * An individual PCI device identified by vendor and device IDs * */ struct pci_id { - unsigned short vendor, dev_id; + unsigned short vendor_id, device_id; const char *name; }; @@ -268,24 +282,23 @@ struct pci_id { * is also parsed by parserom.pl to generate Makefile rules and files * for rom-o-matic. */ -#define PCI_ROM( rom_vendor, rom_dev_id, rom_name, rom_description ) { \ - .vendor = rom_vendor, \ - .dev_id = rom_dev_id, \ - .name = rom_name, \ +#define PCI_ROM( _vendor_id, _device_id, _name, _description ) { \ + .vendor_id = _vendor_id, \ + .device_id = _device_id, \ + .name = _name, \ } /* - * A PCI driver, with a device ID (struct pci_id) table and an - * optional class. + * A PCI driver information table, with a device ID (struct pci_id) + * table and an optional class. * * Set the class to something other than PCI_NO_CLASS if the driver * can handle an entire class of devices. * */ -struct pci_driver { - const char *name; +struct pci_driver_info { struct pci_id *ids; - int id_count; + unsigned int id_count; uint16_t class; }; #define PCI_NO_CLASS 0 @@ -294,30 +307,30 @@ struct pci_driver { * Define a PCI driver. * */ -#define PCI_DRIVER( driver_name, pci_ids, pci_class ) { \ - .name = driver_name, \ - .ids = pci_ids, \ - .id_count = sizeof ( pci_ids ) / sizeof ( pci_ids[0] ), \ - .class = pci_class, \ -} +#define PCI_DRIVER( _info_name, _ids, _class ) \ + static struct pci_driver_info _info_name = { \ + .ids = _ids, \ + .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \ + .class = _class, \ + }; /* * These are the functions we expect pci_io.c to provide. * */ -extern int pci_read_config_byte ( struct pci_device *dev, unsigned int where, +extern int pci_read_config_byte ( struct pci_device *pci, unsigned int where, uint8_t *value ); -extern int pci_write_config_byte ( struct pci_device *dev, unsigned int where, +extern int pci_write_config_byte ( struct pci_device *pci, unsigned int where, uint8_t value ); -extern int pci_read_config_word ( struct pci_device *dev, unsigned int where, +extern int pci_read_config_word ( struct pci_device *pci, unsigned int where, uint16_t *value ); -extern int pci_write_config_word ( struct pci_device *dev, unsigned int where, +extern int pci_write_config_word ( struct pci_device *pci, unsigned int where, uint16_t value ); -extern int pci_read_config_dword ( struct pci_device *dev, unsigned int where, +extern int pci_read_config_dword ( struct pci_device *pci, unsigned int where, uint32_t *value ); -extern int pci_write_config_dword ( struct pci_device *dev, unsigned int where, +extern int pci_write_config_dword ( struct pci_device *pci, unsigned int where, uint32_t value ); -extern unsigned long pci_bus_base ( struct pci_device *dev ); +extern unsigned long pci_bus_base ( struct pci_device *pci ); /* * pci_io.c is allowed to overwrite pci_max_bus if it knows what the @@ -330,13 +343,17 @@ extern unsigned int pci_max_bus; * Functions in pci.c * */ -extern int find_pci_device ( struct pci_device *pci, - struct pci_driver *driver ); -extern int find_pci_boot_device ( struct dev *dev, struct pci_driver *driver ); extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, unsigned int bar ); extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int bar ); extern int pci_find_capability ( struct pci_device *pci, int capability ); +extern void pci_fill_nic ( struct nic *nic, struct pci_device *pci ); + +/* + * PCI bus global definition + * + */ +extern struct bus_driver pci_driver; #endif /* PCI_H */