// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2002 MandrakeSoft S.A.
//
-// This file may be distributed under the terms of the GNU GPLv3 license.
+// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "bregs.h" // struct bregs
-#include "biosvar.h" // struct ipl_entry_s
+#include "farptr.h" // FLATPTR_TO_SEG
+#include "config.h" // CONFIG_*
#include "util.h" // dprintf
-#include "pci.h" // pci_find_class
+#include "pci.h" // foreachpci
#include "pci_regs.h" // PCI_ROM_ADDRESS
#include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
+#include "boot.h" // IPL
+#include "paravirt.h" // qemu_cfg_*
/****************************************************************
* Definitions
****************************************************************/
-// $PnP string with special alignment in romlayout.S
-extern char pnp_string[];
-
struct rom_header {
u16 signature;
u8 size;
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])
+#define PCI_ROM_SIGNATURE 0x52494350 // PCIR
+#define PCIROM_CODETYPE_X86 0
-// Next available position for an option rom.
-static u32 next_rom;
+// The end of the last deployed rom.
+u32 RomEnd;
/****************************************************************
// Execute a given option rom.
static void
-callrom(struct rom_header *rom, u16 offset, u16 bdf)
+__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);
+ u16 seg = FLATPTR_TO_SEG(rom);
+ dprintf(1, "Running option rom at %04x:%04x\n", seg, offset);
struct bregs br;
memset(&br, 0, sizeof(br));
+ br.flags = F_IF;
br.ax = bdf;
br.bx = 0xffff;
br.dx = 0xffff;
br.es = SEG_BIOS;
- br.di = (u32)pnp_string - BUILD_BIOS_ADDR;
- br.cs = seg;
- br.ip = offset;
- call16(&br);
+ br.di = get_pnp_offset();
+ br.code = SEGOFF(seg, offset);
+ start_preempt();
+ call16big(&br);
+ finish_preempt();
debug_serial_setup();
+}
+
+// Execute a given option rom at the standard entry vector.
+static void
+callrom(struct rom_header *rom, u16 bdf)
+{
+ __callrom(rom, OPTION_ROM_INITVECTOR, bdf);
+}
- if (GET_BDA(ebda_seg) != SEG_EBDA)
- BX_PANIC("Option rom at %x:%x attempted to move ebda from %x to %x\n"
- , seg, offset, SEG_EBDA, GET_BDA(ebda_seg));
+// Execute a BCV option rom registered via add_bcv().
+void
+call_bcv(u16 seg, u16 ip)
+{
+ __callrom(MAKE_FLATPTR(seg, 0), ip, 0);
}
// Verify that an option rom looks valid
static int
is_valid_rom(struct rom_header *rom)
{
+ dprintf(6, "Checking rom %p (sig %x size %d)\n"
+ , rom, rom->signature, rom->size);
if (rom->signature != OPTION_ROM_SIGNATURE)
return 0;
+ if (! rom->size)
+ return 0;
u32 len = rom->size * 512;
- u8 sum = checksum((void*)rom, len);
+ u8 sum = checksum(rom, len);
if (sum != 0) {
dprintf(1, "Found option rom with bad checksum: loc=%p len=%d sum=%x\n"
, rom, len, sum);
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)
+ struct pnp_data *pnp = (void*)((u8*)rom + rom->pnpoffset);
+ if (pnp->signature != PNP_SIGNATURE)
return NULL;
return pnp;
}
-// Add a BEV vector for a given pnp compatible option rom.
-static void
-add_ipl(struct rom_header *rom, struct pnp_data *pnp)
+// Check for multiple pnp option rom headers.
+static struct pnp_data *
+get_pnp_next(struct rom_header *rom, struct pnp_data *pnp)
{
-#define ebda ((struct extended_bios_data_area_s *)MAKE_FARPTR(SEG_EBDA, 0))
+ if (! pnp->nextoffset)
+ return NULL;
+ pnp = (void*)((u8*)rom + pnp->nextoffset);
+ if (pnp->signature != PNP_SIGNATURE)
+ return NULL;
+ return pnp;
+}
- // Found a device that thinks it can boot the system. Record
- // its BEV and product name string.
+// Check if a valid option rom has a pci struct; return it if so.
+static struct pci_data *
+get_pci_rom(struct rom_header *rom)
+{
+ struct pci_data *pci = (void*)((u32)rom + rom->pcioffset);
+ if (pci->signature != PCI_ROM_SIGNATURE)
+ return NULL;
+ return pci;
+}
- if (! CONFIG_BOOT)
- return;
+// Return the memory position up to which roms may be located.
+static inline u32
+max_rom(void)
+{
+ extern u8 code32flat_start[];
+ if ((u32)code32flat_start > BUILD_BIOS_ADDR)
+ return BUILD_BIOS_ADDR;
+ return (u32)code32flat_start;
+}
- if (ebda->ipl.count >= ARRAY_SIZE(ebda->ipl.table))
- return;
+// Copy a rom to its permanent location below 1MiB
+static struct rom_header *
+copy_rom(struct rom_header *rom)
+{
+ u32 romsize = rom->size * 512;
+ if (RomEnd + romsize > max_rom()) {
+ // Option rom doesn't fit.
+ warn_noalloc();
+ return NULL;
+ }
+ dprintf(4, "Copying option rom (size %d) from %p to %x\n"
+ , romsize, rom, RomEnd);
+ iomemcpy((void*)RomEnd, rom, romsize);
+ return (void*)RomEnd;
+}
- 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;
+// Run rom init code and note rom size.
+static int
+init_optionrom(struct rom_header *rom, u16 bdf, int isvga)
+{
+ if (! is_valid_rom(rom))
+ return -1;
- u16 desc = pnp->productname;
- if (desc)
- ip->description = MAKE_FARPTR(FARPTR_TO_SEG(rom), desc);
+ if (isvga || get_pnp_rom(rom))
+ // Only init vga and PnP roms here.
+ callrom(rom, bdf);
- ebda->ipl.count++;
+ RomEnd = (u32)rom + ALIGN(rom->size * 512, OPTION_ROM_ALIGN);
+
+ return 0;
}
-// Check if an option rom is at a hardcoded location for a device.
+
+/****************************************************************
+ * Roms in CBFS
+ ****************************************************************/
+
+// Check if an option rom is at a hardcoded location or in CBFS.
static struct rom_header *
-lookup_hardcode(u16 bdf)
+lookup_hardcode(u32 vendev)
{
- if (OPTIONROM_BDF_1 && OPTIONROM_BDF_1 == bdf)
- return (struct rom_header *)OPTIONROM_MEM_1;
- else if (OPTIONROM_BDF_2 && OPTIONROM_BDF_2 == bdf)
- return (struct rom_header *)OPTIONROM_MEM_2;
- // XXX - check LAR when in coreboot?
- return NULL;
+ if (OPTIONROM_VENDEV_1
+ && ((OPTIONROM_VENDEV_1 >> 16)
+ | ((OPTIONROM_VENDEV_1 & 0xffff)) << 16) == vendev)
+ return copy_rom((void*)OPTIONROM_MEM_1);
+ if (OPTIONROM_VENDEV_2
+ && ((OPTIONROM_VENDEV_2 >> 16)
+ | ((OPTIONROM_VENDEV_2 & 0xffff)) << 16) == vendev)
+ return copy_rom((void*)OPTIONROM_MEM_2);
+ char fname[17];
+ snprintf(fname, sizeof(fname), "pci%04x,%04x.rom"
+ , pci_vd_to_ven(vendev), pci_vd_to_dev(vendev));
+ int ret = romfile_copy(romfile_find(fname), (void*)RomEnd
+ , max_rom() - RomEnd);
+ if (ret <= 0)
+ return NULL;
+ return (void*)RomEnd;
+}
+
+// Run all roms in a given CBFS directory.
+static void
+run_file_roms(const char *prefix, int isvga)
+{
+ u32 file = 0;
+ for (;;) {
+ file = romfile_findprefix(prefix, file);
+ if (!file)
+ break;
+ int ret = romfile_copy(file, (void*)RomEnd, max_rom() - RomEnd);
+ if (ret > 0)
+ init_optionrom((void*)RomEnd, 0, isvga);
+ }
}
+
+/****************************************************************
+ * PCI roms
+ ****************************************************************/
+
// Map the option rom of a given PCI device.
static struct rom_header *
-map_optionrom(u16 bdf)
+map_pcirom(u16 bdf, u32 vendev)
{
+ dprintf(6, "Attempting to map option rom on dev %02x:%02x.%x\n"
+ , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf));
+
+ u8 htype = pci_config_readb(bdf, PCI_HEADER_TYPE);
+ if ((htype & 0x7f) != PCI_HEADER_TYPE_NORMAL) {
+ dprintf(6, "Skipping non-normal pci device (type=%x)\n", htype);
+ return NULL;
+ }
+
u32 orig = pci_config_readl(bdf, PCI_ROM_ADDRESS);
pci_config_writel(bdf, PCI_ROM_ADDRESS, ~PCI_ROM_ADDRESS_ENABLE);
u32 sz = pci_config_readl(bdf, PCI_ROM_ADDRESS);
+ dprintf(6, "Option rom sizing returned %x %x\n", orig, sz);
+ orig &= ~PCI_ROM_ADDRESS_ENABLE;
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(bdf, PCI_ROM_ADDRESS, mappos | PCI_ROM_ADDRESS_ENABLE);
-
- struct rom_header *rom = (struct rom_header *)mappos;
- if (rom->signature != OPTION_ROM_SIGNATURE)
+ if (orig == sz || (u32)(orig + 4*1024*1024) < 20*1024*1024) {
+ // Don't try to map to a pci addresses at its max, in the last
+ // 4MiB of ram, or the first 16MiB of ram.
+ dprintf(6, "Preset rom address doesn't look valid\n");
goto fail;
+ }
+
+ // Looks like a rom - enable it.
+ pci_config_writel(bdf, PCI_ROM_ADDRESS, orig | PCI_ROM_ADDRESS_ENABLE);
+
+ struct rom_header *rom = (void*)orig;
+ for (;;) {
+ dprintf(5, "Inspecting possible rom at %p (vd=%04x:%04x"
+ " bdf=%02x:%02x.%x)\n"
+ , rom, pci_vd_to_ven(vendev), pci_vd_to_dev(vendev)
+ , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf));
+ if (rom->signature != OPTION_ROM_SIGNATURE) {
+ dprintf(6, "No option rom signature (got %x)\n", rom->signature);
+ goto fail;
+ }
+ struct pci_data *pci = get_pci_rom(rom);
+ if (! pci) {
+ dprintf(6, "No valid pci signature found\n");
+ goto fail;
+ }
+
+ u32 vd = pci_vd(pci->vendor, pci->device);
+ if (vd == vendev && pci->type == PCIROM_CODETYPE_X86)
+ // A match
+ break;
+ dprintf(6, "Didn't match dev/ven (got %08x) or type (got %d)\n"
+ , vd, pci->type);
+ if (pci->indicator & 0x80) {
+ dprintf(6, "No more images left\n");
+ goto fail;
+ }
+ rom = (void*)((u32)rom + pci->ilen * 512);
+ }
+ rom = copy_rom(rom);
+ pci_config_writel(bdf, PCI_ROM_ADDRESS, orig);
return rom;
fail:
// Not valid - restore original and exit.
}
// Attempt to map and initialize the option rom on a given PCI device.
-static struct rom_header *
-init_optionrom(u16 bdf)
+static int
+init_pcirom(u16 bdf, int isvga)
{
- struct rom_header *rom = lookup_hardcode(bdf);
+ u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
+ dprintf(4, "Attempting to init PCI bdf %02x:%02x.%x (vd %04x:%04x)\n"
+ , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)
+ , pci_vd_to_ven(vendev), pci_vd_to_dev(vendev));
+ struct rom_header *rom = lookup_hardcode(vendev);
if (! rom)
- rom = map_optionrom(bdf);
+ rom = map_pcirom(bdf, vendev);
if (! rom)
// No ROM present.
- return NULL;
-
- 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.", bdf);
- pci_config_writel(bdf, PCI_ROM_ADDRESS, next_rom);
- return NULL;
- }
- memcpy((void*)next_rom, rom, romsize);
- pci_config_writel(bdf, 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, bdf);
-
- next_rom += ALIGN(rom->size * 512, OPTION_ROM_ALIGN);
-
- return rom;
+ return -1;
+ return init_optionrom(rom, bdf, isvga);
}
****************************************************************/
void
-optionrom_setup()
+optionrom_setup(void)
{
if (! CONFIG_OPTIONROMS)
return;
dprintf(1, "Scan for option roms\n");
- u32 post_vga = next_rom;
+ u32 post_vga = RomEnd;
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)) {
+ u32 pos = RomEnd;
+ while (pos < max_rom()) {
+ int ret = init_optionrom((void*)pos, 0, 0);
+ if (ret)
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
+ pos = RomEnd;
}
} else {
// Find and deploy PCI roms.
- int max = GET_VAR(CS, MaxBDF);
- int bdf;
- for (bdf=0; bdf < max; bdf++) {
+ int bdf, max;
+ foreachpci(bdf, max) {
u16 v = pci_config_readw(bdf, PCI_CLASS_DEVICE);
- if (v == 0x0000 || v == 0xffff || v == PCI_CLASS_DISPLAY_VGA)
+ if (v == 0x0000 || v == 0xffff || v == PCI_CLASS_DISPLAY_VGA
+ || (CONFIG_ATA && v == PCI_CLASS_STORAGE_IDE))
continue;
- init_optionrom(bdf);
+ init_pcirom(bdf, 0);
}
+
+ // Find and deploy CBFS roms not associated with a device.
+ run_file_roms("genroms/", 0);
}
// 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;
+ while (pos < RomEnd) {
+ struct rom_header *rom = (void*)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);
+ // Legacy rom.
+ add_bcv(FLATPTR_TO_SEG(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);
+ add_bev(FLATPTR_TO_SEG(rom), pnp->bev, pnp->productname);
+ else
+ // Check for BCV (there may be multiple).
+ while (pnp && pnp->bcv) {
+ add_bcv(FLATPTR_TO_SEG(rom), pnp->bcv, pnp->productname);
+ pnp = get_pnp_next(rom, pnp);
+ }
}
}
// Call into vga code to turn on console.
void
-vga_setup()
+vga_setup(void)
{
+ VGAbdf = -1;
+ RomEnd = BUILD_ROM_START;
+
if (! CONFIG_OPTIONROMS)
return;
dprintf(1, "Scan for VGA option rom\n");
- 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);
+ init_optionrom((void*)BUILD_ROM_START, 0, 1);
} else {
// Find and deploy PCI VGA rom.
- int bdf = pci_find_class(PCI_CLASS_DISPLAY_VGA, 0);
- if (bdf < 0)
- // Device not found
- return;
-
- struct rom_header *rom = init_optionrom(bdf);
- if (rom && !get_pnp_rom(rom))
- // Call rom even if it isn't a pnp rom.
- callrom(rom, OPTION_ROM_INITVECTOR, bdf);
+ int bdf = VGAbdf = pci_find_vga();
+ if (bdf >= 0)
+ init_pcirom(bdf, 1);
+
+ // Find and deploy CBFS vga-style roms not associated with a device.
+ run_file_roms("vgaroms/", 1);
}
- dprintf(1, "Turning on vga console\n");
- struct bregs br;
- memset(&br, 0, sizeof(br));
- br.ax = 0x0003;
- call16_int(0x10, &br);
+ if (RomEnd == BUILD_ROM_START) {
+ // No VGA rom found
+ RomEnd += OPTION_ROM_ALIGN;
+ return;
+ }
+
+ enable_vga_console();
+}
- // Write to screen.
- printf("Starting SeaBIOS\n\n");
+void
+s3_resume_vga_init(void)
+{
+ if (!CONFIG_S3_RESUME_VGA_INIT)
+ return;
+ struct rom_header *rom = (void*)BUILD_ROM_START;
+ if (! is_valid_rom(rom))
+ return;
+ callrom(rom, 0);
}