fate#313584: pass bios information to XEN HVM guest

# HG changeset patch
# User Ross Philipson <ross.philipson@citrix.com>
# Date 1357838241 0
# Node ID cabf395a6c849cc65e56f1640b18db0c3e0faf5d
# Parent  07bf59a7ce837bd795e2df2f28166cfe41990d3d
HVM firmware passthrough control tools support

Xen control tools support for loading the firmware passthrough blocks
during domain construction. SMBIOS and ACPI blocks are passed in using
the new xc_hvm_build_args structure. Each block is read and loaded
into the new domain address space behind the HVMLOADER image. The base
address for the two blocks is returned as an out parameter to the
caller via the args structure.

Signed-off-by: Ross Philipson <ross.philipson@citrix.com>
Committed-by: Keir Fraser <keir@xen.org>

Index: xen-4.2.3-testing/tools/libxc/xc_hvm_build_arm.c
===================================================================
--- xen-4.2.3-testing.orig/tools/libxc/xc_hvm_build_arm.c
+++ xen-4.2.3-testing/tools/libxc/xc_hvm_build_arm.c
@@ -22,7 +22,7 @@
 #include <xenguest.h>
 
 int xc_hvm_build(xc_interface *xch, uint32_t domid,
-                 const struct xc_hvm_build_args *hvm_args)
+                 struct xc_hvm_build_args *hvm_args)
 {
     errno = ENOSYS;
     return -1;
Index: xen-4.2.3-testing/tools/libxc/xc_hvm_build_x86.c
===================================================================
--- xen-4.2.3-testing.orig/tools/libxc/xc_hvm_build_x86.c
+++ xen-4.2.3-testing/tools/libxc/xc_hvm_build_x86.c
@@ -49,6 +49,40 @@
 #define NR_SPECIAL_PAGES     8
 #define special_pfn(x) (0xff000u - NR_SPECIAL_PAGES + (x))
 
+static int modules_init(struct xc_hvm_build_args *args,
+                        uint64_t vend, struct elf_binary *elf,
+                        uint64_t *mstart_out, uint64_t *mend_out)
+{
+#define MODULE_ALIGN 1UL << 7
+#define MB_ALIGN     1UL << 20
+#define MKALIGN(x, a) (((uint64_t)(x) + (a) - 1) & ~(uint64_t)((a) - 1))
+    uint64_t total_len = 0, offset1 = 0;
+
+    if ( (args->acpi_module.length == 0)&&(args->smbios_module.length == 0) )
+        return 0;
+
+    /* Find the total length for the firmware modules with a reasonable large
+     * alignment size to align each the modules.
+     */
+    total_len = MKALIGN(args->acpi_module.length, MODULE_ALIGN);
+    offset1 = total_len;
+    total_len += MKALIGN(args->smbios_module.length, MODULE_ALIGN);
+
+    /* Want to place the modules 1Mb+change behind the loader image. */
+    *mstart_out = MKALIGN(elf->pend, MB_ALIGN) + (MB_ALIGN);
+    *mend_out = *mstart_out + total_len;
+
+    if ( *mend_out > vend )    
+        return -1;
+
+    if ( args->acpi_module.length != 0 )
+        args->acpi_module.guest_addr_out = *mstart_out;
+    if ( args->smbios_module.length != 0 )
+        args->smbios_module.guest_addr_out = *mstart_out + offset1;
+
+    return 0;
+}
+
 static void build_hvm_info(void *hvm_info_page, uint64_t mem_size,
                            uint64_t mmio_start, uint64_t mmio_size)
 {
@@ -86,9 +120,8 @@ static void build_hvm_info(void *hvm_inf
     hvm_info->checksum = -sum;
 }
 
-static int loadelfimage(
-    xc_interface *xch,
-    struct elf_binary *elf, uint32_t dom, unsigned long *parray)
+static int loadelfimage(xc_interface *xch, struct elf_binary *elf,
+                        uint32_t dom, unsigned long *parray)
 {
     privcmd_mmap_entry_t *entries = NULL;
     unsigned long pfn_start = elf->pstart >> PAGE_SHIFT;
@@ -128,6 +161,66 @@ static int loadelfimage(
     return rc;
 }
 
+static int loadmodules(xc_interface *xch,
+                       struct xc_hvm_build_args *args,
+                       uint64_t mstart, uint64_t mend,
+                       uint32_t dom, unsigned long *parray)
+{
+    privcmd_mmap_entry_t *entries = NULL;
+    unsigned long pfn_start;
+    unsigned long pfn_end;
+    size_t pages;
+    uint32_t i;
+    uint8_t *dest;
+    int rc = -1;
+
+    if ( (mstart == 0)||(mend == 0) )
+        return 0;
+
+    pfn_start = (unsigned long)(mstart >> PAGE_SHIFT);
+    pfn_end = (unsigned long)((mend + PAGE_SIZE - 1) >> PAGE_SHIFT);
+    pages = pfn_end - pfn_start;
+
+    /* Map address space for module list. */
+    entries = calloc(pages, sizeof(privcmd_mmap_entry_t));
+    if ( entries == NULL )
+        goto error_out;
+
+    for ( i = 0; i < pages; i++ )
+        entries[i].mfn = parray[(mstart >> PAGE_SHIFT) + i];
+
+    dest = xc_map_foreign_ranges(
+        xch, dom, pages << PAGE_SHIFT, PROT_READ | PROT_WRITE, 1 << PAGE_SHIFT,
+        entries, pages);
+    if ( dest == NULL )
+        goto error_out;
+
+    /* Zero the range so padding is clear between modules */
+    memset(dest, 0, pages << PAGE_SHIFT);
+
+    /* Load modules into range */    
+    if ( args->acpi_module.length != 0 )
+    {
+        memcpy(dest,
+               args->acpi_module.data,
+               args->acpi_module.length);
+    }
+    if ( args->smbios_module.length != 0 )
+    {
+        memcpy(dest + (args->smbios_module.guest_addr_out - mstart),
+               args->smbios_module.data,
+               args->smbios_module.length);
+    }
+
+    munmap(dest, pages << PAGE_SHIFT);
+    rc = 0;
+
+ error_out:
+    free(entries);
+
+    return rc;
+}
+
 /*
  * Check whether there exists mmio hole in the specified memory range.
  * Returns 1 if exists, else returns 0.
@@ -142,7 +235,7 @@ static int check_mmio_hole(uint64_t star
 }
 
 static int setup_guest(xc_interface *xch,
-                       uint32_t dom, const struct xc_hvm_build_args *args,
+                       uint32_t dom, struct xc_hvm_build_args *args,
                        char *image, unsigned long image_size)
 {
     xen_pfn_t *page_array = NULL;
@@ -155,6 +248,7 @@ static int setup_guest(xc_interface *xch
     uint32_t *ident_pt;
     struct elf_binary elf;
     uint64_t v_start, v_end;
+    uint64_t m_start = 0, m_end = 0;
     int rc;
     xen_capabilities_info_t caps;
     unsigned long stat_normal_pages = 0, stat_2mb_pages = 0, 
@@ -180,11 +274,19 @@ static int setup_guest(xc_interface *xch
         goto error_out;
     }
 
+    if ( modules_init(args, v_end, &elf, &m_start, &m_end) != 0 )
+    {
+        ERROR("Insufficient space to load modules.");
+        goto error_out;
+    }
+
     IPRINTF("VIRTUAL MEMORY ARRANGEMENT:\n"
             "  Loader:        %016"PRIx64"->%016"PRIx64"\n"
+            "  Modules:       %016"PRIx64"->%016"PRIx64"\n"
             "  TOTAL:         %016"PRIx64"->%016"PRIx64"\n"
             "  ENTRY ADDRESS: %016"PRIx64"\n",
             elf.pstart, elf.pend,
+            m_start, m_end,
             v_start, v_end,
             elf_uval(&elf, elf.ehdr, e_entry));
 
@@ -339,6 +441,9 @@ static int setup_guest(xc_interface *xch
     if ( loadelfimage(xch, &elf, dom, page_array) != 0 )
         goto error_out;
 
+    if ( loadmodules(xch, args, m_start, m_end, dom, page_array) != 0 )
+        goto error_out;    
+
     if ( (hvm_info_page = xc_map_foreign_range(
               xch, dom, PAGE_SIZE, PROT_READ | PROT_WRITE,
               HVM_INFO_PFN)) == NULL )
@@ -420,7 +525,7 @@ static int setup_guest(xc_interface *xch
  * Create a domain for a virtualized Linux, using files/filenames.
  */
 int xc_hvm_build(xc_interface *xch, uint32_t domid,
-                 const struct xc_hvm_build_args *hvm_args)
+                 struct xc_hvm_build_args *hvm_args)
 {
     struct xc_hvm_build_args args = *hvm_args;
     void *image;
@@ -448,6 +553,15 @@ int xc_hvm_build(xc_interface *xch, uint
 
     sts = setup_guest(xch, domid, &args, image, image_size);
 
+    if (!sts)
+    {
+        /* Return module load addresses to caller */
+        hvm_args->acpi_module.guest_addr_out = 
+            args.acpi_module.guest_addr_out;
+        hvm_args->smbios_module.guest_addr_out = 
+            args.smbios_module.guest_addr_out;
+    }
+
     free(image);
 
     return sts;
@@ -468,6 +582,7 @@ int xc_hvm_build_target_mem(xc_interface
 {
     struct xc_hvm_build_args args = {};
 
+    memset(&args, 0, sizeof(struct xc_hvm_build_args));
     args.mem_size = (uint64_t)memsize << 20;
     args.mem_target = (uint64_t)target << 20;
     args.image_file_name = image_name;
Index: xen-4.2.3-testing/tools/libxc/xenguest.h
===================================================================
--- xen-4.2.3-testing.orig/tools/libxc/xenguest.h
+++ xen-4.2.3-testing/tools/libxc/xenguest.h
@@ -211,11 +211,23 @@ int xc_linux_build_mem(xc_interface *xch
                        unsigned int console_evtchn,
                        unsigned long *console_mfn);
 
+struct xc_hvm_firmware_module {
+    uint8_t  *data;
+    uint32_t  length;
+    uint64_t  guest_addr_out;
+};
+
 struct xc_hvm_build_args {
     uint64_t mem_size;           /* Memory size in bytes. */
     uint64_t mem_target;         /* Memory target in bytes. */
     uint64_t mmio_size;          /* Size of the MMIO hole in bytes. */
     const char *image_file_name; /* File name of the image to load. */
+
+    /* Extra ACPI tables passed to HVMLOADER */
+    struct xc_hvm_firmware_module acpi_module;
+
+    /* Extra SMBIOS structures passed to HVMLOADER */
+    struct xc_hvm_firmware_module smbios_module;
 };
 
 /**
@@ -228,7 +240,7 @@ struct xc_hvm_build_args {
  * are optional.
  */
 int xc_hvm_build(xc_interface *xch, uint32_t domid,
-                 const struct xc_hvm_build_args *hvm_args);
+                 struct xc_hvm_build_args *hvm_args);
 
 int xc_hvm_build_target_mem(xc_interface *xch,
                             uint32_t domid,
Index: xen-4.2.3-testing/tools/libxc/xg_private.c
===================================================================
--- xen-4.2.3-testing.orig/tools/libxc/xg_private.c
+++ xen-4.2.3-testing/tools/libxc/xg_private.c
@@ -192,7 +192,7 @@ unsigned long csum_page(void *page)
 __attribute__((weak)) 
     int xc_hvm_build(xc_interface *xch,
                      uint32_t domid,
-                     const struct xc_hvm_build_args *hvm_args)
+                     struct xc_hvm_build_args *hvm_args)
 {
     errno = ENOSYS;
     return -1;
