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 * ----------------------------------------------------------------------- */
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
{
35 /* The Disk Parameter Table may be required */
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 */
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 */
64 /* EDD disk parameter table */
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 */
79 uint16_t cmdline_off
, cmdline_seg
;
87 #define MAXINT13_NOEDD 0x16
104 #define CONFIG_READONLY 0x01
105 #define CONFIG_RAW 0x02
106 #define CONFIG_SAFEINT 0x04
107 #define CONFIG_BIGRAW 0x08 /* MUST be 8! */
113 struct edd_dpt edd_dpt
;
116 /* This is the header in the boot sector/setup area */
117 struct setup_header
{
129 uint32_t realmode_swtch
;
131 uint8_t type_of_loader
;
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
;
140 uint32_t cmd_line_ptr
;
141 uint32_t initrd_addr_max
;
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 */
152 wrz_8(uint32_t addr
, uint8_t data
)
154 *((uint8_t *)addr
) = data
;
157 wrz_16(uint32_t addr
, uint16_t data
)
159 *((uint16_t *)addr
) = data
;
162 wrz_32(uint32_t addr
, uint32_t data
)
164 *((uint32_t *)addr
) = data
;
166 static inline uint8_t
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
)
206 const char *wp
= what
;
209 for ( p
= shdr
->cmdline
; *p
; p
++ ) {
211 case 0: /* Ground state */
219 case 1: /* Matching */
223 else if ( *p
== ' ' )
234 case 2: /* Mismatch, skip rest of option */
236 match
= 0; /* Next option */
241 /* Check for matching string at end of line */
242 if ( match
== 1 && *wp
== '\0' )
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
;
260 uint32_t startrange
, endrange
;
261 uint32_t gzdatasize
, gzwhere
;
262 uint32_t orig_crc
, offset
;
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
273 puts("internal error: check_zip returned nonsense\n");
277 /* Find a good place to put it: search memory ranges in descending order
278 until we find one that is legal and fits */
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. */
285 if ( ranges
[i
].type
!= 1 )
289 if ( ranges
[i
].start
>= 0xFFFFFFFF )
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
)
309 /* Must be large enough... don't rely on gzwhere for this (wraparound) */
310 if ( endrange
-startrange
< gzdatasize
)
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 */
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",
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
);
340 printf("Not enough memory to decompress image (need 0x%08x bytes)\n",
345 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
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
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 */
368 uint8_t start_h
, start_s
, start_c
;
370 uint8_t end_h
, end_s
, end_c
;
375 /* Format of a DOSEMU header */
376 struct dosemu_header
{
377 uint8_t magic
[7]; /* DOSEMU\0 */
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
;
399 printf("command line: %s\n", shdr
->cmdline
);
402 if ( CMD_HASDATA(p
= getcmditem("offset")) && (v
= atou(p
)) )
405 sectors
= (size
-offset
) >> 9;
407 if (sectors
< 4096*2) {
409 unsigned int xsectors
= sectors
;
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;
426 c
= 80; h
= 2; type
= 6;
431 if (s
< 63 && (xsectors
% track
) == 0) {
439 hd_geometry
.driveno
= 0;
444 /* No valid floppy geometry, fake it by simulating broken
445 sectors at the end of the image... */
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
)) )
472 if ( CMD_HASDATA(p
= getcmditem("h")) && (v
= atou(p
)) )
474 if ( CMD_HASDATA(p
= getcmditem("s")) && (v
= atou(p
)) )
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 */
482 } else if ( (p
= getcmditem("harddisk")) != CMD_NOTFOUND
) {
483 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) | 0x80 : 0x80;
484 hd_geometry
.type
= 0;
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);
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);
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");
538 * Jump here if all hope is gone...
540 void __attribute__((noreturn
)) die(void)
548 * Find a $PnP installation check structure; return (ES << 16) + DI value
550 static uint32_t pnp_install_check(void)
553 unsigned char *p
, csum
;
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
;
563 for (i
= len
; i
; i
--)
568 return (0xf000 << 16) + (uint16_t)(unsigned long)seg
;
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
582 __cdecl syscall_t syscall
;
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
;
591 uint32_t driverptr
, driveraddr
;
594 const struct geometry
*geometry
;
595 int total_size
, cmdlinelen
;
597 uint32_t ramdisk_image
, ramdisk_size
;
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");
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
)
630 else if (getcmditem("noedd") != CMD_NOTFOUND
||
631 getcmditem("noebios") != CMD_NOTFOUND
||
632 getcmditem("cbios") != CMD_NOTFOUND
)
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
)
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 */
695 pptr
->maxint13func
= MAXINT13_NOEDD
;
697 /* Set up a drive parameter table */
698 if ( geometry
->driveno
& 0x80 ) {
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;
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");
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);
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
;
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");
800 pptr
->oldint13
= driverptr
+hptr
->iret_offs
;
802 /* Query drive parameters of this type */
803 memset(®s
, 0, sizeof regs
);
805 regs
.eax
.b
[1] = 0x08;
806 regs
.edx
.b
[0] = geometry
->driveno
& 0x80;
807 syscall(0x13, ®s
, ®s
);
809 if ( regs
.eflags
.l
& 1 ) {
810 printf("INT 13 08: Failure, assuming this is the only drive\n");
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
);
822 uint8_t equip
= rdz_8(BIOS_EQUIP
);
825 bios_drives
= (equip
>> 6)+1;
830 if (pptr
->drivecnt
> bios_drives
) {
831 printf("BIOS equipment byte says count = %d, go with that\n",
833 pptr
->drivecnt
= bios_drives
;
837 /* Add ourselves to the drive count */
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
;
875 wrz_8(BIOS_HD_COUNT
, nhd
);
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 */
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(®s
, 0, sizeof regs
);
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, ®s
, ®s
);
902 if ( regs
.eflags
.l
& 1 ) {
903 puts("MEMDISK: Failed to load new boot sector\n");
907 if ( getcmditem("pause") != CMD_NOTFOUND
) {
908 puts("press any key to boot... ");
910 syscall(0x16, ®s
, 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
;