From: David Woodhouse David.Woodhouse@intel.com
Signed-off-by: David Woodhouse David.Woodhouse@intel.com --- Makefile | 2 +- README.CSM | 21 ++++ src/Kconfig | 19 ++-- src/boot.c | 7 ++ src/csm.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/csm.h | 18 ++++ src/pmm.c | 19 ++++ src/romlayout.S | 35 +++++++ src/util.h | 2 + 9 files changed, 405 insertions(+), 7 deletions(-) create mode 100644 README.CSM create mode 100644 src/csm.c create mode 100644 src/csm.h
diff --git a/Makefile b/Makefile index f28d86c..cb8ecdf 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRCBOTH=misc.c stacks.c pmm.c output.c util.c block.c floppy.c ata.c mouse.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \ virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \ - usb-uas.c lsi-scsi.c esp-scsi.c megasas.c + usb-uas.c lsi-scsi.c esp-scsi.c megasas.c csm.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ diff --git a/README.CSM b/README.CSM new file mode 100644 index 0000000..ea71846 --- /dev/null +++ b/README.CSM @@ -0,0 +1,21 @@ +Enabling CONFIG_CSM allows SeaBIOS to be built as a Compatibility Support +Module for use with the OMVF/EDK-II UEFI firmware. + +It will provide "legacy" BIOS services for booting non-EFI operating +systems and will also allow OVMF to display on otherwise unsupported +video hardware by using the traditional VGA BIOS. + +Windows 2008r2 is known to use INT 10h BIOS calls even when booted via +EFI, and the presence of a CSM makes this work as expected too. + +Having built SeaBIOS with CONFIG_CSM, you should be able to drop the +result into your OVMF build tree at OvmfPkg/Csm/Csm16/Csm16.bin and +then build OVMF with 'build -D CSM_ENABLE'. The SeaBIOS binary will be +included as a discrete file within the 'Flash Volume' which is +created, and there are tools which will extract it and allow it to be +replaced; satisfying the requirements of the LGPL licence. + +A patch to OVMF is required, to prevent it from marking the region from +0xC0000-0xFFFFF as read-only before invoking our Legacy16Boot method. See +http://www.sourceforge.net/mailarchive/forum.php?thread_name=50FD7290.906000... + diff --git a/src/Kconfig b/src/Kconfig index b9206e5..f07cd71 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -18,6 +18,12 @@ choice help Configure as QEMU bios.
+ config CSM + bool "Build as Compatibilty Support Module for EFI BIOS" + help + Configure to be used by EFI firmware as Compatibility Support + module (CSM) to provide legacy BIOS services. + endchoice
config XEN @@ -118,25 +124,25 @@ menu "Hardware support" help Support for AHCI disk code. config VIRTIO_BLK - depends on DRIVES && QEMU + depends on DRIVES && !COREBOOT bool "virtio-blk controllers" default y help Support boot from virtio-blk storage. config VIRTIO_SCSI - depends on DRIVES && QEMU + depends on DRIVES && !COREBOOT bool "virtio-scsi controllers" default y help Support boot from virtio-scsi storage. config ESP_SCSI - depends on DRIVES && QEMU + depends on DRIVES && !COREBOOT bool "AMD PCscsi controllers" default y help Support boot from AMD PCscsi storage. config LSI_SCSI - depends on DRIVES && QEMU + depends on DRIVES && !COREBOOT bool "lsi53c895a scsi controllers" default y help @@ -281,6 +287,7 @@ menu "BIOS interfaces" help Support PnP BIOS entry point. config OPTIONROMS + depends on !CSM bool "Option ROMS" default y help @@ -302,12 +309,12 @@ menu "BIOS interfaces" Bochs or QEMU versions older than 0.12. config PMM depends on OPTIONROMS - bool "PMM interface" + bool "PMM interface" if !CSM default y help Support Post Memory Manager (PMM) entry point. config BOOT - bool "Boot interface" + bool "Boot interface" if !CSM default y help Support int 19/18 system bootup support. diff --git a/src/boot.c b/src/boot.c index 3bafa5a..85d5051 100644 --- a/src/boot.c +++ b/src/boot.c @@ -14,6 +14,7 @@ #include "paravirt.h" // qemu_cfg_show_boot_menu #include "pci.h" // pci_bdf_to_* #include "usb.h" // struct usbdevice_s +#include "csm.h" // csm_bootprio_*
/**************************************************************** @@ -120,6 +121,8 @@ build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci)
int bootprio_find_pci_device(struct pci_device *pci) { + if (CONFIG_CSM) + return csm_bootprio_pci(pci); if (!CONFIG_BOOTORDER) return -1; // Find pci device - for example: /pci@i0cf8/ethernet@5 @@ -144,6 +147,8 @@ int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun)
int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave) { + if (CONFIG_CSM) + return csm_bootprio_ata(pci, chanid, slave); if (!CONFIG_BOOTORDER) return -1; if (!pci) @@ -158,6 +163,8 @@ int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid) { + if (CONFIG_CSM) + return csm_bootprio_fdc(pci, port, fdid); if (!CONFIG_BOOTORDER) return -1; if (!pci) diff --git a/src/csm.c b/src/csm.c new file mode 100644 index 0000000..c453ebd --- /dev/null +++ b/src/csm.c @@ -0,0 +1,289 @@ +// 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 "config.h" // CONFIG_* +#include "csm.h" +#include "util.h" // checksum +#include "bregs.h" +#include "optionroms.h" +#include "pci.h" +#include "memmap.h" +#include "biosvar.h" +#include "post.h" +#include "acpi.h" +#include "boot.h" +#include "smbios.h" +#include "pic.h" + +struct rsdp_descriptor VAR32FLATVISIBLE __aligned(16) csm_rsdp; + +EFI_COMPATIBILITY16_TABLE csm_compat_table VAR32FLATVISIBLE __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; + +/* Legacy16InitializeYourself */ +void +handle_csm_0000(struct bregs *regs) +{ + 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); + + csm_malloc_preinit(csm_init_table->LowPmmMemory, + csm_init_table->LowPmmMemorySizeInBytes, + csm_init_table->HiPmmMemory, + csm_init_table->HiPmmMemorySizeInBytes); + reloc_preinit(); + interface_init(); + timer_setup(); + pci_probe_devices(); + + csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS; + csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset(); + + regs->ax = 0; +} + +/* Legacy16UpdateBbs */ +void VISIBLE32INIT +handle_csm_0001(struct bregs *regs) +{ + 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 */ +void VISIBLE32INIT +handle_csm_0002(struct bregs *regs) +{ + 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++) + add_e820(p[i].start, p[i].size, p[i].type); + + if (csm_init_table->HiPmmMemorySizeInBytes > CONFIG_MAX_HIGHTABLE) { + u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; + add_e820(hi_pmm_end - CONFIG_MAX_HIGHTABLE, CONFIG_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_pmtimer() + if (csm_rsdp.signature == RSDP_SIGNATURE) { + RsdpAddr = &csm_rsdp; + dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr); + + find_pmtimer(); + } + + // 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; + + device_hardware_setup(); + + interactive_bootmenu(); + + prepareboot(); + + regs->ax = 0; +} + +/* Boot */ +void VISIBLE32INIT +handle_csm_0003(struct bregs *regs) +{ + dprintf(3, "Boot\n"); + + startBoot(); + + regs->ax = 1; +} + +/* Legacy16DispatchOprom */ +void VISIBLE32INIT +handle_csm_0005(struct bregs *regs) +{ + EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx); + struct rom_header *rom; + u16 bdf; + + 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_confirm(rom->size * 512); + + // XX PnP seg/ofs should never be other than default + callrom(rom, bdf); + + regs->bx = 0; // FIXME + regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +void VISIBLE32INIT +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; + + if (!region) + region = 3; + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", + size, align, region); + + if (region & 2) + chunk = pmm_malloc(&ZoneLow, PMM_DEFAULT_HANDLE, size, align); + if (!chunk && (region & 1)) + chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, 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 VISIBLE32FLAT +handle_csm(struct bregs *regs) +{ + ASSERT32FLAT(); + + if (!CONFIG_CSM) + return; + + dprintf(3, "handle_csm16 regs %p AX=%04x\n", regs, regs->ax); + + pic_restore_mask(); + + 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; + } + + pic_save_mask(); + + dprintf(3, "handle_csm16 returning AX=%04x\n", regs->ax); +} + +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 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 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 bbs[i].BootPriority; + } + } + return -1; +} diff --git a/src/csm.h b/src/csm.h new file mode 100644 index 0000000..77e5bf0 --- /dev/null +++ b/src/csm.h @@ -0,0 +1,18 @@ +#ifndef __CSM_H +#define __CSM_H + +#include "types.h" +#include "pci.h" + +#define UINT8 u8 +#define UINT16 u16 +#define UINT32 u32 + +// csm.c +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid); +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave); +int csm_bootprio_pci(struct pci_device *pci); + +#include "LegacyBios.h" + +#endif // __CSM_H diff --git a/src/pmm.c b/src/pmm.c index 0dbc86e..eb29a64 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -256,6 +256,25 @@ malloc_preinit(void) } }
+void +csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size) +{ + ASSERT32FLAT(); + + if (hi_pmm_size > CONFIG_MAX_HIGHTABLE) { + void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size; + addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - CONFIG_MAX_HIGHTABLE); + addSpace(&ZoneHigh, hi_pmm_end - CONFIG_MAX_HIGHTABLE, hi_pmm_end); + } else { + addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size); + } + addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size); + addSpace(&ZoneFSeg, BiosTableSpace, &BiosTableSpace[CONFIG_MAX_BIOSTABLE]); + extern u8 final_datalow_start[]; + addSpace(&ZoneLow, datalow_base + OPROM_HEADER_RESERVE, final_datalow_start); + RomBase = findLast(&ZoneLow); +} + // Update pointers after code relocation. void malloc_fixupreloc_init(void) diff --git a/src/romlayout.S b/src/romlayout.S index 8125277..aebe2df 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -384,6 +384,41 @@ entry_elf:
.code16gcc
+ EXPORTFUNC entry_csm +entry_csm: + PUSHBREGS + + // Reset stack and store EFI's old SS:SP on it + movl %esp, %edx + movw %ss, %cx + + xorl %eax, %eax + movw %ax, %ss + movw %ax, %ds + movl $BUILD_STACK_ADDR, %esp + + pushl %ecx // EFI's SS + pushl %edx // EFI's SP + + // Turn %edx into a flat pointer (including segment base) + shll $4, %ecx + addl %ecx, %edx + + // call32(handle_csm32, bregs, -1) + movl $_cfunc32flat_handle_csm, %eax + movl $-1, %ecx + calll call32 + + // Switch back to EFI's stack and return + popl %edx + popl %ecx + movw %cx, %ss + movw %cx, %ds + movl %edx, %esp + + POPBREGS + lretw +
/**************************************************************** * Interrupt entry points diff --git a/src/util.h b/src/util.h index 84915ed..f486c85 100644 --- a/src/util.h +++ b/src/util.h @@ -368,6 +368,8 @@ u32 rom_get_top(void); u32 rom_get_last(void); struct rom_header *rom_reserve(u32 size); int rom_confirm(u32 size); +void csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, + u32 hi_pmm_size); void malloc_preinit(void); void malloc_fixupreloc_init(void); void malloc_prepboot(void);