diff options
Diffstat (limited to 'roms/seabios/src/fw/csm.c')
-rw-r--r-- | roms/seabios/src/fw/csm.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/roms/seabios/src/fw/csm.c b/roms/seabios/src/fw/csm.c new file mode 100644 index 000000000..8359bcba1 --- /dev/null +++ b/roms/seabios/src/fw/csm.c @@ -0,0 +1,374 @@ +// Compatibility Support Module (CSM) for UEFI / EDK-II +// +// Copyright © 2013 Intel Corporation +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "e820map.h" // e820_add +#include "farptr.h" // MAKE_FLATPTR +#include "hw/pci.h" // pci_to_bdf +#include "hw/pcidevice.h" // pci_probe_devices +#include "hw/pic.h" // pic_irqmask_read +#include "malloc.h" // malloc_csm_preinit +#include "memmap.h" // SYMBOL +#include "output.h" // dprintf +#include "paravirt.h" // qemu_preinit +#include "stacks.h" // wait_threads +#include "std/acpi.h" // RSDP_SIGNATURE +#include "std/bda.h" // struct bios_data_area_s +#include "std/optionrom.h" // struct rom_header +#include "util.h" // copy_smbios + +#define UINT8 u8 +#define UINT16 u16 +#define UINT32 u32 +#include "std/LegacyBios.h" + +struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16); + +EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = { + .Signature = 0x24454649, + .TableChecksum = 0 /* Filled in by checkrom.py */, + .TableLength = sizeof(csm_compat_table), + .Compatibility16CallSegment = SEG_BIOS, + .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */, + .OemIdStringPointer = (u32)"SeaBIOS", + .AcpiRsdPtrPointer = (u32)&csm_rsdp, +}; + +EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table; +EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table; + +static u16 PICMask = PIC_IRQMASK_DEFAULT; + +extern void __csm_return(struct bregs *regs) __noreturn; + +static void +csm_return(struct bregs *regs) +{ + u32 rommax = rom_get_max(); + + dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); + + csm_compat_table.UmaAddress = rommax; + csm_compat_table.UmaSize = SYMBOL(final_readonly_start) - rommax; + + PICMask = pic_irqmask_read(); + __csm_return(regs); +} + +static void +csm_maininit(struct bregs *regs) +{ + interface_init(); + pci_probe_devices(); + + csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS; + csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset(); + + regs->ax = 0; + + csm_return(regs); +} + +/* Legacy16InitializeYourself */ +static void +handle_csm_0000(struct bregs *regs) +{ + qemu_preinit(); + + dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es, + regs->bx); + + csm_init_table = MAKE_FLATPTR(regs->es, regs->bx); + + dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB); + dprintf(3, "HiPmmMemory %08x\n", csm_init_table->HiPmmMemory); + dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes); + dprintf(3, "ReverseThunk %04x:%04x\n", csm_init_table->ReverseThunkCallSegment, + csm_init_table->ReverseThunkCallOffset); + dprintf(3, "NumE820Entries %08x\n", csm_init_table->NumberE820Entries); + dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb); + dprintf(3, "ThunkStart %08x\n", csm_init_table->ThunkStart); + dprintf(3, "ThunkSize %08x\n", csm_init_table->ThunkSizeInBytes); + dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory); + dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes); + + malloc_csm_preinit(csm_init_table->LowPmmMemory, + csm_init_table->LowPmmMemorySizeInBytes, + csm_init_table->HiPmmMemory, + csm_init_table->HiPmmMemorySizeInBytes); + reloc_preinit(csm_maininit, regs); +} + +/* Legacy16UpdateBbs */ +static void +handle_csm_0001(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx); + + csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx); + dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion); + dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion); + dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable); + dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable); + dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength); +// dprintf(3, "SioData %08x\n", csm_boot_table->SioData); + dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType); + dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask); + dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries); +// dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo); + dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries); + dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable); + dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable); + dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb); + dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable); + + regs->ax = 0; +} + +/* PrepareToBoot */ +static void +handle_csm_0002(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx); + + struct e820entry *p = (void *)csm_compat_table.E820Pointer; + int i; + for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++) + e820_add(p[i].start, p[i].size, p[i].type); + + if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) { + u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; + e820_add(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); + } + + // For PCIBIOS 1ab10e + if (csm_compat_table.IrqRoutingTablePointer && + csm_compat_table.IrqRoutingTableLength) { + PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer; + dprintf(3, "CSM PIRQ table at %p\n", PirAddr); + } + + // For find_resume_vector()... and find_acpi_features() + if (csm_rsdp.signature == RSDP_SIGNATURE) { + RsdpAddr = &csm_rsdp; + dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr); + + find_acpi_features(); + } + + // SMBIOS table needs to be copied into the f-seg + // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it + if (csm_boot_table->SmbiosTable && !SMBiosAddr) + copy_smbios((void *)csm_boot_table->SmbiosTable); + + // MPTABLE is just there; we don't care where. + + // EFI may have reinitialised the video using its *own* driver. + enable_vga_console(); + + // EFI fills this in for us. Zero it for now... + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + bda->hdcount = 0; + + thread_setup(); + mathcp_setup(); + timer_setup(); + clock_setup(); + device_hardware_setup(); + wait_threads(); + interactive_bootmenu(); + + prepareboot(); + + regs->ax = 0; +} + +/* Boot */ +static void +handle_csm_0003(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Boot\n"); + + startBoot(); + + regs->ax = 1; +} + +/* Legacy16DispatchOprom */ +static void +handle_csm_0005(struct bregs *regs) +{ + EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx); + struct rom_header *rom; + u16 bdf; + + if (!CONFIG_OPTIONROMS) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16DispatchOprom rom %p\n", table); + + dprintf(3, "OpromSegment %04x\n", table->OpromSegment); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + dprintf(3, "PnPInstallationCheck %04x:%04x\n", + table->PnPInstallationCheckSegment, + table->PnPInstallationCheckOffset); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + + rom = MAKE_FLATPTR(table->OpromSegment, 0); + bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction); + + rom_reserve(rom->size * 512); + + // XX PnP seg/ofs should never be other than default + callrom(rom, bdf); + + rom_confirm(rom->size * 512); + + regs->bx = 0; // FIXME + regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +static void +handle_csm_0006(struct bregs *regs) +{ + u16 size = regs->cx; + u16 align = regs->dx; + u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either) + void *chunk = NULL; + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", + size, align, region); + + if (!region) + region = 3; + + // DX = Required address alignment. Bit mapped. + // First non-zero bit from the right is the alignment.*/ + if (align) { + align = 1 << __ffs(align); + if (align < MALLOC_MIN_ALIGN) + align = MALLOC_MIN_ALIGN; + } else { + align = MALLOC_MIN_ALIGN; + } + + if (region & 2) + chunk = _malloc(&ZoneLow, size, align); + if (!chunk && (region & 1)) + chunk = _malloc(&ZoneFSeg, size, align); + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n", + size, align, region, chunk); + if (chunk) { + regs->ds = FLATPTR_TO_SEG(chunk); + regs->bx = FLATPTR_TO_OFFSET(chunk); + regs->ax = 0; + } else { + regs->ax = 1; + } +} + +void VISIBLE32INIT +handle_csm(struct bregs *regs) +{ + ASSERT32FLAT(); + + if (!CONFIG_CSM) + return; + + dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax); + + code_mutable_preinit(); + pic_irqmask_write(PICMask); + + switch(regs->ax) { + case 0000: handle_csm_0000(regs); break; + case 0001: handle_csm_0001(regs); break; + case 0002: handle_csm_0002(regs); break; + case 0003: handle_csm_0003(regs); break; +// case 0004: handle_csm_0004(regs); break; + case 0005: handle_csm_0005(regs); break; + case 0006: handle_csm_0006(regs); break; +// case 0007: handle_csm_0007(regs); break; +// case 0008: hamdle_csm_0008(regs); break; + default: regs->al = 1; + } + + csm_return(regs); +} + +static int csm_prio_to_seabios(u16 csm_prio) +{ + switch (csm_prio) { + case BBS_DO_NOT_BOOT_FROM: + case BBS_IGNORE_ENTRY: + return -1; + + case BBS_LOWEST_PRIORITY: + case BBS_UNPRIORITIZED_ENTRY: + default: + // SeaBIOS default priorities start at 1, with 0 being used for + // an item explicitly selected from interactive_bootmenu(). + // As in find_prio(), add 1 to the value being returned. + return csm_prio + 1; + } +} + +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int index = 1 + (chanid * 2) + slave; + dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave, + index, bbs[index].BootPriority); + return csm_prio_to_seabios(bbs[index].BootPriority); +} + +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority); + return csm_prio_to_seabios(bbs[0].BootPriority); +} + +int csm_bootprio_pci(struct pci_device *pci) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int i; + + for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) { + if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) { + dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus, + bbs[i].Device, bbs[i].Function, bbs[i].BootPriority); + return csm_prio_to_seabios(bbs[i].BootPriority); + } + } + return -1; +} |