Merge remote-tracking branch 'sherbszt/gfxboot32'
[profile/ivi/syslinux.git] / memdisk / setup.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *   Portions copyright 2009-2010 Shao Miller
6  *                                [El Torito code, mBFT, "safe hook"]
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
11  *   Boston MA 02111-1307, USA; either version 2 of the License, or
12  *   (at your option) any later version; incorporated herein by reference.
13  *
14  * ----------------------------------------------------------------------- */
15
16 #include <stdint.h>
17 #include <minmax.h>
18 #include <suffix_number.h>
19 #include "bda.h"
20 #include "dskprobe.h"
21 #include "e820.h"
22 #include "conio.h"
23 #include "version.h"
24 #include "memdisk.h"
25 #include "../version.h"
26
27 const char memdisk_version[] = "MEMDISK " VERSION_STR " " DATE;
28 const char copyright[] =
29     "Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin et al";
30
31 extern const char _binary_memdisk_chs_512_bin_start[];
32 extern const char _binary_memdisk_chs_512_bin_end[];
33 extern const char _binary_memdisk_chs_512_bin_size[];
34 extern const char _binary_memdisk_edd_512_bin_start[];
35 extern const char _binary_memdisk_edd_512_bin_end[];
36 extern const char _binary_memdisk_edd_512_bin_size[];
37 extern const char _binary_memdisk_iso_512_bin_start[];
38 extern const char _binary_memdisk_iso_512_bin_end[];
39 extern const char _binary_memdisk_iso_512_bin_size[];
40 extern const char _binary_memdisk_iso_2048_bin_start[];
41 extern const char _binary_memdisk_iso_2048_bin_end[];
42 extern const char _binary_memdisk_iso_2048_bin_size[];
43
44 /* Pull in structures common to MEMDISK and MDISKCHK.COM */
45 #include "mstructs.h"
46
47 /* An EDD disk packet */
48 struct edd_dsk_pkt {
49     uint8_t size;               /* Packet size        */
50     uint8_t res1;               /* Reserved           */
51     uint16_t count;             /* Count to transfer  */
52     uint32_t buf;               /* Buffer pointer     */
53     uint64_t start;             /* LBA to start from  */
54     uint64_t buf64;             /* 64-bit buf pointer */
55 } __attribute__ ((packed));
56
57 /* Change to 1 for El Torito debugging */
58 #define DBG_ELTORITO 0
59
60 #if DBG_ELTORITO
61 extern void eltorito_dump(uint32_t);
62 #endif
63
64 /*
65  * Routine to seek for a command-line item and return a pointer
66  * to the data portion, if present
67  */
68
69 /* Magic return values */
70 #define CMD_NOTFOUND   ((char *)-1)     /* Not found */
71 #define CMD_BOOL       ((char *)-2)     /* Found boolean option */
72 #define CMD_HASDATA(X) ((int)(X) >= 0)
73
74 static const char *getcmditem(const char *what)
75 {
76     const char *p;
77     const char *wp = what;
78     int match = 0;
79
80     for (p = shdr->cmdline; *p; p++) {
81         switch (match) {
82         case 0:         /* Ground state */
83             if (*p == ' ')
84                 break;
85
86             wp = what;
87             match = 1;
88             /* Fall through */
89
90         case 1:         /* Matching */
91             if (*wp == '\0') {
92                 if (*p == '=')
93                     return p + 1;
94                 else if (*p == ' ')
95                     return CMD_BOOL;
96                 else {
97                     match = 2;
98                     break;
99                 }
100             }
101             if (*p != *wp++)
102                 match = 2;
103             break;
104
105         case 2:         /* Mismatch, skip rest of option */
106             if (*p == ' ')
107                 match = 0;      /* Next option */
108             break;
109         }
110     }
111
112     /* Check for matching string at end of line */
113     if (match == 1 && *wp == '\0')
114         return CMD_BOOL;
115
116     return CMD_NOTFOUND;
117 }
118
119 /*
120  * Check to see if this is a gzip image
121  */
122 #define UNZIP_ALIGN 512
123
124 extern const char _end[];               /* Symbol signalling end of data */
125
126 void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
127 {
128     uint32_t where = *where_p;
129     uint32_t size = *size_p;
130     uint32_t zbytes;
131     uint32_t startrange, endrange;
132     uint32_t gzdatasize, gzwhere;
133     uint32_t orig_crc, offset;
134     uint32_t target = 0;
135     int i, okmem;
136
137     /* Is it a gzip image? */
138     if (check_zip((void *)where, size, &zbytes, &gzdatasize,
139                   &orig_crc, &offset) == 0) {
140
141         if (offset + zbytes > size) {
142             /*
143              * Assertion failure; check_zip is supposed to guarantee this
144              * never happens.
145              */
146             die("internal error: check_zip returned nonsense\n");
147         }
148
149         /*
150          * Find a good place to put it: search memory ranges in descending
151          * order until we find one that is legal and fits
152          */
153         okmem = 0;
154         for (i = nranges - 1; i >= 0; i--) {
155             /*
156              * We can't use > 4G memory (32 bits only.)  Truncate to 2^32-1
157              * so we don't have to deal with funny wraparound issues.
158              */
159
160             /* Must be memory */
161             if (ranges[i].type != 1)
162                 continue;
163
164             /* Range start */
165             if (ranges[i].start >= 0xFFFFFFFF)
166                 continue;
167
168             startrange = (uint32_t) ranges[i].start;
169
170             /* Range end (0 for end means 2^64) */
171             endrange = ((ranges[i + 1].start >= 0xFFFFFFFF ||
172                          ranges[i + 1].start == 0)
173                         ? 0xFFFFFFFF : (uint32_t) ranges[i + 1].start);
174
175             /* Make sure we don't overwrite ourselves */
176             if (startrange < (uint32_t) _end)
177                 startrange = (uint32_t) _end;
178
179             /* Allow for alignment */
180             startrange =
181                 (ranges[i].start + (UNZIP_ALIGN - 1)) & ~(UNZIP_ALIGN - 1);
182
183             /* In case we just killed the whole range... */
184             if (startrange >= endrange)
185                 continue;
186
187             /*
188              * Must be large enough... don't rely on gzwhere for this
189              * (wraparound)
190              */
191             if (endrange - startrange < gzdatasize)
192                 continue;
193
194             /*
195              * This is where the gz image would be put if we put it in this
196              * range...
197              */
198             gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN - 1);
199
200             /* Cast to uint64_t just in case we're flush with the top byte */
201             if ((uint64_t) where + size >= gzwhere && where < endrange) {
202                 /*
203                  * Need to move source data to avoid compressed/uncompressed
204                  * overlap
205                  */
206                 uint32_t newwhere;
207
208                 if (gzwhere - startrange < size)
209                     continue;   /* Can't fit both old and new */
210
211                 newwhere = (gzwhere - size) & ~(UNZIP_ALIGN - 1);
212                 printf("Moving compressed data from 0x%08x to 0x%08x\n",
213                        where, newwhere);
214
215                 memmove((void *)newwhere, (void *)where, size);
216                 where = newwhere;
217             }
218
219             target = gzwhere;
220             okmem = 1;
221             break;
222         }
223
224         if (!okmem)
225             die("Not enough memory to decompress image (need 0x%08x bytes)\n",
226                 gzdatasize);
227
228         printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
229                target, gzdatasize);
230
231         *size_p = gzdatasize;
232         *where_p = (uint32_t) unzip((void *)(where + offset), zbytes,
233                                     gzdatasize, orig_crc, (void *)target);
234     }
235 }
236
237 /*
238  * Figure out the "geometry" of the disk in question
239  */
240 struct geometry {
241     uint32_t sectors;           /* Sector count */
242     uint32_t c, h, s;           /* C/H/S geometry */
243     uint32_t offset;            /* Byte offset for disk */
244     uint32_t boot_lba;          /* LBA of bootstrap code */
245     uint8_t type;               /* Type byte for INT 13h AH=08h */
246     uint8_t driveno;            /* Drive no */
247     uint8_t sector_shift;       /* Sector size as a power of 2 */
248     const char *hsrc, *ssrc;    /* Origins of H and S geometries */
249 };
250
251 /* Format of a DOS partition table entry */
252 struct ptab_entry {
253     uint8_t active;
254     uint8_t start_h, start_s, start_c;
255     uint8_t type;
256     uint8_t end_h, end_s, end_c;
257     uint32_t start;
258     uint32_t size;
259 } __attribute__ ((packed));
260
261 /* Format of a FAT filesystem superblock */
262 struct fat_extra {
263     uint8_t bs_drvnum;
264     uint8_t bs_resv1;
265     uint8_t bs_bootsig;
266     uint32_t bs_volid;
267     char bs_vollab[11];
268     char bs_filsystype[8];
269 } __attribute__ ((packed));
270 struct fat_super {
271     uint8_t bs_jmpboot[3];
272     char bs_oemname[8];
273     uint16_t bpb_bytspersec;
274     uint8_t bpb_secperclus;
275     uint16_t bpb_rsvdseccnt;
276     uint8_t bpb_numfats;
277     uint16_t bpb_rootentcnt;
278     uint16_t bpb_totsec16;
279     uint8_t bpb_media;
280     uint16_t bpb_fatsz16;
281     uint16_t bpb_secpertrk;
282     uint16_t bpb_numheads;
283     uint32_t bpb_hiddsec;
284     uint32_t bpb_totsec32;
285     union {
286         struct {
287             struct fat_extra extra;
288         } fat16;
289         struct {
290             uint32_t bpb_fatsz32;
291             uint16_t bpb_extflags;
292             uint16_t bpb_fsver;
293             uint32_t bpb_rootclus;
294             uint16_t bpb_fsinfo;
295             uint16_t bpb_bkbootsec;
296             char bpb_reserved[12];
297             /* Clever, eh?  Same fields, different offset... */
298             struct fat_extra extra;
299         } fat32 __attribute__ ((packed));
300     } x;
301 } __attribute__ ((packed));
302
303 /* Format of a DOSEMU header */
304 struct dosemu_header {
305     uint8_t magic[7];           /* DOSEMU\0 */
306     uint32_t h;
307     uint32_t s;
308     uint32_t c;
309     uint32_t offset;
310     uint8_t pad[105];
311 } __attribute__ ((packed));
312
313 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
314
315 static const struct geometry *get_disk_image_geometry(uint32_t where,
316                                                       uint32_t size)
317 {
318     static struct geometry hd_geometry;
319     struct dosemu_header dosemu;
320     unsigned int sectors, xsectors, v;
321     unsigned int offset;
322     int i;
323     const char *p;
324
325     printf("command line: %s\n", shdr->cmdline);
326
327     hd_geometry.sector_shift = 9;       /* Assume floppy/HDD at first */
328
329     offset = 0;
330     if (CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)))
331         offset = v;
332
333     sectors = xsectors = (size - offset) >> hd_geometry.sector_shift;
334
335     hd_geometry.hsrc = "guess";
336     hd_geometry.ssrc = "guess";
337     hd_geometry.sectors = sectors;
338     hd_geometry.offset = offset;
339
340     if ((p = getcmditem("iso")) != CMD_NOTFOUND) {
341 #if DBG_ELTORITO
342         eltorito_dump(where);
343 #endif
344         struct edd4_bvd *bvd = (struct edd4_bvd *)(where + 17 * 2048);
345         /* Tiny sanity check */
346         if ((bvd->boot_rec_ind != 0) || (bvd->ver != 1))
347             printf("El Torito BVD sanity check failed.\n");
348         struct edd4_bootcat *boot_cat =
349             (struct edd4_bootcat *)(where + bvd->boot_cat * 2048);
350         /* Another tiny sanity check */
351         if ((boot_cat->validation_entry.platform_id != 0) ||
352             (boot_cat->validation_entry.key55 != 0x55) ||
353             (boot_cat->validation_entry.keyAA != 0xAA))
354             printf("El Torito boot catalog sanity check failed.\n");
355         /* If we have an emulation mode, set the offset to the image */
356         if (boot_cat->initial_entry.media_type)
357             hd_geometry.offset += boot_cat->initial_entry.load_block * 2048;
358         else
359             /* We're a no-emulation mode, so we will boot to an offset */
360             hd_geometry.boot_lba = boot_cat->initial_entry.load_block * 4;
361         if (boot_cat->initial_entry.media_type < 4) {
362             /* We're a floppy emulation mode or our params will be
363              * overwritten by the no emulation mode case
364              */
365             hd_geometry.driveno = 0x00;
366             hd_geometry.c = 80;
367             hd_geometry.h = 2;
368         }
369         switch (boot_cat->initial_entry.media_type) {
370         case 0:         /* No emulation   */
371             hd_geometry.driveno = 0xE0;
372             hd_geometry.type = 10;      /* ATAPI removable media device */
373             hd_geometry.c = 65535;
374             hd_geometry.h = 255;
375             hd_geometry.s = 15;
376             /* 2048-byte sectors, so adjust the size and count */
377             hd_geometry.sector_shift = 11;
378             break;
379         case 1:         /* 1.2 MB floppy  */
380             hd_geometry.s = 15;
381             hd_geometry.type = 2;
382             sectors = 2400;
383             break;
384         case 2:         /* 1.44 MB floppy */
385             hd_geometry.s = 18;
386             hd_geometry.type = 4;
387             sectors = 2880;
388             break;
389         case 3:         /* 2.88 MB floppy */
390             hd_geometry.s = 36;
391             hd_geometry.type = 6;
392             sectors = 5760;
393             break;
394         case 4:
395             hd_geometry.driveno = 0x80;
396             hd_geometry.type = 0;
397             break;
398         }
399         sectors = (size - hd_geometry.offset) >> hd_geometry.sector_shift;
400
401         /* For HDD emulation, we figure out the geometry later. Otherwise: */
402         if (hd_geometry.s) {
403             hd_geometry.hsrc = hd_geometry.ssrc = "El Torito";
404         }
405         hd_geometry.sectors = sectors;
406     }
407
408     /* Do we have a DOSEMU header? */
409     memcpy(&dosemu, (char *)where + hd_geometry.offset, sizeof dosemu);
410     if (!memcmp("DOSEMU", dosemu.magic, 7)) {
411         /* Always a hard disk unless overruled by command-line options */
412         hd_geometry.driveno = 0x80;
413         hd_geometry.type = 0;
414         hd_geometry.c = dosemu.c;
415         hd_geometry.h = dosemu.h;
416         hd_geometry.s = dosemu.s;
417         hd_geometry.offset += dosemu.offset;
418         sectors = (size - hd_geometry.offset) >> hd_geometry.sector_shift;
419
420         hd_geometry.hsrc = hd_geometry.ssrc = "DOSEMU";
421     }
422
423     if (CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)))
424         hd_geometry.c = v;
425     if (CMD_HASDATA(p = getcmditem("h")) && (v = atou(p))) {
426         hd_geometry.h = v;
427         hd_geometry.hsrc = "cmd";
428     }
429     if (CMD_HASDATA(p = getcmditem("s")) && (v = atou(p))) {
430         hd_geometry.s = v;
431         hd_geometry.ssrc = "cmd";
432     }
433
434     if (!hd_geometry.h || !hd_geometry.s) {
435         int h, s, max_h, max_s;
436
437         max_h = hd_geometry.h;
438         max_s = hd_geometry.s;
439
440         if (!(max_h | max_s)) {
441             /* Look for a FAT superblock and if we find something that looks
442                enough like one, use geometry from that.  This takes care of
443                megafloppy images and unpartitioned hard disks. */
444             const struct fat_extra *extra = NULL;
445             const struct fat_super *fs = (const struct fat_super *)
446                 ((char *)where + hd_geometry.offset);
447
448             if ((fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) &&
449                 (fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) &&
450                 fs->bpb_bytspersec == 512 &&
451                 fs->bpb_numheads >= 1 && fs->bpb_numheads <= 256 &&
452                 fs->bpb_secpertrk >= 1 && fs->bpb_secpertrk <= 63) {
453                 extra =
454                     fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
455                 if (!
456                     (extra->bs_bootsig == 0x29 && extra->bs_filsystype[0] == 'F'
457                      && extra->bs_filsystype[1] == 'A'
458                      && extra->bs_filsystype[2] == 'T'))
459                     extra = NULL;
460             }
461             if (extra) {
462                 hd_geometry.driveno = extra->bs_drvnum & 0x80;
463                 max_h = fs->bpb_numheads;
464                 max_s = fs->bpb_secpertrk;
465                 hd_geometry.hsrc = hd_geometry.ssrc = "FAT";
466             }
467         }
468
469         if (!(max_h | max_s)) {
470             /* No FAT filesystem found to steal geometry from... */
471             if ((sectors < 4096 * 2) && (hd_geometry.sector_shift == 9)) {
472                 int ok = 0;
473                 unsigned int xsectors = sectors;
474
475                 hd_geometry.driveno = 0;        /* Assume floppy */
476
477                 while (!ok) {
478                     /* Assume it's a floppy drive, guess a geometry */
479                     unsigned int type, track;
480                     int c, h, s = 0;
481
482                     if (xsectors < 320 * 2) {
483                         c = 40;
484                         h = 1;
485                         type = 1;
486                     } else if (xsectors < 640 * 2) {
487                         c = 40;
488                         h = 2;
489                         type = 1;
490                     } else if (xsectors < 1200 * 2) {
491                         c = 80;
492                         h = 2;
493                         type = 3;
494                     } else if (xsectors < 1440 * 2) {
495                         c = 80;
496                         h = 2;
497                         type = 2;
498                     } else if (xsectors < 2880 * 2) {
499                         c = 80;
500                         h = 2;
501                         type = 4;
502                     } else {
503                         c = 80;
504                         h = 2;
505                         type = 6;
506                     }
507                     track = c * h;
508                     while (c < 256) {
509                         s = xsectors / track;
510                         if (s < 63 && (xsectors % track) == 0) {
511                             ok = 1;
512                             break;
513                         }
514                         c++;
515                         track += h;
516                     }
517                     if (ok) {
518                         max_h = h;
519                         max_s = s;
520                         hd_geometry.hsrc = hd_geometry.ssrc = "fd";
521                     } else {
522                         /* No valid floppy geometry, fake it by simulating broken
523                            sectors at the end of the image... */
524                         xsectors++;
525                     }
526
527                     hd_geometry.type = type;
528                 }
529             } else {
530                 /* Assume it is a hard disk image and scan for a partition table */
531                 const struct ptab_entry *ptab = (const struct ptab_entry *)
532                     ((char *)where + hd_geometry.offset + (512 - 2 - 4 * 16));
533
534                 /* Assume hard disk */
535                 if (!hd_geometry.driveno)
536                     hd_geometry.driveno = 0x80;
537
538                 if (*(uint16_t *) ((char *)where + hd_geometry.offset + 512 - 2) == 0xaa55) {
539                     for (i = 0; i < 4; i++) {
540                         if (ptab[i].type && !(ptab[i].active & 0x7f)) {
541                             s = (ptab[i].start_s & 0x3f);
542                             h = ptab[i].start_h + 1;
543
544                             if (max_h < h)
545                                 max_h = h;
546                             if (max_s < s)
547                                 max_s = s;
548
549                             s = (ptab[i].end_s & 0x3f);
550                             h = ptab[i].end_h + 1;
551
552                             if (max_h < h) {
553                                 max_h = h;
554                                 hd_geometry.hsrc = "MBR";
555                             }
556                             if (max_s < s) {
557                                 max_s = s;
558                                 hd_geometry.ssrc = "MBR";
559                             }
560                         }
561                     }
562                 }
563
564                 hd_geometry.type = 0;
565             }
566         }
567
568         if (!max_h)
569             max_h = xsectors > 2097152 ? 255 : 64;
570         if (!max_s)
571             max_s = xsectors > 2097152 ? 63 : 32;
572
573         hd_geometry.h    = max_h;
574         hd_geometry.s    = max_s;
575     }
576
577     if (!hd_geometry.c)
578         hd_geometry.c = xsectors / (hd_geometry.h * hd_geometry.s);
579
580     if ((p = getcmditem("floppy")) != CMD_NOTFOUND) {
581         hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0;
582     } else if ((p = getcmditem("harddisk")) != CMD_NOTFOUND) {
583         hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80;
584     }
585
586     if (hd_geometry.driveno & 0x80) {
587         hd_geometry.type = 0;   /* Type = hard disk */
588     } else {
589         if (hd_geometry.type == 0)
590             hd_geometry.type = 0x10;    /* ATAPI floppy, e.g. LS-120 */
591     }
592
593     if ((size - hd_geometry.offset) & 0x1ff) {
594         puts("MEMDISK: Image has fractional end sector\n");
595     }
596     if (sectors % (hd_geometry.h * hd_geometry.s)) {
597         puts("MEMDISK: Image seems to have fractional end cylinder\n");
598     }
599     if ((hd_geometry.c * hd_geometry.h * hd_geometry.s) > sectors) {
600         puts("MEMDISK: Image appears to be truncated\n");
601     }
602
603     return &hd_geometry;
604 }
605
606 /*
607  * Find a $PnP installation check structure; return (ES << 16) + DI value
608  */
609 static uint32_t pnp_install_check(void)
610 {
611     uint32_t *seg;
612     unsigned char *p, csum;
613     int i, len;
614
615     for (seg = (uint32_t *) 0xf0000; seg < (uint32_t *) 0x100000; seg += 4) {
616         if (*seg == ('$' + ('P' << 8) + ('n' << 16) + ('P' << 24))) {
617             p = (unsigned char *)seg;
618             len = p[5];
619             if (len < 0x21)
620                 continue;
621             csum = 0;
622             for (i = len; i; i--)
623                 csum += *p++;
624             if (csum != 0)
625                 continue;
626
627             return (0xf000 << 16) + (uint16_t) (unsigned long)seg;
628         }
629     }
630
631     return 0;
632 }
633
634 /*
635  * Relocate the real-mode code to a new segment
636  */
637 struct gdt_ptr {
638     uint16_t limit;
639     uint32_t base;
640 } __attribute__ ((packed));
641
642 static void set_seg_base(uint32_t gdt_base, int seg, uint32_t v)
643 {
644     *(uint16_t *) (gdt_base + seg + 2) = v;
645     *(uint8_t *) (gdt_base + seg + 4) = v >> 16;
646     *(uint8_t *) (gdt_base + seg + 7) = v >> 24;
647 }
648
649 static void relocate_rm_code(uint32_t newbase)
650 {
651     uint32_t gdt_base;
652     uint32_t oldbase = rm_args.rm_base;
653     uint32_t delta = newbase - oldbase;
654
655     cli();
656     memmove((void *)newbase, (void *)oldbase, rm_args.rm_size);
657
658     rm_args.rm_return += delta;
659     rm_args.rm_intcall += delta;
660     rm_args.rm_bounce += delta;
661     rm_args.rm_base += delta;
662     rm_args.rm_gdt += delta;
663     rm_args.rm_pmjmp += delta;
664     rm_args.rm_rmjmp += delta;
665
666     gdt_base = rm_args.rm_gdt;
667
668     *(uint32_t *) (gdt_base + 2) = gdt_base;    /* GDT self-pointer */
669
670     /* Segments 0x10 and 0x18 are real-mode-based */
671     set_seg_base(gdt_base, 0x10, rm_args.rm_base);
672     set_seg_base(gdt_base, 0x18, rm_args.rm_base);
673
674     asm volatile ("lgdtl %0"::"m" (*(char *)gdt_base));
675
676     *(uint32_t *) rm_args.rm_pmjmp += delta;
677     *(uint16_t *) rm_args.rm_rmjmp += delta >> 4;
678
679     rm_args.rm_handle_interrupt += delta;
680
681     sti();
682 }
683
684 static uint8_t checksum_buf(const void *buf, int count)
685 {
686     const uint8_t *p = buf;
687     uint8_t c = 0;
688
689     while (count--)
690         c += *p++;
691
692     return c;
693 }
694
695 static int stack_needed(void)
696 {
697   const unsigned int min_stack = 128;   /* Minimum stack size */
698   const unsigned int def_stack = 512;   /* Default stack size */
699   unsigned int v = 0;
700   const char *p;
701
702   if (CMD_HASDATA(p = getcmditem("stack")))
703     v = atou(p);
704
705   if (!v)
706     v = def_stack;
707
708   if (v < min_stack)
709     v = min_stack;
710
711   return v;
712 }
713
714 /*
715  * Set max memory by reservation
716  * Adds reservations to data in INT15h to prevent access to the top of RAM
717  * if there's any above the point specified.
718  */
719 void setmaxmem(unsigned long long restop_ull)
720 {
721     uint32_t restop;
722     struct e820range *ep;
723     const int int15restype = 2;
724
725     /* insertrange() works on uint32_t */
726     restop = min(restop_ull, UINT32_MAX);
727     /* printf("  setmaxmem  '%08x%08x'  => %08x\n",
728         (unsigned int)(restop_ull>>32), (unsigned int)restop_ull, restop); */
729
730     for (ep = ranges; ep->type != -1U; ep++) {
731         if (ep->type == 1) {    /* Only if available */
732             if (ep->start >= restop) {
733                 /* printf("  %08x -> 2\n", ep->start); */
734                 ep->type = int15restype;
735             } else if (ep[1].start > restop) {
736                 /* printf("  +%08x =2; cut %08x\n", restop, ep->start); */
737                 insertrange(restop, (ep[1].start - restop), int15restype);
738             }
739         }
740     }
741     parse_mem();
742 }
743
744 struct real_mode_args rm_args;
745
746 /*
747  * Actual setup routine
748  * Returns the drive number (which is then passed in %dl to the
749  * called routine.)
750  */
751 void setup(const struct real_mode_args *rm_args_ptr)
752 {
753     unsigned int bin_size;
754     char *memdisk_hook;
755     struct memdisk_header *hptr;
756     struct patch_area *pptr;
757     struct mBFT *mbft;
758     uint16_t driverseg;
759     uint32_t driverptr, driveraddr;
760     uint16_t dosmem_k;
761     uint32_t stddosmem;
762     const struct geometry *geometry;
763     unsigned int total_size;
764     unsigned int cmdline_len, stack_len, e820_len;
765     const struct edd4_bvd *bvd;
766     const struct edd4_bootcat *boot_cat = 0;
767     com32sys_t regs;
768     uint32_t ramdisk_image, ramdisk_size;
769     uint32_t boot_base, rm_base;
770     int bios_drives;
771     int do_edd = 1;             /* 0 = no, 1 = yes, default is yes */
772     int do_eltorito = 0;        /* default is no */
773     int no_bpt;                 /* No valid BPT presented */
774     uint32_t boot_seg = 0;      /* Meaning 0000:7C00 */
775     uint32_t boot_len = 512;    /* One sector */
776     const char *p;
777
778     /* We need to copy the rm_args into their proper place */
779     memcpy(&rm_args, rm_args_ptr, sizeof rm_args);
780     sti();                      /* ... then interrupts are safe */
781
782     /* Show signs of life */
783     printf("%s  %s\n", memdisk_version, copyright);
784
785     if (!shdr->ramdisk_image || !shdr->ramdisk_size)
786         die("MEMDISK: No ramdisk image specified!\n");
787
788     ramdisk_image = shdr->ramdisk_image;
789     ramdisk_size = shdr->ramdisk_size;
790
791     e820map_init();             /* Initialize memory data structure */
792     get_mem();                  /* Query BIOS for memory map */
793     parse_mem();                /* Parse memory map */
794
795     printf("Ramdisk at 0x%08x, length 0x%08x\n", ramdisk_image, ramdisk_size);
796
797     unzip_if_needed(&ramdisk_image, &ramdisk_size);
798
799     geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
800
801     if (getcmditem("edd") != CMD_NOTFOUND ||
802         getcmditem("ebios") != CMD_NOTFOUND)
803         do_edd = 1;
804     else if (getcmditem("noedd") != CMD_NOTFOUND ||
805              getcmditem("noebios") != CMD_NOTFOUND ||
806              getcmditem("cbios") != CMD_NOTFOUND)
807         do_edd = 0;
808     else
809         do_edd = (geometry->driveno & 0x80) ? 1 : 0;
810
811     if (getcmditem("iso") != CMD_NOTFOUND) {
812         do_eltorito = 1;
813         do_edd = 1;             /* Mandatory */
814     }
815
816     /* Choose the appropriate installable memdisk hook */
817     if (do_eltorito) {
818         if (geometry->sector_shift == 11) {
819             bin_size = (int)&_binary_memdisk_iso_2048_bin_size;
820             memdisk_hook = (char *)&_binary_memdisk_iso_2048_bin_start;
821         } else {
822             bin_size = (int)&_binary_memdisk_iso_512_bin_size;
823             memdisk_hook = (char *)&_binary_memdisk_iso_512_bin_start;
824         }
825     } else {
826         if (do_edd) {
827             bin_size = (int)&_binary_memdisk_edd_512_bin_size;
828             memdisk_hook = (char *)&_binary_memdisk_edd_512_bin_start;
829         } else {
830             bin_size = (int)&_binary_memdisk_chs_512_bin_size;
831             memdisk_hook = (char *)&_binary_memdisk_chs_512_bin_start;
832         }
833     }
834
835     /* Reserve the ramdisk memory */
836     insertrange(ramdisk_image, ramdisk_size, 2);
837     parse_mem();                /* Recompute variables */
838
839     /* Figure out where it needs to go */
840     hptr = (struct memdisk_header *)memdisk_hook;
841     pptr = (struct patch_area *)(memdisk_hook + hptr->patch_offs);
842
843     dosmem_k = rdz_16(BIOS_BASEMEM);
844     pptr->mdi.olddosmem = dosmem_k;
845     stddosmem = dosmem_k << 10;
846     /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
847     if (stddosmem > dos_mem)
848         stddosmem = dos_mem;
849
850     pptr->driveno = geometry->driveno;
851     pptr->drivetype = geometry->type;
852     pptr->cylinders = geometry->c;      /* Possible precision loss */
853     pptr->heads = geometry->h;
854     pptr->sectors = geometry->s;
855     pptr->mdi.disksize = geometry->sectors;
856     pptr->mdi.diskbuf = ramdisk_image + geometry->offset;
857     pptr->mdi.sector_shift = geometry->sector_shift;
858     pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
859
860     pptr->mdi.bootloaderid = shdr->type_of_loader;
861
862     pptr->configflags = CONFIG_SAFEINT; /* Default */
863     /* Set config flags */
864     if (getcmditem("ro") != CMD_NOTFOUND) {
865         pptr->configflags |= CONFIG_READONLY;
866     }
867     if (getcmditem("raw") != CMD_NOTFOUND) {
868         pptr->configflags &= ~CONFIG_MODEMASK;
869         pptr->configflags |= CONFIG_RAW;
870     }
871     if (getcmditem("bigraw") != CMD_NOTFOUND) {
872         pptr->configflags &= ~CONFIG_MODEMASK;
873         pptr->configflags |= CONFIG_BIGRAW | CONFIG_RAW;
874     }
875     if (getcmditem("int") != CMD_NOTFOUND) {
876         pptr->configflags &= ~CONFIG_MODEMASK;
877         /* pptr->configflags |= 0; */
878     }
879     if (getcmditem("safeint") != CMD_NOTFOUND) {
880         pptr->configflags &= ~CONFIG_MODEMASK;
881         pptr->configflags |= CONFIG_SAFEINT;
882     }
883
884     printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n",
885            (geometry->driveno & 0x80) ? "hd" : "fd",
886            geometry->driveno & 0x7f,
887            geometry->sectors >> 1,
888            (geometry->sectors & 1) ? ".5" : "",
889            geometry->c, geometry->h, geometry->s,
890            geometry->hsrc, geometry->ssrc,
891            do_edd ? "on" : "off",
892            pptr->configflags & CONFIG_READONLY ? "ro" : "rw");
893
894     puts("Using ");
895     switch (pptr->configflags & CONFIG_MODEMASK) {
896     case 0:
897         puts("standard INT 15h");
898         break;
899     case CONFIG_SAFEINT:
900         puts("safe INT 15h");
901         break;
902     case CONFIG_RAW:
903         puts("raw");
904         break;
905     case CONFIG_RAW | CONFIG_BIGRAW:
906         puts("big real mode raw");
907         break;
908     default:
909         printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK);
910         break;
911     }
912     puts(" access to high memory\n");
913
914     /* Set up a drive parameter table */
915     if (geometry->driveno & 0x80) {
916         /* Hard disk */
917         pptr->dpt.hd.max_cyl = geometry->c - 1;
918         pptr->dpt.hd.max_head = geometry->h - 1;
919         pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08 : 0;
920     } else {
921         /* Floppy - most of these fields are bogus and mimic
922            a 1.44 MB floppy drive */
923         pptr->dpt.fd.specify1 = 0xdf;
924         pptr->dpt.fd.specify2 = 0x02;
925         pptr->dpt.fd.delay = 0x25;
926         pptr->dpt.fd.sectors = geometry->s;
927         pptr->dpt.fd.bps = 0x02;
928         pptr->dpt.fd.isgap = 0x12;
929         pptr->dpt.fd.dlen = 0xff;
930         pptr->dpt.fd.fgap = 0x6c;
931         pptr->dpt.fd.ffill = 0xf6;
932         pptr->dpt.fd.settle = 0x0f;
933         pptr->dpt.fd.mstart = 0x05;
934         pptr->dpt.fd.maxtrack = geometry->c - 1;
935         pptr->dpt.fd.cmos = geometry->type > 5 ? 5 : geometry->type;
936
937         pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
938     }
939
940     /* Set up an EDD drive parameter table */
941     if (do_edd) {
942         pptr->edd_dpt.sectors = geometry->sectors;
943         /* The EDD spec has this as <= 15482880  sectors (1024x240x63);
944            this seems to make very little sense.  Try for something saner. */
945         if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) {
946             pptr->edd_dpt.c = geometry->c;
947             pptr->edd_dpt.h = geometry->h;
948             pptr->edd_dpt.s = geometry->s;
949             /* EDD-4 states that invalid geometry should be returned
950              * for INT 0x13, AH=0x48 "EDD Get Disk Parameters" call on an
951              * El Torito ODD.  Check for 2048-byte sector size
952              */
953             if (geometry->sector_shift != 11)
954                 pptr->edd_dpt.flags |= 0x0002;  /* Geometry valid */
955         }
956         if (!(geometry->driveno & 0x80)) {
957             /* Floppy drive.  Mark it as a removable device with
958                media change notification; media is present. */
959             pptr->edd_dpt.flags |= 0x0014;
960         }
961
962         pptr->edd_dpt.devpath[0] = pptr->mdi.diskbuf;
963         pptr->edd_dpt.chksum = -checksum_buf(&pptr->edd_dpt.dpikey, 73 - 30);
964     }
965
966     if (do_eltorito) {
967         bvd = (struct edd4_bvd *)(ramdisk_image + 17 * 2048);
968         boot_cat =
969             (struct edd4_bootcat *)(ramdisk_image + bvd->boot_cat * 2048);
970         pptr->cd_pkt.type = boot_cat->initial_entry.media_type; /* Cheat */
971         pptr->cd_pkt.driveno = geometry->driveno;
972         pptr->cd_pkt.start = boot_cat->initial_entry.load_block;
973         boot_seg = pptr->cd_pkt.load_seg = boot_cat->initial_entry.load_seg;
974         pptr->cd_pkt.sect_count = boot_cat->initial_entry.sect_count;
975         boot_len = pptr->cd_pkt.sect_count * 512;
976         pptr->cd_pkt.geom1 = (uint8_t)(pptr->cylinders) & 0xFF;
977         pptr->cd_pkt.geom2 =
978             (uint8_t)(pptr->sectors) | (uint8_t)((pptr->cylinders >> 2) & 0xC0);
979         pptr->cd_pkt.geom3 = (uint8_t)(pptr->heads);
980     }
981
982     if ((p = getcmditem("mem")) != CMD_NOTFOUND) {
983         setmaxmem(suffix_number(p));
984     }
985
986     /* The size is given by hptr->total_size plus the size of the E820
987        map -- 12 bytes per range; we may need as many as 2 additional
988        ranges (each insertrange() can worst-case turn 1 area into 3)
989        plus the terminating range, over what nranges currently show. */
990     total_size = hptr->total_size;      /* Actual memdisk code */
991     e820_len = (nranges + 3) * sizeof(ranges[0]);
992     total_size += e820_len;             /* E820 memory ranges */
993     cmdline_len = strlen(shdr->cmdline) + 1;
994     total_size += cmdline_len;          /* Command line */
995     stack_len = stack_needed();
996     total_size += stack_len;            /* Stack */
997     printf("Code %u, meminfo %u, cmdline %u, stack %u\n",
998            hptr->total_size, e820_len, cmdline_len, stack_len);
999     printf("Total size needed = %u bytes, allocating %uK\n",
1000            total_size, (total_size + 0x3ff) >> 10);
1001
1002     if (total_size > dos_mem)
1003         die("MEMDISK: Insufficient low memory\n");
1004
1005     driveraddr = stddosmem - total_size;
1006     driveraddr &= ~0x3FF;
1007
1008     printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
1009            stddosmem, dos_mem, driveraddr);
1010
1011     /* Reserve this range of memory */
1012     wrz_16(BIOS_BASEMEM, driveraddr >> 10);
1013     insertrange(driveraddr, dos_mem - driveraddr, 2);
1014     parse_mem();
1015
1016     pptr->mem1mb = low_mem >> 10;
1017     pptr->mem16mb = high_mem >> 16;
1018     if (low_mem == (15 << 20)) {
1019         /* lowmem maxed out */
1020         uint32_t int1588mem = (high_mem >> 10) + (low_mem >> 10);
1021         pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
1022     } else {
1023         pptr->memint1588 = low_mem >> 10;
1024     }
1025
1026     printf("1588: 0x%04x  15E801: 0x%04x 0x%04x\n",
1027            pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
1028
1029     driverseg = driveraddr >> 4;
1030     driverptr = driverseg << 16;
1031
1032     /* Anything beyond the end is for the stack */
1033     pptr->mystack = (uint16_t) (stddosmem - driveraddr);
1034
1035     pptr->mdi.oldint13.uint32 = rdz_32(BIOS_INT13);
1036     pptr->mdi.oldint15.uint32 = rdz_32(BIOS_INT15);
1037
1038     /* Adjust the E820 table: if there are null ranges (type 0)
1039        at the end, change them to type end of list (-1).
1040        This is necessary for the driver to be able to report end
1041        of list correctly. */
1042     while (nranges && ranges[nranges - 1].type == 0) {
1043         ranges[--nranges].type = -1;
1044     }
1045
1046     if (getcmditem("nopassany") != CMD_NOTFOUND) {
1047         printf("nopassany specified - we're the only drive of any kind\n");
1048         bios_drives = 0;
1049         pptr->drivecnt = 0;
1050         no_bpt = 1;
1051         pptr->mdi.oldint13.uint32 = driverptr + hptr->iret_offs;
1052         wrz_8(BIOS_EQUIP, rdz_8(BIOS_EQUIP) & ~0xc1);
1053         wrz_8(BIOS_HD_COUNT, 0);
1054     } else if (getcmditem("nopass") != CMD_NOTFOUND) {
1055         printf("nopass specified - we're the only drive\n");
1056         bios_drives = 0;
1057         pptr->drivecnt = 0;
1058         no_bpt = 1;
1059     } else {
1060         /* Query drive parameters of this type */
1061         memset(&regs, 0, sizeof regs);
1062         regs.es = 0;
1063         regs.eax.b[1] = 0x08;
1064         regs.edx.b[0] = geometry->driveno & 0x80;
1065         intcall(0x13, &regs, &regs);
1066
1067         /* Note: per suggestion from the Interrupt List, consider
1068            INT 13 08 to have failed if the sector count in CL is zero. */
1069         if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) {
1070             printf("INT 13 08: Failure, assuming this is the only drive\n");
1071             pptr->drivecnt = 0;
1072             no_bpt = 1;
1073         } else {
1074             printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
1075                    regs.edx.b[0], regs.es, regs.edi.w[0]);
1076             pptr->drivecnt = regs.edx.b[0];
1077             no_bpt = !(regs.es | regs.edi.w[0]);
1078         }
1079
1080         /* Compare what INT 13h returned with the appropriate equipment byte */
1081         if (geometry->driveno & 0x80) {
1082             bios_drives = rdz_8(BIOS_HD_COUNT);
1083         } else {
1084             uint8_t equip = rdz_8(BIOS_EQUIP);
1085
1086             if (equip & 1)
1087                 bios_drives = (equip >> 6) + 1;
1088             else
1089                 bios_drives = 0;
1090         }
1091
1092         if (pptr->drivecnt > bios_drives) {
1093             printf("BIOS equipment byte says count = %d, go with that\n",
1094                    bios_drives);
1095             pptr->drivecnt = bios_drives;
1096         }
1097     }
1098
1099     /* Add ourselves to the drive count */
1100     pptr->drivecnt++;
1101
1102     /* Discontiguous drive space.  There is no really good solution for this. */
1103     if (pptr->drivecnt <= (geometry->driveno & 0x7f))
1104         pptr->drivecnt = (geometry->driveno & 0x7f) + 1;
1105
1106     /* Probe for contiguous range of BIOS drives starting with driveno */
1107     pptr->driveshiftlimit = probe_drive_range(geometry->driveno) + 1;
1108     if ((pptr->driveshiftlimit & 0x80) != (geometry->driveno & 0x80))
1109         printf("We lost the last drive in our class of drives.\n");
1110     printf("Drive probing gives drive shift limit: 0x%02x\n",
1111         pptr->driveshiftlimit);
1112
1113     /* Pointer to the command line */
1114     pptr->mdi.cmdline.seg_off.offset = bin_size + (nranges + 1) * sizeof(ranges[0]);
1115     pptr->mdi.cmdline.seg_off.segment = driverseg;
1116
1117     /* Copy driver followed by E820 table followed by command line */
1118     {
1119         unsigned char *dpp = (unsigned char *)(driverseg << 4);
1120
1121         /* Adjust these pointers to point to the installed image */
1122         /* Careful about the order here... the image isn't copied yet! */
1123         pptr = (struct patch_area *)(dpp + hptr->patch_offs);
1124         hptr = (struct memdisk_header *)dpp;
1125
1126         /* Actually copy to low memory */
1127         dpp = mempcpy(dpp, memdisk_hook, bin_size);
1128         dpp = mempcpy(dpp, ranges, (nranges + 1) * sizeof(ranges[0]));
1129         dpp = mempcpy(dpp, shdr->cmdline, cmdline_len);
1130     }
1131
1132     /* Note the previous INT 13h hook in the "safe hook" structure */
1133     hptr->safe_hook.old_hook.uint32 = pptr->mdi.oldint13.uint32;
1134
1135     /* Re-fill the "safe hook" mBFT field with the physical address */
1136     mbft = (struct mBFT *)(((const char *)hptr) + hptr->safe_hook.mbft);
1137     hptr->safe_hook.mbft = (size_t)mbft;
1138
1139     /* Update various BIOS magic data areas (gotta love this shit) */
1140
1141     if (geometry->driveno & 0x80) {
1142         /* Update BIOS hard disk count */
1143         uint8_t nhd = pptr->drivecnt;
1144
1145         if (nhd > 128)
1146             nhd = 128;
1147
1148         if (!do_eltorito)
1149             wrz_8(BIOS_HD_COUNT, nhd);
1150     } else {
1151         /* Update BIOS floppy disk count */
1152         uint8_t equip = rdz_8(BIOS_EQUIP);
1153         uint8_t nflop = pptr->drivecnt;
1154
1155         if (nflop > 4)          /* Limit of equipment byte */
1156             nflop = 4;
1157
1158         equip &= 0x3E;
1159         if (nflop)
1160             equip |= ((nflop - 1) << 6) | 0x01;
1161
1162         wrz_8(BIOS_EQUIP, equip);
1163
1164         /* Install DPT pointer if this was the only floppy */
1165         if (getcmditem("dpt") != CMD_NOTFOUND ||
1166             ((nflop == 1 || no_bpt) && getcmditem("nodpt") == CMD_NOTFOUND)) {
1167             /* Do install a replacement DPT into INT 1Eh */
1168             pptr->mdi.dpt_ptr =
1169                 hptr->patch_offs + offsetof(struct patch_area, dpt);
1170         }
1171     }
1172
1173     /* Complete the mBFT */
1174     mbft->acpi.signature[0] = 'm';      /* "mBFT" */
1175     mbft->acpi.signature[1] = 'B';
1176     mbft->acpi.signature[2] = 'F';
1177     mbft->acpi.signature[3] = 'T';
1178     mbft->safe_hook = (size_t)&hptr->safe_hook;
1179     mbft->acpi.checksum = -checksum_buf(mbft, mbft->acpi.length);
1180
1181     /* Install the interrupt handlers */
1182     printf("old: int13 = %08x  int15 = %08x  int1e = %08x\n",
1183            rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1184
1185     wrz_32(BIOS_INT13, driverptr + hptr->int13_offs);
1186     wrz_32(BIOS_INT15, driverptr + hptr->int15_offs);
1187     if (pptr->mdi.dpt_ptr)
1188         wrz_32(BIOS_INT1E, driverptr + pptr->mdi.dpt_ptr);
1189
1190     printf("new: int13 = %08x  int15 = %08x  int1e = %08x\n",
1191            rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1192
1193     /* Figure out entry point */
1194     if (!boot_seg) {
1195         boot_base = 0x7c00;
1196         shdr->sssp = 0x7c00;
1197         shdr->csip = 0x7c00;
1198     } else {
1199         boot_base = boot_seg << 4;
1200         shdr->sssp = boot_seg << 16;
1201         shdr->csip = boot_seg << 16;
1202     }
1203
1204     /* Relocate the real-mode code to below the stub */
1205     rm_base = (driveraddr - rm_args.rm_size) & ~15;
1206     if (rm_base < boot_base + boot_len)
1207         die("MEMDISK: bootstrap too large to load\n");
1208
1209     relocate_rm_code(rm_base);
1210
1211     /* Reboot into the new "disk" */
1212     puts("Loading boot sector... ");
1213
1214     memcpy((void *)boot_base,
1215            (char *)pptr->mdi.diskbuf + geometry->boot_lba * 512,
1216            boot_len);
1217
1218     if (getcmditem("pause") != CMD_NOTFOUND) {
1219         puts("press any key to boot... ");
1220         regs.eax.w[0] = 0;
1221         intcall(0x16, &regs, NULL);
1222     }
1223
1224     puts("booting...\n");
1225
1226     /* On return the assembly code will jump to the boot vector */
1227     shdr->esdi = pnp_install_check();
1228     shdr->edx = geometry->driveno;
1229 }
1230