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