Adding upstream version 4.00~pre55+dfsg.
[syslinux-debian/hramrach.git] / memdisk / setup.c
blobffaa2c66a2d5d61ba4a0ba1dc30daca1708e112e
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 * ----------------------------------------------------------------------- */
16 #include <stdint.h>
17 #include "bda.h"
18 #include "dskprobe.h"
19 #include "e820.h"
20 #include "conio.h"
21 #include "version.h"
22 #include "memdisk.h"
23 #include "../version.h"
25 const char memdisk_version[] = "MEMDISK " VERSION_STR " " DATE;
26 const char copyright[] =
27 "Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin et al";
29 extern const char _binary_memdisk_chs_512_bin_start[];
30 extern const char _binary_memdisk_chs_512_bin_end[];
31 extern const char _binary_memdisk_chs_512_bin_size[];
32 extern const char _binary_memdisk_edd_512_bin_start[];
33 extern const char _binary_memdisk_edd_512_bin_end[];
34 extern const char _binary_memdisk_edd_512_bin_size[];
35 extern const char _binary_memdisk_iso_512_bin_start[];
36 extern const char _binary_memdisk_iso_512_bin_end[];
37 extern const char _binary_memdisk_iso_512_bin_size[];
38 extern const char _binary_memdisk_iso_2048_bin_start[];
39 extern const char _binary_memdisk_iso_2048_bin_end[];
40 extern const char _binary_memdisk_iso_2048_bin_size[];
42 /* Pull in structures common to MEMDISK and MDISKCHK.COM */
43 #include "mstructs.h"
45 /* An EDD disk packet */
46 struct edd_dsk_pkt {
47 uint8_t size; /* Packet size */
48 uint8_t res1; /* Reserved */
49 uint16_t count; /* Count to transfer */
50 uint32_t buf; /* Buffer pointer */
51 uint64_t start; /* LBA to start from */
52 uint64_t buf64; /* 64-bit buf pointer */
53 } __attribute__ ((packed));
55 /* Change to 1 for El Torito debugging */
56 #define DBG_ELTORITO 0
58 #if DBG_ELTORITO
59 extern void eltorito_dump(uint32_t);
60 #endif
63 * Routine to seek for a command-line item and return a pointer
64 * to the data portion, if present
67 /* Magic return values */
68 #define CMD_NOTFOUND ((char *)-1) /* Not found */
69 #define CMD_BOOL ((char *)-2) /* Found boolean option */
70 #define CMD_HASDATA(X) ((int)(X) >= 0)
72 static const char *getcmditem(const char *what)
74 const char *p;
75 const char *wp = what;
76 int match = 0;
78 for (p = shdr->cmdline; *p; p++) {
79 switch (match) {
80 case 0: /* Ground state */
81 if (*p == ' ')
82 break;
84 wp = what;
85 match = 1;
86 /* Fall through */
88 case 1: /* Matching */
89 if (*wp == '\0') {
90 if (*p == '=')
91 return p + 1;
92 else if (*p == ' ')
93 return CMD_BOOL;
94 else {
95 match = 2;
96 break;
99 if (*p != *wp++)
100 match = 2;
101 break;
103 case 2: /* Mismatch, skip rest of option */
104 if (*p == ' ')
105 match = 0; /* Next option */
106 break;
110 /* Check for matching string at end of line */
111 if (match == 1 && *wp == '\0')
112 return CMD_BOOL;
114 return CMD_NOTFOUND;
118 * Check to see if this is a gzip image
120 #define UNZIP_ALIGN 512
122 extern void _end; /* Symbol signalling end of data */
124 void unzip_if_needed(uint32_t * where_p, uint32_t * size_p)
126 uint32_t where = *where_p;
127 uint32_t size = *size_p;
128 uint32_t zbytes;
129 uint32_t startrange, endrange;
130 uint32_t gzdatasize, gzwhere;
131 uint32_t orig_crc, offset;
132 uint32_t target = 0;
133 int i, okmem;
135 /* Is it a gzip image? */
136 if (check_zip((void *)where, size, &zbytes, &gzdatasize,
137 &orig_crc, &offset) == 0) {
139 if (offset + zbytes > size) {
141 * Assertion failure; check_zip is supposed to guarantee this
142 * never happens.
144 die("internal error: check_zip returned nonsense\n");
148 * Find a good place to put it: search memory ranges in descending
149 * order until we find one that is legal and fits
151 okmem = 0;
152 for (i = nranges - 1; i >= 0; i--) {
154 * We can't use > 4G memory (32 bits only.) Truncate to 2^32-1
155 * so we don't have to deal with funny wraparound issues.
158 /* Must be memory */
159 if (ranges[i].type != 1)
160 continue;
162 /* Range start */
163 if (ranges[i].start >= 0xFFFFFFFF)
164 continue;
166 startrange = (uint32_t) ranges[i].start;
168 /* Range end (0 for end means 2^64) */
169 endrange = ((ranges[i + 1].start >= 0xFFFFFFFF ||
170 ranges[i + 1].start == 0)
171 ? 0xFFFFFFFF : (uint32_t) ranges[i + 1].start);
173 /* Make sure we don't overwrite ourselves */
174 if (startrange < (uint32_t) & _end)
175 startrange = (uint32_t) & _end;
177 /* Allow for alignment */
178 startrange =
179 (ranges[i].start + (UNZIP_ALIGN - 1)) & ~(UNZIP_ALIGN - 1);
181 /* In case we just killed the whole range... */
182 if (startrange >= endrange)
183 continue;
186 * Must be large enough... don't rely on gzwhere for this
187 * (wraparound)
189 if (endrange - startrange < gzdatasize)
190 continue;
193 * This is where the gz image would be put if we put it in this
194 * range...
196 gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN - 1);
198 /* Cast to uint64_t just in case we're flush with the top byte */
199 if ((uint64_t) where + size >= gzwhere && where < endrange) {
201 * Need to move source data to avoid compressed/uncompressed
202 * overlap
204 uint32_t newwhere;
206 if (gzwhere - startrange < size)
207 continue; /* Can't fit both old and new */
209 newwhere = (gzwhere - size) & ~(UNZIP_ALIGN - 1);
210 printf("Moving compressed data from 0x%08x to 0x%08x\n",
211 where, newwhere);
213 memmove((void *)newwhere, (void *)where, size);
214 where = newwhere;
217 target = gzwhere;
218 okmem = 1;
219 break;
222 if (!okmem)
223 die("Not enough memory to decompress image (need 0x%08x bytes)\n",
224 gzdatasize);
226 printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ",
227 target, gzdatasize);
229 *size_p = gzdatasize;
230 *where_p = (uint32_t) unzip((void *)(where + offset), zbytes,
231 gzdatasize, orig_crc, (void *)target);
236 * Figure out the "geometry" of the disk in question
238 struct geometry {
239 uint32_t sectors; /* Sector count */
240 uint32_t c, h, s; /* C/H/S geometry */
241 uint32_t offset; /* Byte offset for disk */
242 uint32_t boot_lba; /* LBA of bootstrap code */
243 uint8_t type; /* Type byte for INT 13h AH=08h */
244 uint8_t driveno; /* Drive no */
245 uint16_t sector_size; /* Sector size in bytes (512 vs. 2048) */
246 const char *hsrc, *ssrc; /* Origins of H and S geometries */
249 /* Format of a DOS partition table entry */
250 struct ptab_entry {
251 uint8_t active;
252 uint8_t start_h, start_s, start_c;
253 uint8_t type;
254 uint8_t end_h, end_s, end_c;
255 uint32_t start;
256 uint32_t size;
257 } __attribute__ ((packed));
259 /* Format of a FAT filesystem superblock */
260 struct fat_extra {
261 uint8_t bs_drvnum;
262 uint8_t bs_resv1;
263 uint8_t bs_bootsig;
264 uint32_t bs_volid;
265 char bs_vollab[11];
266 char bs_filsystype[8];
267 } __attribute__ ((packed));
268 struct fat_super {
269 uint8_t bs_jmpboot[3];
270 char bs_oemname[8];
271 uint16_t bpb_bytspersec;
272 uint8_t bpb_secperclus;
273 uint16_t bpb_rsvdseccnt;
274 uint8_t bpb_numfats;
275 uint16_t bpb_rootentcnt;
276 uint16_t bpb_totsec16;
277 uint8_t bpb_media;
278 uint16_t bpb_fatsz16;
279 uint16_t bpb_secpertrk;
280 uint16_t bpb_numheads;
281 uint32_t bpb_hiddsec;
282 uint32_t bpb_totsec32;
283 union {
284 struct {
285 struct fat_extra extra;
286 } fat16;
287 struct {
288 uint32_t bpb_fatsz32;
289 uint16_t bpb_extflags;
290 uint16_t bpb_fsver;
291 uint32_t bpb_rootclus;
292 uint16_t bpb_fsinfo;
293 uint16_t bpb_bkbootsec;
294 char bpb_reserved[12];
295 /* Clever, eh? Same fields, different offset... */
296 struct fat_extra extra;
297 } fat32 __attribute__ ((packed));
298 } x;
299 } __attribute__ ((packed));
301 /* Format of a DOSEMU header */
302 struct dosemu_header {
303 uint8_t magic[7]; /* DOSEMU\0 */
304 uint32_t h;
305 uint32_t s;
306 uint32_t c;
307 uint32_t offset;
308 uint8_t pad[105];
309 } __attribute__ ((packed));
311 #define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d))
313 static const struct geometry *get_disk_image_geometry(uint32_t where,
314 uint32_t size)
316 static struct geometry hd_geometry;
317 struct dosemu_header dosemu;
318 unsigned int sectors, xsectors, v;
319 unsigned int offset;
320 int i;
321 const char *p;
323 printf("command line: %s\n", shdr->cmdline);
325 hd_geometry.sector_size = 512; /* Assume floppy/HDD at first */
327 offset = 0;
328 if (CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)))
329 offset = v;
331 sectors = xsectors = (size - offset) >> 9;
333 hd_geometry.hsrc = "guess";
334 hd_geometry.ssrc = "guess";
335 hd_geometry.sectors = sectors;
336 hd_geometry.offset = offset;
338 if ((p = getcmditem("iso")) != CMD_NOTFOUND) {
339 #if DBG_ELTORITO
340 eltorito_dump(where);
341 #endif
342 struct edd4_bvd *bvd = (struct edd4_bvd *)(where + 17 * 2048);
343 /* Tiny sanity check */
344 if ((bvd->boot_rec_ind != 0) || (bvd->ver != 1))
345 printf("El Torito BVD sanity check failed.\n");
346 struct edd4_bootcat *boot_cat =
347 (struct edd4_bootcat *)(where + bvd->boot_cat * 2048);
348 /* Another tiny sanity check */
349 if ((boot_cat->validation_entry.platform_id != 0) ||
350 (boot_cat->validation_entry.key55 != 0x55) ||
351 (boot_cat->validation_entry.keyAA != 0xAA))
352 printf("El Torito boot catalog sanity check failed.\n");
353 /* If we have an emulation mode, set the offset to the image */
354 if (boot_cat->initial_entry.media_type)
355 hd_geometry.offset += boot_cat->initial_entry.load_block * 2048;
356 else
357 /* We're a no-emulation mode, so we will boot to an offset */
358 hd_geometry.boot_lba = boot_cat->initial_entry.load_block * 4;
359 if (boot_cat->initial_entry.media_type < 4) {
360 /* We're a floppy emulation mode or our params will be
361 * overwritten by the no emulation mode case
363 hd_geometry.driveno = 0x00;
364 hd_geometry.c = 80;
365 hd_geometry.h = 2;
367 switch (boot_cat->initial_entry.media_type) {
368 case 0: /* No emulation */
369 hd_geometry.driveno = 0xE0;
370 hd_geometry.type = 10; /* ATAPI removable media device */
371 hd_geometry.c = 65535;
372 hd_geometry.h = 255;
373 hd_geometry.s = 15;
374 /* 2048-byte sectors, so adjust the size and count */
375 hd_geometry.sector_size = 2048;
376 sectors = (size - hd_geometry.offset) >> 11;
377 break;
378 case 1: /* 1.2 MB floppy */
379 hd_geometry.s = 15;
380 hd_geometry.type = 2;
381 sectors = 2400;
382 break;
383 case 2: /* 1.44 MB floppy */
384 hd_geometry.s = 18;
385 hd_geometry.type = 4;
386 sectors = 2880;
387 break;
388 case 3: /* 2.88 MB floppy */
389 hd_geometry.s = 36;
390 hd_geometry.type = 6;
391 sectors = 5760;
392 break;
393 case 4:
394 hd_geometry.driveno = 0x80;
395 hd_geometry.type = 0;
396 sectors = (size - hd_geometry.offset) >> 9;
397 break;
399 /* For HDD emulation, we figure out the geometry later. Otherwise: */
400 if (hd_geometry.s) {
401 hd_geometry.hsrc = hd_geometry.ssrc = "El Torito";
403 hd_geometry.sectors = sectors;
406 /* Do we have a DOSEMU header? */
407 memcpy(&dosemu, (char *)where + hd_geometry.offset, sizeof dosemu);
408 if (!memcmp("DOSEMU", dosemu.magic, 7)) {
409 /* Always a hard disk unless overruled by command-line options */
410 hd_geometry.driveno = 0x80;
411 hd_geometry.type = 0;
412 hd_geometry.c = dosemu.c;
413 hd_geometry.h = dosemu.h;
414 hd_geometry.s = dosemu.s;
415 hd_geometry.offset += dosemu.offset;
416 sectors = (size - hd_geometry.offset) >> 9;
418 hd_geometry.hsrc = hd_geometry.ssrc = "DOSEMU";
421 if (CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)))
422 hd_geometry.c = v;
423 if (CMD_HASDATA(p = getcmditem("h")) && (v = atou(p))) {
424 hd_geometry.h = v;
425 hd_geometry.hsrc = "cmd";
427 if (CMD_HASDATA(p = getcmditem("s")) && (v = atou(p))) {
428 hd_geometry.s = v;
429 hd_geometry.ssrc = "cmd";
432 if (!hd_geometry.h || !hd_geometry.s) {
433 int h, s, max_h, max_s;
435 max_h = hd_geometry.h;
436 max_s = hd_geometry.s;
438 if (!(max_h | max_s)) {
439 /* Look for a FAT superblock and if we find something that looks
440 enough like one, use geometry from that. This takes care of
441 megafloppy images and unpartitioned hard disks. */
442 const struct fat_extra *extra = NULL;
443 const struct fat_super *fs = (const struct fat_super *)
444 ((char *)where + hd_geometry.offset);
446 if ((fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) &&
447 (fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) &&
448 fs->bpb_bytspersec == 512 &&
449 fs->bpb_numheads >= 1 && fs->bpb_numheads <= 256 &&
450 fs->bpb_secpertrk >= 1 && fs->bpb_secpertrk <= 63) {
451 extra =
452 fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
453 if (!
454 (extra->bs_bootsig == 0x29 && extra->bs_filsystype[0] == 'F'
455 && extra->bs_filsystype[1] == 'A'
456 && extra->bs_filsystype[2] == 'T'))
457 extra = NULL;
459 if (extra) {
460 hd_geometry.driveno = extra->bs_drvnum & 0x80;
461 max_h = fs->bpb_numheads;
462 max_s = fs->bpb_secpertrk;
463 hd_geometry.hsrc = hd_geometry.ssrc = "FAT";
467 if (!(max_h | max_s)) {
468 /* No FAT filesystem found to steal geometry from... */
469 if ((sectors < 4096 * 2) && (hd_geometry.sector_size == 512)) {
470 int ok = 0;
471 unsigned int xsectors = sectors;
473 hd_geometry.driveno = 0; /* Assume floppy */
475 while (!ok) {
476 /* Assume it's a floppy drive, guess a geometry */
477 unsigned int type, track;
478 int c, h, s = 0;
480 if (xsectors < 320 * 2) {
481 c = 40;
482 h = 1;
483 type = 1;
484 } else if (xsectors < 640 * 2) {
485 c = 40;
486 h = 2;
487 type = 1;
488 } else if (xsectors < 1200 * 2) {
489 c = 80;
490 h = 2;
491 type = 3;
492 } else if (xsectors < 1440 * 2) {
493 c = 80;
494 h = 2;
495 type = 2;
496 } else if (xsectors < 2880 * 2) {
497 c = 80;
498 h = 2;
499 type = 4;
500 } else {
501 c = 80;
502 h = 2;
503 type = 6;
505 track = c * h;
506 while (c < 256) {
507 s = xsectors / track;
508 if (s < 63 && (xsectors % track) == 0) {
509 ok = 1;
510 break;
512 c++;
513 track += h;
515 if (ok) {
516 max_h = h;
517 max_s = s;
518 hd_geometry.hsrc = hd_geometry.ssrc = "fd";
519 } else {
520 /* No valid floppy geometry, fake it by simulating broken
521 sectors at the end of the image... */
522 xsectors++;
525 } else {
526 /* Assume it is a hard disk image and scan for a partition table */
527 const struct ptab_entry *ptab = (const struct ptab_entry *)
528 ((char *)where + hd_geometry.offset + (512 - 2 - 4 * 16));
530 /* Assume hard disk */
531 if (!hd_geometry.driveno)
532 hd_geometry.driveno = 0x80;
534 if (*(uint16_t *) ((char *)where + hd_geometry.offset + 512 - 2) == 0xaa55) {
535 for (i = 0; i < 4; i++) {
536 if (ptab[i].type && !(ptab[i].active & 0x7f)) {
537 s = (ptab[i].start_s & 0x3f);
538 h = ptab[i].start_h + 1;
540 if (max_h < h)
541 max_h = h;
542 if (max_s < s)
543 max_s = s;
545 s = (ptab[i].end_s & 0x3f);
546 h = ptab[i].end_h + 1;
548 if (max_h < h) {
549 max_h = h;
550 hd_geometry.hsrc = "MBR";
552 if (max_s < s) {
553 max_s = s;
554 hd_geometry.ssrc = "MBR";
562 if (!max_h)
563 max_h = xsectors > 2097152 ? 255 : 64;
564 if (!max_s)
565 max_s = xsectors > 2097152 ? 63 : 32;
567 hd_geometry.h = max_h;
568 hd_geometry.s = max_s;
571 if (!hd_geometry.c)
572 hd_geometry.c = xsectors / (hd_geometry.h * hd_geometry.s);
574 if ((p = getcmditem("floppy")) != CMD_NOTFOUND) {
575 hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0;
576 } else if ((p = getcmditem("harddisk")) != CMD_NOTFOUND) {
577 hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80;
580 if (hd_geometry.driveno & 0x80) {
581 hd_geometry.type = 0; /* Type = hard disk */
582 } else {
583 if (hd_geometry.type == 0)
584 hd_geometry.type = 0x10; /* ATAPI floppy, e.g. LS-120 */
587 if ((size - hd_geometry.offset) & 0x1ff) {
588 puts("MEMDISK: Image has fractional end sector\n");
590 if (sectors % (hd_geometry.h * hd_geometry.s)) {
591 puts("MEMDISK: Image seems to have fractional end cylinder\n");
593 if ((hd_geometry.c * hd_geometry.h * hd_geometry.s) > sectors) {
594 puts("MEMDISK: Image appears to be truncated\n");
597 return &hd_geometry;
601 * Find a $PnP installation check structure; return (ES << 16) + DI value
603 static uint32_t pnp_install_check(void)
605 uint32_t *seg;
606 unsigned char *p, csum;
607 int i, len;
609 for (seg = (uint32_t *) 0xf0000; seg < (uint32_t *) 0x100000; seg += 4) {
610 if (*seg == ('$' + ('P' << 8) + ('n' << 16) + ('P' << 24))) {
611 p = (unsigned char *)seg;
612 len = p[5];
613 if (len < 0x21)
614 continue;
615 csum = 0;
616 for (i = len; i; i--)
617 csum += *p++;
618 if (csum != 0)
619 continue;
621 return (0xf000 << 16) + (uint16_t) (unsigned long)seg;
625 return 0;
629 * Relocate the real-mode code to a new segment
631 struct gdt_ptr {
632 uint16_t limit;
633 uint32_t base;
634 } __attribute__ ((packed));
636 static void set_seg_base(uint32_t gdt_base, int seg, uint32_t v)
638 *(uint16_t *) (gdt_base + seg + 2) = v;
639 *(uint8_t *) (gdt_base + seg + 4) = v >> 16;
640 *(uint8_t *) (gdt_base + seg + 7) = v >> 24;
643 static void relocate_rm_code(uint32_t newbase)
645 uint32_t gdt_base;
646 uint32_t oldbase = rm_args.rm_base;
647 uint32_t delta = newbase - oldbase;
649 cli();
650 memmove((void *)newbase, (void *)oldbase, rm_args.rm_size);
652 rm_args.rm_return += delta;
653 rm_args.rm_intcall += delta;
654 rm_args.rm_bounce += delta;
655 rm_args.rm_base += delta;
656 rm_args.rm_gdt += delta;
657 rm_args.rm_pmjmp += delta;
658 rm_args.rm_rmjmp += delta;
660 gdt_base = rm_args.rm_gdt;
662 *(uint32_t *) (gdt_base + 2) = gdt_base; /* GDT self-pointer */
664 /* Segments 0x10 and 0x18 are real-mode-based */
665 set_seg_base(gdt_base, 0x10, rm_args.rm_base);
666 set_seg_base(gdt_base, 0x18, rm_args.rm_base);
668 asm volatile ("lgdtl %0"::"m" (*(char *)gdt_base));
670 *(uint32_t *) rm_args.rm_pmjmp += delta;
671 *(uint16_t *) rm_args.rm_rmjmp += delta >> 4;
673 rm_args.rm_handle_interrupt += delta;
675 sti();
678 static uint8_t checksum_buf(const void *buf, int count)
680 const uint8_t *p = buf;
681 uint8_t c = 0;
683 while (count--)
684 c += *p++;
686 return c;
689 static int stack_needed(void)
691 const unsigned int min_stack = 128; /* Minimum stack size */
692 const unsigned int def_stack = 512; /* Default stack size */
693 unsigned int v = 0;
694 const char *p;
696 if (CMD_HASDATA(p = getcmditem("stack")))
697 v = atou(p);
699 if (!v)
700 v = def_stack;
702 if (v < min_stack)
703 v = min_stack;
705 return v;
708 struct real_mode_args rm_args;
711 * Actual setup routine
712 * Returns the drive number (which is then passed in %dl to the
713 * called routine.)
715 void setup(const struct real_mode_args *rm_args_ptr)
717 unsigned int bin_size;
718 char *memdisk_hook;
719 struct memdisk_header *hptr;
720 struct patch_area *pptr;
721 uint16_t driverseg;
722 uint32_t driverptr, driveraddr;
723 uint16_t dosmem_k;
724 uint32_t stddosmem;
725 const struct geometry *geometry;
726 unsigned int total_size;
727 unsigned int cmdline_len, stack_len, e820_len;
728 const struct edd4_bvd *bvd;
729 const struct edd4_bootcat *boot_cat = 0;
730 com32sys_t regs;
731 uint32_t ramdisk_image, ramdisk_size;
732 uint32_t boot_base, rm_base;
733 int bios_drives;
734 int do_edd = 1; /* 0 = no, 1 = yes, default is yes */
735 int do_eltorito = 0; /* default is no */
736 int no_bpt; /* No valid BPT presented */
737 uint32_t boot_seg = 0; /* Meaning 0000:7C00 */
738 uint32_t boot_len = 512; /* One sector */
740 /* We need to copy the rm_args into their proper place */
741 memcpy(&rm_args, rm_args_ptr, sizeof rm_args);
742 sti(); /* ... then interrupts are safe */
744 /* Show signs of life */
745 printf("%s %s\n", memdisk_version, copyright);
747 if (!shdr->ramdisk_image || !shdr->ramdisk_size)
748 die("MEMDISK: No ramdisk image specified!\n");
750 ramdisk_image = shdr->ramdisk_image;
751 ramdisk_size = shdr->ramdisk_size;
753 e820map_init(); /* Initialize memory data structure */
754 get_mem(); /* Query BIOS for memory map */
755 parse_mem(); /* Parse memory map */
757 printf("Ramdisk at 0x%08x, length 0x%08x\n", ramdisk_image, ramdisk_size);
759 unzip_if_needed(&ramdisk_image, &ramdisk_size);
761 geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size);
763 if (getcmditem("edd") != CMD_NOTFOUND ||
764 getcmditem("ebios") != CMD_NOTFOUND)
765 do_edd = 1;
766 else if (getcmditem("noedd") != CMD_NOTFOUND ||
767 getcmditem("noebios") != CMD_NOTFOUND ||
768 getcmditem("cbios") != CMD_NOTFOUND)
769 do_edd = 0;
770 else
771 do_edd = (geometry->driveno & 0x80) ? 1 : 0;
773 if (getcmditem("iso") != CMD_NOTFOUND) {
774 do_eltorito = 1;
775 do_edd = 1; /* Mandatory */
778 /* Choose the appropriate installable memdisk hook */
779 if (do_eltorito) {
780 if (geometry->sector_size == 2048) {
781 bin_size = (int)&_binary_memdisk_iso_2048_bin_size;
782 memdisk_hook = (char *)&_binary_memdisk_iso_2048_bin_start;
783 } else {
784 bin_size = (int)&_binary_memdisk_iso_512_bin_size;
785 memdisk_hook = (char *)&_binary_memdisk_iso_512_bin_start;
787 } else {
788 if (do_edd) {
789 bin_size = (int)&_binary_memdisk_edd_512_bin_size;
790 memdisk_hook = (char *)&_binary_memdisk_edd_512_bin_start;
791 } else {
792 bin_size = (int)&_binary_memdisk_chs_512_bin_size;
793 memdisk_hook = (char *)&_binary_memdisk_chs_512_bin_start;
797 /* Reserve the ramdisk memory */
798 insertrange(ramdisk_image, ramdisk_size, 2);
799 parse_mem(); /* Recompute variables */
801 /* Figure out where it needs to go */
802 hptr = (struct memdisk_header *)memdisk_hook;
803 pptr = (struct patch_area *)(memdisk_hook + hptr->patch_offs);
805 dosmem_k = rdz_16(BIOS_BASEMEM);
806 pptr->mdi.olddosmem = dosmem_k;
807 stddosmem = dosmem_k << 10;
808 /* If INT 15 E820 and INT 12 disagree, go with the most conservative */
809 if (stddosmem > dos_mem)
810 stddosmem = dos_mem;
812 pptr->driveno = geometry->driveno;
813 pptr->drivetype = geometry->type;
814 pptr->cylinders = geometry->c; /* Possible precision loss */
815 pptr->heads = geometry->h;
816 pptr->sectors = geometry->s;
817 pptr->mdi.disksize = geometry->sectors;
818 pptr->mdi.diskbuf = ramdisk_image + geometry->offset;
819 pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441;
821 pptr->mdi.bootloaderid = shdr->type_of_loader;
823 pptr->configflags = CONFIG_SAFEINT; /* Default */
824 /* Set config flags */
825 if (getcmditem("ro") != CMD_NOTFOUND) {
826 pptr->configflags |= CONFIG_READONLY;
828 if (getcmditem("raw") != CMD_NOTFOUND) {
829 pptr->configflags &= ~CONFIG_MODEMASK;
830 pptr->configflags |= CONFIG_RAW;
832 if (getcmditem("bigraw") != CMD_NOTFOUND) {
833 pptr->configflags &= ~CONFIG_MODEMASK;
834 pptr->configflags |= CONFIG_BIGRAW | CONFIG_RAW;
836 if (getcmditem("int") != CMD_NOTFOUND) {
837 pptr->configflags &= ~CONFIG_MODEMASK;
838 /* pptr->configflags |= 0; */
840 if (getcmditem("safeint") != CMD_NOTFOUND) {
841 pptr->configflags &= ~CONFIG_MODEMASK;
842 pptr->configflags |= CONFIG_SAFEINT;
845 printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n",
846 (geometry->driveno & 0x80) ? "hd" : "fd",
847 geometry->driveno & 0x7f,
848 geometry->sectors >> 1,
849 (geometry->sectors & 1) ? ".5" : "",
850 geometry->c, geometry->h, geometry->s,
851 geometry->hsrc, geometry->ssrc,
852 do_edd ? "on" : "off",
853 pptr->configflags & CONFIG_READONLY ? "ro" : "rw");
855 puts("Using ");
856 switch (pptr->configflags & CONFIG_MODEMASK) {
857 case 0:
858 puts("standard INT 15h");
859 break;
860 case CONFIG_SAFEINT:
861 puts("safe INT 15h");
862 break;
863 case CONFIG_RAW:
864 puts("raw");
865 break;
866 case CONFIG_RAW | CONFIG_BIGRAW:
867 puts("big real mode raw");
868 break;
869 default:
870 printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK);
871 break;
873 puts(" access to high memory\n");
875 /* Set up a drive parameter table */
876 if (geometry->driveno & 0x80) {
877 /* Hard disk */
878 pptr->dpt.hd.max_cyl = geometry->c - 1;
879 pptr->dpt.hd.max_head = geometry->h - 1;
880 pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08 : 0;
881 } else {
882 /* Floppy - most of these fields are bogus and mimic
883 a 1.44 MB floppy drive */
884 pptr->dpt.fd.specify1 = 0xdf;
885 pptr->dpt.fd.specify2 = 0x02;
886 pptr->dpt.fd.delay = 0x25;
887 pptr->dpt.fd.sectors = geometry->s;
888 pptr->dpt.fd.bps = 0x02;
889 pptr->dpt.fd.isgap = 0x12;
890 pptr->dpt.fd.dlen = 0xff;
891 pptr->dpt.fd.fgap = 0x6c;
892 pptr->dpt.fd.ffill = 0xf6;
893 pptr->dpt.fd.settle = 0x0f;
894 pptr->dpt.fd.mstart = 0x05;
895 pptr->dpt.fd.maxtrack = geometry->c - 1;
896 pptr->dpt.fd.cmos = geometry->type > 5 ? 5 : geometry->type;
898 pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E);
901 /* Set up an EDD drive parameter table */
902 if (do_edd) {
903 pptr->edd_dpt.sectors = geometry->sectors;
904 /* The EDD spec has this as <= 15482880 sectors (1024x240x63);
905 this seems to make very little sense. Try for something saner. */
906 if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) {
907 pptr->edd_dpt.c = geometry->c;
908 pptr->edd_dpt.h = geometry->h;
909 pptr->edd_dpt.s = geometry->s;
910 /* EDD-4 states that invalid geometry should be returned
911 * for INT 0x13, AH=0x48 "EDD Get Disk Parameters" call on an
912 * El Torito ODD. Check for 2048-byte sector size
914 if (geometry->sector_size != 2048)
915 pptr->edd_dpt.flags |= 0x0002; /* Geometry valid */
917 if (!(geometry->driveno & 0x80)) {
918 /* Floppy drive. Mark it as a removable device with
919 media change notification; media is present. */
920 pptr->edd_dpt.flags |= 0x0014;
923 pptr->edd_dpt.devpath[0] = pptr->mdi.diskbuf;
924 pptr->edd_dpt.chksum = -checksum_buf(&pptr->edd_dpt.dpikey, 73 - 30);
927 if (do_eltorito) {
928 bvd = (struct edd4_bvd *)(ramdisk_image + 17 * 2048);
929 boot_cat =
930 (struct edd4_bootcat *)(ramdisk_image + bvd->boot_cat * 2048);
931 pptr->cd_pkt.type = boot_cat->initial_entry.media_type; /* Cheat */
932 pptr->cd_pkt.driveno = geometry->driveno;
933 pptr->cd_pkt.start = boot_cat->initial_entry.load_block;
934 boot_seg = pptr->cd_pkt.load_seg = boot_cat->initial_entry.load_seg;
935 pptr->cd_pkt.sect_count = boot_cat->initial_entry.sect_count;
936 boot_len = pptr->cd_pkt.sect_count * 512;
937 pptr->cd_pkt.geom1 = (uint8_t)(pptr->cylinders) & 0xFF;
938 pptr->cd_pkt.geom2 =
939 (uint8_t)(pptr->sectors) | (uint8_t)((pptr->cylinders >> 2) & 0xC0);
940 pptr->cd_pkt.geom3 = (uint8_t)(pptr->heads);
943 /* The size is given by hptr->total_size plus the size of the E820
944 map -- 12 bytes per range; we may need as many as 2 additional
945 ranges (each insertrange() can worst-case turn 1 area into 3)
946 plus the terminating range, over what nranges currently show. */
947 total_size = hptr->total_size; /* Actual memdisk code */
948 e820_len = (nranges + 3) * sizeof(ranges[0]);
949 total_size += e820_len; /* E820 memory ranges */
950 cmdline_len = strlen(shdr->cmdline) + 1;
951 total_size += cmdline_len; /* Command line */
952 stack_len = stack_needed();
953 total_size += stack_len; /* Stack */
954 printf("Code %u, meminfo %u, cmdline %u, stack %u\n",
955 hptr->total_size, e820_len, cmdline_len, stack_len);
956 printf("Total size needed = %u bytes, allocating %uK\n",
957 total_size, (total_size + 0x3ff) >> 10);
959 if (total_size > dos_mem)
960 die("MEMDISK: Insufficient low memory\n");
962 driveraddr = stddosmem - total_size;
963 driveraddr &= ~0x3FF;
965 printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n",
966 stddosmem, dos_mem, driveraddr);
968 /* Reserve this range of memory */
969 wrz_16(BIOS_BASEMEM, driveraddr >> 10);
970 insertrange(driveraddr, dos_mem - driveraddr, 2);
971 parse_mem();
973 pptr->mem1mb = low_mem >> 10;
974 pptr->mem16mb = high_mem >> 16;
975 if (low_mem == (15 << 20)) {
976 /* lowmem maxed out */
977 uint32_t int1588mem = (high_mem >> 10) + (low_mem >> 10);
978 pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem;
979 } else {
980 pptr->memint1588 = low_mem >> 10;
983 printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n",
984 pptr->memint1588, pptr->mem1mb, pptr->mem16mb);
986 driverseg = driveraddr >> 4;
987 driverptr = driverseg << 16;
989 /* Anything beyond the end is for the stack */
990 pptr->mystack = (uint16_t) (stddosmem - driveraddr);
992 pptr->mdi.oldint13.uint32 = rdz_32(BIOS_INT13);
993 pptr->mdi.oldint15.uint32 = rdz_32(BIOS_INT15);
995 /* Adjust the E820 table: if there are null ranges (type 0)
996 at the end, change them to type end of list (-1).
997 This is necessary for the driver to be able to report end
998 of list correctly. */
999 while (nranges && ranges[nranges - 1].type == 0) {
1000 ranges[--nranges].type = -1;
1003 if (getcmditem("nopassany") != CMD_NOTFOUND) {
1004 printf("nopassany specified - we're the only drive of any kind\n");
1005 bios_drives = 0;
1006 pptr->drivecnt = 0;
1007 no_bpt = 1;
1008 pptr->mdi.oldint13.uint32 = driverptr + hptr->iret_offs;
1009 wrz_8(BIOS_EQUIP, rdz_8(BIOS_EQUIP) & ~0xc1);
1010 wrz_8(BIOS_HD_COUNT, 0);
1011 } else if (getcmditem("nopass") != CMD_NOTFOUND) {
1012 printf("nopass specified - we're the only drive\n");
1013 bios_drives = 0;
1014 pptr->drivecnt = 0;
1015 no_bpt = 1;
1016 } else {
1017 /* Query drive parameters of this type */
1018 memset(&regs, 0, sizeof regs);
1019 regs.es = 0;
1020 regs.eax.b[1] = 0x08;
1021 regs.edx.b[0] = geometry->driveno & 0x80;
1022 intcall(0x13, &regs, &regs);
1024 /* Note: per suggestion from the Interrupt List, consider
1025 INT 13 08 to have failed if the sector count in CL is zero. */
1026 if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) {
1027 printf("INT 13 08: Failure, assuming this is the only drive\n");
1028 pptr->drivecnt = 0;
1029 no_bpt = 1;
1030 } else {
1031 printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n",
1032 regs.edx.b[0], regs.es, regs.edi.w[0]);
1033 pptr->drivecnt = regs.edx.b[0];
1034 no_bpt = !(regs.es | regs.edi.w[0]);
1037 /* Compare what INT 13h returned with the appropriate equipment byte */
1038 if (geometry->driveno & 0x80) {
1039 bios_drives = rdz_8(BIOS_HD_COUNT);
1040 } else {
1041 uint8_t equip = rdz_8(BIOS_EQUIP);
1043 if (equip & 1)
1044 bios_drives = (equip >> 6) + 1;
1045 else
1046 bios_drives = 0;
1049 if (pptr->drivecnt > bios_drives) {
1050 printf("BIOS equipment byte says count = %d, go with that\n",
1051 bios_drives);
1052 pptr->drivecnt = bios_drives;
1056 /* Add ourselves to the drive count */
1057 pptr->drivecnt++;
1059 /* Discontiguous drive space. There is no really good solution for this. */
1060 if (pptr->drivecnt <= (geometry->driveno & 0x7f))
1061 pptr->drivecnt = (geometry->driveno & 0x7f) + 1;
1063 /* Probe for contiguous range of BIOS drives starting with driveno */
1064 pptr->driveshiftlimit = probe_drive_range(geometry->driveno) + 1;
1065 if ((pptr->driveshiftlimit & 0x80) != (geometry->driveno & 0x80))
1066 printf("We lost the last drive in our class of drives.\n");
1067 printf("Drive probing gives drive shift limit: 0x%02x\n",
1068 pptr->driveshiftlimit);
1070 /* Pointer to the command line */
1071 pptr->mdi.cmdline.seg_off.offset = bin_size + (nranges + 1) * sizeof(ranges[0]);
1072 pptr->mdi.cmdline.seg_off.segment = driverseg;
1074 /* Copy driver followed by E820 table followed by command line */
1076 unsigned char *dpp = (unsigned char *)(driverseg << 4);
1078 /* Adjust these pointers to point to the installed image */
1079 /* Careful about the order here... the image isn't copied yet! */
1080 pptr = (struct patch_area *)(dpp + hptr->patch_offs);
1081 hptr = (struct memdisk_header *)dpp;
1083 /* Actually copy to low memory */
1084 dpp = mempcpy(dpp, memdisk_hook, bin_size);
1085 dpp = mempcpy(dpp, ranges, (nranges + 1) * sizeof(ranges[0]));
1086 dpp = mempcpy(dpp, shdr->cmdline, cmdline_len);
1089 /* Note the previous INT 13h hook in the "safe hook" structure */
1090 hptr->safe_hook.old_hook.uint32 = pptr->mdi.oldint13.uint32;
1092 /* Re-fill the "safe hook" mBFT field with the physical address */
1093 hptr->safe_hook.mBFT.ptr =
1094 (struct mBFT *)(((const char *)hptr) + hptr->safe_hook.mBFT.offset);
1096 /* Update various BIOS magic data areas (gotta love this shit) */
1098 if (geometry->driveno & 0x80) {
1099 /* Update BIOS hard disk count */
1100 uint8_t nhd = pptr->drivecnt;
1102 if (nhd > 128)
1103 nhd = 128;
1105 if (!do_eltorito)
1106 wrz_8(BIOS_HD_COUNT, nhd);
1107 } else {
1108 /* Update BIOS floppy disk count */
1109 uint8_t equip = rdz_8(BIOS_EQUIP);
1110 uint8_t nflop = pptr->drivecnt;
1112 if (nflop > 4) /* Limit of equipment byte */
1113 nflop = 4;
1115 equip &= 0x3E;
1116 if (nflop)
1117 equip |= ((nflop - 1) << 6) | 0x01;
1119 wrz_8(BIOS_EQUIP, equip);
1121 /* Install DPT pointer if this was the only floppy */
1122 if (getcmditem("dpt") != CMD_NOTFOUND ||
1123 ((nflop == 1 || no_bpt) && getcmditem("nodpt") == CMD_NOTFOUND)) {
1124 /* Do install a replacement DPT into INT 1Eh */
1125 pptr->mdi.dpt_ptr =
1126 hptr->patch_offs + offsetof(struct patch_area, dpt);
1130 /* Complete the mBFT */
1131 hptr->safe_hook.mBFT.ptr->acpi.signature[0] = 'm'; /* "mBFT" */
1132 hptr->safe_hook.mBFT.ptr->acpi.signature[1] = 'B';
1133 hptr->safe_hook.mBFT.ptr->acpi.signature[2] = 'F';
1134 hptr->safe_hook.mBFT.ptr->acpi.signature[3] = 'T';
1135 hptr->safe_hook.mBFT.ptr->safe_hook.ptr = &hptr->safe_hook;
1136 hptr->safe_hook.mBFT.ptr->acpi.checksum =
1137 -checksum_buf(hptr->safe_hook.mBFT.ptr,
1138 hptr->safe_hook.mBFT.ptr->acpi.length);
1140 /* Install the interrupt handlers */
1141 printf("old: int13 = %08x int15 = %08x int1e = %08x\n",
1142 rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1144 wrz_32(BIOS_INT13, driverptr + hptr->int13_offs);
1145 wrz_32(BIOS_INT15, driverptr + hptr->int15_offs);
1146 if (pptr->mdi.dpt_ptr)
1147 wrz_32(BIOS_INT1E, driverptr + pptr->mdi.dpt_ptr);
1149 printf("new: int13 = %08x int15 = %08x int1e = %08x\n",
1150 rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E));
1152 /* Figure out entry point */
1153 if (!boot_seg) {
1154 boot_base = 0x7c00;
1155 shdr->sssp = 0x7c00;
1156 shdr->csip = 0x7c00;
1157 } else {
1158 boot_base = boot_seg << 4;
1159 shdr->sssp = boot_seg << 16;
1160 shdr->csip = boot_seg << 16;
1163 /* Relocate the real-mode code to below the stub */
1164 rm_base = (driveraddr - rm_args.rm_size) & ~15;
1165 if (rm_base < boot_base + boot_len)
1166 die("MEMDISK: bootstrap too large to load\n");
1168 relocate_rm_code(rm_base);
1170 /* Reboot into the new "disk" */
1171 puts("Loading boot sector... ");
1173 memcpy((void *)boot_base,
1174 (char *)pptr->mdi.diskbuf + geometry->boot_lba * 512,
1175 boot_len);
1177 if (getcmditem("pause") != CMD_NOTFOUND) {
1178 puts("press any key to boot... ");
1179 regs.eax.w[0] = 0;
1180 intcall(0x16, &regs, NULL);
1183 puts("booting...\n");
1185 /* On return the assembly code will jump to the boot vector */
1186 shdr->esdi = pnp_install_check();
1187 shdr->edx = geometry->driveno;