direct-io.hg

view tools/misc/mbootpack/mbootpack.c @ 7477:5a7baecb1c70

Fix an issue for passing arguement from control panel to deivce model
for some arguemnt like 'localtime', 'isa', device model need an argument
"-localtime", instead of "-localtime 1"
Signed-off-by: Xiaofeng Ling <xiaofeng.ling@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Sun Oct 23 16:51:47 2005 +0100 (2005-10-23)
parents 06d84bf87159
children 2e58022889a5
line source
1 /*
2 * mbootpack.c
3 *
4 * Takes a multiboot image, command-line and modules, and repackages
5 * them as if they were a linux kernel. Only supports a subset of
6 * the multiboot info page options (enough to boot the Xen hypervisor).
7 *
8 * Copyright (C) 2003-2004 Tim Deegan (tjd21@cl.cam.ac.uk)
9 *
10 * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * $Id: mbootpack.c,v 1.3 2005/03/23 10:38:36 tjd21 Exp tjd21 $
28 *
29 */
31 #define _GNU_SOURCE
32 #include "mbootpack.h"
34 #include <assert.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <getopt.h>
40 #include <elf.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/mman.h>
45 #include <asm/page.h>
47 /* From GNU GRUB */
48 #include "mb_header.h"
49 #include "mb_info.h"
52 /*
53 * The plan: Marshal up the multiboot modules and strings as if we
54 * were loading them into memory on a fresh ix86 PC. Attach
55 * a linux bzImage header to the front, which sets up the machine
56 * appropriately and then jumps to the kernel entry address.
57 *
58 * The memory map will be made up roughly like so:
59 *
60 * =============
61 * multiboot information (mbi) struct
62 * -------
63 * kernel command line
64 * -------
65 * bootloader name
66 * -------
67 * module command lines
68 * -------
69 * module information structs
70 * =============
71 * (memory hole)
72 * =============
73 * kernel
74 * -------------
75 * module 1
76 * -------------
77 * module 2
78 * -------------
79 * .
80 * .
81 * .
82 *
83 * ==============
84 *
85 *
86 * For allocation of memory we assume that the target machine has 'low'
87 * memory from 0 to 640K and 'high' memory starting at 1M. We allocate
88 * the kernel first, wherever it wants to be. After that, sections
89 * are added at the next available aligned address, always in the order
90 * given above, and skipping the memory hole at 640K. Allocated sections
91 * are stored in a linked list of buffers.
92 *
93 * Re-packaging as a bzImage file happens in buildimage.c
94 *
95 */
97 /* Version */
98 static const char version_string[] = "mbootpack " MBOOTPACK_VERSION_STRING;
100 /* Flags */
101 int quiet = 0;
103 /* How much of the start of a kernel we read looking for headers.
104 * Must be >= MULTIBOOT_SEARCH */
105 #define HEADERBUF_SIZE MULTIBOOT_SEARCH
108 /* Linked list of loaded sections, and a pointer to the next
109 * available space (i.e. just above the highest allocation so far). */
110 static section_t *sections = NULL;
111 static section_t *last_section = NULL;
112 static address_t next_free_space = 0;
114 static void usage(void)
115 /* If we don't understand the command-line options */
116 {
117 printf(
118 "Usage: mbpack [OPTIONS] kernel-image\n\n"
119 " -h --help Print this text.\n"
120 " -q --quiet Only output errors and warnings.\n"
121 " -o --output=filename Output to filename (default \"bzImage\").\n"
122 " -M --multiboot-output Produce a multiboot kernel, not a bzImage\n"
123 " (sets default output file to \"mbImage\").\n"
124 " -c --command-line=STRING Set the kernel command line (DEPRECATED!).\n"
125 " -m --module=\"MOD arg1 arg2...\" Load module MOD with arguments \"arg1...\"\n"
126 " (can be used multiple times).\n"
127 "\n");
128 exit(1);
129 }
132 static void place_kernel_section(address_t start, long int size)
133 /* Place the kernel in memory, checking for the memory hole. */
134 {
135 if (start >= MEM_HOLE_END) {
136 /* Above the memory hole: easy */
137 next_free_space = MAX(next_free_space, start + size);
138 if (!quiet) {
139 printf("Placed kernel section (%p+%p)\n", start, size);
140 }
141 return;
142 }
144 if (start >= MEM_HOLE_START) {
145 /* In the memory hole. Not so good */
146 printf("Fatal: kernel load address (%p) is in the memory hole.\n",
147 start);
148 exit(1);
149 }
151 if (start + size > MEM_HOLE_START) {
152 /* Too big for low memory */
153 printf("Fatal: kernel (%p+%p) runs into the memory hole.\n",
154 start, size);
155 exit(1);
156 }
158 /* Kernel loads below the memory hole */
159 next_free_space = MAX(next_free_space, start + size);
161 if (!quiet) {
162 printf("Placed kernel section (%p+%p)\n", start, size);
163 }
164 }
167 static address_t place_section(long int size, int align)
168 /* Find the next available place for this section.
169 * "align" must be a power of 2 */
170 {
171 address_t start;
172 assert(next_free_space != 0);
173 assert(((~align + 1) & align) == align);
175 start = ROUNDUP_P2(next_free_space, align);
177 /* Check that we don't hit the memory hole */
178 if (start < MEM_HOLE_END && (start + size) > MEM_HOLE_START)
179 start = ROUNDUP_P2(MEM_HOLE_END, align);
181 next_free_space = start + size;
183 if (!quiet) {
184 printf("Placed section (%p+%p), align=%p\n",
185 start, size, align);
186 }
187 return start;
188 }
193 static address_t load_kernel(const char *filename)
194 /* Load an elf32/multiboot kernel from this file
195 * Returns the entry address for the kernel. */
196 {
197 unsigned int i;
198 address_t start;
199 size_t len;
200 long int size, loadsize;
201 FILE *fp;
202 char *buffer;
203 section_t *sec, *s;
204 Elf32_Ehdr *ehdr;
205 Elf32_Phdr *phdr;
206 struct multiboot_header *mbh;
207 struct stat sb;
209 static char headerbuf[HEADERBUF_SIZE];
211 /* Stat and open the file */
212 if (stat(filename, &sb) != 0) {
213 printf("Fatal: cannot stat %s: %s\n", filename, strerror(errno));
214 exit(1);
215 }
216 if ((fp = fopen(filename, "r")) == NULL) {
217 printf("Fatal: cannot open %s: %s\n", filename, strerror(errno));
218 exit(1);
219 }
221 /* Load the first 8k of the file */
222 if (fseek(fp, 0, SEEK_SET) < 0) {
223 printf("Fatal: seek error in %s: %s\n", filename, strerror(errno));
224 exit(1);
225 }
226 if ((len = fread(headerbuf, 1, HEADERBUF_SIZE, fp))
227 < HEADERBUF_SIZE)
228 {
229 if (feof(fp)) /* Short file */
230 {
231 if (len < 12) {
232 printf("Fatal: %s is too short to be a multiboot file.",
233 filename);
234 exit(1);
235 }
236 } else {
237 printf("Fatal: read error in %s: %s\n", filename, strerror(errno));
238 exit(1);
239 }
240 }
242 /* Sanity-check: is this file compressed? */
243 if ((headerbuf[0] == '\037' &&
244 (headerbuf[1] == '\235' /* .Z */ ||
245 headerbuf[1] == '\213' /* .gz */)) ||
246 (headerbuf[0] == 'B' && headerbuf[1] == 'Z') /* .bz[2] */) {
247 printf("Warning: %s looks like a compressed file.\n"
248 " You should uncompress it first!\n", filename);
249 }
251 /* Now look for a multiboot header */
252 for (i = 0; i <= MIN(len - 12, MULTIBOOT_SEARCH - 12); i += 4)
253 {
254 mbh = (struct multiboot_header *)(headerbuf + i);
255 if (eswap(mbh->magic) != MULTIBOOT_MAGIC
256 || ((eswap(mbh->magic)+eswap(mbh->flags)+eswap(mbh->checksum))
257 & 0xffffffff))
258 {
259 /* Not a multiboot header */
260 continue;
261 }
262 if (eswap(mbh->flags) & MULTIBOOT_UNSUPPORTED) {
263 /* Requires options we don't support */
264 printf("Fatal: found a multiboot header, but it "
265 "requires multiboot options that I\n"
266 "don't understand. Sorry.\n");
267 exit(1);
268 }
269 if (eswap(mbh->flags) & MULTIBOOT_VIDEO_MODE) {
270 /* Asked for screen mode information */
271 /* XXX carry on regardless */
272 printf("Warning: found a multiboot header which asks "
273 "for screen mode information.\n"
274 " This kernel will NOT be given valid"
275 "screen mode information at boot time.\n");
276 }
277 /* This kernel will do: place and load it */
279 if (eswap(mbh->flags) & MULTIBOOT_AOUT_KLUDGE) {
281 /* Load using the offsets in the multiboot header */
282 if(!quiet)
283 printf("Loading %s using multiboot header.\n", filename);
285 /* How much is there? */
286 start = eswap(mbh->load_addr);
287 if (eswap(mbh->load_end_addr) != 0)
288 loadsize = eswap(mbh->load_end_addr) - eswap(mbh->load_addr);
289 else
290 loadsize = sb.st_size;
292 /* How much memory will it take up? */
293 if (eswap(mbh->bss_end_addr) != 0)
294 size = eswap(mbh->bss_end_addr) - eswap(mbh->load_addr);
295 else
296 size = loadsize;
298 if (loadsize > size) {
299 printf("Fatal: can't load %i bytes of kernel into %i bytes "
300 "of memory.\n", loadsize, size);
301 exit(1);
302 }
304 /* Does it fit where it wants to be? */
305 place_kernel_section(start, size);
307 /* Load the kernel */
308 if ((buffer = malloc(size)) == NULL) {
309 printf("Fatal: malloc() for kernel load failed: %s\n",
310 strerror(errno));
311 exit(1);
312 }
313 if ((fread(buffer, loadsize, 1, fp)) != 1) {
314 printf("Fatal: cannot read %s: %s\n",
315 filename, strerror(errno));
316 exit(1);
317 }
318 fclose(fp);
320 /* Clear the kernel BSS */
321 memset(buffer + loadsize, 0, size - loadsize);
323 /* Start off the linked list of sections */
324 if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) {
325 printf("Fatal: malloc() for section_t failed: %s\n",
326 strerror(errno));
327 exit(1);
328 }
329 sec->buffer = buffer;
330 sec->start = start;
331 sec->size = size;
332 sec->next = NULL;
333 sec->prev = NULL;
334 sections = sec;
335 last_section = sec;
337 /* Done. */
338 if (!quiet) printf("Loaded kernel from %s\n", filename);
339 return eswap(mbh->entry_addr);
341 } else {
343 /* Now look for an ELF32 header */
344 ehdr = (Elf32_Ehdr *)headerbuf;
345 if (*(unsigned long *)ehdr != eswap(0x464c457f)
346 || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
347 || ehdr->e_ident[EI_CLASS] != ELFCLASS32
348 || eswap(ehdr->e_machine) != EM_386)
349 {
350 printf("Fatal: kernel has neither ELF32/x86 nor multiboot load"
351 " headers.\n");
352 exit(1);
353 }
354 if (eswap(ehdr->e_phoff) + eswap(ehdr->e_phnum)*sizeof(*phdr)
355 > HEADERBUF_SIZE) {
356 /* Don't expect this will happen with sane kernels */
357 printf("Fatal: too much ELF for me. Try increasing "
358 "HEADERBUF_SIZE in mbootpack.\n");
359 exit(1);
360 }
361 if (eswap(ehdr->e_phoff) + eswap(ehdr->e_phnum)*sizeof (*phdr)
362 > len) {
363 printf("Fatal: malformed ELF header overruns EOF.\n");
364 exit(1);
365 }
366 if (eswap(ehdr->e_phnum) <= 0) {
367 printf("Fatal: ELF kernel has no program headers.\n");
368 exit(1);
369 }
371 if(!quiet)
372 printf("Loading %s using ELF header.\n", filename);
374 if (eswap(ehdr->e_type) != ET_EXEC
375 || eswap(ehdr->e_version) != EV_CURRENT
376 || eswap(ehdr->e_phentsize) != sizeof (Elf32_Phdr)) {
377 printf("Warning: funny-looking ELF header.\n");
378 }
379 phdr = (Elf32_Phdr *)(headerbuf + eswap(ehdr->e_phoff));
381 /* Obey the program headers to load the kernel */
382 for(i = 0; i < eswap(ehdr->e_phnum); i++) {
384 start = eswap(phdr[i].p_paddr);
385 size = eswap(phdr[i].p_memsz);
386 if (eswap(phdr[i].p_type) != PT_LOAD)
387 loadsize = 0;
388 else
389 loadsize = MIN((long int)eswap(phdr[i].p_filesz), size);
391 if ((buffer = malloc(size)) == NULL) {
392 printf("Fatal: malloc() for kernel load failed: %s\n",
393 strerror(errno));
394 exit(1);
395 }
397 /* Place the section where it wants to be */
398 place_kernel_section(start, size);
400 /* Load section from file */
401 if (loadsize > 0) {
402 if (fseek(fp, eswap(phdr[i].p_offset), SEEK_SET) != 0) {
403 printf("Fatal: seek failed in %s\n",
404 strerror(errno));
405 exit(1);
406 }
407 if ((fread(buffer, loadsize, 1, fp)) != 1) {
408 printf("Fatal: cannot read %s: %s\n",
409 filename, strerror(errno));
410 exit(1);
411 }
412 }
414 /* Clear the rest of the buffer */
415 memset(buffer + loadsize, 0, size - loadsize);
417 /* Add this section to the list (keeping it ordered) */
418 if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) {
419 printf("Fatal: malloc() for section_t failed: %s\n",
420 strerror(errno));
421 exit(1);
422 }
423 sec->buffer = buffer;
424 sec->start = start;
425 sec->size = size;
427 for(s = sections; s; s = s->next) {
428 if (s->start > start) {
429 sec->next = s;
430 if (s->prev == NULL) {
431 /* sec becomes the new first item */
432 s->prev = sec;
433 sections = sec;
434 } else {
435 /* sec goes between s->prev and s */
436 sec->prev = s->prev;
437 sec->prev->next = sec;
438 s->prev = sec;
439 }
440 break;
441 }
442 }
443 if (s == NULL) {
444 /* sec becomes the new last item */
445 sec->next = NULL;
446 sec->prev = last_section;
447 if (last_section) {
448 last_section->next = sec;
449 } else {
450 sections = sec;
451 }
452 last_section = sec;
453 }
454 }
456 /* Done! */
457 if (!quiet) printf("Loaded kernel from %s\n", filename);
458 return eswap(ehdr->e_entry);
459 }
461 }
463 /* This is not a multiboot kernel */
464 printf("Fatal: %s is not a multiboot kernel.\n", filename);
465 exit(1);
466 }
471 int main(int argc, char **argv)
472 {
473 char *buffer, *imagename, *command_line, *p;
474 char *mod_filename, *mod_command_line, *mod_clp;
475 char *out_filename;
476 section_t *sec;
477 FILE *fp;
478 struct stat sb;
479 struct multiboot_info *mbi;
480 struct mod_list *modp;
481 address_t start, kernel_entry;
482 long int size, mod_command_line_space, command_line_len;
483 int modules, opt, mbi_reloc_offset, make_multiboot;
485 static const char short_options[] = "hc:m:o:qM";
486 static const struct option options[] = {
487 { "help", 0, 0, 'h' },
488 { "command-line", 1, 0, 'c' },
489 { "append", 1, 0, 'c' },
490 { "module", 1, 0, 'm' },
491 { "output", 1, 0, 'o' },
492 { "quiet", 0, 0, 'q' },
493 { 0, 0, 0, 0 },
494 };
496 /* Parse the command line */
497 out_filename = NULL;
498 command_line = "";
499 command_line_len = 0;
500 modules = 0;
501 mod_command_line_space = 0;
502 while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1)
503 {
504 switch(opt) {
505 case 'c':
506 command_line = optarg;
507 break;
508 case 'm':
509 modules++;
510 mod_command_line_space += strlen(optarg) + 1;
511 break;
512 case 'o':
513 out_filename = optarg;
514 break;
515 case 'q':
516 quiet = 1;
517 break;
518 case 'h':
519 case '?':
520 default:
521 usage();
522 }
523 }
524 imagename = argv[optind];
525 if (!imagename || strlen(imagename) == 0) usage();
526 command_line_len = strlen(command_line) + strlen(imagename) + 2;
527 /* Leave space to overwritethe command-line at boot time */
528 command_line_len = MAX(command_line_len, CMD_LINE_SPACE);
529 if (!out_filename) out_filename = "bzImage";
531 /* Place and load the kernel */
532 kernel_entry = load_kernel(imagename);
533 assert(sections != NULL);
534 assert(last_section != NULL);
535 assert(next_free_space != 0);
537 /* Next section is all the metadata between kernel and modules */
538 size = ((((sizeof (struct multiboot_info)
539 + command_line_len
540 + strlen(version_string) + 1
541 + mod_command_line_space)
542 + 3 ) & ~3)
543 + modules * sizeof (struct mod_list));
544 /* Locate this section after the setup sectors, in *low* memory */
545 start = place_mbi(size);
547 if ((buffer = malloc(size)) == NULL) {
548 printf("Fatal: malloc() for boot metadata failed: %s\n",
549 strerror(errno));
550 exit(1);
551 }
553 if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) {
554 printf("Fatal: malloc() for section_t failed: %s\n",
555 strerror(errno));
556 exit(1);
557 }
558 sec->buffer = buffer;
559 sec->start = start;
560 sec->size = size;
561 sec->next = NULL;
562 sec->prev = last_section;
563 last_section->next = sec;
564 last_section = sec;
566 /* Multiboot info struct */
567 mbi = (struct multiboot_info *)buffer;
568 memset(buffer, 0, sizeof (struct multiboot_info));
569 mbi_reloc_offset = start - (address_t)buffer;
571 /* Command line */
572 p = (char *)(mbi + 1);
573 sprintf(p, "%s %s", imagename, command_line);
574 mbi->cmdline = eswap(((address_t)p) + mbi_reloc_offset);
575 p += command_line_len;
577 /* Bootloader ID */
578 sprintf(p, version_string);
579 mbi->boot_loader_name = eswap(((address_t)p) + mbi_reloc_offset);
580 p += strlen(version_string) + 1;
582 /* Next is space for the module command lines */
583 mod_clp = p;
585 /* Last come the module info structs */
586 modp = (struct mod_list *)
587 ((((address_t)p + mod_command_line_space) + 3) & ~3);
588 mbi->mods_count = eswap(modules);
589 mbi->mods_addr = eswap(((address_t)modp) + mbi_reloc_offset);
591 /* Memory information will be added at boot time, by setup.S
592 * or trampoline.S. */
593 mbi->flags = eswap(MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME);
596 /* Load the modules */
597 if (modules) {
598 mbi->flags = eswap(eswap(mbi->flags) | MB_INFO_MODS);
600 /* Go back and parse the module command lines */
601 optind = opterr = 1;
602 while((opt = getopt_long(argc, argv,
603 short_options, options, 0)) != -1)
604 {
605 if (opt != 'm') continue;
607 /* Split module filename from command line */
608 mod_command_line = mod_filename = optarg;
609 if ((p = strchr(mod_filename, ' ')) != NULL) {
610 /* See as I discard the 'const' modifier */
611 *p = '\0';
612 }
614 /* Find space for it */
615 if (stat(mod_filename, &sb) != 0) {
616 printf("Fatal: cannot stat %s: %s\n",
617 mod_filename, strerror(errno));
618 exit(1);
619 }
620 size = sb.st_size;
621 start = place_section(size, X86_PAGE_SIZE);
622 /* XXX should be place_section(size, 4) if the MBH hasn't got
623 * XXX MULTIBOOT_PAGE_ALIGN set, but that breaks Xen */
625 /* Load it */
626 if ((buffer = malloc(sb.st_size)) == NULL) {
627 printf("Fatal: malloc failed for module load: %s\n",
628 strerror(errno));
629 exit(1);
630 }
631 if ((fp = fopen(mod_filename, "r")) == NULL) {
632 printf("Fatal: cannot open %s: %s\n",
633 mod_filename, strerror(errno));
634 exit(1);
635 }
636 if ((fread(buffer, sb.st_size, 1, fp)) != 1) {
637 printf("Fatal: cannot read %s: %s\n",
638 mod_filename, strerror(errno));
639 exit(1);
640 }
641 fclose(fp);
643 /* Sanity-check: is this file compressed? */
644 if ((buffer[0] == '\037' &&
645 (buffer[1] == '\235' /* .Z */ ||
646 buffer[1] == '\213' /* .gz */)) ||
647 (buffer[0] == 'B' && buffer[1] == 'Z') /* .bz[2] */) {
648 printf("Warning: %s looks like a compressed file.\n",
649 mod_filename);
650 }
652 if (!quiet) printf("Loaded module from %s\n", mod_filename);
654 /* Restore the command line to its former glory */
655 if (p != NULL) *p = ' ';
657 /* Fill in the module info struct */
658 modp->mod_start = eswap(start);
659 modp->mod_end = eswap(start + size);
660 modp->cmdline = eswap((address_t)mod_clp + mbi_reloc_offset);
661 modp->pad = eswap(0);
662 modp++;
664 /* Store the module command line */
665 sprintf(mod_clp, "%s", mod_command_line);
666 mod_clp += strlen(mod_clp) + 1;
668 /* Add the section to the list */
669 if ((sec = (section_t *)malloc(sizeof (section_t))) == NULL) {
670 printf("Fatal: malloc() for section_t failed: %s\n",
671 strerror(errno));
672 exit(1);
673 }
674 sec->buffer = buffer;
675 sec->start = start;
676 sec->size = size;
677 sec->next = NULL;
678 sec->prev = last_section;
679 last_section->next = sec;
680 last_section = sec;
682 }
684 }
686 /* Everything is placed and loaded. Now we package it all up
687 * as a bzImage */
688 if ((fp = fopen(out_filename, "w")) == NULL) {
689 printf("Fatal: cannot open %s: %s\n", out_filename, strerror(errno));
690 exit(1);
691 }
692 make_bzImage(sections,
693 kernel_entry,
694 ((address_t)mbi) + mbi_reloc_offset,
695 fp);
696 fclose(fp);
698 /* Success! */
699 if(!quiet) printf("Finished.\n");
700 return 0;
701 }
703 /*
704 * EOF (mbootpack.c)
705 */