1 /* ----------------------------------------------------------------------- *
3 * Copyright 2001-2006 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 * ----------------------------------------------------------------------- */
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
{
34 /* The Disk Parameter Table may be required */
37 uint16_t max_cyl
; /* Max cylinder */
38 uint8_t max_head
; /* Max head */
39 uint8_t junk1
[5]; /* Obsolete junk, leave at zero */
40 uint8_t ctrl
; /* Control byte */
41 uint8_t junk2
[7]; /* More obsolete junk */
44 uint8_t specify1
; /* "First specify byte" */
45 uint8_t specify2
; /* "Second specify byte" */
46 uint8_t delay
; /* Delay until motor turn off */
47 uint8_t sectors
; /* Sectors/track */
49 uint8_t bps
; /* Bytes/sector (02h = 512) */
50 uint8_t isgap
; /* Length of intersector gap */
51 uint8_t dlen
; /* Data length (0FFh) */
52 uint8_t fgap
; /* Formatting gap */
54 uint8_t ffill
; /* Format fill byte */
55 uint8_t settle
; /* Head settle time (ms) */
56 uint8_t mstart
; /* Motor start time */
57 uint8_t _pad1
; /* Padding */
59 uint32_t old_fd_dpt
; /* Extension: pointer to old INT 1Eh */
66 uint16_t cmdline_off
, cmdline_seg
;
74 #define MAXINT13_NOEDD 0x16
91 #define CONFIG_READONLY 0x01
92 #define CONFIG_RAW 0x02
93 #define CONFIG_SAFEINT 0x04
94 #define CONFIG_BIGRAW 0x08 /* MUST be 8! */
102 /* This is the header in the boot sector/setup area */
103 struct setup_header
{
115 uint32_t realmode_swtch
;
117 uint8_t type_of_loader
;
119 uint16_t setup_move_size
;
120 uint32_t code32_start
;
121 uint32_t ramdisk_image
;
122 uint32_t ramdisk_size
;
123 uint32_t bootsect_kludge
;
124 uint16_t head_end_ptr
;
126 uint32_t cmd_line_ptr
;
127 uint32_t initrd_addr_max
;
132 struct setup_header
* const shdr
= (struct setup_header
*)(LOW_SEG
<< 4);
134 /* Access to high memory */
136 /* Access to objects in the zero page */
138 wrz_8(uint32_t addr
, uint8_t data
)
140 *((uint8_t *)addr
) = data
;
143 wrz_16(uint32_t addr
, uint16_t data
)
145 *((uint16_t *)addr
) = data
;
148 wrz_32(uint32_t addr
, uint32_t data
)
150 *((uint32_t *)addr
) = data
;
152 static inline uint8_t
155 return *((uint8_t *)addr
);
157 static inline uint16_t
158 rdz_16(uint32_t addr
)
160 return *((uint16_t *)addr
);
162 static inline uint32_t
163 rdz_32(uint32_t addr
)
165 return *((uint32_t *)addr
);
168 /* Addresses in the zero page */
169 #define BIOS_INT13 (0x13*4) /* INT 13h vector */
170 #define BIOS_INT15 (0x15*4) /* INT 15h vector */
171 #define BIOS_INT1E (0x1E*4) /* INT 1Eh vector */
172 #define BIOS_INT40 (0x40*4) /* INT 13h vector */
173 #define BIOS_INT41 (0x41*4) /* INT 41h vector */
174 #define BIOS_INT46 (0x46*4) /* INT 46h vector */
175 #define BIOS_BASEMEM 0x413 /* Amount of DOS memory */
176 #define BIOS_EQUIP 0x410 /* BIOS equipment list */
177 #define BIOS_HD_COUNT 0x475 /* Number of hard drives present */
180 * Routine to seek for a command-line item and return a pointer
181 * to the data portion, if present
184 /* Magic return values */
185 #define CMD_NOTFOUND ((char *)-1) /* Not found */
186 #define CMD_BOOL ((char *)-2) /* Found boolean option */
187 #define CMD_HASDATA(X) ((int)(X) >= 0)
189 const char *getcmditem(const char *what
)
192 const char *wp
= what
;
195 for ( p
= shdr
->cmdline
; *p
; p
++ ) {
197 case 0: /* Ground state */
205 case 1: /* Matching */
209 else if ( *p
== ' ' )
220 case 2: /* Mismatch, skip rest of option */
222 match
= 0; /* Next option */
227 /* Check for matching string at end of line */
228 if ( match
== 1 && *wp
== '\0' )
235 * Check to see if this is a gzip image
237 #define UNZIP_ALIGN 512
239 extern void _end
; /* Symbol signalling end of data */
241 void unzip_if_needed(uint32_t *where_p
, uint32_t *size_p
)
243 uint32_t where
= *where_p
;
244 uint32_t size
= *size_p
;
246 uint32_t startrange
, endrange
;
247 uint32_t gzdatasize
, gzwhere
;
248 uint32_t orig_crc
, offset
;
252 /* Is it a gzip image? */
253 if (check_zip ((void *)where
, size
, &zbytes
, &gzdatasize
,
254 &orig_crc
, &offset
) == 0) {
256 if (offset
+ zbytes
> size
) {
257 /* Assertion failure; check_zip is supposed to guarantee this
259 puts("internal error: check_zip returned nonsense\n");
263 /* Find a good place to put it: search memory ranges in descending order
264 until we find one that is legal and fits */
266 for ( i
= nranges
-1 ; i
>= 0 ; i
-- ) {
267 /* We can't use > 4G memory (32 bits only.) Truncate to 2^32-1
268 so we don't have to deal with funny wraparound issues. */
271 if ( ranges
[i
].type
!= 1 )
275 if ( ranges
[i
].start
>= 0xFFFFFFFF )
277 startrange
= (uint32_t)ranges
[i
].start
;
279 /* Range end (0 for end means 2^64) */
280 endrange
= ((ranges
[i
+1].start
>= 0xFFFFFFFF ||
281 ranges
[i
+1].start
== 0)
282 ? 0xFFFFFFFF : (uint32_t)ranges
[i
+1].start
);
284 /* Make sure we don't overwrite ourselves */
285 if ( startrange
< (uint32_t)&_end
)
286 startrange
= (uint32_t)&_end
;
288 /* Allow for alignment */
289 startrange
= (ranges
[i
].start
+ (UNZIP_ALIGN
-1)) & ~(UNZIP_ALIGN
-1);
291 /* In case we just killed the whole range... */
292 if ( startrange
>= endrange
)
295 /* Must be large enough... don't rely on gzwhere for this (wraparound) */
296 if ( endrange
-startrange
< gzdatasize
)
299 /* This is where the gz image should be put if we put it in this range */
300 gzwhere
= (endrange
- gzdatasize
) & ~(UNZIP_ALIGN
-1);
302 /* Cast to uint64_t just in case we're flush with the top byte */
303 if ( (uint64_t)where
+size
>= gzwhere
&& where
< endrange
) {
304 /* Need to move source data to avoid compressed/uncompressed overlap */
307 if ( gzwhere
-startrange
< size
)
308 continue; /* Can't fit both old and new */
310 newwhere
= (gzwhere
- size
) & ~(UNZIP_ALIGN
-1);
311 printf("Moving compressed data from 0x%08x to 0x%08x\n",
314 /* Our memcpy() is OK, because we always move from a higher
315 address to a lower one */
316 memcpy((void *)newwhere
, (void *)where
, size
);
326 printf("Not enough memory to decompress image (need 0x%08x bytes)\n",
331 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
334 *size_p
= gzdatasize
;
335 *where_p
= (uint32_t)unzip((void *)(where
+ offset
), zbytes
,
336 gzdatasize
, orig_crc
, (void *)target
);
341 * Figure out the "geometry" of the disk in question
344 uint32_t sectors
; /* 512-byte sector count */
345 uint32_t c
, h
, s
; /* C/H/S geometry */
346 uint32_t offset
; /* Byte offset for disk */
347 uint8_t type
; /* Type byte for INT 13h AH=08h */
348 uint8_t driveno
; /* Drive no */
351 static const struct geometry geometries
[] =
353 { 360*2, 40, 2, 9, 0, 0x01, 0 }, /* 360 K */
354 { 720*2, 80, 2, 9, 0, 0x03, 0 }, /* 720 K*/
355 { 1200*2, 80, 2, 15, 0, 0x02, 0 }, /* 1200 K */
356 { 1440*2, 80, 2, 18, 0, 0x04, 0 }, /* 1440 K */
357 { 1680*2, 80, 2, 21, 0, 0x04, 0 }, /* 1680 K */
358 { 1722*2, 82, 2, 21, 0, 0x04, 0 }, /* 1722 K */
359 { 2880*2, 80, 2, 36, 0, 0x06, 0 }, /* 2880 K */
360 { 3840*2, 80, 2, 48, 0, 0x06, 0 }, /* 3840 K */
362 #define known_geometries (sizeof(geometries)/sizeof(struct geometry))
364 /* Format of a DOS partition table entry */
367 uint8_t start_h
, start_s
, start_c
;
369 uint8_t end_h
, end_s
, end_c
;
374 /* Format of a DOSEMU header */
375 struct dosemu_header
{
376 uint8_t magic
[7]; /* DOSEMU\0 */
382 } __attribute__((packed
));
384 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
386 const struct geometry
*get_disk_image_geometry(uint32_t where
, uint32_t size
)
388 static struct geometry hd_geometry
= { 0, 0, 0, 0, 0, 0, 0x80 };
389 struct ptab_entry ptab
[4]; /* Partition table buffer */
390 struct dosemu_header dosemu
;
391 unsigned int sectors
, v
;
392 unsigned int max_c
, max_h
, max_s
;
393 unsigned int c
, h
, s
, offset
;
398 printf("command line: %s\n", shdr
->cmdline
);
401 if ( CMD_HASDATA(p
= getcmditem("offset")) && (v
= atou(p
)) )
404 sectors
= (size
-offset
) >> 9;
405 for ( i
= 0 ; i
< known_geometries
; i
++ ) {
406 if ( sectors
== geometries
[i
].sectors
) {
407 hd_geometry
= geometries
[i
];
412 hd_geometry
.sectors
= sectors
;
413 hd_geometry
.offset
= offset
;
415 /* Do we have a DOSEMU header? */
416 memcpy(&dosemu
, (char *)where
+hd_geometry
.offset
, sizeof dosemu
);
417 if ( !memcmp("DOSEMU", dosemu
.magic
, 7) ) {
418 /* Always a hard disk unless overruled by command-line options */
419 hd_geometry
.driveno
= 0x80;
420 hd_geometry
.type
= 0;
421 hd_geometry
.c
= dosemu
.c
;
422 hd_geometry
.h
= dosemu
.h
;
423 hd_geometry
.s
= dosemu
.s
;
424 hd_geometry
.offset
+= dosemu
.offset
;
425 sectors
= (size
-hd_geometry
.offset
) >> 9;
428 if ( CMD_HASDATA(p
= getcmditem("c")) && (v
= atou(p
)) )
430 if ( CMD_HASDATA(p
= getcmditem("h")) && (v
= atou(p
)) )
432 if ( CMD_HASDATA(p
= getcmditem("s")) && (v
= atou(p
)) )
435 if ( (p
= getcmditem("floppy")) != CMD_NOTFOUND
) {
436 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) & 0x7f : 0;
437 if ( hd_geometry
.type
== 0 )
438 hd_geometry
.type
= 0x10; /* ATAPI floppy, e.g. LS-120 */
440 } else if ( (p
= getcmditem("harddisk")) != CMD_NOTFOUND
) {
441 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) | 0x80 : 0x80;
442 hd_geometry
.type
= 0;
446 if ( (hd_geometry
.c
== 0) || (hd_geometry
.h
== 0) ||
447 (hd_geometry
.s
== 0) ) {
448 /* Hard disk image, need to examine the partition table for geometry */
449 memcpy(&ptab
, (char *)where
+hd_geometry
.offset
+(512-2-4*16), sizeof ptab
);
451 max_c
= max_h
= 0; max_s
= 1;
452 for ( i
= 0 ; i
< 4 ; i
++ ) {
453 if ( ptab
[i
].type
) {
454 c
= ptab
[i
].start_c
+ (ptab
[i
].start_s
>> 6);
455 s
= (ptab
[i
].start_s
& 0x3f);
458 if ( max_c
< c
) max_c
= c
;
459 if ( max_h
< h
) max_h
= h
;
460 if ( max_s
< s
) max_s
= s
;
462 c
= ptab
[i
].end_c
+ (ptab
[i
].end_s
>> 6);
463 s
= (ptab
[i
].end_s
& 0x3f);
466 if ( max_c
< c
) max_c
= c
;
467 if ( max_h
< h
) max_h
= h
;
468 if ( max_s
< s
) max_s
= s
;
472 max_c
++; max_h
++; /* Convert to count (1-based) */
474 if ( !hd_geometry
.h
)
475 hd_geometry
.h
= max_h
;
476 if ( !hd_geometry
.s
)
477 hd_geometry
.s
= max_s
;
478 if ( !hd_geometry
.c
)
479 hd_geometry
.c
= sectors
/(hd_geometry
.h
*hd_geometry
.s
);
482 if ( (size
-hd_geometry
.offset
) & 0x1ff ) {
483 puts("MEMDISK: Image has fractional end sector\n");
485 if ( sectors
% (hd_geometry
.h
*hd_geometry
.s
) ) {
486 puts("MEMDISK: Image seems to have fractional end cylinder\n");
488 if ( (hd_geometry
.c
*hd_geometry
.h
*hd_geometry
.s
) > sectors
) {
489 puts("MEMDISK: Image appears to be truncated\n");
496 * Jump here if all hope is gone...
498 void __attribute__((noreturn
)) die(void)
506 * Find a $PnP installation check structure; return (ES << 16) + DI value
508 static uint32_t pnp_install_check(void)
511 unsigned char *p
, csum
;
514 for (seg
= (uint32_t *)0xf0000; seg
< (uint32_t *)0x100000; seg
+= 4) {
515 if (*seg
== ('$'+('P' << 8)+('n' << 16)+('P' << 24))) {
516 p
= (unsigned char *)seg
;
521 for (i
= len
; i
; i
--)
526 return (0xf000 << 16) + (uint16_t)(unsigned long)seg
;
533 #define STACK_NEEDED 512 /* Number of bytes of stack */
536 * Actual setup routine
537 * Returns the drive number (which is then passed in %dl to the
543 void setup(syscall_t cs_syscall
, void *cs_bounce
)
545 unsigned int bin_size
= (int) &_binary_memdisk_bin_size
;
546 struct memdisk_header
*hptr
;
547 struct patch_area
*pptr
;
549 uint32_t driverptr
, driveraddr
;
552 const struct geometry
*geometry
;
553 int total_size
, cmdlinelen
;
555 uint32_t ramdisk_image
, ramdisk_size
;
557 int do_edd
= -1; /* -1 = default, 0 = no, 1 = yes */
559 /* Set up global variables */
560 syscall
= cs_syscall
;
561 sys_bounce
= cs_bounce
;
563 /* Show signs of life */
564 printf("%s %s\n", memdisk_version
, copyright
);
566 if ( !shdr
->ramdisk_image
|| !shdr
->ramdisk_size
) {
567 puts("MEMDISK: No ramdisk image specified!\n");
571 ramdisk_image
= shdr
->ramdisk_image
;
572 ramdisk_size
= shdr
->ramdisk_size
;
574 e820map_init(); /* Initialize memory data structure */
575 get_mem(); /* Query BIOS for memory map */
576 parse_mem(); /* Parse memory map */
578 printf("Ramdisk at 0x%08x, length 0x%08x\n",
579 ramdisk_image
, ramdisk_size
);
581 unzip_if_needed(&ramdisk_image
, &ramdisk_size
);
583 geometry
= get_disk_image_geometry(ramdisk_image
, ramdisk_size
);
585 if (getcmditem("edd") != CMD_NOTFOUND
||
586 getcmditem("ebios") != CMD_NOTFOUND
)
588 else if (getcmditem("noedd") != CMD_NOTFOUND
||
589 getcmditem("noebios") != CMD_NOTFOUND
||
590 getcmditem("cbios") != CMD_NOTFOUND
)
593 do_edd
= (geometry
->driveno
& 0x80) ? 1 : 0;
595 printf("Disk is %s %d, %u K, C/H/S = %u/%u/%u, EDD %s\n",
596 (geometry
->driveno
& 0x80) ? "hard disk" : "floppy",
597 geometry
->driveno
& 0x7f,
598 geometry
->sectors
>> 1,
599 geometry
->c
, geometry
->h
, geometry
->s
,
600 do_edd
? "on" : "off");
602 /* Reserve the ramdisk memory */
603 insertrange(ramdisk_image
, ramdisk_size
, 2);
604 parse_mem(); /* Recompute variables */
606 /* Figure out where it needs to go */
607 hptr
= (struct memdisk_header
*) &_binary_memdisk_bin_start
;
608 pptr
= (struct patch_area
*)(_binary_memdisk_bin_start
+ hptr
->patch_offs
);
610 dosmem_k
= rdz_16(BIOS_BASEMEM
);
611 pptr
->olddosmem
= dosmem_k
;
612 stddosmem
= dosmem_k
<< 10;
613 /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
614 if ( stddosmem
> dos_mem
)
617 pptr
->driveno
= geometry
->driveno
;
618 pptr
->drivetype
= geometry
->type
;
619 pptr
->cylinders
= geometry
->c
;
620 pptr
->heads
= geometry
->h
;
621 pptr
->sectors
= geometry
->s
;
622 pptr
->disksize
= geometry
->sectors
;
623 pptr
->diskbuf
= ramdisk_image
+ geometry
->offset
;
624 pptr
->statusptr
= (geometry
->driveno
& 0x80) ? 0x474 : 0x441;
626 pptr
->bootloaderid
= shdr
->type_of_loader
;
628 pptr
->configflags
= 0;
629 /* Set config flags */
630 if ( getcmditem("ro") != CMD_NOTFOUND
) {
631 puts("Marking disk readonly\n");
632 pptr
->configflags
|= CONFIG_READONLY
;
634 if ( getcmditem("raw") != CMD_NOTFOUND
) {
635 puts("Using raw access to high memory\n");
636 pptr
->configflags
&= ~CONFIG_SAFEINT
|CONFIG_BIGRAW
;
637 pptr
->configflags
|= CONFIG_RAW
;
639 if ( getcmditem("safeint") != CMD_NOTFOUND
) {
640 puts("Using safe INT 15h access to high memory\n");
641 pptr
->configflags
&= ~CONFIG_RAW
|CONFIG_BIGRAW
;
642 pptr
->configflags
|= CONFIG_SAFEINT
;
644 if ( getcmditem("bigraw") != CMD_NOTFOUND
) {
645 puts("Using raw access to high memory - assuming big real mode\n");
646 pptr
->configflags
&= ~CONFIG_SAFEINT
;
647 pptr
->configflags
|= CONFIG_BIGRAW
|CONFIG_RAW
;
650 /* pptr->maxint13func defaults to EDD enabled, if compiled in */
652 pptr
->maxint13func
= MAXINT13_NOEDD
;
654 /* Set up a drive parameter table */
655 if ( geometry
->driveno
& 0x80 ) {
657 pptr
->dpt
.hd
.max_cyl
= geometry
->c
-1;
658 pptr
->dpt
.hd
.max_head
= geometry
->h
-1;
659 pptr
->dpt
.hd
.ctrl
= (geometry
->h
> 8) ? 0x08: 0;
661 /* Floppy - most of these fields are bogus and mimic
662 a 1.44 MB floppy drive */
663 pptr
->dpt
.fd
.specify1
= 0xdf;
664 pptr
->dpt
.fd
.specify2
= 0x02;
665 pptr
->dpt
.fd
.delay
= 0x25;
666 pptr
->dpt
.fd
.sectors
= geometry
->s
;
667 pptr
->dpt
.fd
.bps
= 0x02;
668 pptr
->dpt
.fd
.isgap
= 0x12;
669 pptr
->dpt
.fd
.dlen
= 0xff;
670 pptr
->dpt
.fd
.fgap
= 0x6c;
671 pptr
->dpt
.fd
.ffill
= 0xf6;
672 pptr
->dpt
.fd
.settle
= 0x0f;
673 pptr
->dpt
.fd
.mstart
= 0x05;
675 pptr
->dpt
.fd
.old_fd_dpt
= rdz_32(BIOS_INT1E
);
678 /* The size is given by hptr->total_size plus the size of the E820
679 map -- 12 bytes per range; we may need as many as 2 additional
680 ranges (each insertrange() can worst-case turn 1 area into 3)
681 plus the terminating range, over what nranges currently show. */
682 cmdlinelen
= strlen(shdr
->cmdline
)+1;
683 total_size
= hptr
->total_size
; /* Actual memdisk code */
684 total_size
+= (nranges
+3)*sizeof(ranges
[0]); /* E820 memory ranges */
685 total_size
+= cmdlinelen
; /* Command line */
686 total_size
+= STACK_NEEDED
; /* Stack */
687 printf("Total size needed = %u bytes, allocating %uK\n",
688 total_size
, (total_size
+0x3ff) >> 10);
690 if ( total_size
> dos_mem
) {
691 puts("MEMDISK: Insufficient low memory\n");
695 driveraddr
= stddosmem
- total_size
;
696 driveraddr
&= ~0x3FF;
698 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
699 stddosmem
, dos_mem
, driveraddr
);
701 /* Reserve this range of memory */
702 wrz_16(BIOS_BASEMEM
, driveraddr
>> 10);
703 insertrange(driveraddr
, dos_mem
-driveraddr
, 2);
706 pptr
->mem1mb
= low_mem
>> 10;
707 pptr
->mem16mb
= high_mem
>> 16;
708 if ( low_mem
== (15 << 20) ) {
709 /* lowmem maxed out */
710 uint32_t int1588mem
= (high_mem
>> 10)+(low_mem
>> 10);
711 pptr
->memint1588
= (int1588mem
> 0xffff) ? 0xffff : int1588mem
;
713 pptr
->memint1588
= low_mem
>> 10;
716 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
717 pptr
->memint1588
, pptr
->mem1mb
, pptr
->mem16mb
);
719 driverseg
= driveraddr
>> 4;
720 driverptr
= driverseg
<< 16;
722 /* Anything beyond the end is for the stack */
723 pptr
->mystack
= (uint16_t)(stddosmem
-driveraddr
);
725 pptr
->oldint13
= rdz_32(BIOS_INT13
);
726 pptr
->oldint15
= rdz_32(BIOS_INT15
);
728 /* Adjust the E820 table: if there are null ranges (type 0)
729 at the end, change them to type end of list (-1).
730 This is necessary for the driver to be able to report end
731 of list correctly. */
732 while ( nranges
&& ranges
[nranges
-1].type
== 0 ) {
733 ranges
[--nranges
].type
= -1;
736 /* Query drive parameters of this type */
737 memset(®s
, 0, sizeof regs
);
739 regs
.eax
.b
[1] = 0x08;
740 regs
.edx
.b
[0] = geometry
->driveno
& 0x80;
741 syscall(0x13, ®s
, ®s
);
743 if ( regs
.eflags
.l
& 1 ) {
744 printf("INT 13 08: Failure, assuming this is the only drive\n");
747 printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
748 regs
.edx
.b
[0], regs
.es
, regs
.edi
.w
[0]);
749 pptr
->drivecnt
= regs
.edx
.b
[0];
752 /* Compare what INT 13h returned with the appropriate equipment byte */
753 if ( geometry
->driveno
& 0x80 ) {
754 bios_drives
= rdz_8(BIOS_HD_COUNT
);
756 uint8_t equip
= rdz_8(BIOS_EQUIP
);
759 bios_drives
= (equip
>> 6)+1;
764 if (pptr
->drivecnt
> bios_drives
) {
765 printf("BIOS equipment byte says count = %d, go with that\n", bios_drives
);
766 pptr
->drivecnt
= bios_drives
;
769 /* Discontiguous drive space. There is no really good solution for this. */
770 if ( pptr
->drivecnt
<= (geometry
->driveno
& 0x7f) )
771 pptr
->drivecnt
= (geometry
->driveno
& 0x7f) + 1;
773 /* Pointer to the command line */
774 pptr
->cmdline_off
= bin_size
+ (nranges
+1)*sizeof(ranges
[0]);
775 pptr
->cmdline_seg
= driverseg
;
777 /* Copy driver followed by E820 table followed by command line */
779 unsigned char *dpp
= (unsigned char *)(driverseg
<< 4);
780 dpp
= memcpy_endptr(dpp
, &_binary_memdisk_bin_start
, bin_size
);
781 dpp
= memcpy_endptr(dpp
, ranges
, (nranges
+1)*sizeof(ranges
[0]));
782 dpp
= memcpy_endptr(dpp
, shdr
->cmdline
, cmdlinelen
+1);
785 /* Install the interrupt handlers */
786 printf("old: int13 = %08x int15 = %08x\n",
787 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
));
789 wrz_32(BIOS_INT13
, driverptr
+hptr
->int13_offs
);
790 wrz_32(BIOS_INT15
, driverptr
+hptr
->int15_offs
);
792 printf("new: int13 = %08x int15 = %08x\n",
793 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
));
795 /* Update various BIOS magic data areas (gotta love this shit) */
797 if ( geometry
->driveno
& 0x80 ) {
798 /* Update BIOS hard disk count */
799 uint8_t nhd
= pptr
->drivecnt
;
804 wrz_8(BIOS_HD_COUNT
, nhd
);
806 /* Update BIOS floppy disk count */
807 uint8_t equip
= rdz_8(BIOS_EQUIP
);
808 uint8_t nflop
= pptr
->drivecnt
;
810 if ( nflop
> 4 ) /* Limit of equipment byte */
815 equip
|= ((nflop
-1) << 6) | 0x01;
817 wrz_8(BIOS_EQUIP
, equip
);
820 /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
821 puts("Loading boot sector... ");
823 memset(®s
, 0, sizeof regs
);
825 regs
.eax
.w
[0] = 0x0201; /* Read sector */
826 regs
.ebx
.w
[0] = 0x7c00; /* 0000:7C00 */
827 regs
.ecx
.w
[0] = 1; /* One sector */
828 regs
.edx
.w
[0] = geometry
->driveno
;
829 syscall(0x13, ®s
, ®s
);
831 if ( regs
.eflags
.l
& 1 ) {
832 puts("MEMDISK: Failed to load new boot sector\n");
836 if ( getcmditem("pause") != CMD_NOTFOUND
) {
837 puts("press any key to boot... ");
839 syscall(0x16, ®s
, NULL
);
842 puts("booting...\n");
844 /* On return the assembly code will jump to the boot vector */
845 shdr
->esdi
= pnp_install_check();
846 shdr
->edx
= geometry
->driveno
;