1 /* ----------------------------------------------------------------------- *
3 * Copyright 2008 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8 * Boston MA 02110-1301, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
16 * Actually wrap the kernel image...
29 #include <sys/types.h>
34 #include "wraplinux.h"
37 extern uint32_t reloc_size;
39 int wrap_kernel(const char *kernel_file, const char *cmdline,
40 const struct string_list *initrd_list, FILE *out)
48 size_t kernel_len = 0;
50 struct segment srel, ssup, scmd, skrn;
51 struct startup_info *info = (void *)reloc;
52 struct setup_header *hdr;
59 const struct string_list *ip;
60 int setup_ver; /* Setup protocol version */
61 int setup_space; /* How much space for the setup */
63 /* Process the kernel file */
65 kernel_fd = open(kernel_file, O_RDONLY);
69 if (fstat(kernel_fd, &st))
72 kernel_len = st.st_size;
74 if (kernel_len < 1024)
77 kernel = mmap(NULL, kernel_len, PROT_READ|PROT_WRITE, MAP_PRIVATE,
79 if (kernel == MAP_FAILED) {
84 /* Pick apart the header... */
86 hdr = (struct setup_header *)(kernel + 0x1f1);
87 setup_len = (hdr->setup_sects + 1) << 9;
89 setup_len += 2048; /* Really old kernel */
91 if (rdle32(&hdr->header) != 0x53726448)
92 setup_ver = 0; /* Ancient kernel */
94 setup_ver = rdle16(&hdr->version);
96 if (setup_ver >= 0x200 && (hdr->loadflags & LOADED_HIGH)) {
97 skrn.address = 0x100000;
98 ssup.address = 0x10000;
99 setup_space = setup_ver >= 0x202 ? 0x10000 : 0xa000;
101 skrn.address = 0x10000;
102 ssup.address = 0x90000;
103 setup_space = 0xa000;
106 if (setup_ver <= 0x200)
107 initrd_list = NULL; /* No initrd for ancient kernel */
109 /* Process the initrd file(s) */
112 for (ip = initrd_list; ip; ip = ip->next)
116 ird = calloc(ninitrd, sizeof *ird);
122 for (ip = initrd_list, i = 0; ip; ip = ip->next, i++) {
123 ird[i].fd = open(ip->str, O_RDONLY);
127 if (fstat(ird[i].fd, &st))
130 ird[i].seg.length = st.st_size;
131 ird[i].seg.data = mmap(NULL, ird[i].seg.length, PROT_READ,
132 MAP_SHARED, ird[i].fd, 0);
133 if (ird[i].seg.data == MAP_FAILED) {
134 ird[i].seg.data = NULL;
138 /* We need to pad the space between initrds to a 4-byte boundary.
139 This is safe because an mmap is always padded with zero
140 to a page boundary... */
142 ird[i].seg.length = (ird[i].seg.length + 3) & ~3;
144 initrd_len += ird[i].seg.length;
147 /* Segment: relocation code */
148 /* We put this immediately after the kernel setup, in the memory
149 which will be reclaimed for setup. */
151 srel.address = (ssup.address + setup_len + 15) & ~15;
152 srel.length = reloc_size;
153 srel.sh_type = SHT_PROGBITS;
154 srel.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
158 /* Segment: Linux kernel setup */
160 ssup.length = setup_len;
161 ssup.sh_type = SHT_PROGBITS;
162 ssup.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
165 if (setup_ver >= 0x200)
166 hdr->type_of_loader = 0xff; /* "Other modern loader" */
168 /* Segment: kernel command line */
170 scmd.length = strlen(cmdline)+1;
171 scmd.address = (ssup.address + setup_space - scmd.length) & ~15;
172 if (srel.address + reloc_size > scmd.address) {
173 /* Uh-oh, we're short on space... push the command line
175 scmd.address = (srel.address + reloc_size + 15) & ~15;
177 scmd.sh_type = SHT_PROGBITS;
178 scmd.sh_flags = SHF_ALLOC;
179 scmd.name = "cmdline";
181 if (setup_ver >= 0x202) {
182 wrle32(scmd.address, &hdr->cmd_line_ptr);
184 /* Old-style command line protocol */
185 wrle16(0xA33F, (uint16_t *)(kernel+0x20));
186 wrle16(scmd.address - ssup.address, (uint16_t *)(kernel+0x22));
188 if (setup_ver >= 0x201) {
189 wrle16(scmd.address - ssup.address - 0x200, &hdr->heap_end_ptr);
190 hdr->loadflags |= CAN_USE_HEAP;
193 /* Segment: Linux kernel proper */
194 skrn.next = ninitrd ? &ird[0].seg : NULL;
195 skrn.length = kernel_len - setup_len;
196 skrn.sh_type = SHT_PROGBITS;
197 skrn.sh_flags = SHF_ALLOC;
198 skrn.name = "kernel";
199 skrn.data = kernel + setup_len;
201 /* Additional segments: initrd */
202 if (skrn.address < 0x100000)
203 initrd_addr = 0x100000;
205 initrd_addr = (skrn.address + skrn.length + 3) & ~3;
207 for (i = 0; i < ninitrd; i++) {
210 ird[i].seg.next = (i < ninitrd-1) ? &ird[i+1].seg : 0;
211 ird[i].seg.address = initrd_addr;
212 ird[i].seg.sh_type = SHT_PROGBITS;
213 ird[i].seg.sh_flags = SHF_ALLOC;
214 asprintf(&name, "initrd.%d", i);
215 ird[i].seg.name = name;
217 initrd_addr += ird[i].seg.length;
219 if (setup_ver >= 0x200)
220 wrle32(initrd_len, &hdr->ramdisk_size);
222 /* Set up the startup info */
223 wrle32(ninitrd ? ird[0].seg.address : 0, &info->rd_addr);
224 wrle32(initrd_len, &info->rd_len);
225 if (setup_ver >= 0x203)
226 wrle32(rdle32(&hdr->initrd_addr_max), &info->rd_maxaddr);
228 wrle32(0x37ffffff, &info->rd_maxaddr);
229 wrle32(ssup.address, &info->setup_addr);
230 wrle32(scmd.address, &info->cmdline_addr);
232 rv = opt.output(&srel, srel.address + sizeof *info, out);
236 for (i = 0; i < ninitrd; i++) {
238 munmap((void *)ird[i].seg.data, ird[i].seg.length);
246 munmap(kernel, kernel_len);