1 /* ----------------------------------------------------------------------- *
3 * Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 * Portions copyright 2009-2010 Shao Miller
6 * [El Torito code, mBFT, "safe hook"]
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
11 * Boston MA 02111-1307, USA; either version 2 of the License, or
12 * (at your option) any later version; incorporated herein by reference.
14 * ----------------------------------------------------------------------- */
18 #include <suffix_number.h>
27 const char memdisk_version
[] = "MEMDISK " VERSION_STR
" " DATE
;
28 const char copyright
[] =
29 "Copyright " FIRSTYEAR
"-" YEAR_STR
" H. Peter Anvin et al";
31 extern const char _binary_memdisk_chs_512_bin_start
[];
32 extern const char _binary_memdisk_chs_512_bin_end
[];
33 extern const char _binary_memdisk_chs_512_bin_size
[];
34 extern const char _binary_memdisk_edd_512_bin_start
[];
35 extern const char _binary_memdisk_edd_512_bin_end
[];
36 extern const char _binary_memdisk_edd_512_bin_size
[];
37 extern const char _binary_memdisk_iso_512_bin_start
[];
38 extern const char _binary_memdisk_iso_512_bin_end
[];
39 extern const char _binary_memdisk_iso_512_bin_size
[];
40 extern const char _binary_memdisk_iso_2048_bin_start
[];
41 extern const char _binary_memdisk_iso_2048_bin_end
[];
42 extern const char _binary_memdisk_iso_2048_bin_size
[];
44 /* Pull in structures common to MEMDISK and MDISKCHK.COM */
47 /* An EDD disk packet */
49 uint8_t size
; /* Packet size */
50 uint8_t res1
; /* Reserved */
51 uint16_t count
; /* Count to transfer */
52 uint32_t buf
; /* Buffer pointer */
53 uint64_t start
; /* LBA to start from */
54 uint64_t buf64
; /* 64-bit buf pointer */
55 } __attribute__ ((packed
));
57 /* Change to 1 for El Torito debugging */
58 #define DBG_ELTORITO 0
61 extern void eltorito_dump(uint32_t);
65 * Routine to seek for a command-line item and return a pointer
66 * to the data portion, if present
69 /* Magic return values */
70 #define CMD_NOTFOUND ((char *)-1) /* Not found */
71 #define CMD_BOOL ((char *)-2) /* Found boolean option */
72 #define CMD_HASDATA(X) ((int)(X) >= 0)
74 static const char *getcmditem(const char *what
)
77 const char *wp
= what
;
80 for (p
= shdr
->cmdline
; *p
; p
++) {
82 case 0: /* Ground state */
90 case 1: /* Matching */
105 case 2: /* Mismatch, skip rest of option */
107 match
= 0; /* Next option */
112 /* Check for matching string at end of line */
113 if (match
== 1 && *wp
== '\0')
120 * Check to see if this is a gzip image
122 #define UNZIP_ALIGN 512
124 extern const char _end
[]; /* Symbol signalling end of data */
126 void unzip_if_needed(uint32_t * where_p
, uint32_t * size_p
)
128 uint32_t where
= *where_p
;
129 uint32_t size
= *size_p
;
131 uint32_t startrange
, endrange
;
132 uint32_t gzdatasize
, gzwhere
;
133 uint32_t orig_crc
, offset
;
137 /* Is it a gzip image? */
138 if (check_zip((void *)where
, size
, &zbytes
, &gzdatasize
,
139 &orig_crc
, &offset
) == 0) {
141 if (offset
+ zbytes
> size
) {
143 * Assertion failure; check_zip is supposed to guarantee this
146 die("internal error: check_zip returned nonsense\n");
150 * Find a good place to put it: search memory ranges in descending
151 * order until we find one that is legal and fits
154 for (i
= nranges
- 1; i
>= 0; i
--) {
156 * We can't use > 4G memory (32 bits only.) Truncate to 2^32-1
157 * so we don't have to deal with funny wraparound issues.
161 if (ranges
[i
].type
!= 1)
165 if (ranges
[i
].start
>= 0xFFFFFFFF)
168 startrange
= (uint32_t) ranges
[i
].start
;
170 /* Range end (0 for end means 2^64) */
171 endrange
= ((ranges
[i
+ 1].start
>= 0xFFFFFFFF ||
172 ranges
[i
+ 1].start
== 0)
173 ? 0xFFFFFFFF : (uint32_t) ranges
[i
+ 1].start
);
175 /* Make sure we don't overwrite ourselves */
176 if (startrange
< (uint32_t) _end
)
177 startrange
= (uint32_t) _end
;
179 /* Allow for alignment */
181 (ranges
[i
].start
+ (UNZIP_ALIGN
- 1)) & ~(UNZIP_ALIGN
- 1);
183 /* In case we just killed the whole range... */
184 if (startrange
>= endrange
)
188 * Must be large enough... don't rely on gzwhere for this
191 if (endrange
- startrange
< gzdatasize
)
195 * This is where the gz image would be put if we put it in this
198 gzwhere
= (endrange
- gzdatasize
) & ~(UNZIP_ALIGN
- 1);
200 /* Cast to uint64_t just in case we're flush with the top byte */
201 if ((uint64_t) where
+ size
>= gzwhere
&& where
< endrange
) {
203 * Need to move source data to avoid compressed/uncompressed
208 if (gzwhere
- startrange
< size
)
209 continue; /* Can't fit both old and new */
211 newwhere
= (gzwhere
- size
) & ~(UNZIP_ALIGN
- 1);
212 printf("Moving compressed data from 0x%08x to 0x%08x\n",
215 memmove((void *)newwhere
, (void *)where
, size
);
225 die("Not enough memory to decompress image (need 0x%08x bytes)\n",
228 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
231 *size_p
= gzdatasize
;
232 *where_p
= (uint32_t) unzip((void *)(where
+ offset
), zbytes
,
233 gzdatasize
, orig_crc
, (void *)target
);
238 * Figure out the "geometry" of the disk in question
241 uint32_t sectors
; /* Sector count */
242 uint32_t c
, h
, s
; /* C/H/S geometry */
243 uint32_t offset
; /* Byte offset for disk */
244 uint32_t boot_lba
; /* LBA of bootstrap code */
245 uint8_t type
; /* Type byte for INT 13h AH=08h */
246 uint8_t driveno
; /* Drive no */
247 uint8_t sector_shift
; /* Sector size as a power of 2 */
248 const char *hsrc
, *ssrc
; /* Origins of H and S geometries */
251 /* Format of a DOS partition table entry */
254 uint8_t start_h
, start_s
, start_c
;
256 uint8_t end_h
, end_s
, end_c
;
259 } __attribute__ ((packed
));
261 /* Format of a FAT filesystem superblock */
268 char bs_filsystype
[8];
269 } __attribute__ ((packed
));
271 uint8_t bs_jmpboot
[3];
273 uint16_t bpb_bytspersec
;
274 uint8_t bpb_secperclus
;
275 uint16_t bpb_rsvdseccnt
;
277 uint16_t bpb_rootentcnt
;
278 uint16_t bpb_totsec16
;
280 uint16_t bpb_fatsz16
;
281 uint16_t bpb_secpertrk
;
282 uint16_t bpb_numheads
;
283 uint32_t bpb_hiddsec
;
284 uint32_t bpb_totsec32
;
287 struct fat_extra extra
;
290 uint32_t bpb_fatsz32
;
291 uint16_t bpb_extflags
;
293 uint32_t bpb_rootclus
;
295 uint16_t bpb_bkbootsec
;
296 char bpb_reserved
[12];
297 /* Clever, eh? Same fields, different offset... */
298 struct fat_extra extra
;
299 } fat32
__attribute__ ((packed
));
301 } __attribute__ ((packed
));
303 /* Format of a DOSEMU header */
304 struct dosemu_header
{
305 uint8_t magic
[7]; /* DOSEMU\0 */
311 } __attribute__ ((packed
));
313 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
315 static const struct geometry
*get_disk_image_geometry(uint32_t where
,
318 static struct geometry hd_geometry
;
319 struct dosemu_header dosemu
;
320 unsigned int sectors
, xsectors
, v
;
325 printf("command line: %s\n", shdr
->cmdline
);
327 hd_geometry
.sector_shift
= 9; /* Assume floppy/HDD at first */
330 if (CMD_HASDATA(p
= getcmditem("offset")) && (v
= atou(p
)))
333 sectors
= xsectors
= (size
- offset
) >> hd_geometry
.sector_shift
;
335 hd_geometry
.hsrc
= "guess";
336 hd_geometry
.ssrc
= "guess";
337 hd_geometry
.sectors
= sectors
;
338 hd_geometry
.offset
= offset
;
340 if ((p
= getcmditem("iso")) != CMD_NOTFOUND
) {
342 eltorito_dump(where
);
344 struct edd4_bvd
*bvd
= (struct edd4_bvd
*)(where
+ 17 * 2048);
345 /* Tiny sanity check */
346 if ((bvd
->boot_rec_ind
!= 0) || (bvd
->ver
!= 1))
347 printf("El Torito BVD sanity check failed.\n");
348 struct edd4_bootcat
*boot_cat
=
349 (struct edd4_bootcat
*)(where
+ bvd
->boot_cat
* 2048);
350 /* Another tiny sanity check */
351 if ((boot_cat
->validation_entry
.platform_id
!= 0) ||
352 (boot_cat
->validation_entry
.key55
!= 0x55) ||
353 (boot_cat
->validation_entry
.keyAA
!= 0xAA))
354 printf("El Torito boot catalog sanity check failed.\n");
355 /* If we have an emulation mode, set the offset to the image */
356 if (boot_cat
->initial_entry
.media_type
)
357 hd_geometry
.offset
+= boot_cat
->initial_entry
.load_block
* 2048;
359 /* We're a no-emulation mode, so we will boot to an offset */
360 hd_geometry
.boot_lba
= boot_cat
->initial_entry
.load_block
* 4;
361 if (boot_cat
->initial_entry
.media_type
< 4) {
362 /* We're a floppy emulation mode or our params will be
363 * overwritten by the no emulation mode case
365 hd_geometry
.driveno
= 0x00;
369 switch (boot_cat
->initial_entry
.media_type
) {
370 case 0: /* No emulation */
371 hd_geometry
.driveno
= 0xE0;
372 hd_geometry
.type
= 10; /* ATAPI removable media device */
373 hd_geometry
.c
= 65535;
376 /* 2048-byte sectors, so adjust the size and count */
377 hd_geometry
.sector_shift
= 11;
379 case 1: /* 1.2 MB floppy */
381 hd_geometry
.type
= 2;
384 case 2: /* 1.44 MB floppy */
386 hd_geometry
.type
= 4;
389 case 3: /* 2.88 MB floppy */
391 hd_geometry
.type
= 6;
395 hd_geometry
.driveno
= 0x80;
396 hd_geometry
.type
= 0;
399 sectors
= (size
- hd_geometry
.offset
) >> hd_geometry
.sector_shift
;
401 /* For HDD emulation, we figure out the geometry later. Otherwise: */
403 hd_geometry
.hsrc
= hd_geometry
.ssrc
= "El Torito";
405 hd_geometry
.sectors
= sectors
;
408 /* Do we have a DOSEMU header? */
409 memcpy(&dosemu
, (char *)where
+ hd_geometry
.offset
, sizeof dosemu
);
410 if (!memcmp("DOSEMU", dosemu
.magic
, 7)) {
411 /* Always a hard disk unless overruled by command-line options */
412 hd_geometry
.driveno
= 0x80;
413 hd_geometry
.type
= 0;
414 hd_geometry
.c
= dosemu
.c
;
415 hd_geometry
.h
= dosemu
.h
;
416 hd_geometry
.s
= dosemu
.s
;
417 hd_geometry
.offset
+= dosemu
.offset
;
418 sectors
= (size
- hd_geometry
.offset
) >> hd_geometry
.sector_shift
;
420 hd_geometry
.hsrc
= hd_geometry
.ssrc
= "DOSEMU";
423 if (CMD_HASDATA(p
= getcmditem("c")) && (v
= atou(p
)))
425 if (CMD_HASDATA(p
= getcmditem("h")) && (v
= atou(p
))) {
427 hd_geometry
.hsrc
= "cmd";
429 if (CMD_HASDATA(p
= getcmditem("s")) && (v
= atou(p
))) {
431 hd_geometry
.ssrc
= "cmd";
434 if (!hd_geometry
.h
|| !hd_geometry
.s
) {
435 int h
, s
, max_h
, max_s
;
437 max_h
= hd_geometry
.h
;
438 max_s
= hd_geometry
.s
;
440 if (!(max_h
| max_s
)) {
441 /* Look for a FAT superblock and if we find something that looks
442 enough like one, use geometry from that. This takes care of
443 megafloppy images and unpartitioned hard disks. */
444 const struct fat_extra
*extra
= NULL
;
445 const struct fat_super
*fs
= (const struct fat_super
*)
446 ((char *)where
+ hd_geometry
.offset
);
448 if ((fs
->bpb_media
== 0xf0 || fs
->bpb_media
>= 0xf8) &&
449 (fs
->bs_jmpboot
[0] == 0xe9 || fs
->bs_jmpboot
[0] == 0xeb) &&
450 fs
->bpb_bytspersec
== 512 &&
451 fs
->bpb_numheads
>= 1 && fs
->bpb_numheads
<= 256 &&
452 fs
->bpb_secpertrk
>= 1 && fs
->bpb_secpertrk
<= 63) {
454 fs
->bpb_fatsz16
? &fs
->x
.fat16
.extra
: &fs
->x
.fat32
.extra
;
456 (extra
->bs_bootsig
== 0x29 && extra
->bs_filsystype
[0] == 'F'
457 && extra
->bs_filsystype
[1] == 'A'
458 && extra
->bs_filsystype
[2] == 'T'))
462 hd_geometry
.driveno
= extra
->bs_drvnum
& 0x80;
463 max_h
= fs
->bpb_numheads
;
464 max_s
= fs
->bpb_secpertrk
;
465 hd_geometry
.hsrc
= hd_geometry
.ssrc
= "FAT";
469 if (!(max_h
| max_s
)) {
470 /* No FAT filesystem found to steal geometry from... */
471 if ((sectors
< 4096 * 2) && (hd_geometry
.sector_shift
== 9)) {
473 unsigned int xsectors
= sectors
;
475 hd_geometry
.driveno
= 0; /* Assume floppy */
478 /* Assume it's a floppy drive, guess a geometry */
479 unsigned int type
, track
;
482 if (xsectors
< 320 * 2) {
486 } else if (xsectors
< 640 * 2) {
490 } else if (xsectors
< 1200 * 2) {
494 } else if (xsectors
< 1440 * 2) {
498 } else if (xsectors
< 2880 * 2) {
509 s
= xsectors
/ track
;
510 if (s
< 63 && (xsectors
% track
) == 0) {
520 hd_geometry
.hsrc
= hd_geometry
.ssrc
= "fd";
522 /* No valid floppy geometry, fake it by simulating broken
523 sectors at the end of the image... */
527 hd_geometry
.type
= type
;
530 /* Assume it is a hard disk image and scan for a partition table */
531 const struct ptab_entry
*ptab
= (const struct ptab_entry
*)
532 ((char *)where
+ hd_geometry
.offset
+ (512 - 2 - 4 * 16));
534 /* Assume hard disk */
535 if (!hd_geometry
.driveno
)
536 hd_geometry
.driveno
= 0x80;
538 if (*(uint16_t *) ((char *)where
+ hd_geometry
.offset
+ 512 - 2) == 0xaa55) {
539 for (i
= 0; i
< 4; i
++) {
540 if (ptab
[i
].type
&& !(ptab
[i
].active
& 0x7f)) {
541 s
= (ptab
[i
].start_s
& 0x3f);
542 h
= ptab
[i
].start_h
+ 1;
549 s
= (ptab
[i
].end_s
& 0x3f);
550 h
= ptab
[i
].end_h
+ 1;
554 hd_geometry
.hsrc
= "MBR";
558 hd_geometry
.ssrc
= "MBR";
564 hd_geometry
.type
= 0;
569 max_h
= xsectors
> 2097152 ? 255 : 64;
571 max_s
= xsectors
> 2097152 ? 63 : 32;
573 hd_geometry
.h
= max_h
;
574 hd_geometry
.s
= max_s
;
578 hd_geometry
.c
= xsectors
/ (hd_geometry
.h
* hd_geometry
.s
);
580 if ((p
= getcmditem("floppy")) != CMD_NOTFOUND
) {
581 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) & 0x7f : 0;
582 } else if ((p
= getcmditem("harddisk")) != CMD_NOTFOUND
) {
583 hd_geometry
.driveno
= CMD_HASDATA(p
) ? atou(p
) | 0x80 : 0x80;
586 if (hd_geometry
.driveno
& 0x80) {
587 hd_geometry
.type
= 0; /* Type = hard disk */
589 if (hd_geometry
.type
== 0)
590 hd_geometry
.type
= 0x10; /* ATAPI floppy, e.g. LS-120 */
593 if ((size
- hd_geometry
.offset
) & 0x1ff) {
594 puts("MEMDISK: Image has fractional end sector\n");
596 if (sectors
% (hd_geometry
.h
* hd_geometry
.s
)) {
597 puts("MEMDISK: Image seems to have fractional end cylinder\n");
599 if ((hd_geometry
.c
* hd_geometry
.h
* hd_geometry
.s
) > sectors
) {
600 puts("MEMDISK: Image appears to be truncated\n");
607 * Find a $PnP installation check structure; return (ES << 16) + DI value
609 static uint32_t pnp_install_check(void)
612 unsigned char *p
, csum
;
615 for (seg
= (uint32_t *) 0xf0000; seg
< (uint32_t *) 0x100000; seg
+= 4) {
616 if (*seg
== ('$' + ('P' << 8) + ('n' << 16) + ('P' << 24))) {
617 p
= (unsigned char *)seg
;
622 for (i
= len
; i
; i
--)
627 return (0xf000 << 16) + (uint16_t) (unsigned long)seg
;
635 * Relocate the real-mode code to a new segment
640 } __attribute__ ((packed
));
642 static void set_seg_base(uint32_t gdt_base
, int seg
, uint32_t v
)
644 *(uint16_t *) (gdt_base
+ seg
+ 2) = v
;
645 *(uint8_t *) (gdt_base
+ seg
+ 4) = v
>> 16;
646 *(uint8_t *) (gdt_base
+ seg
+ 7) = v
>> 24;
649 static void relocate_rm_code(uint32_t newbase
)
652 uint32_t oldbase
= rm_args
.rm_base
;
653 uint32_t delta
= newbase
- oldbase
;
656 memmove((void *)newbase
, (void *)oldbase
, rm_args
.rm_size
);
658 rm_args
.rm_return
+= delta
;
659 rm_args
.rm_intcall
+= delta
;
660 rm_args
.rm_bounce
+= delta
;
661 rm_args
.rm_base
+= delta
;
662 rm_args
.rm_gdt
+= delta
;
663 rm_args
.rm_pmjmp
+= delta
;
664 rm_args
.rm_rmjmp
+= delta
;
666 gdt_base
= rm_args
.rm_gdt
;
668 *(uint32_t *) (gdt_base
+ 2) = gdt_base
; /* GDT self-pointer */
670 /* Segments 0x10 and 0x18 are real-mode-based */
671 set_seg_base(gdt_base
, 0x10, rm_args
.rm_base
);
672 set_seg_base(gdt_base
, 0x18, rm_args
.rm_base
);
674 #if __SIZEOF_POINTER__ == 4
675 asm volatile ("lgdtl %0"::"m" (*(char *)gdt_base
));
676 #elif __SIZEOF_POINTER__ == 8
677 asm volatile ("lgdt %0"::"m" (*(char *)gdt_base
));
679 #error "unsupported architecture"
682 *(uint32_t *) rm_args
.rm_pmjmp
+= delta
;
683 *(uint16_t *) rm_args
.rm_rmjmp
+= delta
>> 4;
685 rm_args
.rm_handle_interrupt
+= delta
;
690 static uint8_t checksum_buf(const void *buf
, int count
)
692 const uint8_t *p
= buf
;
701 static int stack_needed(void)
703 const unsigned int min_stack
= 128; /* Minimum stack size */
704 const unsigned int def_stack
= 512; /* Default stack size */
708 if (CMD_HASDATA(p
= getcmditem("stack")))
721 * Set max memory by reservation
722 * Adds reservations to data in INT15h to prevent access to the top of RAM
723 * if there's any above the point specified.
725 void setmaxmem(unsigned long long restop_ull
)
728 struct e820range
*ep
;
729 const int int15restype
= 2;
731 /* insertrange() works on uint32_t */
732 restop
= min(restop_ull
, UINT32_MAX
);
733 /* printf(" setmaxmem '%08x%08x' => %08x\n",
734 (unsigned int)(restop_ull>>32), (unsigned int)restop_ull, restop); */
736 for (ep
= ranges
; ep
->type
!= -1U; ep
++) {
737 if (ep
->type
== 1) { /* Only if available */
738 if (ep
->start
>= restop
) {
739 /* printf(" %08x -> 2\n", ep->start); */
740 ep
->type
= int15restype
;
741 } else if (ep
[1].start
> restop
) {
742 /* printf(" +%08x =2; cut %08x\n", restop, ep->start); */
743 insertrange(restop
, (ep
[1].start
- restop
), int15restype
);
750 struct real_mode_args rm_args
;
753 * Actual setup routine
754 * Returns the drive number (which is then passed in %dl to the
757 void setup(const struct real_mode_args
*rm_args_ptr
)
759 unsigned int bin_size
;
761 struct memdisk_header
*hptr
;
762 struct patch_area
*pptr
;
765 uint32_t driverptr
, driveraddr
;
768 const struct geometry
*geometry
;
769 unsigned int total_size
;
770 unsigned int cmdline_len
, stack_len
, e820_len
;
771 const struct edd4_bvd
*bvd
;
772 const struct edd4_bootcat
*boot_cat
= 0;
774 uint32_t ramdisk_image
, ramdisk_size
;
775 uint32_t boot_base
, rm_base
;
777 int do_edd
= 1; /* 0 = no, 1 = yes, default is yes */
778 int do_eltorito
= 0; /* default is no */
779 int no_bpt
; /* No valid BPT presented */
780 uint32_t boot_seg
= 0; /* Meaning 0000:7C00 */
781 uint32_t boot_len
= 512; /* One sector */
784 /* We need to copy the rm_args into their proper place */
785 memcpy(&rm_args
, rm_args_ptr
, sizeof rm_args
);
786 sti(); /* ... then interrupts are safe */
788 /* Show signs of life */
789 printf("%s %s\n", memdisk_version
, copyright
);
791 if (!shdr
->ramdisk_image
|| !shdr
->ramdisk_size
)
792 die("MEMDISK: No ramdisk image specified!\n");
794 ramdisk_image
= shdr
->ramdisk_image
;
795 ramdisk_size
= shdr
->ramdisk_size
;
797 e820map_init(); /* Initialize memory data structure */
798 get_mem(); /* Query BIOS for memory map */
799 parse_mem(); /* Parse memory map */
801 printf("Ramdisk at 0x%08x, length 0x%08x\n", ramdisk_image
, ramdisk_size
);
803 unzip_if_needed(&ramdisk_image
, &ramdisk_size
);
805 geometry
= get_disk_image_geometry(ramdisk_image
, ramdisk_size
);
807 if (getcmditem("edd") != CMD_NOTFOUND
||
808 getcmditem("ebios") != CMD_NOTFOUND
)
810 else if (getcmditem("noedd") != CMD_NOTFOUND
||
811 getcmditem("noebios") != CMD_NOTFOUND
||
812 getcmditem("cbios") != CMD_NOTFOUND
)
815 do_edd
= (geometry
->driveno
& 0x80) ? 1 : 0;
817 if (getcmditem("iso") != CMD_NOTFOUND
) {
819 do_edd
= 1; /* Mandatory */
822 /* Choose the appropriate installable memdisk hook */
824 if (geometry
->sector_shift
== 11) {
825 bin_size
= (int)&_binary_memdisk_iso_2048_bin_size
;
826 memdisk_hook
= (char *)&_binary_memdisk_iso_2048_bin_start
;
828 bin_size
= (int)&_binary_memdisk_iso_512_bin_size
;
829 memdisk_hook
= (char *)&_binary_memdisk_iso_512_bin_start
;
833 bin_size
= (int)&_binary_memdisk_edd_512_bin_size
;
834 memdisk_hook
= (char *)&_binary_memdisk_edd_512_bin_start
;
836 bin_size
= (int)&_binary_memdisk_chs_512_bin_size
;
837 memdisk_hook
= (char *)&_binary_memdisk_chs_512_bin_start
;
841 /* Reserve the ramdisk memory */
842 insertrange(ramdisk_image
, ramdisk_size
, 2);
843 parse_mem(); /* Recompute variables */
845 /* Figure out where it needs to go */
846 hptr
= (struct memdisk_header
*)memdisk_hook
;
847 pptr
= (struct patch_area
*)(memdisk_hook
+ hptr
->patch_offs
);
849 dosmem_k
= rdz_16(BIOS_BASEMEM
);
850 pptr
->mdi
.olddosmem
= dosmem_k
;
851 stddosmem
= dosmem_k
<< 10;
852 /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
853 if (stddosmem
> dos_mem
)
856 pptr
->driveno
= geometry
->driveno
;
857 pptr
->drivetype
= geometry
->type
;
858 pptr
->cylinders
= geometry
->c
; /* Possible precision loss */
859 pptr
->heads
= geometry
->h
;
860 pptr
->sectors
= geometry
->s
;
861 pptr
->mdi
.disksize
= geometry
->sectors
;
862 pptr
->mdi
.diskbuf
= ramdisk_image
+ geometry
->offset
;
863 pptr
->mdi
.sector_shift
= geometry
->sector_shift
;
864 pptr
->statusptr
= (geometry
->driveno
& 0x80) ? 0x474 : 0x441;
866 pptr
->mdi
.bootloaderid
= shdr
->type_of_loader
;
868 pptr
->configflags
= CONFIG_SAFEINT
; /* Default */
869 /* Set config flags */
870 if (getcmditem("ro") != CMD_NOTFOUND
) {
871 pptr
->configflags
|= CONFIG_READONLY
;
873 if (getcmditem("raw") != CMD_NOTFOUND
) {
874 pptr
->configflags
&= ~CONFIG_MODEMASK
;
875 pptr
->configflags
|= CONFIG_RAW
;
877 if (getcmditem("bigraw") != CMD_NOTFOUND
) {
878 pptr
->configflags
&= ~CONFIG_MODEMASK
;
879 pptr
->configflags
|= CONFIG_BIGRAW
| CONFIG_RAW
;
881 if (getcmditem("int") != CMD_NOTFOUND
) {
882 pptr
->configflags
&= ~CONFIG_MODEMASK
;
883 /* pptr->configflags |= 0; */
885 if (getcmditem("safeint") != CMD_NOTFOUND
) {
886 pptr
->configflags
&= ~CONFIG_MODEMASK
;
887 pptr
->configflags
|= CONFIG_SAFEINT
;
890 printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n",
891 (geometry
->driveno
& 0x80) ? "hd" : "fd",
892 geometry
->driveno
& 0x7f,
893 geometry
->sectors
>> 1,
894 (geometry
->sectors
& 1) ? ".5" : "",
895 geometry
->c
, geometry
->h
, geometry
->s
,
896 geometry
->hsrc
, geometry
->ssrc
,
897 do_edd
? "on" : "off",
898 pptr
->configflags
& CONFIG_READONLY
? "ro" : "rw");
901 switch (pptr
->configflags
& CONFIG_MODEMASK
) {
903 puts("standard INT 15h");
906 puts("safe INT 15h");
911 case CONFIG_RAW
| CONFIG_BIGRAW
:
912 puts("big real mode raw");
915 printf("unknown %#x", pptr
->configflags
& CONFIG_MODEMASK
);
918 puts(" access to high memory\n");
920 /* Set up a drive parameter table */
921 if (geometry
->driveno
& 0x80) {
923 pptr
->dpt
.hd
.max_cyl
= geometry
->c
- 1;
924 pptr
->dpt
.hd
.max_head
= geometry
->h
- 1;
925 pptr
->dpt
.hd
.ctrl
= (geometry
->h
> 8) ? 0x08 : 0;
927 /* Floppy - most of these fields are bogus and mimic
928 a 1.44 MB floppy drive */
929 pptr
->dpt
.fd
.specify1
= 0xdf;
930 pptr
->dpt
.fd
.specify2
= 0x02;
931 pptr
->dpt
.fd
.delay
= 0x25;
932 pptr
->dpt
.fd
.sectors
= geometry
->s
;
933 pptr
->dpt
.fd
.bps
= 0x02;
934 pptr
->dpt
.fd
.isgap
= 0x12;
935 pptr
->dpt
.fd
.dlen
= 0xff;
936 pptr
->dpt
.fd
.fgap
= 0x6c;
937 pptr
->dpt
.fd
.ffill
= 0xf6;
938 pptr
->dpt
.fd
.settle
= 0x0f;
939 pptr
->dpt
.fd
.mstart
= 0x05;
940 pptr
->dpt
.fd
.maxtrack
= geometry
->c
- 1;
941 pptr
->dpt
.fd
.cmos
= geometry
->type
> 5 ? 5 : geometry
->type
;
943 pptr
->dpt
.fd
.old_fd_dpt
= rdz_32(BIOS_INT1E
);
946 /* Set up an EDD drive parameter table */
948 pptr
->edd_dpt
.sectors
= geometry
->sectors
;
949 /* The EDD spec has this as <= 15482880 sectors (1024x240x63);
950 this seems to make very little sense. Try for something saner. */
951 if (geometry
->c
<= 1024 && geometry
->h
<= 255 && geometry
->s
<= 63) {
952 pptr
->edd_dpt
.c
= geometry
->c
;
953 pptr
->edd_dpt
.h
= geometry
->h
;
954 pptr
->edd_dpt
.s
= geometry
->s
;
955 /* EDD-4 states that invalid geometry should be returned
956 * for INT 0x13, AH=0x48 "EDD Get Disk Parameters" call on an
957 * El Torito ODD. Check for 2048-byte sector size
959 if (geometry
->sector_shift
!= 11)
960 pptr
->edd_dpt
.flags
|= 0x0002; /* Geometry valid */
962 if (!(geometry
->driveno
& 0x80)) {
963 /* Floppy drive. Mark it as a removable device with
964 media change notification; media is present. */
965 pptr
->edd_dpt
.flags
|= 0x0014;
968 pptr
->edd_dpt
.devpath
[0] = pptr
->mdi
.diskbuf
;
969 pptr
->edd_dpt
.chksum
= -checksum_buf(&pptr
->edd_dpt
.dpikey
, 73 - 30);
973 bvd
= (struct edd4_bvd
*)(ramdisk_image
+ 17 * 2048);
975 (struct edd4_bootcat
*)(ramdisk_image
+ bvd
->boot_cat
* 2048);
976 pptr
->cd_pkt
.type
= boot_cat
->initial_entry
.media_type
; /* Cheat */
977 pptr
->cd_pkt
.driveno
= geometry
->driveno
;
978 pptr
->cd_pkt
.start
= boot_cat
->initial_entry
.load_block
;
979 boot_seg
= pptr
->cd_pkt
.load_seg
= boot_cat
->initial_entry
.load_seg
;
980 pptr
->cd_pkt
.sect_count
= boot_cat
->initial_entry
.sect_count
;
981 boot_len
= pptr
->cd_pkt
.sect_count
* 512;
982 pptr
->cd_pkt
.geom1
= (uint8_t)(pptr
->cylinders
) & 0xFF;
984 (uint8_t)(pptr
->sectors
) | (uint8_t)((pptr
->cylinders
>> 2) & 0xC0);
985 pptr
->cd_pkt
.geom3
= (uint8_t)(pptr
->heads
);
988 if ((p
= getcmditem("mem")) != CMD_NOTFOUND
) {
989 setmaxmem(suffix_number(p
));
992 /* The size is given by hptr->total_size plus the size of the E820
993 map -- 12 bytes per range; we may need as many as 2 additional
994 ranges (each insertrange() can worst-case turn 1 area into 3)
995 plus the terminating range, over what nranges currently show. */
996 total_size
= hptr
->total_size
; /* Actual memdisk code */
997 e820_len
= (nranges
+ 3) * sizeof(ranges
[0]);
998 total_size
+= e820_len
; /* E820 memory ranges */
999 cmdline_len
= strlen(shdr
->cmdline
) + 1;
1000 total_size
+= cmdline_len
; /* Command line */
1001 stack_len
= stack_needed();
1002 total_size
+= stack_len
; /* Stack */
1003 printf("Code %u, meminfo %u, cmdline %u, stack %u\n",
1004 hptr
->total_size
, e820_len
, cmdline_len
, stack_len
);
1005 printf("Total size needed = %u bytes, allocating %uK\n",
1006 total_size
, (total_size
+ 0x3ff) >> 10);
1008 if (total_size
> dos_mem
)
1009 die("MEMDISK: Insufficient low memory\n");
1011 driveraddr
= stddosmem
- total_size
;
1012 driveraddr
&= ~0x3FF;
1014 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
1015 stddosmem
, dos_mem
, driveraddr
);
1017 /* Reserve this range of memory */
1018 wrz_16(BIOS_BASEMEM
, driveraddr
>> 10);
1019 insertrange(driveraddr
, dos_mem
- driveraddr
, 2);
1022 pptr
->mem1mb
= low_mem
>> 10;
1023 pptr
->mem16mb
= high_mem
>> 16;
1024 if (low_mem
== (15 << 20)) {
1025 /* lowmem maxed out */
1026 uint32_t int1588mem
= (high_mem
>> 10) + (low_mem
>> 10);
1027 pptr
->memint1588
= (int1588mem
> 0xffff) ? 0xffff : int1588mem
;
1029 pptr
->memint1588
= low_mem
>> 10;
1032 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
1033 pptr
->memint1588
, pptr
->mem1mb
, pptr
->mem16mb
);
1035 driverseg
= driveraddr
>> 4;
1036 driverptr
= driverseg
<< 16;
1038 /* Anything beyond the end is for the stack */
1039 pptr
->mystack
= (uint16_t) (stddosmem
- driveraddr
);
1041 pptr
->mdi
.oldint13
.uint32
= rdz_32(BIOS_INT13
);
1042 pptr
->mdi
.oldint15
.uint32
= rdz_32(BIOS_INT15
);
1044 /* Adjust the E820 table: if there are null ranges (type 0)
1045 at the end, change them to type end of list (-1).
1046 This is necessary for the driver to be able to report end
1047 of list correctly. */
1048 while (nranges
&& ranges
[nranges
- 1].type
== 0) {
1049 ranges
[--nranges
].type
= -1;
1052 if (getcmditem("nopassany") != CMD_NOTFOUND
) {
1053 printf("nopassany specified - we're the only drive of any kind\n");
1057 pptr
->mdi
.oldint13
.uint32
= driverptr
+ hptr
->iret_offs
;
1058 wrz_8(BIOS_EQUIP
, rdz_8(BIOS_EQUIP
) & ~0xc1);
1059 wrz_8(BIOS_HD_COUNT
, 0);
1060 } else if (getcmditem("nopass") != CMD_NOTFOUND
) {
1061 printf("nopass specified - we're the only drive\n");
1066 /* Query drive parameters of this type */
1067 memset(®s
, 0, sizeof regs
);
1069 regs
.eax
.b
[1] = 0x08;
1070 regs
.edx
.b
[0] = geometry
->driveno
& 0x80;
1071 intcall(0x13, ®s
, ®s
);
1073 /* Note: per suggestion from the Interrupt List, consider
1074 INT 13 08 to have failed if the sector count in CL is zero. */
1075 if ((regs
.eflags
.l
& 1) || !(regs
.ecx
.b
[0] & 0x3f)) {
1076 printf("INT 13 08: Failure, assuming this is the only drive\n");
1080 printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
1081 regs
.edx
.b
[0], regs
.es
, regs
.edi
.w
[0]);
1082 pptr
->drivecnt
= regs
.edx
.b
[0];
1083 no_bpt
= !(regs
.es
| regs
.edi
.w
[0]);
1086 /* Compare what INT 13h returned with the appropriate equipment byte */
1087 if (geometry
->driveno
& 0x80) {
1088 bios_drives
= rdz_8(BIOS_HD_COUNT
);
1090 uint8_t equip
= rdz_8(BIOS_EQUIP
);
1093 bios_drives
= (equip
>> 6) + 1;
1098 if (pptr
->drivecnt
> bios_drives
) {
1099 printf("BIOS equipment byte says count = %d, go with that\n",
1101 pptr
->drivecnt
= bios_drives
;
1105 /* Add ourselves to the drive count */
1108 /* Discontiguous drive space. There is no really good solution for this. */
1109 if (pptr
->drivecnt
<= (geometry
->driveno
& 0x7f))
1110 pptr
->drivecnt
= (geometry
->driveno
& 0x7f) + 1;
1112 /* Probe for contiguous range of BIOS drives starting with driveno */
1113 pptr
->driveshiftlimit
= probe_drive_range(geometry
->driveno
) + 1;
1114 if ((pptr
->driveshiftlimit
& 0x80) != (geometry
->driveno
& 0x80))
1115 printf("We lost the last drive in our class of drives.\n");
1116 printf("Drive probing gives drive shift limit: 0x%02x\n",
1117 pptr
->driveshiftlimit
);
1119 /* Pointer to the command line */
1120 pptr
->mdi
.cmdline
.seg_off
.offset
= bin_size
+ (nranges
+ 1) * sizeof(ranges
[0]);
1121 pptr
->mdi
.cmdline
.seg_off
.segment
= driverseg
;
1123 /* Copy driver followed by E820 table followed by command line */
1125 unsigned char *dpp
= (unsigned char *)(driverseg
<< 4);
1127 /* Adjust these pointers to point to the installed image */
1128 /* Careful about the order here... the image isn't copied yet! */
1129 pptr
= (struct patch_area
*)(dpp
+ hptr
->patch_offs
);
1130 hptr
= (struct memdisk_header
*)dpp
;
1132 /* Actually copy to low memory */
1133 dpp
= mempcpy(dpp
, memdisk_hook
, bin_size
);
1134 dpp
= mempcpy(dpp
, ranges
, (nranges
+ 1) * sizeof(ranges
[0]));
1135 dpp
= mempcpy(dpp
, shdr
->cmdline
, cmdline_len
);
1138 /* Note the previous INT 13h hook in the "safe hook" structure */
1139 hptr
->safe_hook
.old_hook
.uint32
= pptr
->mdi
.oldint13
.uint32
;
1141 /* Re-fill the "safe hook" mBFT field with the physical address */
1142 mbft
= (struct mBFT
*)(((const char *)hptr
) + hptr
->safe_hook
.mbft
);
1143 hptr
->safe_hook
.mbft
= (size_t)mbft
;
1145 /* Update various BIOS magic data areas (gotta love this shit) */
1147 if (geometry
->driveno
& 0x80) {
1148 /* Update BIOS hard disk count */
1149 uint8_t nhd
= pptr
->drivecnt
;
1155 wrz_8(BIOS_HD_COUNT
, nhd
);
1157 /* Update BIOS floppy disk count */
1158 uint8_t equip
= rdz_8(BIOS_EQUIP
);
1159 uint8_t nflop
= pptr
->drivecnt
;
1161 if (nflop
> 4) /* Limit of equipment byte */
1166 equip
|= ((nflop
- 1) << 6) | 0x01;
1168 wrz_8(BIOS_EQUIP
, equip
);
1170 /* Install DPT pointer if this was the only floppy */
1171 if (getcmditem("dpt") != CMD_NOTFOUND
||
1172 ((nflop
== 1 || no_bpt
) && getcmditem("nodpt") == CMD_NOTFOUND
)) {
1173 /* Do install a replacement DPT into INT 1Eh */
1175 hptr
->patch_offs
+ offsetof(struct patch_area
, dpt
);
1179 /* Complete the mBFT */
1180 mbft
->acpi
.signature
[0] = 'm'; /* "mBFT" */
1181 mbft
->acpi
.signature
[1] = 'B';
1182 mbft
->acpi
.signature
[2] = 'F';
1183 mbft
->acpi
.signature
[3] = 'T';
1184 mbft
->safe_hook
= (size_t)&hptr
->safe_hook
;
1185 mbft
->acpi
.checksum
= -checksum_buf(mbft
, mbft
->acpi
.length
);
1187 /* Install the interrupt handlers */
1188 printf("old: int13 = %08x int15 = %08x int1e = %08x\n",
1189 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
), rdz_32(BIOS_INT1E
));
1191 wrz_32(BIOS_INT13
, driverptr
+ hptr
->int13_offs
);
1192 wrz_32(BIOS_INT15
, driverptr
+ hptr
->int15_offs
);
1193 if (pptr
->mdi
.dpt_ptr
)
1194 wrz_32(BIOS_INT1E
, driverptr
+ pptr
->mdi
.dpt_ptr
);
1196 printf("new: int13 = %08x int15 = %08x int1e = %08x\n",
1197 rdz_32(BIOS_INT13
), rdz_32(BIOS_INT15
), rdz_32(BIOS_INT1E
));
1199 /* Figure out entry point */
1202 shdr
->sssp
= 0x7c00;
1203 shdr
->csip
= 0x7c00;
1205 boot_base
= boot_seg
<< 4;
1206 shdr
->sssp
= boot_seg
<< 16;
1207 shdr
->csip
= boot_seg
<< 16;
1210 /* Relocate the real-mode code to below the stub */
1211 rm_base
= (driveraddr
- rm_args
.rm_size
) & ~15;
1212 if (rm_base
< boot_base
+ boot_len
)
1213 die("MEMDISK: bootstrap too large to load\n");
1215 relocate_rm_code(rm_base
);
1217 /* Reboot into the new "disk" */
1218 puts("Loading boot sector... ");
1220 memcpy((void *)boot_base
,
1221 (char *)pptr
->mdi
.diskbuf
+ geometry
->boot_lba
* 512,
1224 if (getcmditem("pause") != CMD_NOTFOUND
) {
1225 puts("press any key to boot... ");
1227 intcall(0x16, ®s
, NULL
);
1230 puts("booting...\n");
1232 /* On return the assembly code will jump to the boot vector */
1233 shdr
->esdi
= pnp_install_check();
1234 shdr
->edx
= geometry
->driveno
;