aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2013-07-25 20:14:09 +0100
committerMatt Fleming <matt.fleming@intel.com>2013-07-25 22:39:57 +0100
commit77cadda8fa63289dbd3d2bbc6eaff7c7e42688c4 (patch)
treef376c57888d0e89fa1ef6f3934844c4b63c28bed
parentc8524364ff29e3f0bef0b21d66e571ba76e8d9d4 (diff)
downloadsyslinux-77cadda8fa63289dbd3d2bbc6eaff7c7e42688c4.tar.gz
syslinux-77cadda8fa63289dbd3d2bbc6eaff7c7e42688c4.tar.xz
syslinux-77cadda8fa63289dbd3d2bbc6eaff7c7e42688c4.zip
load_linux: dynamically calculate the cmdline regionsyslinux-6.02-pre12
Users are hitting issues where the offset calculated by, (0x9ff0 - cmdline_size) & ~15; is not useable memory, e.g. it is SMT_RESERVED. Instead we should be trying to find the highest lowmem address. Cc: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--com32/include/syslinux/movebits.h4
-rw-r--r--com32/lib/syslinux/load_linux.c29
-rw-r--r--com32/lib/syslinux/zonelist.c45
3 files changed, 74 insertions, 4 deletions
diff --git a/com32/include/syslinux/movebits.h b/com32/include/syslinux/movebits.h
index d12fb584..f35418f9 100644
--- a/com32/include/syslinux/movebits.h
+++ b/com32/include/syslinux/movebits.h
@@ -83,6 +83,10 @@ enum syslinux_memmap_types syslinux_memmap_type(struct syslinux_memmap *list,
int syslinux_memmap_largest(struct syslinux_memmap *list,
enum syslinux_memmap_types type,
addr_t * start, addr_t * len);
+int syslinux_memmap_highest(struct syslinux_memmap *list,
+ enum syslinux_memmap_types types,
+ addr_t *start, addr_t len,
+ addr_t ceiling, addr_t align);
void syslinux_free_memmap(struct syslinux_memmap *list);
struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list);
int syslinux_memmap_find_type(struct syslinux_memmap *list,
diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c
index 851d4678..37c8df02 100644
--- a/com32/lib/syslinux/load_linux.c
+++ b/com32/lib/syslinux/load_linux.c
@@ -125,10 +125,29 @@ static int map_initramfs(struct syslinux_movelist **fraglist,
}
static size_t calc_cmdline_offset(struct linux_header *hdr,
- size_t cmdline_size)
+ size_t cmdline_size, addr_t base,
+ addr_t start)
{
- if (hdr->version < 0x0202 || !(hdr->loadflags & 0x01))
+ if (hdr->version < 0x0202 || !(hdr->loadflags & 0x01)) {
+ struct syslinux_memmap *mmap;
+
+ mmap = syslinux_memory_map();
+ if (mmap && !syslinux_memmap_highest(mmap, SMT_FREE, &start,
+ cmdline_size, 0xa0000, 16)) {
+ syslinux_free_memmap(mmap);
+ return start - base;
+ }
+
+ if (mmap && !syslinux_memmap_highest(mmap, SMT_TERMINAL, &start,
+ cmdline_size, 0xa0000, 16)) {
+ syslinux_free_memmap(mmap);
+ return start - base;
+ }
+
+ syslinux_free_memmap(mmap);
+ dprintf("Unable to find lowmem for cmdline\n");
return (0x9ff0 - cmdline_size) & ~15;
+ }
return 0x10000;
}
@@ -221,13 +240,15 @@ int bios_boot_linux(void *kernel_buf, size_t kernel_size,
cmdline[cmdline_size - 1] = '\0';
}
- cmdline_offset = calc_cmdline_offset(&hdr, cmdline_size);
-
real_mode_size = (hdr.setup_sects + 1) << 9;
real_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x10000 : 0x90000;
prot_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x100000 : 0x10000;
prot_mode_size = kernel_size - real_mode_size;
+ cmdline_offset = calc_cmdline_offset(&hdr, cmdline_size, real_mode_base,
+ real_mode_base + real_mode_size);
+ dprintf("cmdline_offset at 0x%x\n", real_mode_base + cmdline_offset);
+
if (hdr.version < 0x020a) {
/*
* The 3* here is a total fudge factor... it's supposed to
diff --git a/com32/lib/syslinux/zonelist.c b/com32/lib/syslinux/zonelist.c
index f869bc6f..337eb867 100644
--- a/com32/lib/syslinux/zonelist.c
+++ b/com32/lib/syslinux/zonelist.c
@@ -219,6 +219,51 @@ int syslinux_memmap_largest(struct syslinux_memmap *list,
}
/*
+ * Find the highest zone of a specific type that satisfies the
+ * constraints.
+ *
+ * 'start' is updated with the highest address on success. 'start' can
+ * be used to set a minimum address to begin searching from.
+ *
+ * Returns -1 on failure.
+ */
+int syslinux_memmap_highest(struct syslinux_memmap *list,
+ enum syslinux_memmap_types type,
+ addr_t *start, addr_t len,
+ addr_t ceiling, addr_t align)
+{
+ addr_t size, best;
+
+ for (best = 0; list->type != SMT_END; list = list->next) {
+ size = list->next->start - list->start;
+
+ if (list->type != type)
+ continue;
+
+ if (list->start + size <= *start)
+ continue;
+
+ if (list->start + len >= ceiling)
+ continue;
+
+ if (list->start + size < ceiling)
+ best = ALIGN_DOWN(list->start + size - len, align);
+ else
+ best = ALIGN_DOWN(ceiling - len, align);
+
+ if (best < *start)
+ best = 0;
+ }
+
+ if (!best)
+ return -1;
+
+ *start = best;
+
+ return 0;
+}
+
+/*
* Find the first (lowest address) zone of a specific type and of
* a certain minimum size, with an optional starting address.
* The input values of start and len are used as minima.