syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / memdisk / setup.c
1 #ident "$Id: setup.c,v 1.50 2005/04/29 06:04:45 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2001-2004 H. Peter Anvin - All Rights Reserved
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
20 const char memdisk_version[] =
21 "MEMDISK " VERSION " " DATE;
22 const char copyright[] = 
23 "Copyright " FIRSTYEAR "-" COPYYEAR " H. Peter Anvin";
24
25 extern const char _binary_memdisk_bin_start[], _binary_memdisk_bin_end[];
26 extern const char _binary_memdisk_bin_size[]; /* Weird, I know */
27
28 struct memdisk_header {
29   uint16_t int13_offs;
30   uint16_t int15_offs;
31   uint16_t patch_offs;
32   uint16_t total_size;
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 _pad1;              /* Padding */
59
60     uint32_t old_fd_dpt;        /* Extension: pointer to old INT 1Eh */
61   } fd;
62 } dpt_t;
63
64 struct patch_area {
65   uint32_t diskbuf;
66   uint32_t disksize;
67   uint16_t cmdline_off, cmdline_seg;
68   
69   uint32_t oldint13;
70   uint32_t oldint15;
71
72   uint16_t olddosmem;
73   uint8_t  bootloaderid;
74
75   uint8_t  _pad[3];
76   uint16_t memint1588;
77
78   uint16_t cylinders;
79   uint16_t heads;
80   uint32_t sectors;
81
82   uint32_t mem1mb;
83   uint32_t mem16mb;
84
85   uint8_t  driveno;
86   uint8_t  drivetype;
87   uint8_t  drivecnt;
88   uint8_t  configflags;
89 #define CONFIG_READONLY 0x01
90 #define CONFIG_RAW      0x02
91 #define CONFIG_BIGRAW   0x08            /* MUST be 8! */
92
93   uint16_t mystack;
94   uint16_t statusptr;
95
96   dpt_t dpt;
97 };
98
99 /* This is the header in the boot sector/setup area */
100 struct setup_header {
101   char cmdline[0x1f1];
102   uint8_t setup_secs;
103   uint16_t syssize;
104   uint16_t swap_dev;
105   uint16_t ram_size;
106   uint16_t vid_mode;
107   uint16_t root_dev;
108   uint16_t boot_flag;
109   uint16_t jump;
110   char header[4];
111   uint16_t version;
112   uint32_t realmode_swtch;
113   uint32_t start_sys;
114   uint8_t type_of_loader;
115   uint8_t loadflags;
116   uint16_t setup_move_size;
117   uint32_t code32_start;
118   uint32_t ramdisk_image;
119   uint32_t ramdisk_size;
120   uint32_t bootsect_kludge;
121   uint16_t head_end_ptr;
122   uint16_t pad1;
123   uint32_t cmd_line_ptr;
124   uint32_t initrd_addr_max;
125 };
126
127 const struct setup_header * const shdr = (struct setup_header *)(LOW_SEG << 4);
128
129 /* Access to high memory */
130
131 /* Access to objects in the zero page */
132 static inline void
133 wrz_8(uint32_t addr, uint8_t data)
134 {
135   *((uint8_t *)addr) = data;
136 }
137 static inline void
138 wrz_16(uint32_t addr, uint16_t data)
139 {
140   *((uint16_t *)addr) = data;
141 }
142 static inline void
143 wrz_32(uint32_t addr, uint32_t data)
144 {
145   *((uint32_t *)addr) = data;
146 }
147 static inline uint8_t
148 rdz_8(uint32_t addr)
149 {
150   return *((uint8_t *)addr);
151 }
152 static inline uint16_t
153 rdz_16(uint32_t addr)
154 {
155   return *((uint16_t *)addr);
156 }
157 static inline uint32_t
158 rdz_32(uint32_t addr)
159 {
160   return *((uint32_t *)addr);
161 }
162
163 /* Addresses in the zero page */
164 #define BIOS_INT13      (0x13*4) /* INT 13h vector */
165 #define BIOS_INT15      (0x15*4) /* INT 15h vector */
166 #define BIOS_INT1E      (0x1E*4) /* INT 1Eh vector */
167 #define BIOS_INT40      (0x40*4) /* INT 13h vector */
168 #define BIOS_INT41      (0x41*4) /* INT 41h vector */
169 #define BIOS_INT46      (0x46*4) /* INT 46h vector */
170 #define BIOS_BASEMEM    0x413    /* Amount of DOS memory */
171 #define BIOS_EQUIP      0x410    /* BIOS equipment list */
172 #define BIOS_HD_COUNT   0x475    /* Number of hard drives present */
173
174 /*
175  * Routine to seek for a command-line item and return a pointer
176  * to the data portion, if present
177  */
178
179 /* Magic return values */
180 #define CMD_NOTFOUND   ((char *)-1) /* Not found */
181 #define CMD_BOOL       ((char *)-2) /* Found boolean option */
182 #define CMD_HASDATA(X) ((int)(X) >= 0)
183
184 const char *getcmditem(const char *what)
185 {
186   const char *p;
187   const char *wp = what;
188   int match = 0;
189
190   for ( p = shdr->cmdline ; *p ; p++ ) {
191     switch ( match ) {
192     case 0:                     /* Ground state */
193       if ( *p == ' ' )
194         break;
195
196       wp = what;
197       match = 1;
198       /* Fall through */
199
200     case 1:                     /* Matching */
201       if ( *wp == '\0' ) {
202         if ( *p == '=' )
203           return p+1;
204         else if ( *p == ' ' )
205           return CMD_BOOL;
206         else {
207           match = 2;
208           break;
209         }
210       }
211       if ( *p != *wp++ )
212         match = 2;
213       break;
214
215     case 2:                     /* Mismatch, skip rest of option */
216       if ( *p == ' ' )
217         match = 0;              /* Next option */
218       break;
219     }
220   }
221     
222   /* Check for matching string at end of line */
223   if ( match == 1 && *wp == '\0' )
224     return CMD_BOOL;
225   
226   return CMD_NOTFOUND;
227 }
228
229 /*
230  * Check to see if this is a gzip image
231  */
232 #define UNZIP_ALIGN 512
233
234 extern void _end;               /* Symbol signalling end of data */
235
236 void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
237 {
238   uint32_t where = *where_p;
239   uint32_t size = *size_p;
240   uint32_t zbytes;
241   uint32_t startrange, endrange;
242   uint32_t gzdatasize, gzwhere;
243   uint32_t orig_crc, offset;
244   uint32_t target = 0;
245   int i, okmem;
246
247   /* Is it a gzip image? */
248   if (check_zip ((void *)where, size, &zbytes, &gzdatasize,
249                  &orig_crc, &offset) == 0) {
250
251     if (offset + zbytes > size) {
252       /* Assertion failure; check_zip is supposed to guarantee this
253          never happens. */
254       puts("internal error: check_zip returned nonsense\n");
255       die();
256     }
257
258     /* Find a good place to put it: search memory ranges in descending order
259        until we find one that is legal and fits */
260     okmem = 0;
261     for ( i = nranges-1 ; i >= 0 ; i-- ) {
262       /* We can't use > 4G memory (32 bits only.)  Truncate to 2^32-1
263          so we don't have to deal with funny wraparound issues. */
264       
265       /* Must be memory */
266       if ( ranges[i].type != 1 )
267         continue;
268
269       /* Range start */
270       if ( ranges[i].start >= 0xFFFFFFFF )
271         continue;
272       startrange = (uint32_t)ranges[i].start;
273
274       /* Range end (0 for end means 2^64) */
275       endrange = ((ranges[i+1].start >= 0xFFFFFFFF ||
276                    ranges[i+1].start == 0)
277                   ? 0xFFFFFFFF : (uint32_t)ranges[i+1].start);
278
279       /* Make sure we don't overwrite ourselves */
280       if ( startrange < (uint32_t)&_end )
281         startrange = (uint32_t)&_end;
282
283       /* Allow for alignment */
284       startrange = (ranges[i].start + (UNZIP_ALIGN-1)) & ~(UNZIP_ALIGN-1);
285
286       /* In case we just killed the whole range... */
287       if ( startrange >= endrange )
288         continue;
289
290       /* Must be large enough... don't rely on gzwhere for this (wraparound) */
291       if ( endrange-startrange < gzdatasize )
292         continue;
293
294       /* This is where the gz image should be put if we put it in this range */
295       gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN-1);
296
297       /* Cast to uint64_t just in case we're flush with the top byte */
298       if ( (uint64_t)where+size >= gzwhere && where < endrange ) {
299         /* Need to move source data to avoid compressed/uncompressed overlap */
300         uint32_t newwhere;
301         
302         if ( gzwhere-startrange < size )
303           continue;             /* Can't fit both old and new */
304         
305         newwhere = (gzwhere - size) & ~(UNZIP_ALIGN-1);
306         printf("Moving compressed data from 0x%08x to 0x%08x\n",
307                where, newwhere);
308         
309         /* Our memcpy() is OK, because we always move from a higher
310            address to a lower one */
311         memcpy((void *)newwhere, (void *)where, size);
312         where = newwhere;
313       }
314
315       target = gzwhere;
316       okmem = 1;
317       break;
318     }
319
320     if ( !okmem ) {
321       printf("Not enough memory to decompress image (need 0x%08x bytes)\n",
322              gzdatasize);
323       die();
324     }
325
326     printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
327            target, gzdatasize);
328
329     *size_p  = gzdatasize;
330     *where_p = (uint32_t)unzip((void *)(where + offset), zbytes,
331                                gzdatasize, orig_crc, (void *)target);
332   }
333 }
334
335 /*
336  * Figure out the "geometry" of the disk in question
337  */
338 struct geometry {
339   uint32_t sectors;             /* 512-byte sector count */
340   uint32_t c, h, s;             /* C/H/S geometry */
341   uint32_t offset;              /* Byte offset for disk */
342   uint8_t type;                 /* Type byte for INT 13h AH=08h */
343   uint8_t driveno;              /* Drive no */
344 };
345
346 static const struct geometry geometries[] =
347
348   {  360*2, 40,  2,  9, 0, 0x01, 0 }, /*  360 K */
349   {  720*2, 80,  2,  9, 0, 0x03, 0 }, /*  720 K*/
350   { 1200*2, 80,  2, 15, 0, 0x02, 0 }, /* 1200 K */
351   { 1440*2, 80,  2, 18, 0, 0x04, 0 }, /* 1440 K */
352   { 1680*2, 80,  2, 21, 0, 0x04, 0 }, /* 1680 K */
353   { 1722*2, 82,  2, 21, 0, 0x04, 0 }, /* 1722 K */
354   { 2880*2, 80,  2, 36, 0, 0x06, 0 }, /* 2880 K */
355   { 3840*2, 80,  2, 48, 0, 0x06, 0 }, /* 3840 K */
356 };
357 #define known_geometries (sizeof(geometries)/sizeof(struct geometry))
358
359 /* Format of a DOS partition table entry */
360 struct ptab_entry {
361   uint8_t active;
362   uint8_t start_h, start_s, start_c;
363   uint8_t type;
364   uint8_t end_h, end_s, end_c;
365   uint32_t start;
366   uint32_t size;
367 };
368
369 /* Format of a DOSEMU header */
370 struct dosemu_header {
371   uint8_t magic[7];             /* DOSEMU\0 */
372   uint32_t h;
373   uint32_t s;
374   uint32_t c;
375   uint32_t offset;
376   uint8_t pad[105];
377 } __attribute__((packed));
378
379 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
380
381 const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size)
382 {
383   static struct geometry hd_geometry = { 0, 0, 0, 0, 0, 0, 0x80 };
384   struct ptab_entry ptab[4];    /* Partition table buffer */
385   struct dosemu_header dosemu;
386   unsigned int sectors, v;
387   unsigned int max_c, max_h, max_s;
388   unsigned int c, h, s, offset;
389   int i;
390   int drive_specified;
391   const char *p;
392
393   printf("command line: %s\n", shdr->cmdline);
394
395   offset = 0;
396   if ( CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)) )
397     offset = v;
398
399   sectors = (size-offset) >> 9;
400   for ( i = 0 ; i < known_geometries ; i++ ) {
401     if ( sectors == geometries[i].sectors ) {
402       hd_geometry = geometries[i];
403       break;
404     }
405   }
406
407   hd_geometry.sectors = sectors;
408   hd_geometry.offset  = offset;
409
410   /* Do we have a DOSEMU header? */
411   memcpy(&dosemu, (char *)where+hd_geometry.offset, sizeof dosemu);
412   if ( !memcmp("DOSEMU", dosemu.magic, 7) ) {
413     /* Always a hard disk unless overruled by command-line options */
414     hd_geometry.driveno = 0x80;
415     hd_geometry.type = 0;
416     hd_geometry.c = dosemu.c;
417     hd_geometry.h = dosemu.h;
418     hd_geometry.s = dosemu.s;
419     hd_geometry.offset += dosemu.offset;
420     sectors = (size-hd_geometry.offset) >> 9;
421   }
422
423   if ( CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)) )
424     hd_geometry.c = v;
425   if ( CMD_HASDATA(p = getcmditem("h")) && (v = atou(p)) )
426     hd_geometry.h = v;
427   if ( CMD_HASDATA(p = getcmditem("s")) && (v = atou(p)) )
428     hd_geometry.s = v;
429   
430   if ( getcmditem("floppy") != CMD_NOTFOUND ) {
431     hd_geometry.driveno = 0;
432     if ( hd_geometry.type == 0 )
433       hd_geometry.type = 0x10;  /* ATAPI floppy, e.g. LS-120 */
434     drive_specified = 1;
435   } else if ( getcmditem("harddisk") != CMD_NOTFOUND ) {
436     hd_geometry.driveno = 0x80;
437     hd_geometry.type = 0;
438     drive_specified = 1;
439   }
440
441   if ( (hd_geometry.c == 0) || (hd_geometry.h == 0) ||
442        (hd_geometry.s == 0) ) {
443     /* Hard disk image, need to examine the partition table for geometry */
444     memcpy(&ptab, (char *)where+hd_geometry.offset+(512-2-4*16), sizeof ptab);
445     
446     max_c = max_h = 0;  max_s = 1;
447     for ( i = 0 ; i < 4 ; i++ ) {
448       if ( ptab[i].type ) {
449         c = ptab[i].start_c + (ptab[i].start_s >> 6);
450         s = (ptab[i].start_s & 0x3f);
451         h = ptab[i].start_h;
452         
453         if ( max_c < c ) max_c = c;
454         if ( max_h < h ) max_h = h;
455         if ( max_s < s ) max_s = s;
456         
457         c = ptab[i].end_c + (ptab[i].end_s >> 6);
458         s = (ptab[i].end_s & 0x3f);
459         h = ptab[i].end_h;
460         
461         if ( max_c < c ) max_c = c;
462         if ( max_h < h ) max_h = h;
463         if ( max_s < s ) max_s = s;
464       }
465     }
466     
467     max_c++; max_h++;           /* Convert to count (1-based) */
468     
469     if ( !hd_geometry.h )
470       hd_geometry.h = max_h;
471     if ( !hd_geometry.s )
472       hd_geometry.s = max_s;
473     if ( !hd_geometry.c )
474       hd_geometry.c = sectors/(hd_geometry.h*hd_geometry.s);
475   }
476
477   if ( (size-hd_geometry.offset) & 0x1ff ) {
478     puts("MEMDISK: Image has fractional end sector\n");
479   }
480   if ( sectors % (hd_geometry.h*hd_geometry.s) ) {
481     puts("MEMDISK: Image seems to have fractional end cylinder\n");
482   }
483   if ( (hd_geometry.c*hd_geometry.h*hd_geometry.s) > sectors ) {
484     puts("MEMDISK: Image appears to be truncated\n");
485   }
486
487   return &hd_geometry;
488 }
489
490 /*
491  * Jump here if all hope is gone...
492  */
493 void __attribute__((noreturn)) die(void)
494 {
495   asm volatile("sti");
496   for(;;)
497     asm volatile("hlt");
498 }
499
500 #define STACK_NEEDED    512     /* Number of bytes of stack */
501
502 /*
503  * Actual setup routine
504  * Returns the drive number (which is then passed in %dl to the
505  * called routine.)
506  */
507 syscall_t syscall;
508 void *sys_bounce;
509
510 uint32_t setup(syscall_t cs_syscall, void *cs_bounce)
511 {
512   unsigned int bin_size = (int) &_binary_memdisk_bin_size;
513   struct memdisk_header *hptr;
514   struct patch_area *pptr;
515   uint16_t driverseg;
516   uint32_t driverptr, driveraddr;
517   uint16_t dosmem_k;
518   uint32_t stddosmem;
519   const struct geometry *geometry;
520   int total_size, cmdlinelen;
521   com32sys_t regs;
522   uint32_t ramdisk_image, ramdisk_size;
523
524   /* Set up global variables */
525   syscall = cs_syscall;
526   sys_bounce = cs_bounce;
527
528   /* Show signs of life */
529   printf("%s  %s\n", memdisk_version, copyright);
530
531   if ( !shdr->ramdisk_image || !shdr->ramdisk_size ) {
532     puts("MEMDISK: No ramdisk image specified!\n");
533     die();
534   }
535
536   ramdisk_image = shdr->ramdisk_image;
537   ramdisk_size  = shdr->ramdisk_size;
538
539   e820map_init();               /* Initialize memory data structure */
540   get_mem();                    /* Query BIOS for memory map */
541   parse_mem();                  /* Parse memory map */
542
543   printf("Ramdisk at 0x%08x, length 0x%08x\n",
544          ramdisk_image, ramdisk_size);
545
546   unzip_if_needed(&ramdisk_image, &ramdisk_size);
547
548   geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
549
550   printf("Disk is %s, %u K, C/H/S = %u/%u/%u\n",
551          geometry->driveno ? "hard disk" : "floppy",
552          geometry->sectors >> 1,
553          geometry->c, geometry->h, geometry->s);
554
555   /* Reserve the ramdisk memory */
556   insertrange(ramdisk_image, ramdisk_size, 2);
557   parse_mem();                  /* Recompute variables */
558
559   /* Figure out where it needs to go */
560   hptr = (struct memdisk_header *) &_binary_memdisk_bin_start;
561   pptr = (struct patch_area *)(_binary_memdisk_bin_start + hptr->patch_offs);
562
563   dosmem_k = rdz_16(BIOS_BASEMEM);
564   pptr->olddosmem = dosmem_k;
565   stddosmem = dosmem_k << 10;
566   /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
567   if ( stddosmem > dos_mem )
568     stddosmem = dos_mem;
569
570   pptr->driveno   = geometry->driveno;
571   pptr->drivetype = geometry->type;
572   pptr->cylinders = geometry->c;
573   pptr->heads     = geometry->h;
574   pptr->sectors   = geometry->s;
575   pptr->disksize  = geometry->sectors;
576   pptr->diskbuf   = ramdisk_image + geometry->offset;
577   pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
578
579   pptr->bootloaderid = shdr->type_of_loader;
580
581   pptr->configflags = 0;
582   /* Set config flags */
583   if ( getcmditem("ro") != CMD_NOTFOUND ) {
584     puts("Marking disk readonly\n");
585     pptr->configflags |= CONFIG_READONLY;
586   }
587   if ( getcmditem("raw") != CMD_NOTFOUND ) {
588     puts("Using raw access to high memory\n");
589     pptr->configflags |= CONFIG_RAW;
590   }
591   if ( getcmditem("bigraw") != CMD_NOTFOUND ) {
592     puts("Using raw access to high memory - assuming big real mode\n");
593     pptr->configflags |= CONFIG_BIGRAW|CONFIG_RAW;
594   }
595
596   /* Set up a drive parameter table */
597   if ( geometry->driveno & 0x80 ) {
598     /* Hard disk */
599     pptr->dpt.hd.max_cyl  = geometry->c-1;
600     pptr->dpt.hd.max_head = geometry->h-1;
601     pptr->dpt.hd.ctrl     = (geometry->h > 8) ? 0x08: 0;
602   } else {
603     /* Floppy - most of these fields are bogus and mimic
604        a 1.44 MB floppy drive */
605     pptr->dpt.fd.specify1 = 0xdf;
606     pptr->dpt.fd.specify2 = 0x02;
607     pptr->dpt.fd.delay    = 0x25;
608     pptr->dpt.fd.sectors  = geometry->s;
609     pptr->dpt.fd.bps      = 0x02;
610     pptr->dpt.fd.isgap    = 0x12;
611     pptr->dpt.fd.dlen     = 0xff;
612     pptr->dpt.fd.fgap     = 0x6c;
613     pptr->dpt.fd.ffill    = 0xf6;
614     pptr->dpt.fd.settle   = 0x0f;
615     pptr->dpt.fd.mstart   = 0x05;
616
617     pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
618   }
619
620   /* The size is given by hptr->total_size plus the size of the E820
621      map -- 12 bytes per range; we may need as many as 2 additional
622      ranges (each insertrange() can worst-case turn 1 area into 3)
623      plus the terminating range, over what nranges currently show. */
624   cmdlinelen = strlen(shdr->cmdline)+1;
625   total_size  =  hptr->total_size;              /* Actual memdisk code */
626   total_size += (nranges+3)*sizeof(ranges[0]);  /* E820 memory ranges */
627   total_size += cmdlinelen;                     /* Command line */
628   total_size += STACK_NEEDED;                   /* Stack */
629   printf("Total size needed = %u bytes, allocating %uK\n",
630          total_size, (total_size+0x3ff) >> 10);
631
632   if ( total_size > dos_mem ) {
633     puts("MEMDISK: Insufficient low memory\n");
634     die();
635   }
636
637   driveraddr  = stddosmem - total_size;
638   driveraddr &= ~0x3FF;
639
640   printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
641          stddosmem, dos_mem, driveraddr);
642
643   /* Reserve this range of memory */
644   wrz_16(BIOS_BASEMEM, driveraddr >> 10);
645   insertrange(driveraddr, dos_mem-driveraddr, 2);
646   parse_mem();
647
648   pptr->mem1mb     = low_mem  >> 10;
649   pptr->mem16mb    = high_mem >> 16;
650   if ( low_mem == (15 << 20) ) {
651     /* lowmem maxed out */
652     uint32_t int1588mem = (high_mem >> 10)+(low_mem >> 10);
653     pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
654   } else {
655     pptr->memint1588 = low_mem >> 10;
656   }
657
658   printf("1588: 0x%04x  15E801: 0x%04x 0x%04x\n",
659          pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
660
661   driverseg = driveraddr >> 4;
662   driverptr = driverseg  << 16;
663
664   /* Anything beyond the end is for the stack */
665   pptr->mystack    = (uint16_t)(stddosmem-driveraddr);
666
667   pptr->oldint13 = rdz_32(BIOS_INT13);
668   pptr->oldint15 = rdz_32(BIOS_INT15);
669
670   /* Adjust the E820 table: if there are null ranges (type 0)
671      at the end, change them to type end of list (-1).
672      This is necessary for the driver to be able to report end
673      of list correctly. */
674   while ( nranges && ranges[nranges-1].type == 0 ) {
675     ranges[--nranges].type = -1;
676   }
677
678   /* Query drive parameters of this type */
679   memset(&regs, 0, sizeof regs);
680   regs.es = 0;
681   regs.eax.b[1] = 0x08;
682   regs.edx.b[0] = geometry->driveno;
683   syscall(0x13, &regs, &regs);
684   
685   if ( regs.eflags.l & 1 ) {
686     printf("INT 13 08: Failure, assuming this is the only drive\n");
687     pptr->drivecnt = 1;
688   } else {
689     printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
690            regs.edx.b[0], regs.es, regs.edi.w[0]);
691     pptr->drivecnt = regs.edx.b[0]+1;
692   }
693
694   /* Pointer to the command line */
695   pptr->cmdline_off = bin_size + (nranges+1)*sizeof(ranges[0]);
696   pptr->cmdline_seg = driverseg;
697
698   /* Copy driver followed by E820 table followed by command line */
699   {
700     unsigned char *dpp = (unsigned char *)(driverseg << 4);
701     dpp = memcpy_endptr(dpp, &_binary_memdisk_bin_start, bin_size);
702     dpp = memcpy_endptr(dpp, ranges, (nranges+1)*sizeof(ranges[0]));
703     dpp = memcpy_endptr(dpp, shdr->cmdline, cmdlinelen+1);
704   }
705
706   /* Install the interrupt handlers */
707   printf("old: int13 = %08x  int15 = %08x\n",
708          rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
709   
710   wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
711   wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
712   
713   printf("new: int13 = %08x  int15 = %08x\n",
714          rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
715
716   /* Update various BIOS magic data areas (gotta love this shit) */
717
718   if ( geometry->driveno & 0x80 ) {
719     /* Update BIOS hard disk count */
720     wrz_8(BIOS_HD_COUNT, rdz_8(BIOS_HD_COUNT)+1);
721   } else {
722     /* Update BIOS floppy disk count */
723     uint8_t equip = rdz_8(BIOS_EQUIP);
724     if ( equip & 1 ) {
725       if ( (equip & (3 << 6)) != (3 << 6) ) {
726         equip += (1 << 6);
727       }
728     } else {
729       equip |= 1;
730       equip &= ~(3 << 6);
731     }
732     wrz_8(BIOS_EQUIP, equip);
733   }
734
735   /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
736   puts("Loading boot sector... ");
737
738   memset(&regs, 0, sizeof regs);
739   // regs.es = 0;
740   regs.eax.w[0] = 0x0201;       /* Read sector */
741   regs.ebx.w[0] = 0x7c00;       /* 0000:7C00 */
742   regs.ecx.w[0] = 1;            /* One sector */
743   regs.edx.w[0] = geometry->driveno;
744   syscall(0x13, &regs, &regs);
745
746   if ( regs.eflags.l & 1 ) {
747     puts("MEMDISK: Failed to load new boot sector\n");
748     die();
749   }
750   
751   if ( getcmditem("pause") != CMD_NOTFOUND ) {
752     puts("press any key to boot... ");
753     regs.eax.w[0] = 0;
754     syscall(0x16, &regs, NULL);
755   }
756
757   puts("booting...\n");
758
759   /* On return the assembly code will jump to the boot vector */
760   return geometry->driveno;
761 }