summaryrefslogtreecommitdiff
path: root/kexec/arch/i386/kexec-x86.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/arch/i386/kexec-x86.c')
-rw-r--r--kexec/arch/i386/kexec-x86.c124
1 files changed, 121 insertions, 3 deletions
diff --git a/kexec/arch/i386/kexec-x86.c b/kexec/arch/i386/kexec-x86.c
index 8c8ba7f..554c154 100644
--- a/kexec/arch/i386/kexec-x86.c
+++ b/kexec/arch/i386/kexec-x86.c
@@ -28,15 +28,26 @@
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "../../kexec-syscall.h"
+#include "../../firmware_memmap.h"
#include "kexec-x86.h"
#include "crashdump-x86.h"
#include <arch/options.h>
static struct memory_range memory_range[MAX_MEMORY_RANGES];
-/* Return a sorted list of memory ranges. */
-int get_memory_ranges(struct memory_range **range, int *ranges,
- unsigned long kexec_flags)
+/**
+ * The old /proc/iomem parsing code.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ * @param[in] kexec_flags the kexec_flags to determine if we load a normal
+ * or a crashdump kernel
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int get_memory_ranges_proc_iomem(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
{
const char *iomem= proc_iomem();
int memory_ranges = 0;
@@ -114,6 +125,113 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
return 0;
}
+/**
+ * Calls the architecture independent get_firmware_memmap_ranges() to parse
+ * /sys/firmware/memmap and then do some x86 only modifications.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ * @param[in] kexec_flags the kexec_flags to determine if we load a normal
+ * or a crashdump kernel
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int get_memory_ranges_sysfs(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ int ret;
+ size_t i;
+ size_t range_number = MAX_MEMORY_RANGES;
+ unsigned long long start, end;
+
+ ret = get_firmware_memmap_ranges(memory_range, &range_number);
+ if (ret != 0) {
+ fprintf(stderr, "Parsing the /sys/firmware memory map failed. "
+ "Falling back to /proc/iomem.\n");
+ return get_memory_ranges_proc_iomem(range, ranges, kexec_flags);
+ }
+
+ /* Don't report the interrupt table as ram */
+ for (i = 0; i < range_number; i++) {
+ if (memory_range[i].type == RANGE_RAM &&
+ (memory_range[i].start < 0x100)) {
+ memory_range[i].start = 0x100;
+ break;
+ }
+ }
+
+ /*
+ * Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported values are
+ * subset of user defined values.
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ ret = parse_iomem_single("Crash kernel\n", &start, &end);
+ if (ret != 0) {
+ fprintf(stderr, "parse_iomem_single failed.\n");
+ return -1;
+ }
+
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+
+ *range = memory_range;
+ *ranges = range_number;
+
+ return 0;
+}
+
+/**
+ * Return a sorted list of memory ranges.
+ *
+ * If we have the /sys/firmware/memmap interface, then use that. If not,
+ * or if parsing of that fails, use /proc/iomem as fallback.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ * @param[in] kexec_flags the kexec_flags to determine if we load a normal
+ * or a crashdump kernel
+ *
+ * @return 0 on success, any other value on failure.
+ */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ int ret;
+
+ if (have_sys_firmware_memmap())
+ ret = get_memory_ranges_sysfs(range, ranges,kexec_flags);
+ else
+ ret = get_memory_ranges_proc_iomem(range, ranges, kexec_flags);
+
+ /*
+ * both get_memory_ranges_sysfs() and get_memory_ranges_proc_iomem()
+ * have already printed an error message, so fail silently here
+ */
+ if (ret != 0)
+ return ret;
+
+ /* just set 0 to 1 to enable printing for debugging */
+#if 0
+ {
+ int i;
+ printf("MEMORY RANGES\n");
+ for (i = 0; i < *ranges; i++) {
+ printf("%016Lx-%016Lx (%d)\n", (*range)[i].start,
+ (*range)[i].end, (*range)[i].type);
+ }
+ }
+#endif
+
+ return ret;
+}
+
struct file_type file_type[] = {
{ "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
multiboot_x86_usage },