diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2008-11-08 21:36:35 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2008-11-08 21:36:35 -0500 |
commit | ceea03c235f337062b3ded69cc658ea759e254f6 (patch) | |
tree | 9238a443a2bb9a33374642b890a5d670df870844 /src/optionroms.c | |
parent | 1492708bbf9d44f2b0435b0ee5d9d037deeb9961 (diff) | |
download | seabios-ceea03c235f337062b3ded69cc658ea759e254f6.tar.gz |
Overhaul option rom processing.
Add initial support for scanning PCI devices for option roms.
Implement two pass option rom scan - init first then scan for bev/bcv.
Support calling BCV vectors that are found.
Diffstat (limited to 'src/optionroms.c')
-rw-r--r-- | src/optionroms.c | 293 |
1 files changed, 241 insertions, 52 deletions
diff --git a/src/optionroms.c b/src/optionroms.c index 2d8fe11c..aa6f8bb8 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -8,6 +8,14 @@ #include "bregs.h" // struct bregs #include "biosvar.h" // struct ipl_entry_s #include "util.h" // dprintf +#include "pci.h" // pci_find_class +#include "pci_regs.h" // PCI_ROM_ADDRESS +#include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA + + +/**************************************************************** + * Definitions + ****************************************************************/ // $PnP string with special alignment in romlayout.S extern char pnp_string[]; @@ -57,15 +65,34 @@ struct pnp_data { u16 staticresource; } PACKED; +#define OPTIONROM_BDF_1 0x0000 +#define OPTIONROM_MEM_1 0x00000000 +#define OPTIONROM_BDF_2 0x0000 +#define OPTIONROM_MEM_2 0x00000000 + +#define OPTION_ROM_START 0xc0000 +#define OPTION_ROM_SIGNATURE 0xaa55 +#define OPTION_ROM_ALIGN 2048 +#define OPTION_ROM_INITVECTOR offsetof(struct rom_header, initVector[0]) + +// Next available position for an option rom. +static u32 next_rom; + + +/**************************************************************** + * Helper functions + ****************************************************************/ + // Execute a given option rom. static void -callrom(u16 seg, u16 offset) +callrom(struct rom_header *rom, u16 offset, u16 bdf) { + u16 seg = FARPTR_TO_SEG(rom); dprintf(1, "Running option rom at %x:%x\n", seg, offset); struct bregs br; memset(&br, 0, sizeof(br)); - // XXX - should set br.ax to PCI Bus/DevFn + br.ax = bdf; br.bx = 0xffff; br.dx = 0xffff; br.es = SEG_BIOS; @@ -81,67 +108,236 @@ callrom(u16 seg, u16 offset) , seg, offset, SEG_EBDA, GET_BDA(ebda_seg)); } -#define ebda ((struct extended_bios_data_area_s *)MAKE_FARPTR(SEG_EBDA, 0)) +// Verify that an option rom looks valid +static int +is_valid_rom(struct rom_header *rom) +{ + if (rom->signature != OPTION_ROM_SIGNATURE) + return 0; + u32 len = rom->size * 512; + u8 sum = checksum((void*)rom, len); + if (sum != 0) { + dprintf(1, "Found option rom with bad checksum: loc=%p len=%d sum=%x\n" + , rom, len, sum); + return 0; + } + return 1; +} -// Find and run any "option roms" found in the given address range. +// Check if a valid option rom has a pnp struct; return it if so. +static struct pnp_data * +get_pnp_rom(struct rom_header *rom) +{ + struct pnp_data *pnp = (struct pnp_data *)((u8*)rom + rom->pnpoffset); + if (pnp->signature != *(u32*)pnp_string) + return NULL; + return pnp; +} + +// Add a BEV vector for a given pnp compatible option rom. static void -rom_scan(u32 start, u32 end) +add_ipl(struct rom_header *rom, struct pnp_data *pnp) { - if (! CONFIG_OPTIONROMS) +#define ebda ((struct extended_bios_data_area_s *)MAKE_FARPTR(SEG_EBDA, 0)) + + // Found a device that thinks it can boot the system. Record + // its BEV and product name string. + + if (! CONFIG_BOOT) return; - u8 *p = (u8*)start; - for (; p < (u8*)end; p += 2048) { - struct rom_header *rom = (struct rom_header *)p; - if (rom->signature != 0xaa55) - continue; - u32 len = rom->size * 512; - u8 sum = checksum(p, len); - if (sum != 0) { - dprintf(1, "Found option rom with bad checksum:" - " loc=%p len=%d sum=%x\n" - , rom, len, sum); - continue; - } - p = (u8*)(((u32)p + len) / 2048 * 2048); - callrom(FARPTR_TO_SEG(rom), FARPTR_TO_OFFSET(rom->initVector)); - - // Look at the ROM's PnP Expansion header. Properly, we're supposed - // to init all the ROMs and then go back and build an IPL table of - // all the bootable devices, but we can get away with one pass. - struct pnp_data *pnp = (struct pnp_data *)((u8*)rom + rom->pnpoffset); - if (pnp->signature != *(u32*)pnp_string) - continue; - u16 entry = pnp->bev; - if (!entry) - continue; - // Found a device that thinks it can boot the system. Record - // its BEV and product name string. + if (ebda->ipl.count >= ARRAY_SIZE(ebda->ipl.table)) + return; - if (! CONFIG_BOOT) - continue; + struct ipl_entry_s *ip = &ebda->ipl.table[ebda->ipl.count]; + ip->type = IPL_TYPE_BEV; + ip->vector = (FARPTR_TO_SEG(rom) << 16) | pnp->bev; - if (ebda->ipl.count >= ARRAY_SIZE(ebda->ipl.table)) - continue; + u16 desc = pnp->productname; + if (desc) + ip->description = MAKE_FARPTR(FARPTR_TO_SEG(rom), desc); + + ebda->ipl.count++; +} + +// Check if an option rom is at a hardcoded location for a device. +static struct rom_header * +lookup_hardcode(PCIDevice d) +{ + if (OPTIONROM_BDF_1 + && OPTIONROM_BDF_1 == pci_to_bdf(d)) + return (struct rom_header *)OPTIONROM_MEM_1; + else if (OPTIONROM_BDF_2 + && OPTIONROM_BDF_2 == pci_to_bdf(d)) + return (struct rom_header *)OPTIONROM_MEM_2; + // XXX - check LAR when in coreboot? + return NULL; +} + +// Map the option rom of a given PCI device. +static struct rom_header * +map_optionrom(PCIDevice d) +{ + u32 orig = pci_config_readl(d, PCI_ROM_ADDRESS); + pci_config_writel(d, PCI_ROM_ADDRESS, ~PCI_ROM_ADDRESS_ENABLE); + u32 sz = pci_config_readl(d, PCI_ROM_ADDRESS); + + if (!sz || sz == 0xffffffff) + goto fail; + + // Looks like a rom - map it to just above end of memory. + u32 mappos = ALIGN(GET_EBDA(ram_size), OPTION_ROM_ALIGN); + pci_config_writel(d, PCI_ROM_ADDRESS, mappos | PCI_ROM_ADDRESS_ENABLE); - struct ipl_entry_s *ip = &ebda->ipl.table[ebda->ipl.count]; - ip->type = IPL_TYPE_BEV; - ip->vector = (FARPTR_TO_SEG(rom) << 16) | entry; + struct rom_header *rom = (struct rom_header *)mappos; + if (rom->signature != OPTION_ROM_SIGNATURE) + goto fail; - u16 desc = pnp->productname; - if (desc) - ip->description = MAKE_FARPTR(FARPTR_TO_SEG(rom), desc); + return rom; +fail: + // Not valid - restore original and exit. + pci_config_writel(d, PCI_ROM_ADDRESS, orig); + return NULL; +} + +// Attempt to map and initialize the option rom on a given PCI device. +static struct rom_header * +init_optionrom(PCIDevice d) +{ + struct rom_header *rom = lookup_hardcode(d); + if (! rom) + rom = map_optionrom(d); + if (! rom) + // No ROM present. + return NULL; - ebda->ipl.count++; + u32 romsize = rom->size * 512; + if (next_rom + romsize > BUILD_BIOS_ADDR) { + // Option rom doesn't fit. + dprintf(1, "Option rom %x doesn't fit.", pci_to_bdf(d)); + pci_config_writel(d, PCI_ROM_ADDRESS, next_rom); + return NULL; } + memcpy((void*)next_rom, rom, romsize); + pci_config_writel(d, PCI_ROM_ADDRESS, next_rom); + rom = (struct rom_header *)next_rom; + + if (! is_valid_rom(rom)) + return NULL; + + if (get_pnp_rom(rom)) + // Init the PnP rom. + callrom(rom, OPTION_ROM_INITVECTOR, pci_to_bdf(d)); + + next_rom += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + + return rom; } + +/**************************************************************** + * Non-VGA option rom init + ****************************************************************/ + +void +optionrom_setup() +{ + if (! CONFIG_OPTIONROMS) + return; + + dprintf(1, "Scan for option roms\n"); + + u32 post_vga = next_rom; + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + u32 pos = next_rom; + while (pos < BUILD_BIOS_ADDR) { + struct rom_header *rom = (struct rom_header *)pos; + if (! is_valid_rom(rom)) { + pos += OPTION_ROM_ALIGN; + continue; + } + if (get_pnp_rom(rom)) + callrom(rom, OPTION_ROM_INITVECTOR, 0); + pos += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + next_rom = pos; + } + } else { + // Find and deploy PCI roms. + int devfn, bus; + for (bus=0; bus < CONFIG_PCI_BUS_COUNT; bus++) { + for (devfn=0; devfn<0x100; devfn++) { + PCIDevice d = pci_bd(bus, devfn); + u16 v = pci_config_readw(d, PCI_CLASS_DEVICE); + if (v == 0x0000 || v == 0xffff || v == PCI_CLASS_DISPLAY_VGA) + continue; + init_optionrom(d); + } + } + } + + // All option roms found and deployed - now build BEV/BCV vectors. + + u32 pos = post_vga; + while (pos < next_rom) { + struct rom_header *rom = (struct rom_header *)pos; + if (! is_valid_rom(rom)) { + pos += OPTION_ROM_ALIGN; + continue; + } + pos += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + struct pnp_data *pnp = get_pnp_rom(rom); + if (! pnp) { + // Legacy rom - run init vector now. + callrom(rom, OPTION_ROM_INITVECTOR, 0); + continue; + } + // PnP rom. + if (pnp->bev) + // Can boot system - add to IPL list. + add_ipl(rom, pnp); + else if (pnp->bcv) + // Has BCV - run it now. + callrom(rom, pnp->bcv, 0); + } +} + + +/**************************************************************** + * VGA init + ****************************************************************/ + // Call into vga code to turn on console. void vga_setup() { + if (! CONFIG_OPTIONROMS) + return; + dprintf(1, "Scan for VGA option rom\n"); - rom_scan(0xc0000, 0xc8000); + next_rom = OPTION_ROM_START; + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + struct rom_header *rom = (struct rom_header *)OPTION_ROM_START; + if (! is_valid_rom(rom)) + return; + callrom(rom, OPTION_ROM_INITVECTOR, 0); + next_rom += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + } else { + // Find and deploy PCI VGA rom. + PCIDevice d; + int ret = pci_find_class(PCI_CLASS_DISPLAY_VGA, 0, &d); + if (ret) + // Device not found + return; + + struct rom_header *rom = init_optionrom(d); + if (rom && !get_pnp_rom(rom)) + // Call rom even if it isn't a pnp rom. + callrom(rom, OPTION_ROM_INITVECTOR, pci_to_bdf(d)); + } dprintf(1, "Turning on vga console\n"); struct bregs br; @@ -152,10 +348,3 @@ vga_setup() // Write to screen. printf("Starting SeaBIOS\n\n"); } - -void -optionrom_setup() -{ - dprintf(1, "Scan for option roms\n"); - rom_scan(0xc8000, 0xf0000); -} |