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