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 static const struct geometry geometries
[] =
367 { 360*2, 40, 2, 9, 0, 0x01, 0 }, /* 360 K */
368 { 720*2, 80, 2, 9, 0, 0x03, 0 }, /* 720 K*/
369 { 1200*2, 80, 2, 15, 0, 0x02, 0 }, /* 1200 K */
370 { 1440*2, 80, 2, 18, 0, 0x04, 0 }, /* 1440 K */
371 { 1680*2, 80, 2, 21, 0, 0x04, 0 }, /* 1680 K */
372 { 1722*2, 82, 2, 21, 0, 0x04, 0 }, /* 1722 K */
373 { 2880*2, 80, 2, 36, 0, 0x06, 0 }, /* 2880 K */
374 { 3840*2, 80, 2, 48, 0, 0x06, 0 }, /* 3840 K */
376 #define known_geometries (sizeof(geometries)/sizeof(struct geometry))
378 /* Format of a DOS partition table entry */
381 uint8_t start_h
, start_s
, start_c
;
383 uint8_t end_h
, end_s
, end_c
;
388 /* Format of a DOSEMU header */
389 struct dosemu_header
{
390 uint8_t magic
[7]; /* DOSEMU\0 */
396 } __attribute__((packed
));
398 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
400 const struct geometry
*get_disk_image_geometry(uint32_t where
, uint32_t size
)
402 static struct geometry hd_geometry
= { 0, 0, 0, 0, 0, 0, 0x80 };
403 struct ptab_entry ptab
[4]; /* Partition table buffer */
404 struct dosemu_header dosemu
;
405 unsigned int sectors
, v
;
406 unsigned int max_c
, max_h
, max_s
;
407 unsigned int c
, h
, s
, offset
;
412 printf("command line: %s\n", shdr
->cmdline
);
415 if ( CMD_HASDATA(p
= getcmditem("offset")) && (v
= atou(p
)) )
418 sectors
= (size
-offset
) >> 9;
419 for ( i
= 0 ; i
< known_geometries
; i
++ ) {
420 if ( sectors
== geometries
[i
].sectors
) {
421 hd_geometry
= geometries
[i
];
426 hd_geometry
.sectors
= sectors
;
427 hd_geometry
.offset
= offset
;
429 /* Do we have a DOSEMU header? */
430 memcpy(&dosemu
, (char *)where
+hd_geometry
.offset
, sizeof dosemu
);
431 if ( !memcmp("DOSEMU", dosemu
.magic
, 7) ) {
432 /* Always a hard disk unless overruled by command-line options */
433 hd_geometry
.driveno
= 0x80;
434 hd_geometry
.type
= 0;
435 hd_geometry
.c
= dosemu
.c
;
436 hd_geometry
.h
= dosemu
.h
;
437 hd_geometry
.s
= dosemu
.s
;
438 hd_geometry
.offset
+= dosemu
.offset
;
439 sectors
= (size
-hd_geometry
.offset
) >> 9;
442 if ( CMD_HASDATA(p
= getcmditem("c")) && (v
= atou(p
)) )
444 if ( CMD_HASDATA(p
= getcmditem("h")) && (v
= atou(p
)) )
446 if ( CMD_HASDATA(p
= getcmditem("s")) && (v
= atou(p
)) )
449 if ( (p
= getcmditem("floppy")) != CMD_NOTFOUND
) {
450 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) & 0x7f : 0;
451 if ( hd_geometry
.type
== 0 )
452 hd_geometry
.type
= 0x10; /* ATAPI floppy, e.g. LS-120 */
454 } else if ( (p
= getcmditem("harddisk")) != CMD_NOTFOUND
) {
455 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) | 0x80 : 0x80;
456 hd_geometry
.type
= 0;
460 if ( (hd_geometry
.c
== 0) || (hd_geometry
.h
== 0) ||
461 (hd_geometry
.s
== 0) ) {
462 /* Hard disk image, need to examine the partition table for geometry */
463 memcpy(&ptab
, (char *)where
+hd_geometry
.offset
+(512-2-4*16), sizeof ptab
);
465 max_c
= max_h
= 0; max_s
= 1;
466 for ( i
= 0 ; i
< 4 ; i
++ ) {
467 if ( ptab
[i
].type
) {
468 c
= ptab
[i
].start_c
+ (ptab
[i
].start_s
>> 6);
469 s
= (ptab
[i
].start_s
& 0x3f);
472 if ( max_c
< c
) max_c
= c
;
473 if ( max_h
< h
) max_h
= h
;
474 if ( max_s
< s
) max_s
= s
;
476 c
= ptab
[i
].end_c
+ (ptab
[i
].end_s
>> 6);
477 s
= (ptab
[i
].end_s
& 0x3f);
480 if ( max_c
< c
) max_c
= c
;
481 if ( max_h
< h
) max_h
= h
;
482 if ( max_s
< s
) max_s
= s
;
486 max_c
++; max_h
++; /* Convert to count (1-based) */
488 if ( !hd_geometry
.h
)
489 hd_geometry
.h
= max_h
;
490 if ( !hd_geometry
.s
)
491 hd_geometry
.s
= max_s
;
492 if ( !hd_geometry
.c
)
493 hd_geometry
.c
= sectors
/(hd_geometry
.h
*hd_geometry
.s
);
496 if ( (size
-hd_geometry
.offset
) & 0x1ff ) {
497 puts("MEMDISK: Image has fractional end sector\n");
499 if ( sectors
% (hd_geometry
.h
*hd_geometry
.s
) ) {
500 puts("MEMDISK: Image seems to have fractional end cylinder\n");
502 if ( (hd_geometry
.c
*hd_geometry
.h
*hd_geometry
.s
) > sectors
) {
503 puts("MEMDISK: Image appears to be truncated\n");
510 * Jump here if all hope is gone...
512 void __attribute__((noreturn
)) die(void)
520 * Find a $PnP installation check structure; return (ES << 16) + DI value
522 static uint32_t pnp_install_check(void)
525 unsigned char *p
, csum
;
528 for (seg
= (uint32_t *)0xf0000; seg
< (uint32_t *)0x100000; seg
+= 4) {
529 if (*seg
== ('$'+('P' << 8)+('n' << 16)+('P' << 24))) {
530 p
= (unsigned char *)seg
;
535 for (i
= len
; i
; i
--)
540 return (0xf000 << 16) + (uint16_t)(unsigned long)seg
;
547 #define STACK_NEEDED 512 /* Number of bytes of stack */
550 * Actual setup routine
551 * Returns the drive number (which is then passed in %dl to the
557 void setup(syscall_t cs_syscall
, void *cs_bounce
)
559 unsigned int bin_size
= (int) &_binary_memdisk_bin_size
;
560 struct memdisk_header
*hptr
;
561 struct patch_area
*pptr
;
563 uint32_t driverptr
, driveraddr
;
566 const struct geometry
*geometry
;
567 int total_size
, cmdlinelen
;
569 uint32_t ramdisk_image
, ramdisk_size
;
571 int do_edd
= -1; /* -1 = default, 0 = no, 1 = yes */
573 /* Set up global variables */
574 syscall
= cs_syscall
;
575 sys_bounce
= cs_bounce
;
577 /* Show signs of life */
578 printf("%s %s\n", memdisk_version
, copyright
);
580 if ( !shdr
->ramdisk_image
|| !shdr
->ramdisk_size
) {
581 puts("MEMDISK: No ramdisk image specified!\n");
585 ramdisk_image
= shdr
->ramdisk_image
;
586 ramdisk_size
= shdr
->ramdisk_size
;
588 e820map_init(); /* Initialize memory data structure */
589 get_mem(); /* Query BIOS for memory map */
590 parse_mem(); /* Parse memory map */
592 printf("Ramdisk at 0x%08x, length 0x%08x\n",
593 ramdisk_image
, ramdisk_size
);
595 unzip_if_needed(&ramdisk_image
, &ramdisk_size
);
597 geometry
= get_disk_image_geometry(ramdisk_image
, ramdisk_size
);
599 if (getcmditem("edd") != CMD_NOTFOUND
||
600 getcmditem("ebios") != CMD_NOTFOUND
)
602 else if (getcmditem("noedd") != CMD_NOTFOUND
||
603 getcmditem("noebios") != CMD_NOTFOUND
||
604 getcmditem("cbios") != CMD_NOTFOUND
)
607 do_edd
= (geometry
->driveno
& 0x80) ? 1 : 0;
609 printf("Disk is %s %d, %u K, C/H/S = %u/%u/%u, EDD %s\n",
610 (geometry
->driveno
& 0x80) ? "hard disk" : "floppy",
611 geometry
->driveno
& 0x7f,
612 geometry
->sectors
>> 1,
613 geometry
->c
, geometry
->h
, geometry
->s
,
614 do_edd
? "on" : "off");
616 /* Reserve the ramdisk memory */
617 insertrange(ramdisk_image
, ramdisk_size
, 2);
618 parse_mem(); /* Recompute variables */
620 /* Figure out where it needs to go */
621 hptr
= (struct memdisk_header
*) &_binary_memdisk_bin_start
;
622 pptr
= (struct patch_area
*)(_binary_memdisk_bin_start
+ hptr
->patch_offs
);
624 dosmem_k
= rdz_16(BIOS_BASEMEM
);
625 pptr
->olddosmem
= dosmem_k
;
626 stddosmem
= dosmem_k
<< 10;
627 /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
628 if ( stddosmem
> dos_mem
)
631 pptr
->driveno
= geometry
->driveno
;
632 pptr
->drivetype
= geometry
->type
;
633 pptr
->cylinders
= geometry
->c
;
634 pptr
->heads
= geometry
->h
;
635 pptr
->sectors
= geometry
->s
;
636 pptr
->disksize
= geometry
->sectors
;
637 pptr
->diskbuf
= ramdisk_image
+ geometry
->offset
;
638 pptr
->statusptr
= (geometry
->driveno
& 0x80) ? 0x474 : 0x441;
640 pptr
->bootloaderid
= shdr
->type_of_loader
;
642 pptr
->configflags
= 0;
643 /* Set config flags */
644 if ( getcmditem("ro") != CMD_NOTFOUND
) {
645 puts("Marking disk readonly\n");
646 pptr
->configflags
|= CONFIG_READONLY
;
648 if ( getcmditem("raw") != CMD_NOTFOUND
) {
649 puts("Using raw access to high memory\n");
650 pptr
->configflags
&= ~CONFIG_SAFEINT
|CONFIG_BIGRAW
;
651 pptr
->configflags
|= CONFIG_RAW
;
653 if ( getcmditem("safeint") != CMD_NOTFOUND
) {
654 puts("Using safe INT 15h access to high memory\n");
655 pptr
->configflags
&= ~CONFIG_RAW
|CONFIG_BIGRAW
;
656 pptr
->configflags
|= CONFIG_SAFEINT
;
658 if ( getcmditem("bigraw") != CMD_NOTFOUND
) {
659 puts("Using raw access to high memory - assuming big real mode\n");
660 pptr
->configflags
&= ~CONFIG_SAFEINT
;
661 pptr
->configflags
|= CONFIG_BIGRAW
|CONFIG_RAW
;
664 /* pptr->maxint13func defaults to EDD enabled, if compiled in */
666 pptr
->maxint13func
= MAXINT13_NOEDD
;
668 /* Set up a drive parameter table */
669 if ( geometry
->driveno
& 0x80 ) {
671 pptr
->dpt
.hd
.max_cyl
= geometry
->c
-1;
672 pptr
->dpt
.hd
.max_head
= geometry
->h
-1;
673 pptr
->dpt
.hd
.ctrl
= (geometry
->h
> 8) ? 0x08: 0;
675 /* Floppy - most of these fields are bogus and mimic
676 a 1.44 MB floppy drive */
677 pptr
->dpt
.fd
.specify1
= 0xdf;
678 pptr
->dpt
.fd
.specify2
= 0x02;
679 pptr
->dpt
.fd
.delay
= 0x25;
680 pptr
->dpt
.fd
.sectors
= geometry
->s
;
681 pptr
->dpt
.fd
.bps
= 0x02;
682 pptr
->dpt
.fd
.isgap
= 0x12;
683 pptr
->dpt
.fd
.dlen
= 0xff;
684 pptr
->dpt
.fd
.fgap
= 0x6c;
685 pptr
->dpt
.fd
.ffill
= 0xf6;
686 pptr
->dpt
.fd
.settle
= 0x0f;
687 pptr
->dpt
.fd
.mstart
= 0x05;
689 pptr
->dpt
.fd
.old_fd_dpt
= rdz_32(BIOS_INT1E
);
692 /* Set up an EDD drive parameter table */
693 pptr
->edd_dpt
.sectors
= geometry
->sectors
;
694 /* The EDD spec has this as <= 15482880 sectors (1024x240x63);
695 this seems to make very little sense. Try for something saner. */
696 if (geometry
->c
<= 1024 && geometry
->h
<= 255 && geometry
->s
<= 63) {
697 pptr
->edd_dpt
.c
= geometry
->c
;
698 pptr
->edd_dpt
.h
= geometry
->h
;
699 pptr
->edd_dpt
.s
= geometry
->s
;
700 pptr
->edd_dpt
.flags
|= 0x0002; /* Geometry valid */
702 if (!(geometry
->driveno
& 0x80)) {
703 /* Floppy drive. Mark it as a removable device with
704 media change notification; media is present. */
705 pptr
->edd_dpt
.flags
|= 0x0014;
708 /* The size is given by hptr->total_size plus the size of the E820
709 map -- 12 bytes per range; we may need as many as 2 additional
710 ranges (each insertrange() can worst-case turn 1 area into 3)
711 plus the terminating range, over what nranges currently show. */
712 cmdlinelen
= strlen(shdr
->cmdline
)+1;
713 total_size
= hptr
->total_size
; /* Actual memdisk code */
714 total_size
+= (nranges
+3)*sizeof(ranges
[0]); /* E820 memory ranges */
715 total_size
+= cmdlinelen
; /* Command line */
716 total_size
+= STACK_NEEDED
; /* Stack */
717 printf("Total size needed = %u bytes, allocating %uK\n",
718 total_size
, (total_size
+0x3ff) >> 10);
720 if ( total_size
> dos_mem
) {
721 puts("MEMDISK: Insufficient low memory\n");
725 driveraddr
= stddosmem
- total_size
;
726 driveraddr
&= ~0x3FF;
728 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
729 stddosmem
, dos_mem
, driveraddr
);
731 /* Reserve this range of memory */
732 wrz_16(BIOS_BASEMEM
, driveraddr
>> 10);
733 insertrange(driveraddr
, dos_mem
-driveraddr
, 2);
736 pptr
->mem1mb
= low_mem
>> 10;
737 pptr
->mem16mb
= high_mem
>> 16;
738 if ( low_mem
== (15 << 20) ) {
739 /* lowmem maxed out */
740 uint32_t int1588mem
= (high_mem
>> 10)+(low_mem
>> 10);
741 pptr
->memint1588
= (int1588mem
> 0xffff) ? 0xffff : int1588mem
;
743 pptr
->memint1588
= low_mem
>> 10;
746 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
747 pptr
->memint1588
, pptr
->mem1mb
, pptr
->mem16mb
);
749 driverseg
= driveraddr
>> 4;
750 driverptr
= driverseg
<< 16;
752 /* Anything beyond the end is for the stack */
753 pptr
->mystack
= (uint16_t)(stddosmem
-driveraddr
);
755 pptr
->oldint13
= rdz_32(BIOS_INT13
);
756 pptr
->oldint15
= rdz_32(BIOS_INT15
);
758 /* Adjust the E820 table: if there are null ranges (type 0)
759 at the end, change them to type end of list (-1).
760 This is necessary for the driver to be able to report end
761 of list correctly. */
762 while ( nranges
&& ranges
[nranges
-1].type
== 0 ) {
763 ranges
[--nranges
].type
= -1;
766 if (getcmditem("nopass") != CMD_NOTFOUND
) {
767 /* nopass specified - we're the only drive by definition */
768 printf("nopass specified - we're the only drive\n");
771 pptr
->oldint13
= driverptr
+hptr
->iret_offs
;
773 /* Query drive parameters of this type */
774 memset(®s
, 0, sizeof regs
);
776 regs
.eax
.b
[1] = 0x08;
777 regs
.edx
.b
[0] = geometry
->driveno
& 0x80;
778 syscall(0x13, ®s
, ®s
);
780 if ( regs
.eflags
.l
& 1 ) {
781 printf("INT 13 08: Failure, assuming this is the only drive\n");
784 printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
785 regs
.edx
.b
[0], regs
.es
, regs
.edi
.w
[0]);
786 pptr
->drivecnt
= regs
.edx
.b
[0];
789 /* Compare what INT 13h returned with the appropriate equipment byte */
790 if ( geometry
->driveno
& 0x80 ) {
791 bios_drives
= rdz_8(BIOS_HD_COUNT
);
793 uint8_t equip
= rdz_8(BIOS_EQUIP
);
796 bios_drives
= (equip
>> 6)+1;
801 if (pptr
->drivecnt
> bios_drives
) {
802 printf("BIOS equipment byte says count = %d, go with that\n",
804 pptr
->drivecnt
= bios_drives
;
808 /* Add ourselves to the drive count */
811 /* Discontiguous drive space. There is no really good solution for this. */
812 if ( pptr
->drivecnt
<= (geometry
->driveno
& 0x7f) )
813 pptr
->drivecnt
= (geometry
->driveno
& 0x7f) + 1;
815 /* Pointer to the command line */
816 pptr
->cmdline_off
= bin_size
+ (nranges
+1)*sizeof(ranges
[0]);
817 pptr
->cmdline_seg
= driverseg
;
819 /* Copy driver followed by E820 table followed by command line */
821 unsigned char *dpp
= (unsigned char *)(driverseg
<< 4);
822 dpp
= memcpy_endptr(dpp
, &_binary_memdisk_bin_start
, bin_size
);
823 dpp
= memcpy_endptr(dpp
, ranges
, (nranges
+1)*sizeof(ranges
[0]));
824 dpp
= memcpy_endptr(dpp
, shdr
->cmdline
, cmdlinelen
+1);
827 /* Install the interrupt handlers */
828 printf("old: int13 = %08x int15 = %08x\n",
829 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
));
831 wrz_32(BIOS_INT13
, driverptr
+hptr
->int13_offs
);
832 wrz_32(BIOS_INT15
, driverptr
+hptr
->int15_offs
);
834 printf("new: int13 = %08x int15 = %08x\n",
835 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
));
837 /* Update various BIOS magic data areas (gotta love this shit) */
839 if ( geometry
->driveno
& 0x80 ) {
840 /* Update BIOS hard disk count */
841 uint8_t nhd
= pptr
->drivecnt
;
846 wrz_8(BIOS_HD_COUNT
, nhd
);
848 /* Update BIOS floppy disk count */
849 uint8_t equip
= rdz_8(BIOS_EQUIP
);
850 uint8_t nflop
= pptr
->drivecnt
;
852 if ( nflop
> 4 ) /* Limit of equipment byte */
857 equip
|= ((nflop
-1) << 6) | 0x01;
859 wrz_8(BIOS_EQUIP
, equip
);
862 /* Reboot into the new "disk"; this is also a test for the interrupt hooks */
863 puts("Loading boot sector... ");
865 memset(®s
, 0, sizeof regs
);
867 regs
.eax
.w
[0] = 0x0201; /* Read sector */
868 regs
.ebx
.w
[0] = 0x7c00; /* 0000:7C00 */
869 regs
.ecx
.w
[0] = 1; /* One sector */
870 regs
.edx
.w
[0] = geometry
->driveno
;
871 syscall(0x13, ®s
, ®s
);
873 if ( regs
.eflags
.l
& 1 ) {
874 puts("MEMDISK: Failed to load new boot sector\n");
878 if ( getcmditem("pause") != CMD_NOTFOUND
) {
879 puts("press any key to boot... ");
881 syscall(0x16, ®s
, NULL
);
884 puts("booting...\n");
886 /* On return the assembly code will jump to the boot vector */
887 shdr
->esdi
= pnp_install_check();
888 shdr
->edx
= geometry
->driveno
;