summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-05-28 14:15:50 -0700
committerH. Peter Anvin <hpa@linux.intel.com>2010-05-28 14:15:50 -0700
commitc1a36c3c99f326dd622d8663cf1edf07db91d6a7 (patch)
tree4b5f7ce136613e9a7f566c644972eeb32c049b21
parentbf13f88077f7de4f539d458905b1a35aec245444 (diff)
downloadwraplinux-c1a36c3c99f326dd622d8663cf1edf07db91d6a7.tar.gz
wraplinux-c1a36c3c99f326dd622d8663cf1edf07db91d6a7.tar.xz
wraplinux-c1a36c3c99f326dd622d8663cf1edf07db91d6a7.zip
Handle loading above 1 MB, for bug-compatibility with Grub
Handle being loaded by bootloaders which can only load above 1 MB. This is for bug-compatibility with Grub, which will err out with an angry message if it sees addresses below 1 MB in Multiboot. Thus add a new --loadhigh option, and make it the default when building a --multiboot image. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--Makefile26
-rw-r--r--highmove.S23
-rw-r--r--highmove/highmove.S73
-rw-r--r--linux.c63
-rw-r--r--main.c21
-rw-r--r--setup.h7
-rw-r--r--wraplinux.h1
7 files changed, 204 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 42ae862..17388a0 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,9 @@ PERL ?= perl
RELOC_OBJS = $(patsubst %.c,%.o,$(wildcard reloc/*.c)) \
$(patsubst %.S,%.o,$(wildcard reloc/*.S))
+HIGHMOVE_OBJS = $(patsubst %.c,%.o,$(wildcard highmove/*.c)) \
+ $(patsubst %.S,%.o,$(wildcard highmove/*.S))
+
all: wraplinux
reloc/%.o: reloc/%.c
@@ -12,6 +15,12 @@ reloc/%.o: reloc/%.c
reloc/%.o: reloc/%.S
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) -c -D__ASSEMBLY__ -o $@ $<
+highmove/%.o: highmove/%.c
+ $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) -c -o $@ $<
+
+highmove/%.o: highmove/%.S
+ $(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) -c -D__ASSEMBLY__ -o $@ $<
+
%.o: %.S
$(CC) $(CFLAGS) -c -o $@ $<
@@ -36,7 +45,16 @@ reloc/reloc.elf: $(RELOC_OBJS) reloc/reloc.ld
reloc.o: reloc.S reloc/reloc.bin
-wraplinux: main.o linux.o reloc.o elf.o nbi.o segment.o mapfile.o \
+highmove/highmove.bin: highmove/highmove.elf
+ $(OBJCOPY_FOR_TARGET) -O binary $< $@
+
+highmove/highmove.elf: $(HIGHMOVE_OBJS) reloc/reloc.ld
+ $(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) -T reloc/reloc.ld \
+ -o $@ $(HIGHMOVE_OBJS)
+
+highmove.o: highmove.S highmove/highmove.bin
+
+wraplinux: main.o linux.o reloc.o highmove.o elf.o nbi.o segment.o mapfile.o \
cwrite.o xmalloc.o
$(CC) $(LDFLAGS) -o $@ $^
@@ -53,7 +71,9 @@ install: all
# Cleanup
#
clean:
- rm -f wraplinux *.o *.s *.i reloc/*.o reloc/*.bin reloc/*.elf
+ rm -f wraplinux *.o *.s *.i
+ rm -f reloc/*.o reloc/*.bin reloc/*.elf
+ rm -f highmove/*.o highmove/*.bin highmove/*.elf
cleaner: clean
rm -rf MCONFIG config.h *.cache config.status config.log
@@ -114,6 +134,6 @@ depend: .depend
.depend:
: > $@
- $(PERL) mkdep.pl -M $@ -- . reloc
+ $(PERL) mkdep.pl -M $@ -- . reloc highmove
-include .depend
diff --git a/highmove.S b/highmove.S
new file mode 100644
index 0000000..ae30826
--- /dev/null
+++ b/highmove.S
@@ -0,0 +1,23 @@
+/*
+ * All this does is it wraps highmove/highmove.bin in a .o file.
+ * This could also be done with a binary-to-C converter.
+ */
+
+ .data
+ .balign 4
+ .globl highmove_size
+highmove_size:
+ .long .L_highmove_end-highmove
+ .size highmove_size, 4
+
+ .globl highmove
+highmove:
+ .incbin "highmove/highmove.bin"
+ .size highmove,.-highmove
+.L_highmove_end:
+
+/*
+ * This is necessary to keep the whole executable
+ * from needing a writable stack.
+ */
+ .section .note.GNU-stack,"",@progbits
diff --git a/highmove/highmove.S b/highmove/highmove.S
new file mode 100644
index 0000000..ac1cc2a
--- /dev/null
+++ b/highmove/highmove.S
@@ -0,0 +1,73 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 rPath, Inc. - All Rights Reserved
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Stub to work around Grub, or other loaders which can't load directly
+ * into low memory. This code needs four parameters which should
+ * immediately precede it: source of code to be copied, target of code
+ * to be copied, length of code to be copied, post-copy entry point.
+ */
+ /* Parameters */
+ .section ".startupinfo","a",@progbits
+mv_src: .long 0
+mv_dst: .long 0
+mv_len: .long 0
+mv_entry: .long 0
+
+ /* We will be entered in flat 32-bit protected mode... */
+ .section ".start","ax",@progbits
+ .globl _start
+ .code32
+_start:
+ call 1f
+1: pushfl
+ pushl %ecx
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ movl (5*4)(%esp), %ebx
+ subl $1b, %ebx
+
+ movl mv_entry(%ebx), %ecx
+ movl %ecx, (5*4)(%esp) /* Where to go next */
+
+ movl mv_src(%ebx), %esi
+ movl mv_dst(%ebx), %edi
+ movl mv_len(%ebx), %ecx
+ addl $3, %ecx
+ shrl $2, %ecx
+
+ cld
+ rep ; movsl
+
+ popl %edi
+ popl %esi
+ popl %ebx
+ popl %ecx
+ popfl
+ retl /* Goes to the entry point */
diff --git a/linux.c b/linux.c
index 4f97d88..9aab786 100644
--- a/linux.c
+++ b/linux.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2008 rPath, Inc. - All Rights Reserved
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,6 +35,8 @@
extern char reloc[];
extern uint32_t reloc_size;
+extern char highmove[];
+extern uint32_t highmove_size;
/* Find the last instance of a particular command line argument
(which should include the final =; do not use for boolean arguments) */
@@ -105,9 +108,10 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
int kernel_fd = -1;
char *kernel = NULL;
size_t kernel_len = 0;
- struct segment srel, ssup, scmd, skrn;
+ struct segment srel, ssup, scmd, skrn, shmv;
struct startup_info *info = (void *)reloc;
struct setup_header *hdr;
+ struct highmove_info *hmv = (void *)highmove;
size_t setup_len;
size_t initrd_len;
size_t initrd_addr;
@@ -119,6 +123,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
int setup_space; /* How much space for the setup */
const char *cmd;
uint32_t initrd_max;
+ uint32_t entry;
/* Process the kernel file */
@@ -288,6 +293,12 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
hdr->loadflags |= CAN_USE_HEAP;
}
+
+ /* Setup information */
+ wrle32(ssup.address, &info->setup_addr);
+ wrle32(scmd.address, &info->cmdline_addr);
+ entry = srel.address + sizeof *info;
+
/* Segment: Linux kernel proper */
skrn.next = ninitrd ? &ird[0].seg : NULL;
skrn.align = 4; /* 2**4 = 16 bytes */
@@ -297,12 +308,54 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
skrn.name = "kernel";
skrn.data = kernel + setup_len;
- /* Additional segments: initrd */
if (skrn.address < 0x100000)
initrd_addr = 0x100000;
else
initrd_addr = skrn.address + skrn.length;
+ /* Loadhigh modifications */
+ if (opt.loadhigh) {
+ uint32_t rm_base, rm_len, delta;
+
+ rm_base = ssup.address;
+ if (skrn.address < 0x100000) {
+ rm_base = skrn.address;
+ skrn.address = 0x100000;
+ initrd_addr = 0x100000 + skrn.length;
+ }
+
+ shmv.next = skrn.next;
+ skrn.next = &shmv;
+ shmv.address = align_up(initrd_addr, 4);
+ shmv.align = 4;
+ shmv.length = highmove_size;
+ shmv.sh_type = SHT_PROGBITS;
+ shmv.sh_flags = SHF_ALLOC | SHF_EXECINSTR;
+ shmv.name = "highmove";
+ shmv.data = highmove;
+
+ rm_len = scmd.address + scmd.length - rm_base;
+ wrle32(rm_base, &hmv->mv_dst);
+ wrle32(rm_len, &hmv->mv_len);
+ wrle32(entry, &hmv->mv_entry);
+
+ entry = shmv.address + sizeof *hmv;
+
+ initrd_addr = shmv.address + shmv.length;
+ initrd_addr = align_up(initrd_addr, 4);
+
+ wrle32(initrd_addr, &hmv->mv_src);
+
+ delta = initrd_addr - ssup.address;
+
+ ssup.address += delta;
+ srel.address += delta;
+ scmd.address += delta;
+
+ initrd_addr += rm_len;
+ }
+
+ /* Additional segments: initrd */
for (i = 0; i < ninitrd; i++) {
char *name;
@@ -321,14 +374,12 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
if (setup_ver >= 0x200)
wrle32(initrd_len, &hdr->ramdisk_size);
- /* Set up the startup info */
+ /* Initrd information in the startup info */
wrle32(ninitrd ? ird[0].seg.address : 0, &info->rd_addr);
wrle32(initrd_len, &info->rd_len);
wrle32(initrd_max, &info->rd_maxaddr);
- wrle32(ssup.address, &info->setup_addr);
- wrle32(scmd.address, &info->cmdline_addr);
- rv = opt.output(&srel, srel.address + sizeof *info, out);
+ rv = opt.output(&srel, entry, out);
err:
if (ird) {
diff --git a/main.c b/main.c
index 347ad45..568db4c 100644
--- a/main.c
+++ b/main.c
@@ -31,12 +31,18 @@
const char *program;
+enum longopts {
+ OPT_NOLOADHIGH = 256,
+};
+
const struct option long_options[] = {
{"params", 1, 0, 'p'},
{"cmdline", 1, 0, 'p'},
{"commandline", 1, 0, 'p'},
{"initrd", 1, 0, 'i'},
{"output", 1, 0, 'o'},
+ {"loadhigh", 0, 0, 'l'},
+ {"noloadhigh", 0, 0, OPT_NOLOADHIGH},
{"elf", 0, 0, 'E'},
{"multiboot", 0, 0, 'M'},
{"nbi", 0, 0, 'N'},
@@ -45,7 +51,7 @@ const struct option long_options[] = {
{0, 0, 0, 0}
};
-#define OPTSTRING "p:i:o:EMNhV"
+#define OPTSTRING "p:i:o:lEMNhV"
static void usage(int err)
{
@@ -58,6 +64,7 @@ static void usage(int err)
" --elf -E output in ELF format (default)\n"
" --multiboot -M output in Multiboot ELF format\n"
" --nbi -N output in NBI format\n"
+ " --loadhigh -l load entirely above 1 MB\n"
" --help -h display this help text\n"
" --version -V print the program version\n",
WRAPLINUX_PACKAGE, WRAPLINUX_VERSION,
@@ -71,6 +78,7 @@ int main(int argc, char *argv[])
const char *kernel;
struct string_list *ird = NULL, **irdp = &ird, *ip;
FILE *out = stdout;
+ bool loadhighopt = false;
program = argv[0];
@@ -101,6 +109,14 @@ int main(int argc, char *argv[])
}
}
break;
+ case 'l':
+ opt.loadhigh = true;
+ loadhighopt = true;
+ break;
+ case OPT_NOLOADHIGH:
+ opt.loadhigh = false;
+ loadhighopt = true;
+ break;
case 'E':
opt.output = output_elf;
break;
@@ -125,6 +141,9 @@ int main(int argc, char *argv[])
if ((argc - optind) != 1)
usage(EX_USAGE);
+ if (opt.output == output_multiboot && !loadhighopt)
+ opt.loadhigh = true;
+
kernel = argv[optind];
if (!opt.params)
diff --git a/setup.h b/setup.h
index a28a94c..4313bc7 100644
--- a/setup.h
+++ b/setup.h
@@ -12,6 +12,13 @@ struct startup_info {
uint32_t reloc_size;
};
+struct highmove_info {
+ uint32_t mv_src;
+ uint32_t mv_dst;
+ uint32_t mv_len;
+ uint32_t mv_entry;
+};
+
#define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24))
#define OLD_CMDLINE_MAGIC 0xA33F
diff --git a/wraplinux.h b/wraplinux.h
index 7e22b02..d655ef9 100644
--- a/wraplinux.h
+++ b/wraplinux.h
@@ -30,6 +30,7 @@ extern const char *program;
struct opt {
const char *params;
int (*output) (struct segment *, addr_t, FILE *);
+ bool loadhigh;
} opt;
struct string_list {