1 /* boot.c - Boot image composition
3 * Copyright 1992-1998 Werner Almesberger
4 * Copyright 1999-2007 John Coffman
5 * Copyright 2009-2011 Joachim Wiedorn
8 * Licensed under the terms contained in the file 'COPYING'
9 * in the source directory.
31 #include "partition.h"
36 * The number of sectors between the 15M memory hole and 1M
37 * is the largest size for a bzImage kernel + initrd = 14M
38 * if "small-memory" is set. To use larger kernel + initrd
39 * use option "large-memory" or "mem=XXX" raises the limit
41 #define HIGH_SECTORS ((15-1)*1024*1024/SECTOR_SIZE)
42 #define HIGH_4M (3*1024*1024/SECTOR_SIZE)
45 static struct stat st;
48 static void check_size(char *name,int setup_secs,int sectors)
50 if (sectors > setup_secs+MAX_KERNEL_SECS)
51 die("Kernel %s is too big",name);
55 void boot_image(char *spec,IMAGE_DESCR *descr)
60 int setup,fd,sectors,hi_sectors=MAX_KERNEL_SECS*4;
64 printf("Boot image: %s",spec);
65 show_link(spec); /* in common.c */
68 fd = geo_open(&geo,spec,O_RDONLY);
69 if (fstat(fd,&st) < 0) die("fstat %s: %s",spec,strerror(errno));
70 if (read(fd,(char *) &buff,SECTOR_SIZE) != SECTOR_SIZE)
71 die("read %s: %s",spec,strerror(errno));
72 setup = buff.sector[VSS_NUM] ? buff.sector[VSS_NUM] : SETUPSECS;
73 if (read(fd,(char *) &hdr,sizeof(hdr)) != sizeof(hdr))
74 die("read %s: %s",spec,strerror(errno));
75 modern_kernel = !strncmp(hdr.signature,NEW_HDR_SIG,4) && hdr.version >=
77 if (modern_kernel) descr->flags |= FLAG_MODKRN;
79 printf("Setup length is %d sector%s.\n",setup,setup == 1 ? "" : "s");
80 if (setup > MAX_SETUPSECS)
81 die("Setup length exceeds %d maximum; kernel setup will overwrite boot loader", MAX_SETUPSECS);
82 map_add(&geo,0,(st.st_size+SECTOR_SIZE-1)/SECTOR_SIZE);
83 sectors = map_end_section(&descr->start,setup+SPECIAL_SECTORS+SPECIAL_BOOTSECT);
84 if (!modern_kernel || !(hdr.flags & LFLAG_HIGH))
85 check_size(spec,setup,sectors);
87 if (hdr.start % PAGE_SIZE)
88 die("Can't load kernel at mis-aligned address 0x%08x\n",hdr.start);
89 descr->flags |= FLAG_LOADHI; /* load kernel high */
90 hi_sectors = sectors - setup; /* number of sectors loaded high */
91 hi_sectors *= 6; /* account for decompression */
92 if (hi_sectors < HIGH_4M) hi_sectors = HIGH_4M;
97 printf("Mapped %d sector%s.\n",sectors,sectors == 1 ? "" : "s");
98 if ((initrd = cfg_get_strg(cf_kernel,"initrd")) || (initrd = cfg_get_strg(
99 cf_options,"initrd"))) {
100 if (!modern_kernel) die("Kernel doesn't support initial RAM disks");
102 printf("Mapping RAM disk %s",initrd);
106 fd = geo_open(&geo,initrd,O_RDONLY);
107 if (fstat(fd,&st) < 0) die("fstat %s: %s",initrd,strerror(errno));
109 *(unsigned int *) descr->rd_size = st.st_size;
111 descr->rd_size = (st.st_size + SECTOR_SIZE - 1)/SECTOR_SIZE;
114 map_add(&geo,0,(st.st_size+SECTOR_SIZE-1)/SECTOR_SIZE);
115 sectors = map_end_section(&descr->initrd,0);
117 printf("RAM disk: %d sector%s.\n",sectors,sectors == 1 ? "" : "s");
120 if (hi_sectors + sectors > HIGH_SECTORS) {
121 descr->flags |= FLAG_TOOBIG;
122 warn("The initial RAM disk is TOO BIG to fit in the memory below 15M.");
125 if (cfg_get_flag(cf_options,"large-memory")
126 && !cfg_get_flag(cf_options,"small-memory")) {
127 /* CASE 10 = large & not small */
129 printf("The initial RAM disk will be loaded in the high memory above 16M.\n");
130 } else if (hi_sectors + sectors > HIGH_SECTORS) {
131 descr->flags |= FLAG_TOOBIG;
132 if (cfg_get_flag(cf_options,"small-memory")) {
133 /* CASE 01 or 11 = small */
134 warn("The initial RAM disk is TOO BIG to fit in the memory below 15M.\n"
135 " It will be loaded in the high memory it will be \n"
136 " assumed that the BIOS supports memory moves above 16M.");
138 /* CASE 00 = !large & !small */
140 printf("The initial RAM disk will be loaded in the high memory above 16M.\n");
143 /* CASE 01 or 10 or 11 */
145 printf("The initial RAM disk will be loaded in the low memory below 15M.\n");
154 void boot_device(char *spec,char *range,IMAGE_DESCR *descr)
160 if (verbose > 0) printf("Boot device: %s, range %s\n",spec,range);
161 (void) geo_open(&geo,spec,O_NOACCESS);
162 here = strchr(range,'-');
165 start = to_number(range);
166 if ((secs = to_number(here)-start+1) < 0) die("Invalid range");
169 here = strchr(range,'+');
172 start = to_number(range);
173 secs = to_number(here);
176 start = to_number(range);
180 map_add(&geo,start,secs);
181 check_size(spec,SETUPSECS,sectors = map_end_section(&descr->start,60));
182 /* this is a crude hack ... ----------^^*/
185 printf("Mapped %d sector%s.\n",sectors,sectors == 1 ? "" : "s");
189 void do_map_drive(void)
195 tmp = cfg_get_strg(cf_other,"map-drive");
196 from = strtoul(tmp,&end,0);
197 if (from > 0xff || *end)
198 cfg_error("Invalid drive specification \"%s\"",tmp);
199 cfg_init(cf_map_drive);
200 (void) cfg_parse(cf_map_drive);
201 tmp = cfg_get_strg(cf_map_drive,"to");
202 if (!tmp) cfg_error("TO is required");
203 to = strtoul(tmp,&end,0);
204 if (to > 0xff || *end)
205 cfg_error("Invalid drive specification \"%s\"",tmp);
206 if (from || to) { /* 0 -> 0 is special */
209 for (i = 0; i < curr_drv_map; i++) {
210 if (drv_map[i] == ((to << 8) | from))
211 die("Mapping 0x%02x to 0x%02x already exists",from,to);
212 if ((drv_map[i] & 0xff) == from)
213 die("Ambiguous mapping 0x%02x to 0x%02x or 0x%02x",from,
216 if (curr_drv_map == DRVMAP_SIZE)
217 cfg_error("Too many drive mappings (more than %d)",DRVMAP_SIZE);
219 printf(" Mapping BIOS drive 0x%02x to 0x%02x\n",from,to);
220 drv_map[curr_drv_map++] = (to << 8) | from;
222 cfg_unset(cf_other,"map-drive");
226 * Derive the name of the MBR from the partition name
228 * /dev/scsi/host2/bus0/target1/lun0/part2 => disc
229 * /dev/sd/c0b0t0u0p7 => c0b0t0u0
232 * If table==0, do no check for primary partition; if table==1, check
233 * that we started from a primary (1-4) partition.
235 * A NULL return indicates an error
239 char *boot_mbr(const char *boot, int table)
242 char *part, *npart, *endptr;
245 npart = stralloc(boot);
246 part = strrchr(npart, '/');
247 if (!part++) die ("No '/' in partition/device name.");
250 endptr = part + i - 1;
252 /* j is the count of digits at the end of the name */
254 while (isdigit(*endptr)) { j++; --endptr; }
255 if (j==0 && !table) die ("Not a partition name; no digits at the end.");
257 k = !table || (j==1 && endptr[1]>='1' && endptr[1]<='4');
259 /* test for devfs partNN */
260 if (strncmp(part, "part", 4)==0) {
261 strcpy(part, "disc");
263 /* test for ..NpNN */
264 else if (*endptr=='p' && isdigit(endptr[-1])) {
265 *endptr = 0; /* truncate the pNN part */
267 /* test for old /dev/hda3 or /dev/sda11 */
268 else if (endptr[-1]=='d' && endptr[-3]=='/' &&
269 (endptr[-2]=='h' || endptr[-2]=='s')
271 endptr[1] = 0; /* truncate the NN part */
284 if (stat(boot,&st) < 0) die("stat %s: %s",boot,strerror(errno));
285 dev = S_ISREG(st.st_mode) ? st.st_dev : st.st_rdev;
286 if ( (mask = has_partitions(dev)) ) {
288 k = !table ? 1 : k>=1 && k<=4;
290 dev_open(&d, dev&mask, O_BYPASS); /* bypass any open */
291 npart = stralloc(d.name);
299 printf("Name: %s yields MBR: %s (with%s primary partition check)\n",
300 boot, k ? npart : "(NULL)", table ? "" : "out");
309 #define PART(s,n) (((struct partition *) (s)[0].par_c.ptable)[(n)])
312 void boot_other(char *loader,char *boot,char *part,IMAGE_DESCR *descr)
314 int b_fd,p_fd,walk,found,size;
321 unsigned short magic;
323 BOOT_SECTOR buff[BIG_CHAIN];
327 BOOT_SECTOR buff[SETUPSECS-1];
335 if (!loader) loader = DFL_CHAIN;
342 if (part && strlen(part)>0 && strlen(part)<=2) {
343 if (part[1]==0 || part[1]==':') {
344 letter = toupper(part[0]);
345 if (letter>='C' && letter<='Z') {
352 unsafe = cfg_get_flag(cf_other, "unsafe");
353 if (!part && !unsafe) part = boot_mbr(boot, 1);
354 /* part may still be NULL */
358 printf("Boot other: %s%s%s, loader %s\n",
364 printf("Boot other: %s%s%s, loader %s",
375 if (!cfg_get_flag(cf_other, "change")) {
376 autoauto = 1; /* flag that change rules may be automatically inserted */
383 (void) geo_open_boot(&geo,boot);
384 if (part) die("TABLE and UNSAFE are mutually incompatible.");
387 b_fd = geo_open(&geo,boot,O_RDONLY);
388 if (fstat(b_fd,&st) < 0)
389 die("fstat %s: %s",boot,strerror(errno));
390 if (!geo.file) part_verify(st.st_rdev,0);
391 else if (st.st_size > SECTOR_SIZE) {
392 warn("'other = %s' specifies a file that is longer\n"
393 " than a single sector.", boot);
394 if (st.st_size >= SECTOR_SIZE*(SETUPSECS-1) &&
395 read(b_fd, buff[0].sector, SECTOR_SIZE*(SETUPSECS-1)) ==
396 SECTOR_SIZE*(SETUPSECS-1) &&
397 !strncmp((char*)buff[2].sector+2,"HdrS",4)
399 warn("This file may actually be an 'image ='");
402 if (lseek(b_fd,(int) BOOT_SIG_OFFSET,SEEK_SET) < 0)
403 die("lseek %s: %s",boot,strerror(errno));
404 if ((size = read(b_fd, (char *)&magic, 2)) != 2) {
405 if (size < 0) die("read %s: %s",boot,strerror(errno));
406 else die("Can't get magic number of %s",boot); }
407 if (magic != BOOT_SIGNATURE)
408 die("First sector of %s doesn't have a valid boot signature",boot);
411 /* process the 'master-boot' or 'boot-as' options */
412 i = cfg_get_flag(cf_other,"master-boot");
413 pos = cfg_get_strg(cf_other,"boot-as");
414 if (i && pos) die("'master-boot' and 'boot-as' are mutually exclusive 'other=' options");
416 i = cfg_get_flag(cf_options,"master-boot");
417 pos = cfg_get_strg(cf_options,"boot-as");
418 if (i && pos) die("'master-boot' and 'boot-as' are mutually exclusive global options");
420 if (i) code = -1; /* master-boot in use */
422 code = to_number(pos);
423 if (code >= 80 && code <= 89) {
424 /* convert to 0x80 to 0x89 */
425 warn("Radix error, 'boot-as=%d' taken to mean 'boot-as=0x%x'",
429 if ( !((code>=0 && code<=3) || (code>=0x80 && code<=DEV_MASK)) )
430 die("Illegal BIOS device code specified in 'boot-as=0x%02x'", code);
435 curr_drv_map += 2; /* add 2 spaces */
436 if (curr_drv_map >= DRVMAP_SIZE)
437 cfg_error("Too many drive mappings (more than %d)",DRVMAP_SIZE);
440 if (code==-1) s = "0/0x80";
441 else sprintf((s=n),"0x%02x", code);
442 printf(" Swapping BIOS boot drive with %s, as needed\n", s);
444 for (i=curr_drv_map-1; i>1; i--) drv_map[i] = drv_map[i-2];
445 drv_map[0] = 0xFFFF; /* reserve 2 slots */
446 drv_map[1] = code<<8 | 0xFF;
449 memset(buff,0,sizeof(buff));
451 zbuff = buff[0]; /* zero out zbuff */
454 if ((l_fd = open(loader,O_RDONLY)) < 0)
455 die("open %s: %s",loader,strerror(errno));
456 if ((size = read(l_fd,buff,sizeof(buff)+1)) < 0)
457 die("read %s: %s",loader,strerror(errno));
458 if (size > sizeof(buff))
459 die("Chain loader %s is too big",loader);
460 check_version(buff,STAGE_CHAIN);
463 if (size > sizeof(buff))
464 die("Chain loader %s is too big",loader);
465 memcpy(buff, chain->data, size);
468 p_fd = -1; /* pacify GCC */
469 PART(buff,0).boot_ind = geo.device;
470 PART(buff,0).start_sect = geo.start; /* pseudo partition table */
471 if (verbose > 0) printf("Pseudo partition start: %d\n", geo.start);
474 if ((p_fd = open(part,O_RDONLY)) < 0)
475 die("open %s: %s",part,strerror(errno));
476 if (lseek(p_fd,(int) PART_TABLE_OFFSET,SEEK_SET) < 0)
477 die("lseek %s: %s",part,strerror(errno));
478 if (read(p_fd,(char *) buff[0].par_c.ptable,PART_TABLE_SIZE) !=
480 die("read %s: %s",part,strerror(errno));
482 for (walk = 0; walk < PARTITION_ENTRIES; walk++)
483 if (!PART(buff,walk).sys_ind || PART(buff,walk).start_sect !=
486 * Don't remember what this is supposed to be good for :-(
488 if (PART(buff,walk).sys_ind != PART_DOS12 && PART(buff,walk).
489 sys_ind != PART_DOS16_SMALL && PART(buff,walk).sys_ind !=
491 PART(buff,walk).sys_ind = PART_INVALID;
494 if (found) die("Duplicate entry in partition table");
495 buff[0].par_c.offset = walk*PARTITION_ENTRY;
496 PART(buff,walk).boot_ind = 0x80;
499 if (!found) die("Partition entry not found.");
505 buff[0].par_c.drive = geo.device;
506 buff[0].par_c.head = letter ? letter : geo.device;
507 /* IBM boot manager passes drive letter in offset 0x25 */
508 if (verbose>=5) printf("boot_other: drive=0x%02x logical=0x%02x\n",
509 buff[0].par_c.drive, buff[0].par_c.head);
510 drv_map[curr_drv_map] = 0;
511 prt_map[curr_prt_map] = 0;
512 pos = (char *) buff+buff[0].par_c.drvmap;
513 memcpy(pos,drv_map,sizeof(drv_map));
514 memcpy(pos+sizeof(drv_map),prt_map,sizeof(prt_map)-2);
516 size = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
520 if (size > SETUPSECS-1) {
521 zbuff.sector[VSS_NUM] = mapped = size+1;
522 map_add_sector(zbuff.sector);
529 for (i = 0; i < size; i++) map_add_sector(&buff[i]);
530 for (i = size; i < SETUPSECS-1; i++) map_add_zero();
534 (void) map_end_section(&descr->start,SETUPSECS+SPECIAL_SECTORS);
538 printf("Mapped %d (%d+1+1) sectors.\n",
539 SETUPSECS+SPECIAL_SECTORS, SETUPSECS);
541 (void) map_end_section(&descr->start, mapped+SPECIAL_SECTORS+SPECIAL_BOOTSECT);
545 printf("Mapped %d (%d+1+1) sectors.\n",
546 mapped+SPECIAL_SECTORS, mapped);