From 0638945c7eeb80a70fc9303e4cd886e13040a431 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 5 Jan 2007 01:52:06 +0000 Subject: [PATCH] Add PXE driver-scanning code, capable of locating all PXE ROMs in a system and matching them up to (e.g. PCI) devices. --- src/arch/i386/drivers/bus/pxedrv.c | 223 +++++++++++++++++++++++++++++ src/include/pxe.h | 115 ++++++++++++++- 2 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 src/arch/i386/drivers/bus/pxedrv.c diff --git a/src/arch/i386/drivers/bus/pxedrv.c b/src/arch/i386/drivers/bus/pxedrv.c new file mode 100644 index 000000000..4e4325740 --- /dev/null +++ b/src/arch/i386/drivers/bus/pxedrv.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * PXE drivers + * + */ + +static LIST_HEAD ( pxe_drivers ); + +/** + * Parse PXE ROM ID structure + * + * @v pxedrv PXE driver + * @v pxeromid Offset within ROM to PXE ROM ID structure + * @ret rc Return status code + */ +static int pxedrv_parse_pxeromid ( struct pxe_driver *pxedrv, + unsigned int pxeromid ) { + struct undi_rom_id undi_rom_id; + unsigned int undiloader; + + DBGC ( pxedrv, "PXEDRV %p has PXE ROM ID at %04x:%04x\n", pxedrv, + pxedrv->rom_segment, pxeromid ); + + /* Read PXE ROM ID structure and verify */ + copy_from_real ( &undi_rom_id, pxedrv->rom_segment, pxeromid, + sizeof ( undi_rom_id ) ); + if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) { + DBGC ( pxedrv, "PXEDRV %p has bad PXE ROM ID signature " + "%08lx\n", pxedrv, undi_rom_id.Signature ); + return -EINVAL; + } + + /* Check for UNDI loader */ + undiloader = undi_rom_id.UNDILoader; + if ( ! undiloader ) { + DBGC ( pxedrv, "PXEDRV %p has no UNDI loader\n", pxedrv ); + return -EINVAL; + } + + /* Fill in PXE driver loader fields */ + pxedrv->loader.segment = pxedrv->rom_segment; + pxedrv->loader.offset = undiloader; + pxedrv->code_size = undi_rom_id.CodeSize; + pxedrv->data_size = undi_rom_id.DataSize; + + DBGC ( pxedrv, "PXEDRV %p has UNDI loader at %04x:%04x " + "(code %04x data %04x)\n", pxedrv, pxedrv->loader.segment, + pxedrv->loader.offset, pxedrv->code_size, pxedrv->data_size ); + return 0; +} + +/** + * Parse PCI expansion header + * + * @v pxedrv PXE driver + * @v pcirheader Offset within ROM to PCI expansion header + */ +static int pxedrv_parse_pcirheader ( struct pxe_driver *pxedrv, + unsigned int pcirheader ) { + struct pcir_header pcir_header; + + DBGC ( pxedrv, "PXEDRV %p has PCI expansion header at %04x:%04x\n", + pxedrv, pxedrv->rom_segment, pcirheader ); + + /* Read PCI expansion header and verify */ + copy_from_real ( &pcir_header, pxedrv->rom_segment, pcirheader, + sizeof ( pcir_header ) ); + if ( pcir_header.signature != PCIR_SIGNATURE ) { + DBGC ( pxedrv, "PXEDRV %p has bad PCI expansion header " + "signature %08lx\n", pxedrv, pcir_header.signature ); + return -EINVAL; + } + DBGC ( pxedrv, "PXEDRV %p is a PCI ROM\n", pxedrv ); + + /* Fill in PXE driver PCI device fields */ + pxedrv->bus_type = PCI_NIC; + pxedrv->bus_id.pci.vendor_id = pcir_header.vendor_id; + pxedrv->bus_id.pci.device_id = pcir_header.device_id; + + DBGC ( pxedrv, "PXEDRV %p is for PCI devices %04x:%04x\n", pxedrv, + pxedrv->bus_id.pci.vendor_id, pxedrv->bus_id.pci.device_id ); + return 0; + +} + +/** + * Create PXE driver for expansion ROM + * + * @v rom_segment ROM segment address + * @ret rc Return status code + */ +static int pxedrv_probe_rom ( unsigned int rom_segment ) { + struct pxe_driver *pxedrv = NULL; + struct undi_rom rom; + unsigned int pxeromid; + unsigned int pcirheader; + int rc; + + /* Read expansion ROM header and verify */ + copy_from_real ( &rom, rom_segment, 0, sizeof ( rom ) ); + if ( rom.Signature != ROM_SIGNATURE ) { + rc = -EINVAL; + goto err; + } + + /* Allocate memory for PXE driver */ + pxedrv = malloc ( sizeof ( *pxedrv ) ); + if ( ! pxedrv ) { + DBG ( "Could not allocate PXE driver structure\n" ); + rc = -ENOMEM; + goto err; + } + memset ( pxedrv, 0, sizeof ( *pxedrv ) ); + DBGC ( pxedrv, "PXEDRV %p using expansion ROM at %04x:0000 (%zdkB)\n", + pxedrv, rom_segment, ( rom.ROMLength / 2 ) ); + pxedrv->rom_segment = rom_segment; + + /* Check for and parse PXE ROM ID */ + pxeromid = rom.PXEROMID; + if ( ! pxeromid ) { + DBGC ( pxedrv, "PXEDRV %p has no PXE ROM ID\n", pxedrv ); + rc = -EINVAL; + goto err; + } + if ( ( rc = pxedrv_parse_pxeromid ( pxedrv, pxeromid ) ) != 0 ) + goto err; + + /* Parse PCIR header, if present */ + pcirheader = rom.PCIRHeader; + if ( pcirheader ) + pxedrv_parse_pcirheader ( pxedrv, pcirheader ); + + /* Add to PXE driver list and return */ + DBGC ( pxedrv, "PXEDRV %p registered\n", pxedrv ); + list_add ( &pxedrv->list, &pxe_drivers ); + return 0; + + err: + free ( pxedrv ); + return rc; +} + +/** + * Create PXE drivers for all possible expansion ROMs + * + * @ret + */ +static void pxedrv_probe_all_roms ( void ) { + static int probed = 0; + unsigned int rom_segment; + + /* Perform probe only once */ + if ( probed ) + return; + + DBG ( "Scanning for PXE expansion ROMs\n" ); + + /* Scan through expansion ROM region at 2kB intervals */ + for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ; + rom_segment += 0x80 ) { + pxedrv_probe_rom ( rom_segment ); + } + + probed = 1; +} + +/** + * Find PXE driver for PCI device + * + * @v vendor_id PCI vendor ID + * @v device_id PCI device ID + * @v rombase ROM base address, or 0 for any + * @ret pxedrv PXE driver, or NULL + */ +struct pxe_driver * pxedrv_find_pci_driver ( unsigned int vendor_id, + unsigned int device_id, + unsigned int rombase ) { + struct pxe_driver *pxedrv; + + pxedrv_probe_all_roms(); + + list_for_each_entry ( pxedrv, &pxe_drivers, list ) { + if ( pxedrv->bus_type != PCI_NIC ) + continue; + if ( pxedrv->bus_id.pci.vendor_id != vendor_id ) + continue; + if ( pxedrv->bus_id.pci.device_id != device_id ) + continue; + if ( rombase && ( ( pxedrv->rom_segment << 4 ) != rombase ) ) + continue; + DBGC ( pxedrv, "PXEDRV %p matched PCI %04x:%04x (%08x)\n", + pxedrv, vendor_id, device_id, rombase ); + return pxedrv; + } + + DBG ( "No PXE driver matched PCI %04x:%04x (%08x)\n", + vendor_id, device_id, rombase ); + return NULL; +} diff --git a/src/include/pxe.h b/src/include/pxe.h index 497f20c4a..e85e7e270 100644 --- a/src/include/pxe.h +++ b/src/include/pxe.h @@ -62,6 +62,113 @@ union u_PXENV_ANY { typedef union u_PXENV_ANY PXENV_ANY_t; +/** An UNDI expansion ROM */ +struct undi_rom { + /** Signature + * + * Must be equal to @c ROM_SIGNATURE + */ + UINT16_t Signature; + /** ROM length in 512-byte blocks */ + UINT8_t ROMLength; + /** Unused */ + UINT8_t unused[0x13]; + /** Offset of the PXE ROM ID structure */ + UINT16_t PXEROMID; + /** Offset of the PCI ROM structure */ + UINT16_t PCIRHeader; +} PACKED; + +/** Signature for an expansion ROM */ +#define ROM_SIGNATURE 0xaa55 + +/** An UNDI ROM ID structure */ +struct undi_rom_id { + /** Signature + * + * Must be equal to @c UNDI_ROM_ID_SIGNATURE + */ + UINT32_t Signature; + /** Length of structure */ + UINT8_t StructLength; + /** Checksum */ + UINT8_t StructCksum; + /** Structure revision + * + * Must be zero. + */ + UINT8_t StructRev; + /** UNDI revision + * + * Version 2.1.0 is encoded as the byte sequence 0x00, 0x01, 0x02. + */ + UINT8_t UNDIRev[3]; + /** Offset to UNDI loader */ + UINT16_t UNDILoader; + /** Minimum required stack segment size */ + UINT16_t StackSize; + /** Minimum required data segment size */ + UINT16_t DataSize; + /** Minimum required code segment size */ + UINT16_t CodeSize; +} PACKED; + +/** Signature for an UNDI ROM ID structure */ +#define UNDI_ROM_ID_SIGNATURE \ + ( ( 'U' << 0 ) + ( 'N' << 8 ) + ( 'D' << 16 ) + ( 'I' << 24 ) ) + +/** A PCI expansion header */ +struct pcir_header { + /** Signature + * + * Must be equal to @c PCIR_SIGNATURE + */ + uint32_t signature; + /** PCI vendor ID */ + uint16_t vendor_id; + /** PCI device ID */ + uint16_t device_id; +} PACKED; + +/** Signature for an UNDI ROM ID structure */ +#define PCIR_SIGNATURE \ + ( ( 'P' << 0 ) + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) ) + +/** A PXE PCI device ID */ +struct pxe_pci_device_id { + /** PCI vendor ID */ + unsigned int vendor_id; + /** PCI device ID */ + unsigned int device_id; +}; + +/** A PXE device ID */ +union pxe_device_id { + /** PCI device ID */ + struct pxe_pci_device_id pci; +}; + +/** A PXE driver */ +struct pxe_driver { + /** List of PXE drivers */ + struct list_head list; + /** ROM segment address */ + unsigned int rom_segment; + /** UNDI loader entry point */ + SEGOFF16_t loader; + /** Code segment size */ + size_t code_size; + /** Data segment size */ + size_t data_size; + /** Bus type + * + * Values are as used by @c PXENV_UNDI_GET_NIC_TYPE + */ + unsigned int bus_type; + /** Device ID */ + union pxe_device_id bus_id; +}; + /** A PXE device */ struct pxe_device { /** Generic device */ @@ -83,8 +190,8 @@ struct pxe_device { MAC_ADDR_t hwaddr; /** Assigned IRQ number */ UINT16_t irq; - /** ROM address */ - SEGSEL_t rom; + /** ROM segment address */ + SEGSEL_t rom_segment; }; /** @@ -112,6 +219,10 @@ extern int pxe_call ( struct pxe_device *pxe, unsigned int function, extern int undi_probe ( struct pxe_device *pxe ); extern void undi_remove ( struct pxe_device *pxe ); +extern struct pxe_driver * pxedrv_find_pci_driver ( unsigned int vendor_id, + unsigned int device_id, + unsigned int rombase ); + extern struct net_device *pxe_netdev; #endif /* PXE_H */