Not particularly functional, but it's a start...
Build with CONFIG_CSM enabled (and CONFIG_RELOCATE_INIT disabled) and drop the resulting bios.bin into OvmfPkg/Csm/Csm16/Csm16.bin in your EDK-II build tree, then build with 'build -D CSM_ENABLE'.
You'll see it initialise SeaBIOS as a CSM as it's starting up, and then crash in a storm of what Qemu's debug log says is 'hardware INT=0x68'... which I suppose is what I have to debug next...
Signed-off-by: David Woodhouse David.Woodhouse@intel.com --- Makefile | 2 +- src/Kconfig | 7 +++ src/csm.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/post.c | 3 ++ src/romlayout.S | 7 +++ src/shadow.c | 6 +-- 6 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 src/csm.c
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/src/Kconfig b/src/Kconfig index 0b112ed..f35b0f5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -27,6 +27,13 @@ endchoice help Configure to be used by xen hvmloader, for a HVM guest.
+ config CSM + bool "Build as Compatibilty Support Module for EFI BIOS" + default n + help + Configure to be used by EFI firmware as Compatibility Support + module (CSM) to provide legacy BIOS services. + config THREADS bool "Parallelize hardware init" default y diff --git a/src/csm.c b/src/csm.c new file mode 100644 index 0000000..7ff173b --- /dev/null +++ b/src/csm.c @@ -0,0 +1,142 @@ +// CSM table generation (for providing legacy BIOS support to EFI) +// +// Copyright © 2012 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" + + +#if CONFIG_CSM +extern void entry_csm16(void); +EFI_COMPATIBILITY16_TABLE csm_compat_table VAR16EXPORT __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", +}; +#endif + +extern void handle_post(void); +extern void _cfunc32flat_handle_csm16(struct bregs *); + +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); + + handle_post(); + regs->ax = 0; +} + +/* Legacy16UpdateBbs */ +void 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; +} + +/* Legacy16DispatchOprom */ +void handle_csm_0005(struct bregs *regs) +{ + EFI_DISPATCH_OPROM_TABLE *rom = MAKE_FLATPTR(regs->es, regs->bx); + + dprintf(3, "Legacy16DispatchOprom rom %p\n", rom); + + dprintf(3, "OpromSegment %04x\n", rom->OpromSegment); + dprintf(3, "RuntimeSegment %04x\n", rom->RuntimeSegment); + + /* FIXME: Actually do it! */ + + regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +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; + + /* FIXME: I don't know if we can allocate in the E000 segment at all. */ + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", + size, align, region); + 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 __VISIBLE +handle_csm16(struct bregs *regs) +{ + if (MODESEGMENT) { + void *flatptr = MAKE_FLATPTR(GET_SEG(SS), regs); + call32(_cfunc32flat_handle_csm16, (u32)flatptr, 0); + return; + } + dprintf(3, "handle_csm16 AX=%04x\n", regs->ax); + + 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; + } + + dprintf(3, "handle_csm16 returning AX=%04x\n", regs->ax); +} diff --git a/src/post.c b/src/post.c index f3b56b8..dc07e8b 100644 --- a/src/post.c +++ b/src/post.c @@ -225,6 +225,9 @@ maininit(void) init_ivt(); init_bda();
+ if (CONFIG_CSM) + return; + // Init base pc hardware. pic_setup(); timer_setup(); diff --git a/src/romlayout.S b/src/romlayout.S index 8125277..f1a276e 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -468,6 +468,13 @@ irqentryarg: DECL_IRQ_ENTRY hwpic1 DECL_IRQ_ENTRY hwpic2
+#if CONFIG_CSM + EXPORTFUNC entry_csm16 +entry_csm16: + ENTRY_ARG handle_csm16 + iretw +#endif + // int 18/19 are special - they reset stack and call into 32bit mode. DECLFUNC entry_19 entry_19: diff --git a/src/shadow.c b/src/shadow.c index a2195da..881d5a6 100644 --- a/src/shadow.c +++ b/src/shadow.c @@ -119,7 +119,7 @@ static const struct pci_device_id dram_controller_make_readonly_tbl[] = { void make_bios_writable(void) { - if (CONFIG_COREBOOT || usingXen()) + if (CONFIG_COREBOOT || CONFIG_CSM || usingXen()) return;
dprintf(3, "enabling shadow ram\n"); @@ -148,7 +148,7 @@ make_bios_writable(void) void make_bios_readonly(void) { - if (CONFIG_COREBOOT || usingXen()) + if (CONFIG_COREBOOT || CONFIG_CSM || usingXen()) return;
dprintf(3, "locking shadow ram\n"); @@ -161,7 +161,7 @@ make_bios_readonly(void) void qemu_prep_reset(void) { - if (CONFIG_COREBOOT) + if (CONFIG_COREBOOT || CONFIG_CSM ) return; // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a // reset, so do that manually before invoking a hard reset.
On Thu, Jan 17, 2013 at 09:50:45PM +0000, David Woodhouse wrote:
Not particularly functional, but it's a start...
Build with CONFIG_CSM enabled (and CONFIG_RELOCATE_INIT disabled) and drop the resulting bios.bin into OvmfPkg/Csm/Csm16/Csm16.bin in your EDK-II build tree, then build with 'build -D CSM_ENABLE'.
You'll see it initialise SeaBIOS as a CSM as it's starting up, and then crash in a storm of what Qemu's debug log says is 'hardware INT=0x68'... which I suppose is what I have to debug next...
Very interesting.
BTW, we're about to make a SeaBIOS release - this patch and your other patches would be something to look at for the next release.
[...]
+++ b/src/csm.c @@ -0,0 +1,142 @@ +// CSM table generation (for providing legacy BIOS support to EFI) +// +// Copyright © 2012 Intel Corporation +// +// This file may be distributed under the terms of the GNU LGPLv3 license.
+#include "config.h" // CONFIG_* +#include "csm.h"
Looks like you forgot to include csm.h in the patch.
+#include "util.h" // checksum +#include "bregs.h"
+#if CONFIG_CSM +extern void entry_csm16(void); +EFI_COMPATIBILITY16_TABLE csm_compat_table VAR16EXPORT __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",
+}; +#endif
Does this table have to be in the f-segment, or can it be anywhere in the binary? BTW, how does OVMF know where to place the seabios blob in memory?
+/* 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);
[...] +void 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);
Are these values that SeaBIOS is expected to fill, or values from EFI that SeaBIOS is expected to utilize? In either case it shouldn't be difficult to wire them into SeaBIOS' existing structures. I'd need to see csm.h to confirm, but it mostly looks like it requires some function calls and variable assignments.
[...]
+/* Legacy16GetTableAddress */ +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;
- /* FIXME: I don't know if we can allocate in the E000 segment at all. */
- dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);
- chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, size, align);
This is the same as malloc_fseg(). Allocating in the e-segment is done by calling malloc_low(). Is the align guarenteed to be a power of 2 - if not it may mess up SeaBIOS' allocator.
+void __VISIBLE +handle_csm16(struct bregs *regs) +{
- if (MODESEGMENT) {
void *flatptr = MAKE_FLATPTR(GET_SEG(SS), regs);
call32(_cfunc32flat_handle_csm16, (u32)flatptr, 0);
BTW, SeaBIOS uses 4 space indentation and no tabs.
-Kevin
On Thu, 2013-01-17 at 19:58 -0500, Kevin O'Connor wrote:
Looks like you forgot to include csm.h in the patch.
Oops, sorry. Added in the git tree at http://git.infradead.org/users/dwmw2/seabios.git git://git.infradead.org/users/dwmw2/seabios.git
+#include "util.h" // checksum +#include "bregs.h"
+#if CONFIG_CSM +extern void entry_csm16(void); +EFI_COMPATIBILITY16_TABLE csm_compat_table VAR16EXPORT __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",
+}; +#endif
Does this table have to be in the f-segment, or can it be anywhere in the binary? BTW, how does OVMF know where to place the seabios blob in memory?
It can be anywhere in memory. OVMF loads the blob so that it ends at 1MiB.
Are these values that SeaBIOS is expected to fill, or values from EFI that SeaBIOS is expected to utilize? In either case it shouldn't be difficult to wire them into SeaBIOS' existing structures.
There is a mixture of both. The ACPI one we *need* to point at a buffer which is big enough to hold the table. Leave it at zero, and OVMF will scribble an ACPI table over the interrupt vectors and then promptly go off into the weeds the next time a timer interrupt comes in (hence the problem I was seeing when I posted the patches).
Others it'll call the 'GetTableAddress' call (which is actually malloc) and then fill in the address it's given.
More details at http://www.intel.com/content/dam/doc/reference-guide/efi-compatibility-suppo...
I'd need to see csm.h to confirm, but it mostly looks like it requires some function calls and variable assignments.
Yeah, I figured it shouldn't be *so* hard. We'll get handed a full E820 table instead of our own for example. In memory which OVMF allocated by calling the GetTableAddress function.
[...]
+/* Legacy16GetTableAddress */ +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;
- /* FIXME: I don't know if we can allocate in the E000 segment at all. */
- dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);
- chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, size, align);
This is the same as malloc_fseg().
Apart from the alignment. Which is a power of two, yes. I'll fix it to use ZoneLow for the e-segment too; thanks. If no preference is expressed by the caller, should I try ZoneLow first? That'll be larger, right?
BTW, SeaBIOS uses 4 space indentation and no tabs.
I must teach emacs that rather than attempting to fix it up manually when I notice that it's not conforming... :)
On Fri, Jan 18, 2013 at 01:20:05AM +0000, David Woodhouse wrote:
On Thu, 2013-01-17 at 19:58 -0500, Kevin O'Connor wrote:
Does this table have to be in the f-segment, or can it be anywhere in the binary? BTW, how does OVMF know where to place the seabios blob in memory?
It can be anywhere in memory. OVMF loads the blob so that it ends at 1MiB.
Okay, sounds like we could end up doing csm.c entirely in 32bit code then. (Enhance the build with an "export" equivalent of VAR32FLATVISIBLE and invoke call32 directly from entry_csm.)
- /* FIXME: I don't know if we can allocate in the E000 segment at all. */
- dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);
- chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, size, align);
This is the same as malloc_fseg().
Apart from the alignment. Which is a power of two, yes. I'll fix it to use ZoneLow for the e-segment too; thanks. If no preference is expressed by the caller, should I try ZoneLow first? That'll be larger, right?
Yes, ZoneLow will be larger than ZoneFSeg, though both should be pretty large. This is assuming CONFIG_RELOCATE_INIT is made to work - without that ZoneLow will likely go below the e-segment and there wont be much space in the f-segment. It should be possible to enable CONFIG_RELOCATE_INIT once the PMM regions are filled.
-Kevin
That's a amazing workGet code ,Seems that in csm.c csm.h --> LegacyBios.h and add UINT8/UINT16/UINT32 type definations, otherwise compile error Thanks! From: dwmw2@infradead.org To: kevin@koconnor.net Date: Fri, 18 Jan 2013 01:20:05 +0000 CC: seabios@seabios.org Subject: Re: [SeaBIOS] [WIP PATCH 3/3] Add initial CSM support
On Thu, 2013-01-17 at 19:58 -0500, Kevin O'Connor wrote:
Looks like you forgot to include csm.h in the patch.
Oops, sorry. Added in the git tree at http://git.infradead.org/users/dwmw2/seabios.git git://git.infradead.org/users/dwmw2/seabios.git
+#include "util.h" // checksum +#include "bregs.h"
+#if CONFIG_CSM +extern void entry_csm16(void); +EFI_COMPATIBILITY16_TABLE csm_compat_table VAR16EXPORT __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",
+}; +#endif
Does this table have to be in the f-segment, or can it be anywhere in the binary? BTW, how does OVMF know where to place the seabios blob in memory?
It can be anywhere in memory. OVMF loads the blob so that it ends at 1MiB.
Are these values that SeaBIOS is expected to fill, or values from EFI that SeaBIOS is expected to utilize? In either case it shouldn't be difficult to wire them into SeaBIOS' existing structures.
There is a mixture of both. The ACPI one we *need* to point at a buffer which is big enough to hold the table. Leave it at zero, and OVMF will scribble an ACPI table over the interrupt vectors and then promptly go off into the weeds the next time a timer interrupt comes in (hence the problem I was seeing when I posted the patches).
Others it'll call the 'GetTableAddress' call (which is actually malloc) and then fill in the address it's given.
More details at http://www.intel.com/content/dam/doc/reference-guide/efi-compatibility-suppo...
I'd need to see csm.h to confirm, but it mostly looks like it requires some function calls and variable assignments.
Yeah, I figured it shouldn't be *so* hard. We'll get handed a full E820 table instead of our own for example. In memory which OVMF allocated by calling the GetTableAddress function.
[...]
+/* Legacy16GetTableAddress */ +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;
- /* FIXME: I don't know if we can allocate in the E000 segment at all. */
- dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
size, align, region);
- chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, size, align);
This is the same as malloc_fseg().
Apart from the alignment. Which is a power of two, yes. I'll fix it to use ZoneLow for the e-segment too; thanks. If no preference is expressed by the caller, should I try ZoneLow first? That'll be larger, right?
BTW, SeaBIOS uses 4 space indentation and no tabs.
I must teach emacs that rather than attempting to fix it up manually when I notice that it's not conforming... :)