source: bootcd/isolinux/syslinux-6.03/memdisk/setup.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 35.3 KB
Line 
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
27const char memdisk_version[] = "MEMDISK " VERSION_STR " " DATE;
28const char copyright[] =
29    "Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin et al";
30
31extern const char _binary_memdisk_chs_512_bin_start[];
32extern const char _binary_memdisk_chs_512_bin_end[];
33extern const char _binary_memdisk_chs_512_bin_size[];
34extern const char _binary_memdisk_edd_512_bin_start[];
35extern const char _binary_memdisk_edd_512_bin_end[];
36extern const char _binary_memdisk_edd_512_bin_size[];
37extern const char _binary_memdisk_iso_512_bin_start[];
38extern const char _binary_memdisk_iso_512_bin_end[];
39extern const char _binary_memdisk_iso_512_bin_size[];
40extern const char _binary_memdisk_iso_2048_bin_start[];
41extern const char _binary_memdisk_iso_2048_bin_end[];
42extern 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 */
48struct 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
61extern 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
74static 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
124extern const char _end[];               /* Symbol signalling end of data */
125
126void 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 */
240struct 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 */
252struct 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 */
262struct 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));
270struct 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 */
304struct 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
315static 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 */
609static 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 */
637struct gdt_ptr {
638    uint16_t limit;
639    uint32_t base;
640} __attribute__ ((packed));
641
642static 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
649static 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#if __SIZEOF_POINTER__ == 4
675    asm volatile ("lgdtl %0"::"m" (*(char *)gdt_base));
676#elif __SIZEOF_POINTER__ == 8
677    asm volatile ("lgdt %0"::"m" (*(char *)gdt_base));
678#else
679#error "unsupported architecture"
680#endif
681
682    *(uint32_t *) rm_args.rm_pmjmp += delta;
683    *(uint16_t *) rm_args.rm_rmjmp += delta >> 4;
684
685    rm_args.rm_handle_interrupt += delta;
686
687    sti();
688}
689
690static uint8_t checksum_buf(const void *buf, int count)
691{
692    const uint8_t *p = buf;
693    uint8_t c = 0;
694
695    while (count--)
696        c += *p++;
697
698    return c;
699}
700
701static int stack_needed(void)
702{
703  const unsigned int min_stack = 128;   /* Minimum stack size */
704  const unsigned int def_stack = 512;   /* Default stack size */
705  unsigned int v = 0;
706  const char *p;
707
708  if (CMD_HASDATA(p = getcmditem("stack")))
709    v = atou(p);
710
711  if (!v)
712    v = def_stack;
713
714  if (v < min_stack)
715    v = min_stack;
716
717  return v;
718}
719
720/*
721 * Set max memory by reservation
722 * Adds reservations to data in INT15h to prevent access to the top of RAM
723 * if there's any above the point specified.
724 */
725void setmaxmem(unsigned long long restop_ull)
726{
727    uint32_t restop;
728    struct e820range *ep;
729    const int int15restype = 2;
730
731    /* insertrange() works on uint32_t */
732    restop = min(restop_ull, UINT32_MAX);
733    /* printf("  setmaxmem  '%08x%08x'  => %08x\n",
734        (unsigned int)(restop_ull>>32), (unsigned int)restop_ull, restop); */
735
736    for (ep = ranges; ep->type != -1U; ep++) {
737        if (ep->type == 1) {    /* Only if available */
738            if (ep->start >= restop) {
739                /* printf("  %08x -> 2\n", ep->start); */
740                ep->type = int15restype;
741            } else if (ep[1].start > restop) {
742                /* printf("  +%08x =2; cut %08x\n", restop, ep->start); */
743                insertrange(restop, (ep[1].start - restop), int15restype);
744            }
745        }
746    }
747    parse_mem();
748}
749
750struct real_mode_args rm_args;
751
752/*
753 * Actual setup routine
754 * Returns the drive number (which is then passed in %dl to the
755 * called routine.)
756 */
757void setup(const struct real_mode_args *rm_args_ptr)
758{
759    unsigned int bin_size;
760    char *memdisk_hook;
761    struct memdisk_header *hptr;
762    struct patch_area *pptr;
763    struct mBFT *mbft;
764    uint16_t driverseg;
765    uint32_t driverptr, driveraddr;
766    uint16_t dosmem_k;
767    uint32_t stddosmem;
768    const struct geometry *geometry;
769    unsigned int total_size;
770    unsigned int cmdline_len, stack_len, e820_len;
771    const struct edd4_bvd *bvd;
772    const struct edd4_bootcat *boot_cat = 0;
773    com32sys_t regs;
774    uint32_t ramdisk_image, ramdisk_size;
775    uint32_t boot_base, rm_base;
776    int bios_drives;
777    int do_edd = 1;             /* 0 = no, 1 = yes, default is yes */
778    int do_eltorito = 0;        /* default is no */
779    int no_bpt;                 /* No valid BPT presented */
780    uint32_t boot_seg = 0;      /* Meaning 0000:7C00 */
781    uint32_t boot_len = 512;    /* One sector */
782    const char *p;
783
784    /* We need to copy the rm_args into their proper place */
785    memcpy(&rm_args, rm_args_ptr, sizeof rm_args);
786    sti();                      /* ... then interrupts are safe */
787
788    /* Show signs of life */
789    printf("%s  %s\n", memdisk_version, copyright);
790
791    if (!shdr->ramdisk_image || !shdr->ramdisk_size)
792        die("MEMDISK: No ramdisk image specified!\n");
793
794    ramdisk_image = shdr->ramdisk_image;
795    ramdisk_size = shdr->ramdisk_size;
796
797    e820map_init();             /* Initialize memory data structure */
798    get_mem();                  /* Query BIOS for memory map */
799    parse_mem();                /* Parse memory map */
800
801    printf("Ramdisk at 0x%08x, length 0x%08x\n", ramdisk_image, ramdisk_size);
802
803    unzip_if_needed(&ramdisk_image, &ramdisk_size);
804
805    geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
806
807    if (getcmditem("edd") != CMD_NOTFOUND ||
808        getcmditem("ebios") != CMD_NOTFOUND)
809        do_edd = 1;
810    else if (getcmditem("noedd") != CMD_NOTFOUND ||
811             getcmditem("noebios") != CMD_NOTFOUND ||
812             getcmditem("cbios") != CMD_NOTFOUND)
813        do_edd = 0;
814    else
815        do_edd = (geometry->driveno & 0x80) ? 1 : 0;
816
817    if (getcmditem("iso") != CMD_NOTFOUND) {
818        do_eltorito = 1;
819        do_edd = 1;             /* Mandatory */
820    }
821
822    /* Choose the appropriate installable memdisk hook */
823    if (do_eltorito) {
824        if (geometry->sector_shift == 11) {
825            bin_size = (int)&_binary_memdisk_iso_2048_bin_size;
826            memdisk_hook = (char *)&_binary_memdisk_iso_2048_bin_start;
827        } else {
828            bin_size = (int)&_binary_memdisk_iso_512_bin_size;
829            memdisk_hook = (char *)&_binary_memdisk_iso_512_bin_start;
830        }
831    } else {
832        if (do_edd) {
833            bin_size = (int)&_binary_memdisk_edd_512_bin_size;
834            memdisk_hook = (char *)&_binary_memdisk_edd_512_bin_start;
835        } else {
836            bin_size = (int)&_binary_memdisk_chs_512_bin_size;
837            memdisk_hook = (char *)&_binary_memdisk_chs_512_bin_start;
838        }
839    }
840
841    /* Reserve the ramdisk memory */
842    insertrange(ramdisk_image, ramdisk_size, 2);
843    parse_mem();                /* Recompute variables */
844
845    /* Figure out where it needs to go */
846    hptr = (struct memdisk_header *)memdisk_hook;
847    pptr = (struct patch_area *)(memdisk_hook + hptr->patch_offs);
848
849    dosmem_k = rdz_16(BIOS_BASEMEM);
850    pptr->mdi.olddosmem = dosmem_k;
851    stddosmem = dosmem_k << 10;
852    /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
853    if (stddosmem > dos_mem)
854        stddosmem = dos_mem;
855
856    pptr->driveno = geometry->driveno;
857    pptr->drivetype = geometry->type;
858    pptr->cylinders = geometry->c;      /* Possible precision loss */
859    pptr->heads = geometry->h;
860    pptr->sectors = geometry->s;
861    pptr->mdi.disksize = geometry->sectors;
862    pptr->mdi.diskbuf = ramdisk_image + geometry->offset;
863    pptr->mdi.sector_shift = geometry->sector_shift;
864    pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
865
866    pptr->mdi.bootloaderid = shdr->type_of_loader;
867
868    pptr->configflags = CONFIG_SAFEINT; /* Default */
869    /* Set config flags */
870    if (getcmditem("ro") != CMD_NOTFOUND) {
871        pptr->configflags |= CONFIG_READONLY;
872    }
873    if (getcmditem("raw") != CMD_NOTFOUND) {
874        pptr->configflags &= ~CONFIG_MODEMASK;
875        pptr->configflags |= CONFIG_RAW;
876    }
877    if (getcmditem("bigraw") != CMD_NOTFOUND) {
878        pptr->configflags &= ~CONFIG_MODEMASK;
879        pptr->configflags |= CONFIG_BIGRAW | CONFIG_RAW;
880    }
881    if (getcmditem("int") != CMD_NOTFOUND) {
882        pptr->configflags &= ~CONFIG_MODEMASK;
883        /* pptr->configflags |= 0; */
884    }
885    if (getcmditem("safeint") != CMD_NOTFOUND) {
886        pptr->configflags &= ~CONFIG_MODEMASK;
887        pptr->configflags |= CONFIG_SAFEINT;
888    }
889
890    printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n",
891           (geometry->driveno & 0x80) ? "hd" : "fd",
892           geometry->driveno & 0x7f,
893           geometry->sectors >> 1,
894           (geometry->sectors & 1) ? ".5" : "",
895           geometry->c, geometry->h, geometry->s,
896           geometry->hsrc, geometry->ssrc,
897           do_edd ? "on" : "off",
898           pptr->configflags & CONFIG_READONLY ? "ro" : "rw");
899
900    puts("Using ");
901    switch (pptr->configflags & CONFIG_MODEMASK) {
902    case 0:
903        puts("standard INT 15h");
904        break;
905    case CONFIG_SAFEINT:
906        puts("safe INT 15h");
907        break;
908    case CONFIG_RAW:
909        puts("raw");
910        break;
911    case CONFIG_RAW | CONFIG_BIGRAW:
912        puts("big real mode raw");
913        break;
914    default:
915        printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK);
916        break;
917    }
918    puts(" access to high memory\n");
919
920    /* Set up a drive parameter table */
921    if (geometry->driveno & 0x80) {
922        /* Hard disk */
923        pptr->dpt.hd.max_cyl = geometry->c - 1;
924        pptr->dpt.hd.max_head = geometry->h - 1;
925        pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08 : 0;
926    } else {
927        /* Floppy - most of these fields are bogus and mimic
928           a 1.44 MB floppy drive */
929        pptr->dpt.fd.specify1 = 0xdf;
930        pptr->dpt.fd.specify2 = 0x02;
931        pptr->dpt.fd.delay = 0x25;
932        pptr->dpt.fd.sectors = geometry->s;
933        pptr->dpt.fd.bps = 0x02;
934        pptr->dpt.fd.isgap = 0x12;
935        pptr->dpt.fd.dlen = 0xff;
936        pptr->dpt.fd.fgap = 0x6c;
937        pptr->dpt.fd.ffill = 0xf6;
938        pptr->dpt.fd.settle = 0x0f;
939        pptr->dpt.fd.mstart = 0x05;
940        pptr->dpt.fd.maxtrack = geometry->c - 1;
941        pptr->dpt.fd.cmos = geometry->type > 5 ? 5 : geometry->type;
942
943        pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
944    }
945
946    /* Set up an EDD drive parameter table */
947    if (do_edd) {
948        pptr->edd_dpt.sectors = geometry->sectors;
949        /* The EDD spec has this as <= 15482880  sectors (1024x240x63);
950           this seems to make very little sense.  Try for something saner. */
951        if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) {
952            pptr->edd_dpt.c = geometry->c;
953            pptr->edd_dpt.h = geometry->h;
954            pptr->edd_dpt.s = geometry->s;
955            /* EDD-4 states that invalid geometry should be returned
956             * for INT 0x13, AH=0x48 "EDD Get Disk Parameters" call on an
957             * El Torito ODD.  Check for 2048-byte sector size
958             */
959            if (geometry->sector_shift != 11)
960                pptr->edd_dpt.flags |= 0x0002;  /* Geometry valid */
961        }
962        if (!(geometry->driveno & 0x80)) {
963            /* Floppy drive.  Mark it as a removable device with
964               media change notification; media is present. */
965            pptr->edd_dpt.flags |= 0x0014;
966        }
967
968        pptr->edd_dpt.devpath[0] = pptr->mdi.diskbuf;
969        pptr->edd_dpt.chksum = -checksum_buf(&pptr->edd_dpt.dpikey, 73 - 30);
970    }
971
972    if (do_eltorito) {
973        bvd = (struct edd4_bvd *)(ramdisk_image + 17 * 2048);
974        boot_cat =
975            (struct edd4_bootcat *)(ramdisk_image + bvd->boot_cat * 2048);
976        pptr->cd_pkt.type = boot_cat->initial_entry.media_type; /* Cheat */
977        pptr->cd_pkt.driveno = geometry->driveno;
978        pptr->cd_pkt.start = boot_cat->initial_entry.load_block;
979        boot_seg = pptr->cd_pkt.load_seg = boot_cat->initial_entry.load_seg;
980        pptr->cd_pkt.sect_count = boot_cat->initial_entry.sect_count;
981        boot_len = pptr->cd_pkt.sect_count * 512;
982        pptr->cd_pkt.geom1 = (uint8_t)(pptr->cylinders) & 0xFF;
983        pptr->cd_pkt.geom2 =
984            (uint8_t)(pptr->sectors) | (uint8_t)((pptr->cylinders >> 2) & 0xC0);
985        pptr->cd_pkt.geom3 = (uint8_t)(pptr->heads);
986    }
987
988    if ((p = getcmditem("mem")) != CMD_NOTFOUND) {
989        setmaxmem(suffix_number(p));
990    }
991
992    /* The size is given by hptr->total_size plus the size of the E820
993       map -- 12 bytes per range; we may need as many as 2 additional
994       ranges (each insertrange() can worst-case turn 1 area into 3)
995       plus the terminating range, over what nranges currently show. */
996    total_size = hptr->total_size;      /* Actual memdisk code */
997    e820_len = (nranges + 3) * sizeof(ranges[0]);
998    total_size += e820_len;             /* E820 memory ranges */
999    cmdline_len = strlen(shdr->cmdline) + 1;
1000    total_size += cmdline_len;          /* Command line */
1001    stack_len = stack_needed();
1002    total_size += stack_len;            /* Stack */
1003    printf("Code %u, meminfo %u, cmdline %u, stack %u\n",
1004           hptr->total_size, e820_len, cmdline_len, stack_len);
1005    printf("Total size needed = %u bytes, allocating %uK\n",
1006           total_size, (total_size + 0x3ff) >> 10);
1007
1008    if (total_size > dos_mem)
1009        die("MEMDISK: Insufficient low memory\n");
1010
1011    driveraddr = stddosmem - total_size;
1012    driveraddr &= ~0x3FF;
1013
1014    printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
1015           stddosmem, dos_mem, driveraddr);
1016
1017    /* Reserve this range of memory */
1018    wrz_16(BIOS_BASEMEM, driveraddr >> 10);
1019    insertrange(driveraddr, dos_mem - driveraddr, 2);
1020    parse_mem();
1021
1022    pptr->mem1mb = low_mem >> 10;
1023    pptr->mem16mb = high_mem >> 16;
1024    if (low_mem == (15 << 20)) {
1025        /* lowmem maxed out */
1026        uint32_t int1588mem = (high_mem >> 10) + (low_mem >> 10);
1027        pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
1028    } else {
1029        pptr->memint1588 = low_mem >> 10;
1030    }
1031
1032    printf("1588: 0x%04x  15E801: 0x%04x 0x%04x\n",
1033           pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
1034
1035    driverseg = driveraddr >> 4;
1036    driverptr = driverseg << 16;
1037
1038    /* Anything beyond the end is for the stack */
1039    pptr->mystack = (uint16_t) (stddosmem - driveraddr);
1040
1041    pptr->mdi.oldint13.uint32 = rdz_32(BIOS_INT13);
1042    pptr->mdi.oldint15.uint32 = rdz_32(BIOS_INT15);
1043
1044    /* Adjust the E820 table: if there are null ranges (type 0)
1045       at the end, change them to type end of list (-1).
1046       This is necessary for the driver to be able to report end
1047       of list correctly. */
1048    while (nranges && ranges[nranges - 1].type == 0) {
1049        ranges[--nranges].type = -1;
1050    }
1051
1052    if (getcmditem("nopassany") != CMD_NOTFOUND) {
1053        printf("nopassany specified - we're the only drive of any kind\n");
1054        bios_drives = 0;
1055        pptr->drivecnt = 0;
1056        no_bpt = 1;
1057        pptr->mdi.oldint13.uint32 = driverptr + hptr->iret_offs;
1058        wrz_8(BIOS_EQUIP, rdz_8(BIOS_EQUIP) & ~0xc1);
1059        wrz_8(BIOS_HD_COUNT, 0);
1060    } else if (getcmditem("nopass") != CMD_NOTFOUND) {
1061        printf("nopass specified - we're the only drive\n");
1062        bios_drives = 0;
1063        pptr->drivecnt = 0;
1064        no_bpt = 1;
1065    } else {
1066        /* Query drive parameters of this type */
1067        memset(&regs, 0, sizeof regs);
1068        regs.es = 0;
1069        regs.eax.b[1] = 0x08;
1070        regs.edx.b[0] = geometry->driveno & 0x80;
1071        intcall(0x13, &regs, &regs);
1072
1073        /* Note: per suggestion from the Interrupt List, consider
1074           INT 13 08 to have failed if the sector count in CL is zero. */
1075        if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) {
1076            printf("INT 13 08: Failure, assuming this is the only drive\n");
1077            pptr->drivecnt = 0;
1078            no_bpt = 1;
1079        } else {
1080            printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
1081                   regs.edx.b[0], regs.es, regs.edi.w[0]);
1082            pptr->drivecnt = regs.edx.b[0];
1083            no_bpt = !(regs.es | regs.edi.w[0]);
1084        }
1085
1086        /* Compare what INT 13h returned with the appropriate equipment byte */
1087        if (geometry->driveno & 0x80) {
1088            bios_drives = rdz_8(BIOS_HD_COUNT);
1089        } else {
1090            uint8_t equip = rdz_8(BIOS_EQUIP);
1091
1092            if (equip & 1)
1093                bios_drives = (equip >> 6) + 1;
1094            else
1095                bios_drives = 0;
1096        }
1097
1098        if (pptr->drivecnt > bios_drives) {
1099            printf("BIOS equipment byte says count = %d, go with that\n",
1100                   bios_drives);
1101            pptr->drivecnt = bios_drives;
1102        }
1103    }
1104
1105    /* Add ourselves to the drive count */
1106    pptr->drivecnt++;
1107
1108    /* Discontiguous drive space.  There is no really good solution for this. */
1109    if (pptr->drivecnt <= (geometry->driveno & 0x7f))
1110        pptr->drivecnt = (geometry->driveno & 0x7f) + 1;
1111
1112    /* Probe for contiguous range of BIOS drives starting with driveno */
1113    pptr->driveshiftlimit = probe_drive_range(geometry->driveno) + 1;
1114    if ((pptr->driveshiftlimit & 0x80) != (geometry->driveno & 0x80))
1115        printf("We lost the last drive in our class of drives.\n");
1116    printf("Drive probing gives drive shift limit: 0x%02x\n",
1117        pptr->driveshiftlimit);
1118
1119    /* Pointer to the command line */
1120    pptr->mdi.cmdline.seg_off.offset = bin_size + (nranges + 1) * sizeof(ranges[0]);
1121    pptr->mdi.cmdline.seg_off.segment = driverseg;
1122
1123    /* Copy driver followed by E820 table followed by command line */
1124    {
1125        unsigned char *dpp = (unsigned char *)(driverseg << 4);
1126
1127        /* Adjust these pointers to point to the installed image */
1128        /* Careful about the order here... the image isn't copied yet! */
1129        pptr = (struct patch_area *)(dpp + hptr->patch_offs);
1130        hptr = (struct memdisk_header *)dpp;
1131
1132        /* Actually copy to low memory */
1133        dpp = mempcpy(dpp, memdisk_hook, bin_size);
1134        dpp = mempcpy(dpp, ranges, (nranges + 1) * sizeof(ranges[0]));
1135        dpp = mempcpy(dpp, shdr->cmdline, cmdline_len);
1136    }
1137
1138    /* Note the previous INT 13h hook in the "safe hook" structure */
1139    hptr->safe_hook.old_hook.uint32 = pptr->mdi.oldint13.uint32;
1140
1141    /* Re-fill the "safe hook" mBFT field with the physical address */
1142    mbft = (struct mBFT *)(((const char *)hptr) + hptr->safe_hook.mbft);
1143    hptr->safe_hook.mbft = (size_t)mbft;
1144
1145    /* Update various BIOS magic data areas (gotta love this shit) */
1146
1147    if (geometry->driveno & 0x80) {
1148        /* Update BIOS hard disk count */
1149        uint8_t nhd = pptr->drivecnt;
1150
1151        if (nhd > 128)
1152            nhd = 128;
1153
1154        if (!do_eltorito)
1155            wrz_8(BIOS_HD_COUNT, nhd);
1156    } else {
1157        /* Update BIOS floppy disk count */
1158        uint8_t equip = rdz_8(BIOS_EQUIP);
1159        uint8_t nflop = pptr->drivecnt;
1160
1161        if (nflop > 4)          /* Limit of equipment byte */
1162            nflop = 4;
1163
1164        equip &= 0x3E;
1165        if (nflop)
1166            equip |= ((nflop - 1) << 6) | 0x01;
1167
1168        wrz_8(BIOS_EQUIP, equip);
1169
1170        /* Install DPT pointer if this was the only floppy */
1171        if (getcmditem("dpt") != CMD_NOTFOUND ||
1172            ((nflop == 1 || no_bpt) && getcmditem("nodpt") == CMD_NOTFOUND)) {
1173            /* Do install a replacement DPT into INT 1Eh */
1174            pptr->mdi.dpt_ptr =
1175                hptr->patch_offs + offsetof(struct patch_area, dpt);
1176        }
1177    }
1178
1179    /* Complete the mBFT */
1180    mbft->acpi.signature[0] = 'm';      /* "mBFT" */
1181    mbft->acpi.signature[1] = 'B';
1182    mbft->acpi.signature[2] = 'F';
1183    mbft->acpi.signature[3] = 'T';
1184    mbft->safe_hook = (size_t)&hptr->safe_hook;
1185    mbft->acpi.checksum = -checksum_buf(mbft, mbft->acpi.length);
1186
1187    /* Install the interrupt handlers */
1188    printf("old: int13 = %08x  int15 = %08x  int1e = %08x\n",
1189           rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1190
1191    wrz_32(BIOS_INT13, driverptr + hptr->int13_offs);
1192    wrz_32(BIOS_INT15, driverptr + hptr->int15_offs);
1193    if (pptr->mdi.dpt_ptr)
1194        wrz_32(BIOS_INT1E, driverptr + pptr->mdi.dpt_ptr);
1195
1196    printf("new: int13 = %08x  int15 = %08x  int1e = %08x\n",
1197           rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1198
1199    /* Figure out entry point */
1200    if (!boot_seg) {
1201        boot_base = 0x7c00;
1202        shdr->sssp = 0x7c00;
1203        shdr->csip = 0x7c00;
1204    } else {
1205        boot_base = boot_seg << 4;
1206        shdr->sssp = boot_seg << 16;
1207        shdr->csip = boot_seg << 16;
1208    }
1209
1210    /* Relocate the real-mode code to below the stub */
1211    rm_base = (driveraddr - rm_args.rm_size) & ~15;
1212    if (rm_base < boot_base + boot_len)
1213        die("MEMDISK: bootstrap too large to load\n");
1214
1215    relocate_rm_code(rm_base);
1216
1217    /* Reboot into the new "disk" */
1218    puts("Loading boot sector... ");
1219
1220    memcpy((void *)boot_base,
1221           (char *)pptr->mdi.diskbuf + geometry->boot_lba * 512,
1222           boot_len);
1223
1224    if (getcmditem("pause") != CMD_NOTFOUND) {
1225        puts("press any key to boot... ");
1226        memset(&regs, 0, sizeof regs);
1227        regs.eax.w[0] = 0;
1228        intcall(0x16, &regs, NULL);
1229    }
1230
1231    puts("booting...\n");
1232
1233    /* On return the assembly code will jump to the boot vector */
1234    shdr->esdi = pnp_install_check();
1235    shdr->edx = geometry->driveno;
1236}
1237
Note: See TracBrowser for help on using the repository browser.