Imported Upstream version 23.2
[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-2011 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%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;    
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     {
338         chain = &Chain;
339         cname = "CHAIN";
340     }
341 #endif
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') {
346                 letter += 0x80-'C';
347                 part = NULL;
348             }
349             else letter = 0;
350         }
351     }
352     unsafe = cfg_get_flag(cf_other, "unsafe");
353     if (!part && !unsafe) part = boot_mbr(boot, 1);
354     /* part may still be NULL */
355
356     if (verbose > 0) {
357 #ifdef LCF_BUILTIN
358         printf("Boot other: %s%s%s, loader %s\n",
359                 boot,
360                 part ? ", on " : "",
361                 part ? part : "",
362                 cname);
363 #else
364         printf("Boot other: %s%s%s, loader %s",
365                 boot,
366                 part ? ", on " : "",
367                 part ? part : "",
368                 loader);
369         show_link(loader);
370         printf("\n");
371 #endif
372     }
373
374 #ifdef LCF_AUTOAUTO
375     if (!cfg_get_flag(cf_other, "change")) {
376         autoauto = 1;   /* flag that change rules may be automatically inserted */
377         do_cr_auto();
378         autoauto = 0;
379     }
380 #endif    
381
382     if (unsafe) {
383         (void) geo_open_boot(&geo,boot);
384         if (part) die("TABLE and UNSAFE are mutually incompatible.");
385     }
386     else {
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)
398                                 ) {
399                 warn("This file may actually be an 'image ='");
400             }
401         }
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);
409     }
410
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");
415     if (!i && !pos) {
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");
419     }
420     if (i) code = -1;   /* master-boot in use */
421     else if (pos) {
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'",
426                                 code, code+0x30);
427             code += 0x30;
428         }
429         if ( !((code>=0 && code<=3) || (code>=0x80 && code<=DEV_MASK)) )
430             die("Illegal BIOS device code specified in 'boot-as=0x%02x'", code);
431     }
432     else code = -2;
433     
434     if (code != -2) {
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);
438         if (verbose > 1) {
439             char *s, n[8];
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);
443         }
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;
447     }
448
449     memset(buff,0,sizeof(buff));
450 #ifdef BIG_CHAIN
451     zbuff = buff[0];    /* zero out zbuff */
452 #endif
453 #ifndef LCF_BUILTIN
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);
461 #else
462     size = chain->size;
463     if (size > sizeof(buff))
464         die("Chain loader %s is too big",loader);
465     memcpy(buff, chain->data, size);
466 #endif
467     if (!part) {
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);
472     }
473     else {
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) !=
479           PART_TABLE_SIZE)
480             die("read %s: %s",part,strerror(errno));
481         found = 0;
482         for (walk = 0; walk < PARTITION_ENTRIES; walk++)
483             if (!PART(buff,walk).sys_ind || PART(buff,walk).start_sect !=
484               geo.start) {
485                 /*
486                  * Don't remember what this is supposed to be good for :-(
487                  */
488                 if (PART(buff,walk).sys_ind != PART_DOS12 && PART(buff,walk).
489                   sys_ind != PART_DOS16_SMALL && PART(buff,walk).sys_ind !=
490                   PART_DOS16_BIG)
491                   PART(buff,walk).sys_ind = PART_INVALID;
492             }
493             else {
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;
497                 found = 1;
498             }
499         if (!found) die("Partition entry not found.");
500         (void) close(p_fd);
501     }
502 #ifndef LCF_BUILTIN
503     (void) close(l_fd);
504 #endif
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);
515
516     size = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
517 #ifndef BIG_CHAIN
518     map_add_zero();
519 #else
520     if (size > SETUPSECS-1) {
521         zbuff.sector[VSS_NUM] = mapped = size+1;
522         map_add_sector(zbuff.sector);
523     }
524     else {
525         map_add_zero();
526         mapped = SETUPSECS;
527     }
528 #endif
529     for (i = 0; i < size; i++) map_add_sector(&buff[i]);
530     for (i = size; i < SETUPSECS-1; i++) map_add_zero();
531     map_add(&geo,0,1);
532
533 #ifndef BIG_CHAIN
534     (void) map_end_section(&descr->start,SETUPSECS+SPECIAL_SECTORS);
535         /* size is known */
536     geo_close(&geo);
537     if (verbose > 1)
538         printf("Mapped %d (%d+1+1) sectors.\n",
539                 SETUPSECS+SPECIAL_SECTORS, SETUPSECS);
540 #else
541     (void) map_end_section(&descr->start, mapped+SPECIAL_SECTORS+SPECIAL_BOOTSECT);
542         /* size is known */
543     geo_close(&geo);
544     if (verbose > 1)
545         printf("Mapped %d (%d+1+1) sectors.\n",
546                 mapped+SPECIAL_SECTORS, mapped);
547 #endif
548 }