#include "le.h"
#include "wraplinux.h"
-extern const char reloc[];
+extern char reloc[];
extern uint32_t reloc_size;
int wrap_kernel(const char *kernel_file, const char *cmdline,
char *kernel = NULL;
size_t kernel_len = 0;
struct stat st;
- struct segment srel, sinf, ssup, scmd, skrn;
- struct startup_info info;
+ struct segment srel, ssup, scmd, skrn;
+ struct startup_info *info = (void *)reloc;
struct setup_header *hdr;
size_t setup_len;
size_t initrd_len;
int ninitrd = 0;
int i;
const struct string_list *ip;
+ int setup_ver; /* Setup protocol version */
+ int setup_space; /* How much space for the setup */
- ninitrd = 0;
- for (ip = initrd_list; ip; ip = ip->next)
- ninitrd++;
-
- if (ninitrd) {
- ird = calloc(ninitrd, sizeof *ird);
- if (!ird)
- goto err;
- }
+ /* Process the kernel file */
kernel_fd = open(kernel_file, O_RDONLY);
if (kernel_fd < 0)
goto err;
}
+ /* Pick apart the header... */
+
+ hdr = (struct setup_header *)(kernel + 0x1f1);
+ setup_len = (hdr->setup_sects + 1) << 9;
+ if (setup_len == 512)
+ setup_len += 2048; /* Really old kernel */
+
+ if (rdle32(&hdr->header) != 0x53726448)
+ setup_ver = 0; /* Ancient kernel */
+ else
+ setup_ver = rdle16(&hdr->version);
+
+ if (setup_ver >= 0x200 && (hdr->loadflags & LOADED_HIGH)) {
+ skrn.address = 0x100000;
+ ssup.address = 0x10000;
+ setup_space = setup_ver >= 0x202 ? 0x10000 : 0xa000;
+ } else {
+ skrn.address = 0x10000;
+ ssup.address = 0x90000;
+ setup_space = 0xa000;
+ }
+
+ if (setup_ver <= 0x200)
+ initrd_list = NULL; /* No initrd for ancient kernel */
+
+ /* Process the initrd file(s) */
+
+ ninitrd = 0;
+ for (ip = initrd_list; ip; ip = ip->next)
+ ninitrd++;
+
+ if (ninitrd) {
+ ird = calloc(ninitrd, sizeof *ird);
+ if (!ird)
+ goto err;
+ }
+
initrd_len = 0;
for (ip = initrd_list, i = 0; ip; ip = ip->next, i++) {
ird[i].fd = open(ip->str, O_RDONLY);
initrd_len += ird[i].seg.length;
}
- hdr = (struct setup_header *)(kernel + 0x1f1);
- setup_len = (hdr->setup_sects + 1) << 9;
- if (setup_len == 512)
- setup_len += 2048; /* Really old kernel */
-
- /* Segment 1 is the relocation code */
+ /* Segment: relocation code */
+ /* We put this immediately after the kernel setup, in the memory
+ which will be reclaimed for setup. */
srel.next = &ssup;
- srel.address = 0xc000; /* Can be adjusted (align 16),
- code is relocatable */
+ srel.address = (ssup.address + setup_len + 15) & ~15;
srel.length = reloc_size;
srel.sh_type = SHT_PROGBITS;
srel.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
srel.name = "reloc";
srel.data = reloc;
- /* Segment 0 is the struct startup_info */
- sinf.next = &srel;
- sinf.address = srel.address - sizeof info;
- sinf.length = sizeof info;
- sinf.sh_type = SHT_PROGBITS;
- sinf.sh_flags = SHF_ALLOC;
- sinf.name = "startup_info";
- sinf.data = &info;
-
- /* Segment 2 is the Linux kernel setup */
+ /* Segment: Linux kernel setup */
ssup.next = &scmd;
- ssup.address = 0x10000; /* XXX */
ssup.length = setup_len;
ssup.sh_type = SHT_PROGBITS;
ssup.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
ssup.name = "setup";
ssup.data = kernel;
- hdr->type_of_loader = 0xff; /* "Other modern loader" */
+ if (setup_ver >= 0x200)
+ hdr->type_of_loader = 0xff; /* "Other modern loader" */
- /* Segment 3 is the kernel command line */
+ /* Segment: kernel command line */
scmd.next = &skrn;
- scmd.address = 0x20000; /* XXX */
scmd.length = strlen(cmdline)+1;
+ scmd.address = (ssup.address + setup_space - scmd.length) & ~15;
+ if (srel.address + reloc_size > scmd.address) {
+ /* Uh-oh, we're short on space... push the command line
+ higher. */
+ scmd.address = (srel.address + reloc_size + 15) & ~15;
+ }
scmd.sh_type = SHT_PROGBITS;
scmd.sh_flags = SHF_ALLOC;
scmd.name = "cmdline";
scmd.data = cmdline;
- wrle32(scmd.address, &hdr->cmd_line_ptr); /* XXX */
- wrle16(scmd.address - ssup.address - 0x200, &hdr->heap_end_ptr);
- hdr->loadflags |= CAN_USE_HEAP;
+ if (setup_ver >= 0x202) {
+ wrle32(scmd.address, &hdr->cmd_line_ptr);
+ } else {
+ /* Old-style command line protocol */
+ wrle16(0xA33F, (uint16_t *)(kernel+0x20));
+ wrle16(scmd.address - ssup.address, (uint16_t *)(kernel+0x22));
+ }
+ if (setup_ver >= 0x201) {
+ wrle16(scmd.address - ssup.address - 0x200, &hdr->heap_end_ptr);
+ hdr->loadflags |= CAN_USE_HEAP;
+ }
- /* Segment 4 is the Linux kernel proper */
+ /* Segment: Linux kernel proper */
skrn.next = ninitrd ? &ird[0].seg : NULL;
- skrn.address = 0x100000; /* XXX */
skrn.length = kernel_len - setup_len;
skrn.sh_type = SHT_PROGBITS;
skrn.sh_flags = SHF_ALLOC;
skrn.name = "kernel";
skrn.data = kernel + setup_len;
- /* Segment 5 is the initrd */
+ /* Additional segments: initrd */
if (skrn.address < 0x100000)
initrd_addr = 0x100000;
else
initrd_addr += ird[i].seg.length;
}
- wrle32(initrd_len, &hdr->ramdisk_size);
+ if (setup_ver >= 0x200)
+ wrle32(initrd_len, &hdr->ramdisk_size);
/* Set up the startup info */
- wrle32(ninitrd ? ird[0].seg.address : 0, &info.rd_addr);
- wrle32(initrd_len, &info.rd_len);
- wrle32(rdle32(&hdr->initrd_addr_max), &info.rd_maxaddr); /* XXX */
- wrle32(ssup.address, &info.setup_addr);
- wrle32(scmd.address, &info.cmdline_addr);
+ wrle32(ninitrd ? ird[0].seg.address : 0, &info->rd_addr);
+ wrle32(initrd_len, &info->rd_len);
+ if (setup_ver >= 0x203)
+ wrle32(rdle32(&hdr->initrd_addr_max), &info->rd_maxaddr);
+ else
+ wrle32(0x37ffffff, &info->rd_maxaddr);
+ wrle32(ssup.address, &info->setup_addr);
+ wrle32(scmd.address, &info->cmdline_addr);
- rv = output_elf(&sinf, srel.address, out);
+ rv = output_elf(&srel, srel.address + sizeof *info, out);
err:
if (ird) {
return probe_memory_88();
}
+extern char _end[];
+/* This structure is hacked by the installer */
+static const struct startup_info startup_info
+__attribute__((section(".startupinfo"))) =
+{
+ .reloc_size = (uint32_t)&_end,
+};
+
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);
+ struct setup_header *hdr = (void *)(startup_info.setup_addr + 0x1f1);
- if (info->rd_len) {
- initrd_len = info->rd_len;
- max_addr = info->rd_maxaddr;
+ if (startup_info.rd_len) {
+ initrd_len = startup_info.rd_len;
+ max_addr = startup_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);
+ startup_info.rd_addr, initrd_addr, startup_info.rd_len);
- memmove((void *)initrd_addr, (void *)info->rd_addr,
- info->rd_len);
+ memmove((void *)initrd_addr, (void *)startup_info.rd_addr,
+ startup_info.rd_len);
hdr->ramdisk_image = initrd_addr;
}
- jump_to_kernel(info->setup_addr >> 4,
- info->cmdline_addr - info->setup_addr);
+ jump_to_kernel(startup_info.setup_addr >> 4,
+ startup_info.cmdline_addr - startup_info.setup_addr);
return -1; /* Shouldn't return... */
}