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