Add address aliases for freedos and msdos
[syslinux.git] / memdisk / setup.c
blobba02582f94db50497a9b75d7833e214094aed446
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
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.
11 * ----------------------------------------------------------------------- */
13 #include <stdint.h>
14 #include "e820.h"
15 #include "conio.h"
16 #include "version.h"
17 #include "memdisk.h"
19 const char memdisk_version[] =
20 "MEMDISK " VERSION " " DATE;
21 const char copyright[] =
22 "Copyright " FIRSTYEAR "-" COPYYEAR " H. Peter Anvin";
24 extern const char _binary_memdisk_bin_start[], _binary_memdisk_bin_end[];
25 extern const char _binary_memdisk_bin_size[]; /* Weird, I know */
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;
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 */
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 */
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 */
60 uint32_t old_fd_dpt; /* Extension: pointer to old INT 1Eh */
61 } fd;
62 } dpt_t;
64 /* EDD disk parameter table */
65 struct edd_dpt {
66 uint16_t len; /* Length of table */
67 uint16_t flags; /* Information flags */
68 uint32_t c; /* Physical cylinders (count!) */
69 uint32_t h; /* Physical heads (count!) */
70 uint32_t s; /* Physical sectors/track (count!) */
71 uint64_t sectors; /* Total sectors */
72 uint16_t bytespersec; /* Bytes/sector */
73 uint16_t dpte_off, dpte_seg; /* DPTE pointer */
76 struct patch_area {
77 uint32_t diskbuf;
78 uint32_t disksize;
79 uint16_t cmdline_off, cmdline_seg;
81 uint32_t oldint13;
82 uint32_t oldint15;
84 uint16_t olddosmem;
85 uint8_t bootloaderid;
86 uint8_t maxint13func;
87 #define MAXINT13_NOEDD 0x16
89 uint8_t _pad[2];
90 uint16_t memint1588;
92 uint16_t cylinders;
93 uint16_t heads;
94 uint32_t sectors;
96 uint32_t mem1mb;
97 uint32_t mem16mb;
99 uint8_t driveno;
100 uint8_t drivetype;
101 uint8_t drivecnt;
102 uint8_t configflags;
104 #define CONFIG_READONLY 0x01
105 #define CONFIG_RAW 0x02
106 #define CONFIG_SAFEINT 0x04
107 #define CONFIG_BIGRAW 0x08 /* MUST be 8! */
109 uint16_t mystack;
110 uint16_t statusptr;
112 dpt_t dpt;
113 struct edd_dpt edd_dpt;
116 /* This is the header in the boot sector/setup area */
117 struct setup_header {
118 char cmdline[0x1f1];
119 uint8_t setup_secs;
120 uint16_t syssize;
121 uint16_t swap_dev;
122 uint16_t ram_size;
123 uint16_t vid_mode;
124 uint16_t root_dev;
125 uint16_t boot_flag;
126 uint16_t jump;
127 char header[4];
128 uint16_t version;
129 uint32_t realmode_swtch;
130 uint32_t start_sys;
131 uint8_t type_of_loader;
132 uint8_t loadflags;
133 uint16_t setup_move_size;
134 uint32_t code32_start;
135 uint32_t ramdisk_image;
136 uint32_t ramdisk_size;
137 uint32_t bootsect_kludge;
138 uint16_t head_end_ptr;
139 uint16_t pad1;
140 uint32_t cmd_line_ptr;
141 uint32_t initrd_addr_max;
142 uint32_t esdi;
143 uint32_t edx;
146 struct setup_header * const shdr = (struct setup_header *)(LOW_SEG << 4);
148 /* Access to high memory */
150 /* Access to objects in the zero page */
151 static inline void
152 wrz_8(uint32_t addr, uint8_t data)
154 *((uint8_t *)addr) = data;
156 static inline void
157 wrz_16(uint32_t addr, uint16_t data)
159 *((uint16_t *)addr) = data;
161 static inline void
162 wrz_32(uint32_t addr, uint32_t data)
164 *((uint32_t *)addr) = data;
166 static inline uint8_t
167 rdz_8(uint32_t addr)
169 return *((uint8_t *)addr);
171 static inline uint16_t
172 rdz_16(uint32_t addr)
174 return *((uint16_t *)addr);
176 static inline uint32_t
177 rdz_32(uint32_t addr)
179 return *((uint32_t *)addr);
182 /* Addresses in the zero page */
183 #define BIOS_INT13 (0x13*4) /* INT 13h vector */
184 #define BIOS_INT15 (0x15*4) /* INT 15h vector */
185 #define BIOS_INT1E (0x1E*4) /* INT 1Eh vector */
186 #define BIOS_INT40 (0x40*4) /* INT 13h vector */
187 #define BIOS_INT41 (0x41*4) /* INT 41h vector */
188 #define BIOS_INT46 (0x46*4) /* INT 46h vector */
189 #define BIOS_BASEMEM 0x413 /* Amount of DOS memory */
190 #define BIOS_EQUIP 0x410 /* BIOS equipment list */
191 #define BIOS_HD_COUNT 0x475 /* Number of hard drives present */
194 * Routine to seek for a command-line item and return a pointer
195 * to the data portion, if present
198 /* Magic return values */
199 #define CMD_NOTFOUND ((char *)-1) /* Not found */
200 #define CMD_BOOL ((char *)-2) /* Found boolean option */
201 #define CMD_HASDATA(X) ((int)(X) >= 0)
203 const char *getcmditem(const char *what)
205 const char *p;
206 const char *wp = what;
207 int match = 0;
209 for ( p = shdr->cmdline ; *p ; p++ ) {
210 switch ( match ) {
211 case 0: /* Ground state */
212 if ( *p == ' ' )
213 break;
215 wp = what;
216 match = 1;
217 /* Fall through */
219 case 1: /* Matching */
220 if ( *wp == '\0' ) {
221 if ( *p == '=' )
222 return p+1;
223 else if ( *p == ' ' )
224 return CMD_BOOL;
225 else {
226 match = 2;
227 break;
230 if ( *p != *wp++ )
231 match = 2;
232 break;
234 case 2: /* Mismatch, skip rest of option */
235 if ( *p == ' ' )
236 match = 0; /* Next option */
237 break;
241 /* Check for matching string at end of line */
242 if ( match == 1 && *wp == '\0' )
243 return CMD_BOOL;
245 return CMD_NOTFOUND;
249 * Check to see if this is a gzip image
251 #define UNZIP_ALIGN 512
253 extern void _end; /* Symbol signalling end of data */
255 void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
257 uint32_t where = *where_p;
258 uint32_t size = *size_p;
259 uint32_t zbytes;
260 uint32_t startrange, endrange;
261 uint32_t gzdatasize, gzwhere;
262 uint32_t orig_crc, offset;
263 uint32_t target = 0;
264 int i, okmem;
266 /* Is it a gzip image? */
267 if (check_zip ((void *)where, size, &zbytes, &gzdatasize,
268 &orig_crc, &offset) == 0) {
270 if (offset + zbytes > size) {
271 /* Assertion failure; check_zip is supposed to guarantee this
272 never happens. */
273 puts("internal error: check_zip returned nonsense\n");
274 die();
277 /* Find a good place to put it: search memory ranges in descending order
278 until we find one that is legal and fits */
279 okmem = 0;
280 for ( i = nranges-1 ; i >= 0 ; i-- ) {
281 /* We can't use > 4G memory (32 bits only.) Truncate to 2^32-1
282 so we don't have to deal with funny wraparound issues. */
284 /* Must be memory */
285 if ( ranges[i].type != 1 )
286 continue;
288 /* Range start */
289 if ( ranges[i].start >= 0xFFFFFFFF )
290 continue;
291 startrange = (uint32_t)ranges[i].start;
293 /* Range end (0 for end means 2^64) */
294 endrange = ((ranges[i+1].start >= 0xFFFFFFFF ||
295 ranges[i+1].start == 0)
296 ? 0xFFFFFFFF : (uint32_t)ranges[i+1].start);
298 /* Make sure we don't overwrite ourselves */
299 if ( startrange < (uint32_t)&_end )
300 startrange = (uint32_t)&_end;
302 /* Allow for alignment */
303 startrange = (ranges[i].start + (UNZIP_ALIGN-1)) & ~(UNZIP_ALIGN-1);
305 /* In case we just killed the whole range... */
306 if ( startrange >= endrange )
307 continue;
309 /* Must be large enough... don't rely on gzwhere for this (wraparound) */
310 if ( endrange-startrange < gzdatasize )
311 continue;
313 /* This is where the gz image should be put if we put it in this range */
314 gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN-1);
316 /* Cast to uint64_t just in case we're flush with the top byte */
317 if ( (uint64_t)where+size >= gzwhere && where < endrange ) {
318 /* Need to move source data to avoid compressed/uncompressed overlap */
319 uint32_t newwhere;
321 if ( gzwhere-startrange < size )
322 continue; /* Can't fit both old and new */
324 newwhere = (gzwhere - size) & ~(UNZIP_ALIGN-1);
325 printf("Moving compressed data from 0x%08x to 0x%08x\n",
326 where, newwhere);
328 /* Our memcpy() is OK, because we always move from a higher
329 address to a lower one */
330 memcpy((void *)newwhere, (void *)where, size);
331 where = newwhere;
334 target = gzwhere;
335 okmem = 1;
336 break;
339 if ( !okmem ) {
340 printf("Not enough memory to decompress image (need 0x%08x bytes)\n",
341 gzdatasize);
342 die();
345 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
346 target, gzdatasize);
348 *size_p = gzdatasize;
349 *where_p = (uint32_t)unzip((void *)(where + offset), zbytes,
350 gzdatasize, orig_crc, (void *)target);
355 * Figure out the "geometry" of the disk in question
357 struct geometry {
358 uint32_t sectors; /* 512-byte sector count */
359 uint32_t c, h, s; /* C/H/S geometry */
360 uint32_t offset; /* Byte offset for disk */
361 uint8_t type; /* Type byte for INT 13h AH=08h */
362 uint8_t driveno; /* Drive no */
365 /* Format of a DOS partition table entry */
366 struct ptab_entry {
367 uint8_t active;
368 uint8_t start_h, start_s, start_c;
369 uint8_t type;
370 uint8_t end_h, end_s, end_c;
371 uint32_t start;
372 uint32_t size;
375 /* Format of a DOSEMU header */
376 struct dosemu_header {
377 uint8_t magic[7]; /* DOSEMU\0 */
378 uint32_t h;
379 uint32_t s;
380 uint32_t c;
381 uint32_t offset;
382 uint8_t pad[105];
383 } __attribute__((packed));
385 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
387 const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size)
389 static struct geometry hd_geometry;
390 struct ptab_entry ptab[4]; /* Partition table buffer */
391 struct dosemu_header dosemu;
392 unsigned int sectors, v;
393 unsigned int max_c, max_h, max_s;
394 unsigned int c, h, s, offset;
395 int i;
396 int drive_specified;
397 const char *p;
399 printf("command line: %s\n", shdr->cmdline);
401 offset = 0;
402 if ( CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)) )
403 offset = v;
405 sectors = (size-offset) >> 9;
407 if (sectors < 4096*2) {
408 int ok = 0;
409 unsigned int xsectors = sectors;
411 while (!ok) {
412 /* Assume it's a floppy drive, guess a geometry */
413 unsigned int type, track;
415 if (xsectors < 320*2) {
416 c = 40; h = 1; type = 1;
417 } else if (xsectors < 640*2) {
418 c = 40; h = 2; type = 1;
419 } else if (xsectors < 1200*2) {
420 c = 80; h = 2; type = 3;
421 } else if (xsectors < 1440*2) {
422 c = 80; h = 2; type = 2;
423 } else if (xsectors < 2880*2) {
424 c = 80; h = 2; type = 4;
425 } else {
426 c = 80; h = 2; type = 6;
428 track = c*h;
429 while (c < 256) {
430 s = xsectors/track;
431 if (s < 63 && (xsectors % track) == 0) {
432 ok = 1;
433 break;
435 c++;
436 track += h;
438 if (ok) {
439 hd_geometry.driveno = 0;
440 hd_geometry.c = c;
441 hd_geometry.h = h;
442 hd_geometry.s = s;
443 } else {
444 /* No valid floppy geometry, fake it by simulating broken
445 sectors at the end of the image... */
446 xsectors++;
449 } else {
450 /* Hard disk */
451 hd_geometry.driveno = 0x80;
454 hd_geometry.sectors = sectors;
455 hd_geometry.offset = offset;
457 /* Do we have a DOSEMU header? */
458 memcpy(&dosemu, (char *)where+hd_geometry.offset, sizeof dosemu);
459 if ( !memcmp("DOSEMU", dosemu.magic, 7) ) {
460 /* Always a hard disk unless overruled by command-line options */
461 hd_geometry.driveno = 0x80;
462 hd_geometry.type = 0;
463 hd_geometry.c = dosemu.c;
464 hd_geometry.h = dosemu.h;
465 hd_geometry.s = dosemu.s;
466 hd_geometry.offset += dosemu.offset;
467 sectors = (size-hd_geometry.offset) >> 9;
470 if ( CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)) )
471 hd_geometry.c = v;
472 if ( CMD_HASDATA(p = getcmditem("h")) && (v = atou(p)) )
473 hd_geometry.h = v;
474 if ( CMD_HASDATA(p = getcmditem("s")) && (v = atou(p)) )
475 hd_geometry.s = v;
477 if ( (p = getcmditem("floppy")) != CMD_NOTFOUND ) {
478 hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0;
479 if ( hd_geometry.type == 0 )
480 hd_geometry.type = 0x10; /* ATAPI floppy, e.g. LS-120 */
481 drive_specified = 1;
482 } else if ( (p = getcmditem("harddisk")) != CMD_NOTFOUND ) {
483 hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80;
484 hd_geometry.type = 0;
485 drive_specified = 1;
488 if ( (hd_geometry.c == 0) || (hd_geometry.h == 0) ||
489 (hd_geometry.s == 0) ) {
490 /* Hard disk image, need to examine the partition table for geometry */
491 memcpy(&ptab, (char *)where+hd_geometry.offset+(512-2-4*16), sizeof ptab);
493 max_c = max_h = 0; max_s = 1;
494 for ( i = 0 ; i < 4 ; i++ ) {
495 if ( ptab[i].type ) {
496 c = ptab[i].start_c + (ptab[i].start_s >> 6);
497 s = (ptab[i].start_s & 0x3f);
498 h = ptab[i].start_h;
500 if ( max_c < c ) max_c = c;
501 if ( max_h < h ) max_h = h;
502 if ( max_s < s ) max_s = s;
504 c = ptab[i].end_c + (ptab[i].end_s >> 6);
505 s = (ptab[i].end_s & 0x3f);
506 h = ptab[i].end_h;
508 if ( max_c < c ) max_c = c;
509 if ( max_h < h ) max_h = h;
510 if ( max_s < s ) max_s = s;
514 max_c++; max_h++; /* Convert to count (1-based) */
516 if ( !hd_geometry.h )
517 hd_geometry.h = max_h;
518 if ( !hd_geometry.s )
519 hd_geometry.s = max_s;
520 if ( !hd_geometry.c )
521 hd_geometry.c = sectors/(hd_geometry.h*hd_geometry.s);
524 if ( (size-hd_geometry.offset) & 0x1ff ) {
525 puts("MEMDISK: Image has fractional end sector\n");
527 if ( sectors % (hd_geometry.h*hd_geometry.s) ) {
528 puts("MEMDISK: Image seems to have fractional end cylinder\n");
530 if ( (hd_geometry.c*hd_geometry.h*hd_geometry.s) > sectors ) {
531 puts("MEMDISK: Image appears to be truncated\n");
534 return &hd_geometry;
538 * Jump here if all hope is gone...
540 void __attribute__((noreturn)) die(void)
542 asm volatile("sti");
543 for(;;)
544 asm volatile("hlt");
548 * Find a $PnP installation check structure; return (ES << 16) + DI value
550 static uint32_t pnp_install_check(void)
552 uint32_t *seg;
553 unsigned char *p, csum;
554 int i, len;
556 for (seg = (uint32_t *)0xf0000; seg < (uint32_t *)0x100000; seg += 4) {
557 if (*seg == ('$'+('P' << 8)+('n' << 16)+('P' << 24))) {
558 p = (unsigned char *)seg;
559 len = p[5];
560 if (len < 0x21)
561 continue;
562 csum = 0;
563 for (i = len; i; i--)
564 csum += *p++;
565 if (csum != 0)
566 continue;
568 return (0xf000 << 16) + (uint16_t)(unsigned long)seg;
572 return 0;
575 #define STACK_NEEDED 512 /* Number of bytes of stack */
578 * Actual setup routine
579 * Returns the drive number (which is then passed in %dl to the
580 * called routine.)
582 __cdecl syscall_t syscall;
583 void *sys_bounce;
585 __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
587 unsigned int bin_size = (int) &_binary_memdisk_bin_size;
588 struct memdisk_header *hptr;
589 struct patch_area *pptr;
590 uint16_t driverseg;
591 uint32_t driverptr, driveraddr;
592 uint16_t dosmem_k;
593 uint32_t stddosmem;
594 const struct geometry *geometry;
595 int total_size, cmdlinelen;
596 com32sys_t regs;
597 uint32_t ramdisk_image, ramdisk_size;
598 int bios_drives;
599 int do_edd = -1; /* -1 = default, 0 = no, 1 = yes */
601 /* Set up global variables */
602 syscall = cs_syscall;
603 sys_bounce = cs_bounce;
605 /* Show signs of life */
606 printf("%s %s\n", memdisk_version, copyright);
608 if ( !shdr->ramdisk_image || !shdr->ramdisk_size ) {
609 puts("MEMDISK: No ramdisk image specified!\n");
610 die();
613 ramdisk_image = shdr->ramdisk_image;
614 ramdisk_size = shdr->ramdisk_size;
616 e820map_init(); /* Initialize memory data structure */
617 get_mem(); /* Query BIOS for memory map */
618 parse_mem(); /* Parse memory map */
620 printf("Ramdisk at 0x%08x, length 0x%08x\n",
621 ramdisk_image, ramdisk_size);
623 unzip_if_needed(&ramdisk_image, &ramdisk_size);
625 geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
627 if (getcmditem("edd") != CMD_NOTFOUND ||
628 getcmditem("ebios") != CMD_NOTFOUND)
629 do_edd = 1;
630 else if (getcmditem("noedd") != CMD_NOTFOUND ||
631 getcmditem("noebios") != CMD_NOTFOUND ||
632 getcmditem("cbios") != CMD_NOTFOUND)
633 do_edd = 0;
634 else
635 do_edd = (geometry->driveno & 0x80) ? 1 : 0;
637 printf("Disk is %s %d, %u%s K, C/H/S = %u/%u/%u, EDD %s\n",
638 (geometry->driveno & 0x80) ? "hard disk" : "floppy",
639 geometry->driveno & 0x7f,
640 geometry->sectors >> 1,
641 (geometry->sectors & 1) ? ".5" : "",
642 geometry->c, geometry->h, geometry->s,
643 do_edd ? "on" : "off");
645 /* Reserve the ramdisk memory */
646 insertrange(ramdisk_image, ramdisk_size, 2);
647 parse_mem(); /* Recompute variables */
649 /* Figure out where it needs to go */
650 hptr = (struct memdisk_header *) &_binary_memdisk_bin_start;
651 pptr = (struct patch_area *)(_binary_memdisk_bin_start + hptr->patch_offs);
653 dosmem_k = rdz_16(BIOS_BASEMEM);
654 pptr->olddosmem = dosmem_k;
655 stddosmem = dosmem_k << 10;
656 /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
657 if ( stddosmem > dos_mem )
658 stddosmem = dos_mem;
660 pptr->driveno = geometry->driveno;
661 pptr->drivetype = geometry->type;
662 pptr->cylinders = geometry->c;
663 pptr->heads = geometry->h;
664 pptr->sectors = geometry->s;
665 pptr->disksize = geometry->sectors;
666 pptr->diskbuf = ramdisk_image + geometry->offset;
667 pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
669 pptr->bootloaderid = shdr->type_of_loader;
671 pptr->configflags = 0;
672 /* Set config flags */
673 if ( getcmditem("ro") != CMD_NOTFOUND ) {
674 puts("Marking disk readonly\n");
675 pptr->configflags |= CONFIG_READONLY;
677 if ( getcmditem("raw") != CMD_NOTFOUND ) {
678 puts("Using raw access to high memory\n");
679 pptr->configflags &= ~CONFIG_SAFEINT|CONFIG_BIGRAW;
680 pptr->configflags |= CONFIG_RAW;
682 if ( getcmditem("safeint") != CMD_NOTFOUND ) {
683 puts("Using safe INT 15h access to high memory\n");
684 pptr->configflags &= ~CONFIG_RAW|CONFIG_BIGRAW;
685 pptr->configflags |= CONFIG_SAFEINT;
687 if ( getcmditem("bigraw") != CMD_NOTFOUND ) {
688 puts("Using raw access to high memory - assuming big real mode\n");
689 pptr->configflags &= ~CONFIG_SAFEINT;
690 pptr->configflags |= CONFIG_BIGRAW|CONFIG_RAW;
693 /* pptr->maxint13func defaults to EDD enabled, if compiled in */
694 if (!do_edd)
695 pptr->maxint13func = MAXINT13_NOEDD;
697 /* Set up a drive parameter table */
698 if ( geometry->driveno & 0x80 ) {
699 /* Hard disk */
700 pptr->dpt.hd.max_cyl = geometry->c-1;
701 pptr->dpt.hd.max_head = geometry->h-1;
702 pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08: 0;
703 } else {
704 /* Floppy - most of these fields are bogus and mimic
705 a 1.44 MB floppy drive */
706 pptr->dpt.fd.specify1 = 0xdf;
707 pptr->dpt.fd.specify2 = 0x02;
708 pptr->dpt.fd.delay = 0x25;
709 pptr->dpt.fd.sectors = geometry->s;
710 pptr->dpt.fd.bps = 0x02;
711 pptr->dpt.fd.isgap = 0x12;
712 pptr->dpt.fd.dlen = 0xff;
713 pptr->dpt.fd.fgap = 0x6c;
714 pptr->dpt.fd.ffill = 0xf6;
715 pptr->dpt.fd.settle = 0x0f;
716 pptr->dpt.fd.mstart = 0x05;
718 pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
721 /* Set up an EDD drive parameter table */
722 pptr->edd_dpt.sectors = geometry->sectors;
723 /* The EDD spec has this as <= 15482880 sectors (1024x240x63);
724 this seems to make very little sense. Try for something saner. */
725 if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) {
726 pptr->edd_dpt.c = geometry->c;
727 pptr->edd_dpt.h = geometry->h;
728 pptr->edd_dpt.s = geometry->s;
729 pptr->edd_dpt.flags |= 0x0002; /* Geometry valid */
731 if (!(geometry->driveno & 0x80)) {
732 /* Floppy drive. Mark it as a removable device with
733 media change notification; media is present. */
734 pptr->edd_dpt.flags |= 0x0014;
737 /* The size is given by hptr->total_size plus the size of the E820
738 map -- 12 bytes per range; we may need as many as 2 additional
739 ranges (each insertrange() can worst-case turn 1 area into 3)
740 plus the terminating range, over what nranges currently show. */
741 cmdlinelen = strlen(shdr->cmdline)+1;
742 total_size = hptr->total_size; /* Actual memdisk code */
743 total_size += (nranges+3)*sizeof(ranges[0]); /* E820 memory ranges */
744 total_size += cmdlinelen; /* Command line */
745 total_size += STACK_NEEDED; /* Stack */
746 printf("Total size needed = %u bytes, allocating %uK\n",
747 total_size, (total_size+0x3ff) >> 10);
749 if ( total_size > dos_mem ) {
750 puts("MEMDISK: Insufficient low memory\n");
751 die();
754 driveraddr = stddosmem - total_size;
755 driveraddr &= ~0x3FF;
757 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
758 stddosmem, dos_mem, driveraddr);
760 /* Reserve this range of memory */
761 wrz_16(BIOS_BASEMEM, driveraddr >> 10);
762 insertrange(driveraddr, dos_mem-driveraddr, 2);
763 parse_mem();
765 pptr->mem1mb = low_mem >> 10;
766 pptr->mem16mb = high_mem >> 16;
767 if ( low_mem == (15 << 20) ) {
768 /* lowmem maxed out */
769 uint32_t int1588mem = (high_mem >> 10)+(low_mem >> 10);
770 pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
771 } else {
772 pptr->memint1588 = low_mem >> 10;
775 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
776 pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
778 driverseg = driveraddr >> 4;
779 driverptr = driverseg << 16;
781 /* Anything beyond the end is for the stack */
782 pptr->mystack = (uint16_t)(stddosmem-driveraddr);
784 pptr->oldint13 = rdz_32(BIOS_INT13);
785 pptr->oldint15 = rdz_32(BIOS_INT15);
787 /* Adjust the E820 table: if there are null ranges (type 0)
788 at the end, change them to type end of list (-1).
789 This is necessary for the driver to be able to report end
790 of list correctly. */
791 while ( nranges && ranges[nranges-1].type == 0 ) {
792 ranges[--nranges].type = -1;
795 if (getcmditem("nopass") != CMD_NOTFOUND) {
796 /* nopass specified - we're the only drive by definition */
797 printf("nopass specified - we're the only drive\n");
798 bios_drives = 0;
799 pptr->drivecnt = 0;
800 pptr->oldint13 = driverptr+hptr->iret_offs;
801 } else {
802 /* Query drive parameters of this type */
803 memset(&regs, 0, sizeof regs);
804 regs.es = 0;
805 regs.eax.b[1] = 0x08;
806 regs.edx.b[0] = geometry->driveno & 0x80;
807 syscall(0x13, &regs, &regs);
809 if ( regs.eflags.l & 1 ) {
810 printf("INT 13 08: Failure, assuming this is the only drive\n");
811 pptr->drivecnt = 0;
812 } else {
813 printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
814 regs.edx.b[0], regs.es, regs.edi.w[0]);
815 pptr->drivecnt = regs.edx.b[0];
818 /* Compare what INT 13h returned with the appropriate equipment byte */
819 if ( geometry->driveno & 0x80 ) {
820 bios_drives = rdz_8(BIOS_HD_COUNT);
821 } else {
822 uint8_t equip = rdz_8(BIOS_EQUIP);
824 if (equip & 1)
825 bios_drives = (equip >> 6)+1;
826 else
827 bios_drives = 0;
830 if (pptr->drivecnt > bios_drives) {
831 printf("BIOS equipment byte says count = %d, go with that\n",
832 bios_drives);
833 pptr->drivecnt = bios_drives;
837 /* Add ourselves to the drive count */
838 pptr->drivecnt++;
840 /* Discontiguous drive space. There is no really good solution for this. */
841 if ( pptr->drivecnt <= (geometry->driveno & 0x7f) )
842 pptr->drivecnt = (geometry->driveno & 0x7f) + 1;
844 /* Pointer to the command line */
845 pptr->cmdline_off = bin_size + (nranges+1)*sizeof(ranges[0]);
846 pptr->cmdline_seg = driverseg;
848 /* Copy driver followed by E820 table followed by command line */
850 unsigned char *dpp = (unsigned char *)(driverseg << 4);
851 dpp = memcpy_endptr(dpp, &_binary_memdisk_bin_start, bin_size);
852 dpp = memcpy_endptr(dpp, ranges, (nranges+1)*sizeof(ranges[0]));
853 dpp = memcpy_endptr(dpp, shdr->cmdline, cmdlinelen+1);
856 /* Install the interrupt handlers */
857 printf("old: int13 = %08x int15 = %08x\n",
858 rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
860 wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
861 wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
863 printf("new: int13 = %08x int15 = %08x\n",
864 rdz_32(BIOS_INT13), rdz_32(BIOS_INT15));
866 /* Update various BIOS magic data areas (gotta love this shit) */
868 if ( geometry->driveno & 0x80 ) {
869 /* Update BIOS hard disk count */
870 uint8_t nhd = pptr->drivecnt;
872 if ( nhd > 128 )
873 nhd = 128;
875 wrz_8(BIOS_HD_COUNT, nhd);
876 } else {
877 /* Update BIOS floppy disk count */
878 uint8_t equip = rdz_8(BIOS_EQUIP);
879 uint8_t nflop = pptr->drivecnt;
881 if ( nflop > 4 ) /* Limit of equipment byte */
882 nflop = 4;
884 equip &= 0x3E;
885 if ( nflop )
886 equip |= ((nflop-1) << 6) | 0x01;
888 wrz_8(BIOS_EQUIP, equip);
891 /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
892 puts("Loading boot sector... ");
894 memset(&regs, 0, sizeof regs);
895 // regs.es = 0;
896 regs.eax.w[0] = 0x0201; /* Read sector */
897 regs.ebx.w[0] = 0x7c00; /* 0000:7C00 */
898 regs.ecx.w[0] = 1; /* One sector */
899 regs.edx.w[0] = geometry->driveno;
900 syscall(0x13, &regs, &regs);
902 if ( regs.eflags.l & 1 ) {
903 puts("MEMDISK: Failed to load new boot sector\n");
904 die();
907 if ( getcmditem("pause") != CMD_NOTFOUND ) {
908 puts("press any key to boot... ");
909 regs.eax.w[0] = 0;
910 syscall(0x16, &regs, NULL);
913 puts("booting...\n");
915 /* On return the assembly code will jump to the boot vector */
916 shdr->esdi = pnp_install_check();
917 shdr->edx = geometry->driveno;