]> xenbits.xensource.com Git - people/dwmw2/xen.git/commitdiff
x86/boot: Add CONFIG_LIVE_UPDATE and liveupdate= command line parameter
authorDavid Woodhouse <dwmw@amazon.co.uk>
Fri, 31 Jan 2020 22:01:00 +0000 (22:01 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 18 Mar 2020 22:55:19 +0000 (22:55 +0000)
For live update to work, the newly booting Xen will need a region of
memory that can be given to the boot allocator while it parses the state
information from the previous Xen and works out which of the other pages
of memory it can consume.

Reserve that like the crashdump region, and accept it on the command
line. Use only that region for early boot, and register the remaining
RAM later.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
docs/misc/xen-command-line.pandoc
xen/arch/x86/Kconfig
xen/arch/x86/setup.c
xen/common/Kconfig
xen/include/asm-x86/config.h

index 02432bdb2513b875bd0f6e1bc03f4e089747a150..13221a0a95b52d1fb61b52a7a9e29a37c08e16ea 100644 (file)
@@ -1406,6 +1406,15 @@ This option is intended for debugging purposes only.  Enable MSR_DEBUGCTL.LBR
 in hypervisor context to be able to dump the Last Interrupt/Exception To/From
 record with other registers.
 
+### liveupdate
+> `= <size>[@<offset>]`
+
+Specify size and optionally placement of the boot memory reserved for
+Xen live update. The size must be a multiple of 2MiB.
+
+A trailing `@<offset>` specifies the exact address this area should be
+placed at, which must be below 4GiB.
+
 ### loglvl
 > `= <level>[/<rate-limited level>]` where level is `none | error | warning | info | debug | all`
 
index 8149362bdef3a153e80ae62c1bdef974801fe031..8f759d8130cf6b97fd3481ed27a4fe81cab695d5 100644 (file)
@@ -15,6 +15,7 @@ config X86
        select HAS_FAST_MULTIPLY
        select HAS_IOPORTS
        select HAS_KEXEC
+       select HAS_LIVE_UPDATE
        select MEM_ACCESS_ALWAYS_ON
        select HAS_MEM_PAGING
        select HAS_NS16550
index 30068eace2ebe0039ea956f4da467aef7434b3a3..e15680dd2d549649b3c08fddc043186df9f06e37 100644 (file)
@@ -768,6 +768,43 @@ static struct domain * __init create_dom0(const module_t *image,
 /* How much of the directmap is prebuilt at compile time. */
 #define PREBUILT_MAP_LIMIT (1 << L2_PAGETABLE_SHIFT)
 
+unsigned long lu_bootmem_start, lu_bootmem_size;
+
+#ifdef CONFIG_LIVE_UPDATE
+static int __init parse_liveupdate(const char *str)
+{
+    const char *cur;
+
+    lu_bootmem_size = parse_size_and_unit(cur = str, &str);
+    if ( !lu_bootmem_size || cur == str )
+        return -EINVAL;
+
+    if ( lu_bootmem_size & ((MB(2) - 1)) )
+    {
+        printk(XENLOG_WARNING "Live update size must be a multiple of 2MiB\n");
+        return -EINVAL;
+    }
+
+    if (!*str) {
+        printk(XENLOG_INFO "Live update size 0x%lx\n", lu_bootmem_size);
+        return 0;
+    }
+
+    if (*str != '@')
+        return -EINVAL;
+
+    lu_bootmem_start = simple_strtoull(cur = str + 1, &str, 0);
+    if ( !lu_bootmem_start || cur == str )
+        return -EINVAL;
+
+    printk(XENLOG_INFO "Live update area 0x%lx-0x%lx (0x%lx)\n", lu_bootmem_start,
+           lu_bootmem_start + lu_bootmem_size, lu_bootmem_size);
+
+    return 0;
+}
+custom_param("liveupdate", parse_liveupdate);
+#endif /* CONFIG_LIVE_UPDATE */
+
 void __init noreturn __start_xen(unsigned long mbi_p)
 {
     char *memmap_type = NULL;
@@ -778,7 +815,7 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     unsigned long nr_pages, raw_max_page, modules_headroom, module_map[1];
     int i, j, e820_warn = 0, bytes = 0;
     bool acpi_boot_table_init_done = false, relocated = false;
-    bool vm_init_done = false;
+    bool vm_init_done = false, lu_reserved = false;
     int ret;
     struct ns16550_defaults ns16550 = {
         .data_bits = 8,
@@ -1065,6 +1102,22 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     set_kexec_crash_area_size((u64)nr_pages << PAGE_SHIFT);
     kexec_reserve_area(&boot_e820);
 
+    if ( lu_bootmem_start )
+    {
+        /* XX: Check it's in usable memory first */
+        reserve_e820_ram(&boot_e820, lu_bootmem_start, lu_bootmem_start + lu_bootmem_size);
+
+        /* Since it will already be out of the e820 map by the time the first
+         * loop over physical memory, map it manually already. */
+        set_pdx_range(lu_bootmem_start >> PAGE_SHIFT,
+                      (lu_bootmem_start + lu_bootmem_size) >> PAGE_SHIFT);
+        map_pages_to_xen((unsigned long)__va(lu_bootmem_start),
+                         maddr_to_mfn(lu_bootmem_start),
+                         PFN_DOWN(lu_bootmem_size), PAGE_HYPERVISOR);
+
+        lu_reserved = true;
+    }
+
     initial_images = mod;
     nr_initial_images = mbi->mods_count;
 
@@ -1292,6 +1345,17 @@ void __init noreturn __start_xen(unsigned long mbi_p)
             printk("New Xen image base address: %#lx\n", xen_phys_start);
         }
 
+#ifdef CONFIG_LIVE_UPDATE
+        /* Is the region suitable for the live update bootmem region? */
+        if ( lu_bootmem_size && ! lu_bootmem_start && e < limit )
+        {
+            end = consider_modules(s, e, lu_bootmem_size, mod, mbi->mods_count + relocated, -1);
+            if ( end )
+            {
+                e = lu_bootmem_start = end - lu_bootmem_size;
+            }
+        }
+#endif
         /* Is the region suitable for relocating the multiboot modules? */
         for ( j = mbi->mods_count - 1; j >= 0; j-- )
         {
@@ -1355,6 +1419,15 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     if ( !xen_phys_start )
         panic("Not enough memory to relocate Xen\n");
 
+    if ( lu_bootmem_start )
+    {
+        if ( !lu_reserved )
+            reserve_e820_ram(&boot_e820, lu_bootmem_start, lu_bootmem_start + lu_bootmem_size);
+        printk("LU bootmem: 0x%lx - 0x%lx\n", lu_bootmem_start, lu_bootmem_start + lu_bootmem_size);
+        init_boot_pages(lu_bootmem_start, lu_bootmem_start + lu_bootmem_size);
+        lu_reserved = true;
+    }
+
     /* This needs to remain in sync with xen_in_range(). */
     reserve_e820_ram(&boot_e820, __pa(_stext), __pa(__2M_rwdata_end));
 
@@ -1366,8 +1439,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
         xenheap_max_mfn(PFN_DOWN(highmem_start - 1));
 
     /*
-     * Walk every RAM region and map it in its entirety (on x86/64, at least)
-     * and notify it to the boot allocator.
+     * Walk every RAM region and map it in its entirety and (unless in
+     * live update mode) notify it to the boot allocator.
      */
     for ( i = 0; i < boot_e820.nr_map; i++ )
     {
@@ -1431,6 +1504,7 @@ void __init noreturn __start_xen(unsigned long mbi_p)
                 printk(XENLOG_WARNING "Ignoring inaccessible memory range"
                                       " %013"PRIx64"-%013"PRIx64"\n",
                        s, e);
+                reserve_e820_ram(&boot_e820, s, e);
                 continue;
             }
             map_e = e;
@@ -1438,6 +1512,7 @@ void __init noreturn __start_xen(unsigned long mbi_p)
             printk(XENLOG_WARNING "Ignoring inaccessible memory range"
                                   " %013"PRIx64"-%013"PRIx64"\n",
                    e, map_e);
+            reserve_e820_ram(&boot_e820, e, map_e);
         }
 
         set_pdx_range(s >> PAGE_SHIFT, e >> PAGE_SHIFT);
@@ -1448,7 +1523,9 @@ void __init noreturn __start_xen(unsigned long mbi_p)
                       ARRAY_SIZE(l2_directmap) << L2_PAGETABLE_SHIFT);
 
         /* Pass mapped memory to allocator /before/ creating new mappings. */
-        init_boot_pages(s, min(map_s, e));
+        if ( !lu_reserved)
+            init_boot_pages(s, min(map_s, e));
+
         s = map_s;
         if ( s < map_e )
         {
@@ -1456,7 +1533,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
 
             map_s = (s + mask) & ~mask;
             map_e &= ~mask;
-            init_boot_pages(map_s, map_e);
+            if ( !lu_reserved)
+                init_boot_pages(map_s, map_e);
         }
 
         if ( map_s > map_e )
@@ -1472,7 +1550,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
             {
                 map_pages_to_xen((unsigned long)__va(map_e), maddr_to_mfn(map_e),
                                  PFN_DOWN(end - map_e), PAGE_HYPERVISOR);
-                init_boot_pages(map_e, end);
+                if ( !lu_reserved)
+                    init_boot_pages(map_e, end);
                 map_e = end;
             }
         }
@@ -1487,7 +1566,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
         {
             map_pages_to_xen((unsigned long)__va(s), maddr_to_mfn(s),
                              PFN_DOWN(map_s - s), PAGE_HYPERVISOR);
-            init_boot_pages(s, map_s);
+            if ( !lu_reserved)
+                init_boot_pages(s, map_s);
         }
     }
 
@@ -1588,6 +1668,29 @@ void __init noreturn __start_xen(unsigned long mbi_p)
 
     numa_initmem_init(0, raw_max_page);
 
+    if ( lu_bootmem_start )
+    {
+        unsigned long limit = virt_to_mfn(HYPERVISOR_VIRT_END - 1);
+        uint64_t mask = PAGE_SIZE - 1;
+
+        for ( i = 0; i < boot_e820.nr_map; i++ )
+        {
+            uint64_t s, e;
+
+            if ( boot_e820.map[i].type != E820_RAM )
+                continue;
+            s = (boot_e820.map[i].addr + mask) & ~mask;
+            e = (boot_e820.map[i].addr + boot_e820.map[i].size) & ~mask;
+            s = max_t(uint64_t, s, 1<<20);
+            if ( PFN_DOWN(s) > limit )
+                continue;
+            if ( PFN_DOWN(e) > limit )
+                e = pfn_to_paddr(limit);
+
+            init_boot_pages(s, e);
+        }
+    }
+
     if ( max_page - 1 > virt_to_mfn(HYPERVISOR_VIRT_END - 1) )
     {
         unsigned long limit = virt_to_mfn(HYPERVISOR_VIRT_END - 1);
index a6914fcae98b9fadce49d1ef3270a4f7978ac19c..e1bc85e235a96a7280424012e7300e2cbc62c011 100644 (file)
@@ -57,6 +57,9 @@ config HAS_UBSAN
 config HAS_KEXEC
        bool
 
+config HAS_LIVE_UPDATE
+       bool
+
 config HAS_IOPORTS
        bool
 
@@ -127,6 +130,16 @@ config KEXEC
 
          If unsure, say Y.
 
+config LIVE_UPDATE
+       bool "Live update support"
+       default y
+       depends on KEXEC && HAS_LIVE_UPDATE
+       ---help---
+         Allows a running Xen hypervisor to be replaced with a new version of
+         Xen via kexec, migrating running guests from the first Xen to the next.
+
+         If unsure, say Y.
+
 config EFI_SET_VIRTUAL_ADDRESS_MAP
     bool "EFI: call SetVirtualAddressMap()" if EXPERT = "y"
     ---help---
index a34053c4c0f2d3baa7a250096a892b45a412ca86..26f98e58a58c35f4cef537988c4301f65a462c8c 100644 (file)
@@ -279,6 +279,7 @@ extern unsigned char boot_edid_info[128];
 
 #ifndef __ASSEMBLY__
 extern unsigned long xen_phys_start;
+extern unsigned long lu_bootmem_start, lu_bootmem_size;
 #endif
 
 /* GDT/LDT shadow mapping area. The first per-domain-mapping sub-area. */