diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-01-03 21:54:24 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2008-01-03 21:54:24 -0800 |
commit | b3e7732cf39938575a287cd55907216c318d940c (patch) | |
tree | 4aaf0a8f22dcf2cc5fbb46cbdab6afcad2c99daa /reloc/reloc_linux.c | |
download | wraplinux-b3e7732cf39938575a287cd55907216c318d940c.tar.gz wraplinux-b3e7732cf39938575a287cd55907216c318d940c.tar.xz wraplinux-b3e7732cf39938575a287cd55907216c318d940c.zip |
Initial commit: functional for newer bzImage kernels
Diffstat (limited to 'reloc/reloc_linux.c')
-rw-r--r-- | reloc/reloc_linux.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/reloc/reloc_linux.c b/reloc/reloc_linux.c new file mode 100644 index 0000000..5849c54 --- /dev/null +++ b/reloc/reloc_linux.c @@ -0,0 +1,187 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * 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. + * + * ----------------------------------------------------------------------- */ + +/* + * reloc_linux.c + * + * This is the initial program run before the Linux kernel. + * It serves to hoist the initrd as high as permitted. + * + * This runs in protected mode, but in low memory, and should be kept + * as small as possible. + */ + +#include "reloc.h" +#include "setup.h" +#include "conio.h" + +static uint32_t initrd_len, initrd_addr; +static uint64_t max_addr; + +static int initrd_fit(uint32_t base, uint32_t end) +{ + uint64_t ibase; + + printf("Fit: base = %08x, end = %08x\n", base, end); + + if (end <= base) + return -1; /* Invalid */ + + if (base > max_addr) + return -1; /* Not accessible */ + + if (end > max_addr) + end = max_addr; + + if (end < initrd_len) + return -1; + + ibase = (end - initrd_len) & ~0xfff; + if (ibase < base) + return -1; + + if (initrd_addr < ibase) { + initrd_addr = ibase; /* This is a better one... */ + printf("Best: initrd_addr = %08x\n", initrd_addr); + } + + return 0; +} + +static int probe_memory_e820(void) +{ + com32sys_t regs; + struct e820_info { + uint64_t base; + uint64_t len; + uint32_t type; + } buf; + uint64_t base, end; + int rv = -1; + uint32_t copied; + + memset(®s, 0, sizeof regs); + + do { + regs.eax.l = 0x0000e820; + regs.ecx.l = sizeof buf; + regs.edx.l = 0x534d4150; + regs.edi.w[0] = OFFS(&buf); + regs.es = SEG(&buf); + + intcall(0x15, ®s, ®s); + copied = (regs.eflags.l & EFLAGS_CF) + ? 0 : regs.ecx.l; + + if ( regs.eax.l != 0x534d4150 || copied < 20 ) + break; + + if (buf.type != 1) + continue; /* Not memory */ + + rv &= initrd_fit(buf.base, buf.base+buf.len); + } while (regs.ebx.l); + + return rv; +} + +static int probe_memory_e801(void) +{ + com32sys_t regs; + uint64_t base, end; + + memset(®s, 0, sizeof regs); + regs.eax.w[0] = 0xe801; + intcall(0x15, ®s, ®s); + + if (regs.eflags.l & EFLAGS_CF) + return -1; /* No e801 */ + + if (regs.eax.w[0] < 15*1024) + end = (uint64_t)(regs.eax.w[0] << 10) + 0x100000; + else + end = (uint64_t)(regs.ebx.w[0] << 16) + 0x1000000; + + return initrd_fit(0x100000, end); +} + +static int probe_memory_88(void) +{ + com32sys_t regs; + + memset(®s, 0, sizeof regs); + regs.eax.b[1] = 0x88; + intcall(0x15, ®s, ®s); + + if (regs.eflags.l & EFLAGS_CF) + return -1; + + return initrd_fit(0x100000, (regs.eax.w[0] << 10)+0x100000); +} + +static int place_initrd(void) +{ + int rv; + + rv = probe_memory_e820(); + if (!rv) + return 0; + + rv = probe_memory_e801(); + if (!rv) + return 0; + + return probe_memory_88(); +} + +int main(void) +{ + extern struct startup_info _start[]; /* Cute hack, eh? */ + struct startup_info *info = _start - 1; + struct setup_header *hdr = (void *)(info->setup_addr + 0x1f1); + + if (info->rd_len) { + initrd_len = info->rd_len; + max_addr = info->rd_maxaddr; + + if (place_initrd()) + return -1; + + /* Move the initrd into place */ + printf("Moving initrd: 0x%08x -> 0x%08x (0x%08x bytes)\n", + info->rd_addr, initrd_addr, info->rd_len); + + memmove((void *)initrd_addr, (void *)info->rd_addr, + info->rd_len); + + hdr->ramdisk_image = initrd_addr; + } + + jump_to_kernel(info->setup_addr >> 4, + info->cmdline_addr - info->setup_addr); + return -1; /* Shouldn't return... */ +} |