aboutsummaryrefslogtreecommitdiffstats
path: root/src/fw/paravirt.c
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2013-09-02 21:25:21 -0400
committerKevin O'Connor <kevin@koconnor.net>2013-09-02 21:25:21 -0400
commitccee6e8491a2dbc5f8f2085ee9567bfb4146693b (patch)
tree3deca9e3b7904b7139c365490fcc6e497a9ed24a /src/fw/paravirt.c
parent5d369d8d9eb5326db111cc2e518c74739dbe3f84 (diff)
downloadseabios-ccee6e8491a2dbc5f8f2085ee9567bfb4146693b.tar.gz
Move code cenetered around firmware initialization to src/fw/
Move many C files from the src/ directory to the new src/fw/ directory. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/fw/paravirt.c')
-rw-r--r--src/fw/paravirt.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c
new file mode 100644
index 00000000..b1dd8b0c
--- /dev/null
+++ b/src/fw/paravirt.c
@@ -0,0 +1,333 @@
+// Paravirtualization support.
+//
+// Copyright (C) 2013 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2009 Red Hat Inc.
+//
+// Authors:
+// Gleb Natapov <gnatapov@redhat.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "config.h" // CONFIG_QEMU
+#include "util.h" // dprintf
+#include "byteorder.h" // be32_to_cpu
+#include "ioport.h" // outw
+#include "paravirt.h" // qemu_cfg_preinit
+#include "smbios.h" // smbios_setup
+#include "memmap.h" // add_e820
+#include "hw/cmos.h" // CMOS_*
+#include "acpi.h" // acpi_setup
+#include "mptable.h" // mptable_setup
+#include "hw/pci.h" // create_pirtable
+#include "xen.h" // xen_biostable_setup
+
+// Amount of continuous ram under 4Gig
+u32 RamSize;
+// Amount of continuous ram >4Gig
+u64 RamSizeOver4G;
+// Type of emulator platform.
+int PlatformRunningOn VARFSEG;
+
+/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It
+ * should be used to determine that a VM is running under KVM.
+ */
+#define KVM_CPUID_SIGNATURE 0x40000000
+
+static void kvm_preinit(void)
+{
+ if (!CONFIG_QEMU)
+ return;
+ unsigned int eax, ebx, ecx, edx;
+ char signature[13];
+
+ cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx);
+ memcpy(signature + 0, &ebx, 4);
+ memcpy(signature + 4, &ecx, 4);
+ memcpy(signature + 8, &edx, 4);
+ signature[12] = 0;
+
+ if (strcmp(signature, "KVMKVMKVM") == 0) {
+ dprintf(1, "Running on KVM\n");
+ PlatformRunningOn |= PF_KVM;
+ }
+}
+
+void
+qemu_preinit(void)
+{
+ if (!CONFIG_QEMU)
+ return;
+
+ if (runningOnXen()) {
+ xen_ramsize_preinit();
+ return;
+ }
+
+ PlatformRunningOn = PF_QEMU;
+ kvm_preinit();
+
+ // On emulators, get memory size from nvram.
+ u32 rs = ((inb_cmos(CMOS_MEM_EXTMEM2_LOW) << 16)
+ | (inb_cmos(CMOS_MEM_EXTMEM2_HIGH) << 24));
+ if (rs)
+ rs += 16 * 1024 * 1024;
+ else
+ rs = (((inb_cmos(CMOS_MEM_EXTMEM_LOW) << 10)
+ | (inb_cmos(CMOS_MEM_EXTMEM_HIGH) << 18))
+ + 1 * 1024 * 1024);
+ RamSize = rs;
+ add_e820(0, rs, E820_RAM);
+
+ // Check for memory over 4Gig
+ u64 high = ((inb_cmos(CMOS_MEM_HIGHMEM_LOW) << 16)
+ | ((u32)inb_cmos(CMOS_MEM_HIGHMEM_MID) << 24)
+ | ((u64)inb_cmos(CMOS_MEM_HIGHMEM_HIGH) << 32));
+ RamSizeOver4G = high;
+ add_e820(0x100000000ull, high, E820_RAM);
+
+ /* reserve 256KB BIOS area at the end of 4 GB */
+ add_e820(0xfffc0000, 256*1024, E820_RESERVED);
+
+ dprintf(1, "Ram Size=0x%08x (0x%016llx high)\n", RamSize, RamSizeOver4G);
+}
+
+void
+qemu_platform_setup(void)
+{
+ if (!CONFIG_QEMU)
+ return;
+
+ if (runningOnXen()) {
+ pci_probe_devices();
+ xen_hypercall_setup();
+ xen_biostable_setup();
+ return;
+ }
+
+ // Initialize pci
+ pci_setup();
+ smm_device_setup();
+ smm_setup();
+
+ // Initialize mtrr and smp
+ mtrr_setup();
+ smp_setup();
+
+ // Create bios tables
+ pirtable_setup();
+ mptable_setup();
+ smbios_setup();
+ acpi_setup();
+}
+
+
+/****************************************************************
+ * QEMU firmware config (fw_cfg) interface
+ ****************************************************************/
+
+// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content
+// should be passed via the fw_cfg "file" interface.)
+#define QEMU_CFG_SIGNATURE 0x00
+#define QEMU_CFG_ID 0x01
+#define QEMU_CFG_UUID 0x02
+#define QEMU_CFG_NUMA 0x0d
+#define QEMU_CFG_BOOT_MENU 0x0e
+#define QEMU_CFG_MAX_CPUS 0x0f
+#define QEMU_CFG_FILE_DIR 0x19
+#define QEMU_CFG_ARCH_LOCAL 0x8000
+#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0)
+#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1)
+#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2)
+#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3)
+
+static void
+qemu_cfg_select(u16 f)
+{
+ outw(f, PORT_QEMU_CFG_CTL);
+}
+
+static void
+qemu_cfg_read(void *buf, int len)
+{
+ insb(PORT_QEMU_CFG_DATA, buf, len);
+}
+
+static void
+qemu_cfg_skip(int len)
+{
+ while (len--)
+ inb(PORT_QEMU_CFG_DATA);
+}
+
+static void
+qemu_cfg_read_entry(void *buf, int e, int len)
+{
+ qemu_cfg_select(e);
+ qemu_cfg_read(buf, len);
+}
+
+struct qemu_romfile_s {
+ struct romfile_s file;
+ int select, skip;
+};
+
+static int
+qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen)
+{
+ if (file->size > maxlen)
+ return -1;
+ struct qemu_romfile_s *qfile;
+ qfile = container_of(file, struct qemu_romfile_s, file);
+ qemu_cfg_select(qfile->select);
+ qemu_cfg_skip(qfile->skip);
+ qemu_cfg_read(dst, file->size);
+ return file->size;
+}
+
+static void
+qemu_romfile_add(char *name, int select, int skip, int size)
+{
+ struct qemu_romfile_s *qfile = malloc_tmp(sizeof(*qfile));
+ if (!qfile) {
+ warn_noalloc();
+ return;
+ }
+ memset(qfile, 0, sizeof(*qfile));
+ strtcpy(qfile->file.name, name, sizeof(qfile->file.name));
+ qfile->file.size = size;
+ qfile->select = select;
+ qfile->skip = skip;
+ qfile->file.copy = qemu_cfg_read_file;
+ romfile_add(&qfile->file);
+}
+
+struct e820_reservation {
+ u64 address;
+ u64 length;
+ u32 type;
+};
+
+#define SMBIOS_FIELD_ENTRY 0
+#define SMBIOS_TABLE_ENTRY 1
+
+struct qemu_smbios_header {
+ u16 length;
+ u8 headertype;
+ u8 tabletype;
+ u16 fieldoffset;
+} PACKED;
+
+// Populate romfile entries for legacy fw_cfg ports (that predate the
+// "file" interface).
+static void
+qemu_cfg_legacy(void)
+{
+ if (!CONFIG_QEMU)
+ return;
+
+ // Misc config items.
+ qemu_romfile_add("etc/show-boot-menu", QEMU_CFG_BOOT_MENU, 0, 2);
+ qemu_romfile_add("etc/irq0-override", QEMU_CFG_IRQ0_OVERRIDE, 0, 1);
+ qemu_romfile_add("etc/max-cpus", QEMU_CFG_MAX_CPUS, 0, 2);
+
+ // NUMA data
+ u64 numacount;
+ qemu_cfg_read_entry(&numacount, QEMU_CFG_NUMA, sizeof(numacount));
+ int max_cpu = romfile_loadint("etc/max-cpus", 0);
+ qemu_romfile_add("etc/numa-cpu-map", QEMU_CFG_NUMA, sizeof(numacount)
+ , max_cpu*sizeof(u64));
+ qemu_romfile_add("etc/numa-nodes", QEMU_CFG_NUMA
+ , sizeof(numacount) + max_cpu*sizeof(u64)
+ , numacount*sizeof(u64));
+
+ // e820 data
+ u32 count32;
+ qemu_cfg_read_entry(&count32, QEMU_CFG_E820_TABLE, sizeof(count32));
+ if (count32) {
+ struct e820_reservation entry;
+ int i;
+ for (i = 0; i < count32; i++) {
+ qemu_cfg_read(&entry, sizeof(entry));
+ add_e820(entry.address, entry.length, entry.type);
+ }
+ } else if (runningOnKVM()) {
+ // Backwards compatibility - provide hard coded range.
+ // 4 pages before the bios, 3 pages for vmx tss pages, the
+ // other page for EPT real mode pagetable
+ add_e820(0xfffbc000, 4*4096, E820_RESERVED);
+ }
+
+ // ACPI tables
+ char name[128];
+ u16 cnt;
+ qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt));
+ int i, offset = sizeof(cnt);
+ for (i = 0; i < cnt; i++) {
+ u16 len;
+ qemu_cfg_read(&len, sizeof(len));
+ offset += sizeof(len);
+ snprintf(name, sizeof(name), "acpi/table%d", i);
+ qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len);
+ qemu_cfg_skip(len);
+ offset += len;
+ }
+
+ // SMBIOS info
+ qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt));
+ offset = sizeof(cnt);
+ for (i = 0; i < cnt; i++) {
+ struct qemu_smbios_header header;
+ qemu_cfg_read(&header, sizeof(header));
+ if (header.headertype == SMBIOS_FIELD_ENTRY) {
+ snprintf(name, sizeof(name), "smbios/field%d-%d"
+ , header.tabletype, header.fieldoffset);
+ qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES
+ , offset + sizeof(header)
+ , header.length - sizeof(header));
+ } else {
+ snprintf(name, sizeof(name), "smbios/table%d-%d"
+ , header.tabletype, i);
+ qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES
+ , offset + 3, header.length - 3);
+ }
+ qemu_cfg_skip(header.length - sizeof(header));
+ offset += header.length;
+ }
+}
+
+struct QemuCfgFile {
+ u32 size; /* file size */
+ u16 select; /* write this to 0x510 to read it */
+ u16 reserved;
+ char name[56];
+};
+
+void qemu_cfg_init(void)
+{
+ if (!runningOnQEMU())
+ return;
+
+ // Detect fw_cfg interface.
+ qemu_cfg_select(QEMU_CFG_SIGNATURE);
+ char *sig = "QEMU";
+ int i;
+ for (i = 0; i < 4; i++)
+ if (inb(PORT_QEMU_CFG_DATA) != sig[i])
+ return;
+ dprintf(1, "Found QEMU fw_cfg\n");
+
+ // Populate romfiles for legacy fw_cfg entries
+ qemu_cfg_legacy();
+
+ // Load files found in the fw_cfg file directory
+ u32 count;
+ qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
+ count = be32_to_cpu(count);
+ u32 e;
+ for (e = 0; e < count; e++) {
+ struct QemuCfgFile qfile;
+ qemu_cfg_read(&qfile, sizeof(qfile));
+ qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select)
+ , 0, be32_to_cpu(qfile.size));
+ }
+}