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 * ----------------------------------------------------------------------- */
18 #include "../version.h"
20 const char memdisk_version[] =
21 "MEMDISK " VERSION_STR " " DATE;
22 const char copyright[] =
23 "Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin";
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[];
32 struct memdisk_header {
40 /* The Disk Parameter Table may be required */
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 */
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 */
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 */
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 */
65 uint8_t rate; /* Data transfer rate */
66 uint8_t cmos; /* CMOS type */
69 uint32_t old_fd_dpt; /* Extension: pointer to old INT 1Eh */
73 /* EDD disk parameter table */
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 */
88 uint16_t cmdline_off, cmdline_seg;
98 /* End of the official MemDisk_Info */
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
127 struct edd_dpt edd_dpt;
130 /* This is the header in the boot sector/setup area */
131 struct setup_header {
143 uint32_t realmode_swtch;
145 uint8_t type_of_loader;
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;
154 uint32_t cmd_line_ptr;
155 uint32_t initrd_addr_max;
160 struct setup_header * const shdr = (struct setup_header *)(LOW_SEG << 4);
162 /* Access to high memory */
164 /* Access to objects in the zero page */
166 wrz_8(uint32_t addr, uint8_t data)
168 *((uint8_t *)addr) = data;
171 wrz_16(uint32_t addr, uint16_t data)
173 *((uint16_t *)addr) = data;
176 wrz_32(uint32_t addr, uint32_t data)
178 *((uint32_t *)addr) = data;
180 static inline uint8_t
183 return *((uint8_t *)addr);
185 static inline uint16_t
186 rdz_16(uint32_t addr)
188 return *((uint16_t *)addr);
190 static inline uint32_t
191 rdz_32(uint32_t addr)
193 return *((uint32_t *)addr);
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 */
208 * Routine to seek for a command-line item and return a pointer
209 * to the data portion, if present
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)
217 const char *getcmditem(const char *what)
220 const char *wp = what;
223 for ( p = shdr->cmdline ; *p ; p++ ) {
225 case 0: /* Ground state */
233 case 1: /* Matching */
237 else if ( *p == ' ' )
248 case 2: /* Mismatch, skip rest of option */
250 match = 0; /* Next option */
255 /* Check for matching string at end of line */
256 if ( match == 1 && *wp == '\0' )
263 * Check to see if this is a gzip image
265 #define UNZIP_ALIGN 512
267 extern void _end; /* Symbol signalling end of data */
269 void unzip_if_needed(uint32_t *where_p, uint32_t *size_p)
271 uint32_t where = *where_p;
272 uint32_t size = *size_p;
274 uint32_t startrange, endrange;
275 uint32_t gzdatasize, gzwhere;
276 uint32_t orig_crc, offset;
280 /* Is it a gzip image? */
281 if (check_zip((void *)where, size, &zbytes, &gzdatasize,
282 &orig_crc, &offset) == 0) {
284 if (offset + zbytes > size) {
285 /* Assertion failure; check_zip is supposed to guarantee this
287 puts("internal error: check_zip returned nonsense\n");
291 /* Find a good place to put it: search memory ranges in descending order
292 until we find one that is legal and fits */
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. */
299 if ( ranges[i].type != 1 )
301 if (!(ranges[i].extattr & 1))
305 if ( ranges[i].start >= 0xFFFFFFFF )
308 startrange = (uint32_t)ranges[i].start;
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);
315 /* Make sure we don't overwrite ourselves */
316 if ( startrange < (uint32_t)&_end )
317 startrange = (uint32_t)&_end;
319 /* Allow for alignment */
320 startrange = (ranges[i].start + (UNZIP_ALIGN-1)) & ~(UNZIP_ALIGN-1);
322 /* In case we just killed the whole range... */
323 if ( startrange >= endrange )
326 /* Must be large enough... don't rely on gzwhere for this (wraparound) */
327 if ( endrange-startrange < gzdatasize )
330 /* This is where the gz image should be put if we put it in this range */
331 gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN-1);
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 */
338 if ( gzwhere-startrange < size )
339 continue; /* Can't fit both old and new */
341 newwhere = (gzwhere - size) & ~(UNZIP_ALIGN-1);
342 printf("Moving compressed data from 0x%08x to 0x%08x\n",
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);
357 printf("Not enough memory to decompress image (need 0x%08x bytes)\n",
362 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
365 *size_p = gzdatasize;
366 *where_p = (uint32_t)unzip((void *)(where + offset), zbytes,
367 gzdatasize, orig_crc, (void *)target);
372 * Figure out the "geometry" of the disk in question
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 */
382 /* Format of a DOS partition table entry */
385 uint8_t start_h, start_s, start_c;
387 uint8_t end_h, end_s, end_c;
392 /* Format of a DOSEMU header */
393 struct dosemu_header {
394 uint8_t magic[7]; /* DOSEMU\0 */
400 } __attribute__((packed));
402 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
404 const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size)
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;
416 printf("command line: %s\n", shdr->cmdline);
419 if ( CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)) )
422 sectors = (size-offset) >> 9;
424 if (sectors < 4096*2) {
426 unsigned int xsectors = sectors;
429 /* Assume it's a floppy drive, guess a geometry */
430 unsigned int type, track;
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;
443 c = 80; h = 2; type = 6;
448 if (s < 63 && (xsectors % track) == 0) {
456 hd_geometry.driveno = 0;
461 /* No valid floppy geometry, fake it by simulating broken
462 sectors at the end of the image... */
468 hd_geometry.driveno = 0x80;
471 hd_geometry.sectors = sectors;
472 hd_geometry.offset = offset;
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;
487 if ( CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)) )
489 if ( CMD_HASDATA(p = getcmditem("h")) && (v = atou(p)) )
491 if ( CMD_HASDATA(p = getcmditem("s")) && (v = atou(p)) )
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 */
499 } else if ( (p = getcmditem("harddisk")) != CMD_NOTFOUND ) {
500 hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80;
501 hd_geometry.type = 0;
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);
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);
517 if ( max_c < c ) max_c = c;
518 if ( max_h < h ) max_h = h;
519 if ( max_s < s ) max_s = s;
521 c = ptab[i].end_c + (ptab[i].end_s >> 6);
522 s = (ptab[i].end_s & 0x3f);
525 if ( max_c < c ) max_c = c;
526 if ( max_h < h ) max_h = h;
527 if ( max_s < s ) max_s = s;
531 max_c++; max_h++; /* Convert to count (1-based) */
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);
541 if ( (size-hd_geometry.offset) & 0x1ff ) {
542 puts("MEMDISK: Image has fractional end sector\n");
544 if ( sectors % (hd_geometry.h*hd_geometry.s) ) {
545 puts("MEMDISK: Image seems to have fractional end cylinder\n");
547 if ( (hd_geometry.c*hd_geometry.h*hd_geometry.s) > sectors ) {
548 puts("MEMDISK: Image appears to be truncated\n");
555 * Jump here if all hope is gone...
557 void __attribute__((noreturn)) die(void)
565 * Find a $PnP installation check structure; return (ES << 16) + DI value
567 static uint32_t pnp_install_check(void)
570 unsigned char *p, csum;
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;
580 for (i = len; i; i--)
585 return (0xf000 << 16) + (uint16_t)(unsigned long)seg;
592 #define STACK_NEEDED 512 /* Number of bytes of stack */
595 * Actual setup routine
596 * Returns the drive number (which is then passed in %dl to the
599 __cdecl syscall_t syscall;
602 __cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce)
604 unsigned int bin_size;
606 struct memdisk_header *hptr;
607 struct patch_area *pptr;
609 uint32_t driverptr, driveraddr;
612 const struct geometry *geometry;
613 int total_size, cmdlinelen;
615 uint32_t ramdisk_image, ramdisk_size;
617 int do_edd = 1; /* 0 = no, 1 = yes, default is yes */
618 int no_bpt; /* No valid BPT presented */
620 /* Set up global variables */
621 syscall = cs_syscall;
622 sys_bounce = cs_bounce;
624 /* Show signs of life */
625 printf("%s %s\n", memdisk_version, copyright);
627 if ( !shdr->ramdisk_image || !shdr->ramdisk_size ) {
628 puts("MEMDISK: No ramdisk image specified!\n");
632 ramdisk_image = shdr->ramdisk_image;
633 ramdisk_size = shdr->ramdisk_size;
635 e820map_init(); /* Initialize memory data structure */
636 get_mem(); /* Query BIOS for memory map */
637 parse_mem(); /* Parse memory map */
639 printf("Ramdisk at 0x%08x, length 0x%08x\n",
640 ramdisk_image, ramdisk_size);
642 unzip_if_needed(&ramdisk_image, &ramdisk_size);
644 geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
646 if (getcmditem("edd") != CMD_NOTFOUND ||
647 getcmditem("ebios") != CMD_NOTFOUND)
649 else if (getcmditem("noedd") != CMD_NOTFOUND ||
650 getcmditem("noebios") != CMD_NOTFOUND ||
651 getcmditem("cbios") != CMD_NOTFOUND)
654 do_edd = (geometry->driveno & 0x80) ? 1 : 0;
656 /* Choose the appropriate installable memdisk hook */
658 bin_size = (int) &_binary_memdisk_edd_bin_size;
659 memdisk_hook = (char *) &_binary_memdisk_edd_bin_start;
661 bin_size = (int) &_binary_memdisk_chs_bin_size;
662 memdisk_hook = (char *) &_binary_memdisk_chs_bin_start;
665 /* Reserve the ramdisk memory */
666 insertrange(ramdisk_image, ramdisk_size, 2, 1);
667 parse_mem(); /* Recompute variables */
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);
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 )
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;
689 pptr->bootloaderid = shdr->type_of_loader;
691 pptr->configflags = CONFIG_SAFEINT; /* Default */
692 /* Set config flags */
693 if ( getcmditem("ro") != CMD_NOTFOUND ) {
694 pptr->configflags |= CONFIG_READONLY;
696 if ( getcmditem("raw") != CMD_NOTFOUND ) {
697 pptr->configflags &= ~CONFIG_MODEMASK;
698 pptr->configflags |= CONFIG_RAW;
700 if ( getcmditem("bigraw") != CMD_NOTFOUND ) {
701 pptr->configflags &= ~CONFIG_MODEMASK;
702 pptr->configflags |= CONFIG_BIGRAW|CONFIG_RAW;
704 if ( getcmditem("int") != CMD_NOTFOUND ) {
705 pptr->configflags &= ~CONFIG_MODEMASK;
706 /* pptr->configflags |= 0; */
708 if ( getcmditem("safeint") != CMD_NOTFOUND ) {
709 pptr->configflags &= ~CONFIG_MODEMASK;
710 pptr->configflags |= CONFIG_SAFEINT;
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");
723 switch (pptr->configflags & CONFIG_MODEMASK) {
725 puts("standard INT 15h");
728 puts("safe INT 15h");
733 case CONFIG_RAW|CONFIG_BIGRAW:
734 puts("big real mode raw");
737 printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK);
740 puts(" access to high memory\n");
742 /* Set up a drive parameter table */
743 if ( geometry->driveno & 0x80 ) {
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;
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;
765 pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
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 */
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;
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);
796 if ( total_size > dos_mem ) {
797 puts("MEMDISK: Insufficient low memory\n");
801 driveraddr = stddosmem - total_size;
802 driveraddr &= ~0x3FF;
804 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
805 stddosmem, dos_mem, driveraddr);
807 /* Reserve this range of memory */
808 wrz_16(BIOS_BASEMEM, driveraddr >> 10);
809 insertrange(driveraddr, dos_mem-driveraddr, 2, 1);
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;
819 pptr->memint1588 = low_mem >> 10;
822 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
823 pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
825 driverseg = driveraddr >> 4;
826 driverptr = driverseg << 16;
828 /* Anything beyond the end is for the stack */
829 pptr->mystack = (uint16_t)(stddosmem-driveraddr);
831 pptr->oldint13 = rdz_32(BIOS_INT13);
832 pptr->oldint15 = rdz_32(BIOS_INT15);
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;
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");
847 pptr->oldint13 = driverptr+hptr->iret_offs;
850 /* Query drive parameters of this type */
851 memset(®s, 0, sizeof regs);
853 regs.eax.b[1] = 0x08;
854 regs.edx.b[0] = geometry->driveno & 0x80;
855 syscall(0x13, ®s, ®s);
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");
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]);
870 /* Compare what INT 13h returned with the appropriate equipment byte */
871 if ( geometry->driveno & 0x80 ) {
872 bios_drives = rdz_8(BIOS_HD_COUNT);
874 uint8_t equip = rdz_8(BIOS_EQUIP);
877 bios_drives = (equip >> 6)+1;
882 if (pptr->drivecnt > bios_drives) {
883 printf("BIOS equipment byte says count = %d, go with that\n",
885 pptr->drivecnt = bios_drives;
889 /* Add ourselves to the drive count */
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;
896 /* Pointer to the command line */
897 pptr->cmdline_off = bin_size + (nranges+1)*sizeof(ranges[0]);
898 pptr->cmdline_seg = driverseg;
900 /* Copy driver followed by E820 table followed by command line */
902 unsigned char *dpp = (unsigned char *)(driverseg << 4);
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;
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);
915 /* Update various BIOS magic data areas (gotta love this shit) */
917 if ( geometry->driveno & 0x80 ) {
918 /* Update BIOS hard disk count */
919 uint8_t nhd = pptr->drivecnt;
924 wrz_8(BIOS_HD_COUNT, nhd);
926 /* Update BIOS floppy disk count */
927 uint8_t equip = rdz_8(BIOS_EQUIP);
928 uint8_t nflop = pptr->drivecnt;
930 if ( nflop > 4 ) /* Limit of equipment byte */
935 equip |= ((nflop-1) << 6) | 0x01;
937 wrz_8(BIOS_EQUIP, equip);
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);
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));
952 wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
953 wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
955 wrz_32(BIOS_INT1E, driverptr+pptr->dpt_ptr);
957 printf("new: int13 = %08x int15 = %08x int1e = %08x\n",
958 rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
960 /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
961 puts("Loading boot sector... ");
963 memset(®s, 0, sizeof regs);
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, ®s, ®s);
971 if ( regs.eflags.l & 1 ) {
972 puts("MEMDISK: Failed to load new boot sector\n");
976 if ( getcmditem("pause") != CMD_NOTFOUND ) {
977 puts("press any key to boot... ");
979 syscall(0x16, ®s, NULL);
982 puts("booting...\n");
984 /* On return the assembly code will jump to the boot vector */
985 shdr->esdi = pnp_install_check();
986 shdr->edx = geometry->driveno;