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