0669f91257f8e097508971ee04fc22afedce0fbe
[rrq/maintain_lilo.git] / src / boot.c
1 /* boot.c  -  Boot image composition
2  * 
3  * Copyright 1992-1998 Werner Almesberger
4  * Copyright 1999-2007 John Coffman
5  * Copyright 2009-2010 Joachim Wiedorn
6  * All rights reserved.
7  * 
8  * Licensed under the terms contained in the file 'COPYING'
9  * in the source directory.
10  */
11
12 #define BIG_CHAIN 5
13
14 #define _GNU_SOURCE
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <sys/stat.h>
23
24 #include "config.h"
25 #include "lilo.h"
26 #include "common.h"
27 #include "geometry.h"
28 #include "device.h"
29 #include "cfg.h"
30 #include "map.h"
31 #include "partition.h"
32 #include "boot.h"
33 #include "loader.h"
34
35 /*
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
40  */
41 #define HIGH_SECTORS    ((15-1)*1024*1024/SECTOR_SIZE) 
42 #define HIGH_4M         (3*1024*1024/SECTOR_SIZE)
43
44 static GEOMETRY geo;
45 static struct stat st;
46
47
48 static void check_size(char *name,int setup_secs,int sectors)
49 {
50     if (sectors > setup_secs+MAX_KERNEL_SECS)
51         die("Kernel %s is too big",name);
52 }
53
54
55 void boot_image(char *spec,IMAGE_DESCR *descr)
56 {
57         BOOT_SECTOR buff;
58         SETUP_HDR hdr;
59         char *initrd;
60         int setup,fd,sectors,hi_sectors=MAX_KERNEL_SECS*4;
61         int modern_kernel;
62
63         if (verbose > 0) {
64                 printf("Boot image: %s",spec);
65                 show_link(spec);        /* in common.c */
66                 printf("\n");
67     }
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 >= 
76                 NEW_HDR_VERSION;
77         if (modern_kernel) descr->flags |= FLAG_MODKRN;
78         if (verbose > 1)
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);
86         else {
87                 if (hdr.start % PAGE_SIZE)
88                         die("Can't load kernel at mis-aligned address 0x%08lx\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;    
93     }
94     geo_close(&geo);
95
96     if (verbose > 1)
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");
101         if (verbose > 0) {
102             printf("Mapping RAM disk %s",initrd);
103             show_link(initrd);
104             printf("\n");
105         }
106         fd = geo_open(&geo,initrd,O_RDONLY);
107         if (fstat(fd,&st) < 0) die("fstat %s: %s",initrd,strerror(errno));
108 #if 1
109         *(unsigned int *) descr->rd_size = st.st_size;
110 #else
111         descr->rd_size = (st.st_size + SECTOR_SIZE - 1)/SECTOR_SIZE;
112 #endif
113         map_begin_section();
114         map_add(&geo,0,(st.st_size+SECTOR_SIZE-1)/SECTOR_SIZE);
115         sectors = map_end_section(&descr->initrd,0);
116         if (verbose > 1)
117                 printf("RAM disk: %d sector%s.\n",sectors,sectors == 1 ?  "" : "s");
118
119 #ifdef LCF_INITRDLOW
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.");
123         }
124 #else
125         if (cfg_get_flag(cf_options,"large-memory")
126                         && !cfg_get_flag(cf_options,"small-memory")) {
127                 /* CASE 10 = large & not small */
128                 if (verbose>=2)
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.");
137                 } else {
138                         /* CASE 00 = !large & !small */
139                         if (verbose>=1)
140                         printf("The initial RAM disk will be loaded in the high memory above 16M.\n");
141                 }
142         } else {
143                 /* CASE 01 or 10 or 11 */
144                 if (verbose>=2)
145                 printf("The initial RAM disk will be loaded in the low memory below 15M.\n");
146         }
147 #endif  
148
149         geo_close(&geo);
150     }
151 }
152
153
154 void boot_device(char *spec,char *range,IMAGE_DESCR *descr)
155 {
156     char *here;
157     int start,secs;
158     int sectors;
159
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,'-');
163     if (here) {
164         *here++ = 0;
165         start = to_number(range);
166         if ((secs = to_number(here)-start+1) < 0) die("Invalid range");
167     }
168     else {
169         here = strchr(range,'+');
170         if (here) {
171             *here++ = 0;
172             start = to_number(range);
173             secs = to_number(here);
174         }
175         else {
176             start = to_number(range);
177             secs = 1;
178         }
179     }
180     map_add(&geo,start,secs);
181     check_size(spec,SETUPSECS,sectors = map_end_section(&descr->start,60));
182                                 /* this is a crude hack ... ----------^^*/
183     geo_close(&geo);
184     if (verbose > 1)
185         printf("Mapped %d sector%s.\n",sectors,sectors == 1 ? "" : "s");
186 }
187
188
189 void do_map_drive(void)
190 {
191     const char *tmp;
192     char *end;
193     int from,to;
194
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 */
207         int i;
208
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,
214                   drv_map[i] >> 8,to);
215         }
216         if (curr_drv_map == DRVMAP_SIZE)
217             cfg_error("Too many drive mappings (more than %d)",DRVMAP_SIZE);
218         if (verbose > 1)
219             printf("  Mapping BIOS drive 0x%02x to 0x%02x\n",from,to);
220         drv_map[curr_drv_map++] = (to << 8) | from;
221     }
222     cfg_unset(cf_other,"map-drive");
223 }
224
225 /* 
226  *  Derive the name of the MBR from the partition name
227  *  e.g.
228  *   /dev/scsi/host2/bus0/target1/lun0/part2    => disc
229  *   /dev/sd/c0b0t0u0p7                         => c0b0t0u0
230  *   /dev/sda11                                 => sda
231  *
232  * If table==0, do no check for primary partition; if table==1, check
233  * that we started from a primary (1-4) partition.
234  *
235  * A NULL return indicates an error
236  *
237  */
238  
239 char *boot_mbr(const char *boot, int table)
240 {
241 #if 0
242     char *part, *npart, *endptr;
243     int i, j, k;
244     
245     npart = stralloc(boot);
246     part = strrchr(npart, '/');
247     if (!part++) die ("No '/' in partition/device name.");
248     
249     i = strlen(part);
250     endptr = part + i - 1;
251     
252    /* j is the count of digits at the end of the name */ 
253     j = 0;
254     while (isdigit(*endptr)) { j++; --endptr; }
255     if (j==0 && !table) die ("Not a partition name; no digits at the end.");
256     
257     k = !table || (j==1 && endptr[1]>='1' && endptr[1]<='4');
258     
259    /* test for devfs  partNN */
260     if (strncmp(part, "part", 4)==0) {
261         strcpy(part, "disc");
262     } 
263    /* test for ..NpNN */
264     else if (*endptr=='p' && isdigit(endptr[-1])) {
265         *endptr = 0;  /* truncate the pNN part */
266     }
267    /* test for old /dev/hda3 or /dev/sda11 */
268     else if (endptr[-1]=='d' && endptr[-3]=='/' &&
269                 (endptr[-2]=='h' || endptr[-2]=='s')
270             ) {
271         endptr[1] = 0;  /* truncate the NN part */
272     }
273     else 
274         k = 0;
275
276 #else
277     struct stat st;
278     dev_t dev;
279     DEVICE d;
280     int mask, k;
281     char *npart = NULL;
282     
283     k = 0;
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)) ) {
287         k = dev & ~mask;
288         k = !table ? 1 : k>=1 && k<=4;
289         if (k) {
290             dev_open(&d, dev&mask, O_BYPASS);   /* bypass any open */
291             npart = stralloc(d.name); 
292             dev_close(&d);
293         }
294     }
295     
296
297 #endif
298     if (verbose>=3) {
299         printf("Name: %s  yields MBR: %s  (with%s primary partition check)\n",
300            boot, k ? npart : "(NULL)", table ? "" : "out");
301     }
302
303     if (k) return npart;
304     else return NULL;
305 }
306
307
308
309 #define PART(s,n) (((struct partition *) (s)[0].par_c.ptable)[(n)])
310
311
312 void boot_other(char *loader,char *boot,char *part,IMAGE_DESCR *descr)
313 {
314     int b_fd,p_fd,walk,found,size;
315 #ifdef LCF_BUILTIN
316     BUILTIN_FILE *chain;
317     char *cname;
318 #else
319     int l_fd;
320 #endif
321     unsigned short magic;
322 #ifdef BIG_CHAIN
323     BOOT_SECTOR buff[BIG_CHAIN];
324     BOOT_SECTOR zbuff;
325     int mapped;
326 #else
327     BOOT_SECTOR buff[SETUPSECS-1];
328 #endif
329     struct stat st;
330     char *pos;
331     int i, code;
332     int letter = 0;
333     int unsafe;
334
335     if (!loader) loader = DFL_CHAIN;
336 #ifdef LCF_BUILTIN
337 #ifndef LCF_SOLO_CHAIN
338     if (strstr(loader,"os2")) {
339         chain = &Os2_d;
340         cname = "OS/2";
341     }
342     else
343 #endif
344     {
345         chain = &Chain;
346         cname = "CHAIN";
347     }
348 #endif
349     if (part && strlen(part)>0 && strlen(part)<=2) {
350         if (part[1]==0 || part[1]==':') {
351             letter = toupper(part[0]);
352             if (letter>='C' && letter<='Z') {
353                 letter += 0x80-'C';
354                 part = NULL;
355             }
356             else letter = 0;
357         }
358     }
359     unsafe = cfg_get_flag(cf_other, "unsafe");
360     if (!part && !unsafe) part = boot_mbr(boot, 1);
361     /* part may still be NULL */
362
363     if (verbose > 0) {
364 #ifdef LCF_BUILTIN
365         printf("Boot other: %s%s%s, loader %s\n",
366                 boot,
367                 part ? ", on " : "",
368                 part ? part : "",
369                 cname);
370 #else
371         printf("Boot other: %s%s%s, loader %s",
372                 boot,
373                 part ? ", on " : "",
374                 part ? part : "",
375                 loader);
376         show_link(loader);
377         printf("\n");
378 #endif
379     }
380
381 #ifdef LCF_AUTOAUTO
382     if (!cfg_get_flag(cf_other, "change")) {
383         autoauto = 1;   /* flag that change rules may be automatically inserted */
384         do_cr_auto();
385         autoauto = 0;
386     }
387 #endif    
388
389     if (unsafe) {
390         (void) geo_open_boot(&geo,boot);
391         if (part) die("TABLE and UNSAFE are mutually incompatible.");
392     }
393     else {
394         b_fd = geo_open(&geo,boot,O_RDONLY);
395         if (fstat(b_fd,&st) < 0)
396             die("fstat %s: %s",boot,strerror(errno));
397         if (!geo.file) part_verify(st.st_rdev,0);
398         else if (st.st_size > SECTOR_SIZE) {
399             warn("'other = %s' specifies a file that is longer\n"
400                 "    than a single sector.", boot);
401             if (st.st_size >= SECTOR_SIZE*(SETUPSECS-1) &&
402                 read(b_fd, buff[0].sector, SECTOR_SIZE*(SETUPSECS-1)) == 
403                         SECTOR_SIZE*(SETUPSECS-1)   &&
404                 !strncmp((char*)buff[2].sector+2,"HdrS",4)
405                                 ) {
406                 warn("This file may actually be an 'image ='");
407             }
408         }
409         if (lseek(b_fd,(int) BOOT_SIG_OFFSET,SEEK_SET) < 0)
410             die("lseek %s: %s",boot,strerror(errno));
411         if ((size = read(b_fd, (char *)&magic, 2)) != 2) {
412             if (size < 0) die("read %s: %s",boot,strerror(errno));
413             else die("Can't get magic number of %s",boot); }
414         if (magic != BOOT_SIGNATURE)
415             die("First sector of %s doesn't have a valid boot signature",boot);
416     }
417
418 /* process the 'master-boot' or 'boot-as' options */
419     i = cfg_get_flag(cf_other,"master-boot");
420     pos = cfg_get_strg(cf_other,"boot-as");
421     if (i && pos) die("'master-boot' and 'boot-as' are mutually exclusive 'other=' options");
422     if (!i && !pos) {
423         i = cfg_get_flag(cf_options,"master-boot");
424         pos = cfg_get_strg(cf_options,"boot-as");
425         if (i && pos) die("'master-boot' and 'boot-as' are mutually exclusive global options");
426     }
427     if (i) code = -1;   /* master-boot in use */
428     else if (pos) {
429         code = to_number(pos);
430         if (code >= 80 && code <= 89) {
431             /* convert to 0x80 to 0x89 */
432             warn("Radix error, 'boot-as=%d' taken to mean 'boot-as=0x%x'",
433                                 code, code+0x30);
434             code += 0x30;
435         }
436         if ( !((code>=0 && code<=3) || (code>=0x80 && code<=DEV_MASK)) )
437             die("Illegal BIOS device code specified in 'boot-as=0x%02x'", code);
438     }
439     else code = -2;
440     
441     if (code != -2) {
442         curr_drv_map += 2;      /* add 2 spaces */
443         if (curr_drv_map >= DRVMAP_SIZE)
444             cfg_error("Too many drive mappings (more than %d)",DRVMAP_SIZE);
445         if (verbose > 1) {
446             char *s, n[8];
447             if (code==-1) s = "0/0x80";
448             else sprintf((s=n),"0x%02x", code);
449             printf("  Swapping BIOS boot drive with %s, as needed\n", s);
450         }
451         for (i=curr_drv_map-1; i>1; i--) drv_map[i] = drv_map[i-2];
452         drv_map[0] = 0xFFFF;    /* reserve 2 slots */
453         drv_map[1] = code<<8 | 0xFF;
454     }
455
456     memset(buff,0,sizeof(buff));
457 #ifdef BIG_CHAIN
458     zbuff = buff[0];    /* zero out zbuff */
459 #endif
460 #ifndef LCF_BUILTIN
461     if ((l_fd = open(loader,O_RDONLY)) < 0)
462         die("open %s: %s",loader,strerror(errno));
463     if ((size = read(l_fd,buff,sizeof(buff)+1)) < 0)
464         die("read %s: %s",loader,strerror(errno));
465     if (size > sizeof(buff))
466         die("Chain loader %s is too big",loader);
467     check_version(buff,STAGE_CHAIN);
468 #else
469     size = chain->size;
470     if (size > sizeof(buff))
471         die("Chain loader %s is too big",loader);
472     memcpy(buff, chain->data, size);
473 #endif
474     if (!part) {
475         p_fd = -1; /* pacify GCC */
476         PART(buff,0).boot_ind = geo.device;
477         PART(buff,0).start_sect = geo.start;     /* pseudo partition table */
478         if (verbose > 0) printf("Pseudo partition start: %d\n", geo.start);
479     }
480     else {
481         if ((p_fd = open(part,O_RDONLY)) < 0)
482             die("open %s: %s",part,strerror(errno));
483         if (lseek(p_fd,(int) PART_TABLE_OFFSET,SEEK_SET) < 0)
484             die("lseek %s: %s",part,strerror(errno));
485         if (read(p_fd,(char *) buff[0].par_c.ptable,PART_TABLE_SIZE) !=
486           PART_TABLE_SIZE)
487             die("read %s: %s",part,strerror(errno));
488         found = 0;
489         for (walk = 0; walk < PARTITION_ENTRIES; walk++)
490             if (!PART(buff,walk).sys_ind || PART(buff,walk).start_sect !=
491               geo.start) {
492                 /*
493                  * Don't remember what this is supposed to be good for :-(
494                  */
495                 if (PART(buff,walk).sys_ind != PART_DOS12 && PART(buff,walk).
496                   sys_ind != PART_DOS16_SMALL && PART(buff,walk).sys_ind !=
497                   PART_DOS16_BIG)
498                   PART(buff,walk).sys_ind = PART_INVALID;
499             }
500             else {
501                 if (found) die("Duplicate entry in partition table");
502                 buff[0].par_c.offset = walk*PARTITION_ENTRY;
503                 PART(buff,walk).boot_ind = 0x80;
504                 found = 1;
505             }
506         if (!found) die("Partition entry not found.");
507         (void) close(p_fd);
508     }
509 #ifndef LCF_BUILTIN
510     (void) close(l_fd);
511 #endif
512     buff[0].par_c.drive = geo.device;
513     buff[0].par_c.head = letter ? letter : geo.device;
514                 /* IBM boot manager passes drive letter in offset 0x25 */
515     if (verbose>=5) printf("boot_other:  drive=0x%02x   logical=0x%02x\n",
516                         buff[0].par_c.drive, buff[0].par_c.head);
517     drv_map[curr_drv_map] = 0;
518     prt_map[curr_prt_map] = 0;
519     pos = (char *) buff+buff[0].par_c.drvmap;
520     memcpy(pos,drv_map,sizeof(drv_map));
521     memcpy(pos+sizeof(drv_map),prt_map,sizeof(prt_map)-2);
522
523     size = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
524 #ifndef BIG_CHAIN
525     map_add_zero();
526 #else
527     if (size > SETUPSECS-1) {
528         zbuff.sector[VSS_NUM] = mapped = size+1;
529         map_add_sector(zbuff.sector);
530     }
531     else {
532         map_add_zero();
533         mapped = SETUPSECS;
534     }
535 #endif
536     for (i = 0; i < size; i++) map_add_sector(&buff[i]);
537     for (i = size; i < SETUPSECS-1; i++) map_add_zero();
538     map_add(&geo,0,1);
539
540 #ifndef BIG_CHAIN
541     (void) map_end_section(&descr->start,SETUPSECS+SPECIAL_SECTORS);
542         /* size is known */
543     geo_close(&geo);
544     if (verbose > 1)
545         printf("Mapped %d (%d+1+1) sectors.\n",
546                 SETUPSECS+SPECIAL_SECTORS, SETUPSECS);
547 #else
548     (void) map_end_section(&descr->start, mapped+SPECIAL_SECTORS+SPECIAL_BOOTSECT);
549         /* size is known */
550     geo_close(&geo);
551     if (verbose > 1)
552         printf("Mapped %d (%d+1+1) sectors.\n",
553                 mapped+SPECIAL_SECTORS, mapped);
554 #endif
555 }