>From c2f5909e835903f7678f77b0a1626b9a974ef3aa Mon Sep 17 00:00:00 2001
From: Ian Campbell <ian.campbell@citrix.com>
Date: Thu, 26 May 2011 16:10:25 +0100
Subject: [PATCH] Add basic support for use as Xen HVM BIOS.

SeaBIOS is called by Xen's hvmloader which does the basic platform
setup and provides various BIOS tables. Therefore avoid re-doing that
setup and copy out the tables as necessary.

Establish the basic infrastructure to make hypercalls.

XXX TODO put hypercall page at a safe location.
XXX TODO consider re-using coreboot table datastructure instead of ad-hoc.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
 Makefile       |    2 +-
 src/Kconfig    |    6 ++
 src/coreboot.c |    4 +-
 src/mtrr.c     |    3 +-
 src/pciinit.c  |    5 +-
 src/post.c     |   11 +++
 src/shadow.c   |    5 +-
 src/xen.c      |  231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/xen.h      |   56 ++++++++++++++
 9 files changed, 315 insertions(+), 8 deletions(-)
 create mode 100644 src/xen.c
 create mode 100644 src/xen.h

diff --git a/Makefile b/Makefile
index d17f85a..5a9a7a1 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ 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 \
       lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \
-      pci_region.c
+      pci_region.c xen.c
 SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
 
 cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \
diff --git a/src/Kconfig b/src/Kconfig
index 123db01..c2fe086 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -10,6 +10,12 @@ menu "General Features"
         help
             Configure as a coreboot payload.
 
+    config XEN
+        bool "Build for Xen HVM"
+        default n
+        help
+            Configure to be used by xen hvmloader, for a HVM guest.
+
     config THREADS
         bool "Parallelize hardware init"
         default y
diff --git a/src/coreboot.c b/src/coreboot.c
index f627531..ac480ad 100644
--- a/src/coreboot.c
+++ b/src/coreboot.c
@@ -216,7 +216,7 @@ copy_pir(void *pos)
     PirOffset = (u32)newpos - BUILD_BIOS_ADDR;
 }
 
-static void
+/*static*/ void
 copy_mptable(void *pos)
 {
     struct mptable_floating_s *p = pos;
@@ -240,7 +240,7 @@ copy_mptable(void *pos)
     memcpy((void*)newpos + length, (void*)p->physaddr, mpclength);
 }
 
-static void
+/*static*/ void
 copy_acpi_rsdp(void *pos)
 {
     if (RsdpAddr)
diff --git a/src/mtrr.c b/src/mtrr.c
index 0502c18..0548043 100644
--- a/src/mtrr.c
+++ b/src/mtrr.c
@@ -6,6 +6,7 @@
 
 #include "util.h" // dprintf
 #include "biosvar.h" // GET_EBDA
+#include "xen.h" // usingXen
 
 #define MSR_MTRRcap                    0x000000fe
 #define MSR_MTRRfix64K_00000           0x00000250
@@ -32,7 +33,7 @@
 
 void mtrr_setup(void)
 {
-    if (!CONFIG_MTRR_INIT || CONFIG_COREBOOT)
+    if (!CONFIG_MTRR_INIT || CONFIG_COREBOOT || usingXen())
         return;
 
     u32 eax, ebx, ecx, edx, cpuid_features;
diff --git a/src/pciinit.c b/src/pciinit.c
index ee2e72d..0bd9b72 100644
--- a/src/pciinit.c
+++ b/src/pciinit.c
@@ -11,6 +11,7 @@
 #include "pci_ids.h" // PCI_VENDOR_ID_INTEL
 #include "pci_regs.h" // PCI_COMMAND
 #include "dev-i440fx.h"
+#include "xen.h" // usingXen
 
 #define PCI_ROM_SLOT 6
 #define PCI_NUM_REGIONS 7
@@ -396,8 +397,8 @@ pci_bios_init_bus(void)
 void
 pci_setup(void)
 {
-    if (CONFIG_COREBOOT)
-        // Already done by coreboot.
+    if (CONFIG_COREBOOT || usingXen())
+        // Already done by coreboot or Xen.
         return;
 
     dprintf(3, "pci setup\n");
diff --git a/src/post.c b/src/post.c
index 7d2b5f2..07167c8 100644
--- a/src/post.c
+++ b/src/post.c
@@ -23,6 +23,7 @@
 #include "usb.h" // usb_setup
 #include "smbios.h" // smbios_init
 #include "paravirt.h" // qemu_cfg_port_probe
+#include "xen.h" // xen_probe_hvm_info
 #include "ps2port.h" // ps2port_setup
 #include "virtio-blk.h" // virtio_blk_setup
 
@@ -101,6 +102,8 @@ ram_probe(void)
     dprintf(3, "Find memory size\n");
     if (CONFIG_COREBOOT) {
         coreboot_setup();
+    } else if (usingXen()) {
+	xen_setup();
     } else {
         // On emulators, get memory size from nvram.
         u32 rs = ((inb_cmos(CMOS_MEM_EXTMEM2_LOW) << 16)
@@ -158,6 +161,10 @@ init_bios_tables(void)
         coreboot_copy_biostable();
         return;
     }
+    if (usingXen()) {
+	xen_copy_biostable();
+	return;
+    }
 
     create_pirtable();
 
@@ -329,6 +336,7 @@ post(void)
 {
     // Detect ram and setup internal malloc.
     qemu_cfg_port_probe();
+    xen_probe_hvm_info();
     ram_probe();
     malloc_setup();
 
@@ -380,6 +388,9 @@ _start(void)
         // This is a soft reboot - invoke a hard reboot.
         tryReboot();
 
+    // Check if we are running under Xen.
+    xen_probe();
+
     // Allow writes to modify bios area (0xf0000)
     make_bios_writable();
     HaveRunPost = 1;
diff --git a/src/shadow.c b/src/shadow.c
index ed530e0..cb39ddf 100644
--- a/src/shadow.c
+++ b/src/shadow.c
@@ -10,6 +10,7 @@
 #include "config.h" // CONFIG_*
 #include "pci_ids.h" // PCI_VENDOR_ID_INTEL
 #include "dev-i440fx.h"
+#include "xen.h" // usingXen
 
 // On the emulators, the bios at 0xf0000 is also at 0xffff0000
 #define BIOS_SRC_OFFSET 0xfff00000
@@ -102,7 +103,7 @@ static const struct pci_device_id dram_controller_make_writable_tbl[] = {
 void
 make_bios_writable(void)
 {
-    if (CONFIG_COREBOOT)
+    if (CONFIG_COREBOOT || usingXen())
         return;
 
     dprintf(3, "enabling shadow ram\n");
@@ -127,7 +128,7 @@ static const struct pci_device_id dram_controller_make_readonly_tbl[] = {
 void
 make_bios_readonly(void)
 {
-    if (CONFIG_COREBOOT)
+    if (CONFIG_COREBOOT || usingXen())
         return;
 
     dprintf(3, "locking shadow ram\n");
diff --git a/src/xen.c b/src/xen.c
new file mode 100644
index 0000000..0161b65
--- /dev/null
+++ b/src/xen.c
@@ -0,0 +1,231 @@
+// Xen HVM support
+//
+// Copyright (C) 2011 Citrix Systems.
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "config.h"
+#include "xen.h"
+
+#include "memmap.h" // add_e820
+
+#define INFO_PHYSICAL_ADDRESS 0x00001000
+#define HYPERCALL_PHYSICAL_ADDRESS    0x00080000 /* MADE UP */
+
+u32 xen_cpuid_base = 0;
+
+static void init_hypercalls(void)
+{
+    u32 eax, ebx, ecx, edx;
+    unsigned long i;
+
+    /* Fill in hypercall transfer pages. */
+    cpuid(xen_cpuid_base + 2, &eax, &ebx, &ecx, &edx);
+    for ( i = 0; i < eax; i++ )
+        wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
+}
+
+#define __STR(...) #__VA_ARGS__
+#define STR(...) __STR(__VA_ARGS__)
+
+/*
+ * NB. Hypercall address needs to be relative to a linkage symbol for
+ * some version of ld to relocate the relative calls properly.
+ */
+//#define hypercall_pa "_start - " STR(HVMLOADER_PHYSICAL_ADDRESS)	\
+//                           " + " STR(HYPERCALL_PHYSICAL_ADDRESS)
+#define hypercall_pa STR(HYPERCALL_PHYSICAL_ADDRESS)
+
+#define _hypercall0(type, name)						\
+({									\
+	long __res;							\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res)						\
+		:							\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+#define _hypercall1(type, name, a1)					\
+({									\
+	long __res, __ign1;						\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res), "=b" (__ign1)				\
+		: "1" ((long)(a1))					\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+#define _hypercall2(type, name, a1, a2)					\
+({									\
+	long __res, __ign1, __ign2;					\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res), "=b" (__ign1), "=c" (__ign2)		\
+		: "1" ((long)(a1)), "2" ((long)(a2))			\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+#define _hypercall3(type, name, a1, a2, a3)				\
+({									\
+	long __res, __ign1, __ign2, __ign3;				\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res), "=b" (__ign1), "=c" (__ign2),		\
+		"=d" (__ign3)						\
+		: "1" ((long)(a1)), "2" ((long)(a2)),			\
+		"3" ((long)(a3))					\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+#define _hypercall4(type, name, a1, a2, a3, a4)				\
+({									\
+	long __res, __ign1, __ign2, __ign3, __ign4;			\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res), "=b" (__ign1), "=c" (__ign2),		\
+		"=d" (__ign3), "=S" (__ign4)				\
+		: "1" ((long)(a1)), "2" ((long)(a2)),			\
+		"3" ((long)(a3)), "4" ((long)(a4))			\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+#define _hypercall5(type, name, a1, a2, a3, a4, a5)			\
+({									\
+	long __res, __ign1, __ign2, __ign3, __ign4, __ign5;		\
+	asm volatile (							\
+		"call "hypercall_pa" + " STR(__HYPERVISOR_##name * 32)	\
+		: "=a" (__res), "=b" (__ign1), "=c" (__ign2),		\
+		"=d" (__ign3), "=S" (__ign4), "=D" (__ign5)		\
+		: "1" ((long)(a1)), "2" ((long)(a2)),			\
+		"3" ((long)(a3)), "4" ((long)(a4)),			\
+		"5" ((long)(a5))					\
+		: "memory" );						\
+	(type)__res;							\
+})
+
+static inline int
+hypercall_xen_version(
+	int cmd, void *arg)
+{
+	return _hypercall2(int, xen_version, cmd, arg);
+}
+
+void xen_probe(void)
+{
+    u32 base, eax, ebx, ecx, edx;
+    char signature[13];
+    xen_extraversion_t extraversion;
+
+    if (!CONFIG_XEN)
+	return;
+
+    for (base = 0x40000000; base < 0x40010000; base += 0x100) {
+        cpuid(base, &eax, &ebx, &ecx, &edx);
+        memcpy(signature + 0, &ebx, 4);
+        memcpy(signature + 4, &ecx, 4);
+        memcpy(signature + 8, &edx, 4);
+        signature[12] = 0;
+
+        dprintf(1, "Found hypervisor signature \"%s\" at %x\n",
+                signature, base);
+        if (strcmp(signature, "XenVMMXenVMM") == 0) {
+            if ((eax - base) < 2)
+                panic("Insufficient Xen cpuid leaves. eax=%x at base %x\n",
+                      eax, base);
+            xen_cpuid_base = base;
+            break;
+        }
+    }
+
+    if (!xen_cpuid_base)
+	return;
+
+    init_hypercalls();
+
+    /* Print version information. */
+    cpuid(xen_cpuid_base + 1, &eax, &ebx, &ecx, &edx);
+    hypercall_xen_version(XENVER_extraversion, extraversion);
+    dprintf(1, "Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
+}
+
+struct xen_seabios_info {
+    char signature[14]; /* XenHVMSeaBIOS\0 */
+    u16 length;
+    u32 acpi_rsdp;
+    u32 mptable;
+    u32 e820_nr;
+    struct e820entry e820[128];
+    u8 checksum;
+};
+
+static void validate_info(struct xen_seabios_info *t)
+{
+    if ( memcmp(t->signature, "XenHVMSeaBIOS", 14) )
+        panic("Bad Xen info signature\n");
+
+    if ( t->length < sizeof(struct xen_seabios_info) )
+        panic("Bad Xen info length\n");
+
+    if (checksum(t, t->length) != 0)
+        panic("Bad Xen info checksum\n");
+}
+
+void xen_probe_hvm_info(void)
+{
+    if (!usingXen())
+        return;
+
+    dprintf(1, "Probing for Xen info\n");
+
+    struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS;
+
+    validate_info(info);
+
+    dprintf(1, "BIOS INFO: SIGNATURE %s\n", info->signature);
+}
+
+void xen_copy_biostable(void)
+{
+    struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS;
+
+    dprintf(1, "xen: copy BIOS tables...\n");
+    extern void copy_acpi_rsdp(void *pos);
+    dprintf(1, "xen: ... ACPI RSDP at %x\n", info->acpi_rsdp);
+    copy_acpi_rsdp((void *)info->acpi_rsdp);
+
+    extern void copy_mptable(void *pos);
+    dprintf(1, "xen: ... MPTABLE at %x\n", info->mptable);
+    copy_mptable((void *)info->mptable);
+}
+
+void xen_setup(void)
+{
+    u64 maxram = 0, maxram_over4G = 0;
+    int i;
+    struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS;
+
+    dprintf(1, "xen: copy e820...\n");
+
+    for (i = 0; i < info->e820_nr; i++) {
+	struct e820entry *e = &info->e820[i];
+	if (e->type == E820_ACPI || e->type == E820_RAM) {
+            u64 end = e->start + e->size;
+            if (end > 0x100000000ull) {
+                end -= 0x100000000ull;
+                if (end > maxram_over4G)
+                    maxram_over4G = end;
+            } else if (end > maxram)
+                maxram = end;
+	}
+	add_e820(e->start, e->size, e->type);
+    }
+
+    RamSize = maxram;
+    RamSizeOver4G = maxram_over4G;
+}
diff --git a/src/xen.h b/src/xen.h
new file mode 100644
index 0000000..ac89ff9
--- /dev/null
+++ b/src/xen.h
@@ -0,0 +1,56 @@
+#ifndef __XEN_H
+#define __XEN_H
+
+#include "util.h"
+
+void xen_probe(void);
+void xen_probe_hvm_info(void);
+
+extern u32 xen_cpuid_base;
+
+static inline int usingXen(void) {
+    if (!CONFIG_XEN)
+	return 0;
+    return (xen_cpuid_base != 0);
+}
+
+void xen_copy_biostable(void);
+
+void xen_setup(void);
+
+/******************************************************************************
+ *
+ * The following interface definitions are taken from Xen and have the
+ * following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/* xen.h */
+
+#define __HYPERVISOR_xen_version          17
+
+/* version.h */
+
+/* arg == xen_extraversion_t. */
+#define XENVER_extraversion 1
+typedef char xen_extraversion_t[16];
+#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t))
+
+#endif
-- 
1.7.2.5

