aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/misc/xen-command-line.markdown14
-rw-r--r--xen/arch/x86/setup.c4
-rw-r--r--xen/common/kexec.c133
-rw-r--r--xen/drivers/char/console.c2
-rw-r--r--xen/include/xen/kexec.h13
5 files changed, 163 insertions, 3 deletions
diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
index 61f5328bdf..beb84625fa 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -188,6 +188,13 @@ The optional trailing `x` indicates that Xen should not automatically switch the
### cpuid\_mask\_xsave\_eax
### cpuidle
### cpuinfo
+### crashinfo_maxaddr
+> `= <size>`
+
+> Default: `4G`
+
+Specify the maximum address to allocate certain strucutres, if used in combination with the `low_crashinfo` command line option.
+
### crashkernel
### credit2\_balance\_over
### credit2\_balance\_under
@@ -273,6 +280,13 @@ Set the logging level for Xen. Any log message with equal more more importance
The optional `<rate-limited level>` options instructs which severities should be rate limited.
+### low\_crashinfo
+> `= none | min | all`
+
+> Default: `none` if not specified at all, or to `min` if `low\_crashinfo` is present without qualification.
+
+This option is only useful for hosts with a 32bit dom0 kernel, wishing to use kexec functionality in the case of a crash. It represents which data structures should be deliberatly allocated in low memory, so the crash kernel may find find them. Should be used in combination with `crashinfo_maxaddr`.
+
### max\_cstate
### max\_gsi\_irqs
### maxcpus
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index b9da9851c9..49f91ab543 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -583,6 +583,10 @@ void __init __start_xen(unsigned long mbi_p)
}
cmdline_parse(cmdline);
+ /* Must be after command line argument parsing and before
+ * allocing any xenheap structures wanted in lower memory. */
+ kexec_early_calculations();
+
parse_video_info();
set_current((struct vcpu *)0xfffff000); /* debug sanity */
diff --git a/xen/common/kexec.c b/xen/common/kexec.c
index 91a643560b..dd47e27e3c 100644
--- a/xen/common/kexec.c
+++ b/xen/common/kexec.c
@@ -36,7 +36,9 @@ bool_t kexecing = FALSE;
typedef struct { Elf_Note * start; size_t size; } crash_note_range_t;
static crash_note_range_t * crash_notes;
-/* Lock to prevent race conditions when allocating the crash note buffers. */
+/* Lock to prevent race conditions when allocating the crash note buffers.
+ * It also serves to protect calls to alloc_from_crash_heap when allocating
+ * crash note buffers in lower memory. */
static DEFINE_SPINLOCK(crash_notes_lock);
static Elf_Note *xen_crash_note;
@@ -62,6 +64,24 @@ static struct {
unsigned long size;
} ranges[16] __initdata;
+/* Low crashinfo mode. Start as INVALID so serveral codepaths can set up
+ * defaults without needing to know the state of the others. */
+enum low_crashinfo low_crashinfo_mode = LOW_CRASHINFO_INVALID;
+
+/* This value is only considered if low_crash_mode is set to MIN or ALL, so
+ * setting a default here is safe. Default to 4GB. This is because the current
+ * KEXEC_CMD_get_range compat hypercall trucates 64bit pointers to 32 bits. The
+ * typical usecase for crashinfo_maxaddr will be for 64bit Xen with 32bit dom0
+ * and 32bit crash kernel. */
+static paddr_t __initdata crashinfo_maxaddr = 4ULL << 30;
+
+/* = log base 2 of crashinfo_maxaddr after checking for sanity. Default to
+ * larger than the entire physical address space. */
+paddr_t crashinfo_maxaddr_bits = 64;
+
+/* Pointers to keep track of the crash heap region. */
+static void *crash_heap_current = NULL, *crash_heap_end = NULL;
+
/*
* Parse command lines in the format
*
@@ -140,6 +160,58 @@ static void __init parse_crashkernel(const char *str)
}
custom_param("crashkernel", parse_crashkernel);
+/* Parse command lines in the format:
+ *
+ * low_crashinfo=[none,min,all]
+ *
+ * - none disables the low allocation of crash info.
+ * - min will allocate enough low information for the crash kernel to be able
+ * to extract the hypervisor and dom0 message ring buffers.
+ * - all will allocate additional structures such as domain and vcpu structs
+ * low so the crash kernel can perform an extended analysis of state.
+ */
+static void __init parse_low_crashinfo(const char * str)
+{
+
+ if ( !strlen(str) )
+ /* default to min if user just specifies "low_crashinfo" */
+ low_crashinfo_mode = LOW_CRASHINFO_MIN;
+ else if ( !strcmp(str, "none" ) )
+ low_crashinfo_mode = LOW_CRASHINFO_NONE;
+ else if ( !strcmp(str, "min" ) )
+ low_crashinfo_mode = LOW_CRASHINFO_MIN;
+ else if ( !strcmp(str, "all" ) )
+ low_crashinfo_mode = LOW_CRASHINFO_ALL;
+ else
+ {
+ printk("Unknown low_crashinfo parameter '%s'. Defaulting to min.\n", str);
+ low_crashinfo_mode = LOW_CRASHINFO_MIN;
+ }
+}
+custom_param("low_crashinfo", parse_low_crashinfo);
+
+/* Parse command lines in the format:
+ *
+ * crashinfo_maxaddr=<addr>
+ *
+ * <addr> will be rounded down to the nearest power of two. Defaults to 64G
+ */
+static void __init parse_crashinfo_maxaddr(const char * str)
+{
+ u64 addr;
+
+ /* if low_crashinfo_mode is unset, default to min. */
+ if ( low_crashinfo_mode == LOW_CRASHINFO_INVALID )
+ low_crashinfo_mode = LOW_CRASHINFO_MIN;
+
+ if ( (addr = parse_size_and_unit(str, NULL)) )
+ crashinfo_maxaddr = addr;
+ else
+ printk("Unable to parse crashinfo_maxaddr. Defaulting to %p\n",
+ (void*)crashinfo_maxaddr);
+}
+custom_param("crashinfo_maxaddr", parse_crashinfo_maxaddr);
+
void __init set_kexec_crash_area_size(u64 system_ram)
{
unsigned int idx;
@@ -298,6 +370,20 @@ static size_t sizeof_cpu_notes(const unsigned long cpu)
return bytes;
}
+/* Allocate size_t bytes of space from the previously allocated
+ * crash heap if the user has requested that crash notes be allocated
+ * in lower memory. There is currently no case where the crash notes
+ * should be free()'d. */
+static void * alloc_from_crash_heap(const size_t bytes)
+{
+ void * ret;
+ if ( crash_heap_current + bytes > crash_heap_end )
+ return NULL;
+ ret = (void*)crash_heap_current;
+ crash_heap_current += bytes;
+ return ret;
+}
+
/* Allocate a crash note buffer for a newly onlined cpu. */
static int kexec_init_cpu_notes(const unsigned long cpu)
{
@@ -312,7 +398,10 @@ static int kexec_init_cpu_notes(const unsigned long cpu)
return ret;
nr_bytes = sizeof_cpu_notes(cpu);
- note = xmalloc_bytes(nr_bytes);
+
+ /* If we dont care about the position of allocation, malloc. */
+ if ( low_crashinfo_mode == LOW_CRASHINFO_NONE )
+ note = xmalloc_bytes(nr_bytes);
/* Protect the write into crash_notes[] with a spinlock, as this function
* is on a hotplug path and a hypercall path. */
@@ -330,6 +419,11 @@ static int kexec_init_cpu_notes(const unsigned long cpu)
}
else
{
+ /* If we care about memory possition, alloc from the crash heap,
+ * also protected by the crash_notes_lock. */
+ if ( low_crashinfo_mode > LOW_CRASHINFO_NONE )
+ note = alloc_from_crash_heap(nr_bytes);
+
crash_notes[cpu].start = note;
crash_notes[cpu].size = nr_bytes;
spin_unlock(&crash_notes_lock);
@@ -389,6 +483,18 @@ static struct notifier_block cpu_nfb = {
.notifier_call = cpu_callback
};
+void __init kexec_early_calculations(void)
+{
+ /* If low_crashinfo_mode is still INVALID, neither "low_crashinfo" nor
+ * "crashinfo_maxaddr" have been specified on the command line, so
+ * explicitly set to NONE. */
+ if ( low_crashinfo_mode == LOW_CRASHINFO_INVALID )
+ low_crashinfo_mode = LOW_CRASHINFO_NONE;
+
+ if ( low_crashinfo_mode > LOW_CRASHINFO_NONE )
+ crashinfo_maxaddr_bits = fls(crashinfo_maxaddr) - 1;
+}
+
static int __init kexec_init(void)
{
void *cpu = (void *)(unsigned long)smp_processor_id();
@@ -397,6 +503,29 @@ static int __init kexec_init(void)
if ( !kexec_crash_area.size )
return 0;
+ if ( low_crashinfo_mode > LOW_CRASHINFO_NONE )
+ {
+ size_t crash_heap_size;
+
+ /* This calculation is safe even if the machine is booted in
+ * uniprocessor mode. */
+ crash_heap_size = sizeof_cpu_notes(0) +
+ sizeof_cpu_notes(1) * (nr_cpu_ids - 1);
+ crash_heap_size = PAGE_ALIGN(crash_heap_size);
+
+ crash_heap_current = alloc_xenheap_pages(
+ get_order_from_bytes(crash_heap_size),
+ MEMF_bits(crashinfo_maxaddr_bits) );
+
+ if ( ! crash_heap_current )
+ return -ENOMEM;
+
+ crash_heap_end = crash_heap_current + crash_heap_size;
+ }
+
+ /* crash_notes may be allocated anywhere Xen can reach in memory.
+ Only the individual CPU crash notes themselves must be allocated
+ in lower memory if requested. */
crash_notes = xzalloc_array(crash_note_range_t, nr_cpu_ids);
if ( ! crash_notes )
return -ENOMEM;
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 64a99a67f6..5cbbf814ad 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -596,7 +596,7 @@ void __init console_init_postirq(void)
opt_conring_size = num_present_cpus() << (9 + xenlog_lower_thresh);
order = get_order_from_bytes(max(opt_conring_size, conring_size));
- while ( (ring = alloc_xenheap_pages(order, 0)) == NULL )
+ while ( (ring = alloc_xenheap_pages(order, MEMF_bits(crashinfo_maxaddr_bits))) == NULL )
{
BUG_ON(order == 0);
order--;
diff --git a/xen/include/xen/kexec.h b/xen/include/xen/kexec.h
index 1039d0966b..8e9c1d36d9 100644
--- a/xen/include/xen/kexec.h
+++ b/xen/include/xen/kexec.h
@@ -25,6 +25,19 @@ void set_kexec_crash_area_size(u64 system_ram);
#define KEXEC_IMAGE_CRASH_BASE 2
#define KEXEC_IMAGE_NR 4
+enum low_crashinfo {
+ LOW_CRASHINFO_INVALID = 0,
+ LOW_CRASHINFO_NONE = 1,
+ LOW_CRASHINFO_MIN = 2,
+ LOW_CRASHINFO_ALL = 3
+};
+
+/* Low crashinfo mode. Start as INVALID so serveral codepaths can set up
+ * defaults without needing to know the state of the others. */
+extern enum low_crashinfo low_crashinfo_mode;
+extern paddr_t crashinfo_maxaddr_bits;
+void kexec_early_calculations(void);
+
int machine_kexec_load(int type, int slot, xen_kexec_image_t *image);
void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image);
void machine_kexec_reserved(xen_kexec_reserve_t *reservation);