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