aboutsummaryrefslogtreecommitdiffstats
path: root/src/optionroms.c
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2008-11-08 21:36:35 -0500
committerKevin O'Connor <kevin@koconnor.net>2008-11-08 21:36:35 -0500
commitceea03c235f337062b3ded69cc658ea759e254f6 (patch)
tree9238a443a2bb9a33374642b890a5d670df870844 /src/optionroms.c
parent1492708bbf9d44f2b0435b0ee5d9d037deeb9961 (diff)
downloadseabios-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.c293
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);
-}