com32/chain/chain.c: fix if-order during handover preparation
[syslinux/sherbszt.git] / com32 / chain / chain.c
blob99eed0ebdcb11de666aef1885ae65a728b3f80f5
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 * Copyright 2010 Shao Miller
6 * Copyright 2010 Michal Soltys
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 * ----------------------------------------------------------------------- */
17 * Please see doc/chain.txt for the detailed documentation.
20 #include <com32.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <console.h>
26 #include <consoles.h>
27 #include <minmax.h>
28 #include <stdbool.h>
29 #include <dprintf.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <syslinux/loadfile.h>
33 #include <syslinux/bootrm.h>
34 #include <syslinux/config.h>
35 #include <syslinux/disk.h>
36 #include <syslinux/video.h>
37 #include "common.h"
38 #include "chain.h"
39 #include "utility.h"
40 #include "options.h"
41 #include "partiter.h"
42 #include "mangle.h"
44 struct options opt;
46 static int fixed_cnt = 128; /* see comments in main() */
48 static int overlap(const struct data_area *a, const struct data_area *b)
50 return
51 a->base + a->size > b->base &&
52 b->base + b->size > a->base;
55 static int is_phys(uint8_t sdifs)
57 return
58 sdifs == SYSLINUX_FS_SYSLINUX ||
59 sdifs == SYSLINUX_FS_EXTLINUX ||
60 sdifs == SYSLINUX_FS_ISOLINUX;
64 * Search for a specific drive, based on the MBR signature.
65 * Return drive and iterator at 0th position.
67 static int find_by_sig(uint32_t mbr_sig,
68 struct part_iter **_boot_part)
70 struct part_iter *boot_part = NULL;
71 struct disk_info diskinfo;
72 int drive;
74 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
75 if (disk_get_params(drive, &diskinfo))
76 continue; /* Drive doesn't exist */
77 if (!(boot_part = pi_begin(&diskinfo, 0)))
78 continue;
79 /* Check for a MBR disk */
80 if (boot_part->type != typedos) {
81 pi_del(&boot_part);
82 continue;
84 if (boot_part->sub.dos.disk_sig == mbr_sig) {
85 goto ok;
88 drive = -1;
89 ok:
90 *_boot_part = boot_part;
91 return drive;
95 * Search for a specific drive/partition, based on the GPT GUID.
96 * Return drive and iterator at proper position.
98 static int find_by_guid(const struct guid *gpt_guid,
99 struct part_iter **_boot_part)
101 struct part_iter *boot_part = NULL;
102 struct disk_info diskinfo;
103 int drive;
105 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
106 if (disk_get_params(drive, &diskinfo))
107 continue; /* Drive doesn't exist */
108 if (!(boot_part = pi_begin(&diskinfo, 0)))
109 continue;
110 /* Check for a GPT disk */
111 if (boot_part->type != typegpt) {
112 pi_del(&boot_part);
113 continue;
115 /* Check for a matching GPT disk guid */
116 if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
117 goto ok;
119 /* disk guid doesn't match, maybe partition guid will */
120 while (!pi_next(&boot_part)) {
121 if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
122 goto ok;
125 drive = -1;
127 *_boot_part = boot_part;
128 return drive;
132 * Search for a specific drive/partition, based on the GPT label.
133 * Return drive and iterator at proper position.
135 static int find_by_label(const char *label, struct part_iter **_boot_part)
137 struct part_iter *boot_part = NULL;
138 struct disk_info diskinfo;
139 int drive;
141 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
142 if (disk_get_params(drive, &diskinfo))
143 continue; /* Drive doesn't exist */
144 if (!(boot_part = pi_begin(&diskinfo, 0)))
145 continue;
146 /* Check for a GPT disk */
147 if (!(boot_part->type == typegpt)) {
148 pi_del(&boot_part);
149 continue;
151 /* Check for a matching partition */
152 while (!pi_next(&boot_part)) {
153 if (!strcmp(label, boot_part->sub.gpt.part_label))
154 goto ok;
157 drive = -1;
159 *_boot_part = boot_part;
160 return drive;
163 static void do_boot(struct data_area *data, int ndata)
165 uint16_t *const bios_fbm = (uint16_t *) 0x413;
166 addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
167 struct syslinux_memmap *mmap;
168 struct syslinux_movelist *mlist = NULL;
169 addr_t endimage;
170 uint8_t driveno = opt.regs.edx.b[0];
171 uint8_t swapdrive = driveno & 0x80;
172 int i;
174 mmap = syslinux_memory_map();
176 if (!mmap) {
177 error("Cannot read system memory map\n");
178 return;
181 endimage = 0;
182 for (i = 0; i < ndata; i++) {
183 if (data[i].base + data[i].size > endimage)
184 endimage = data[i].base + data[i].size;
186 if (endimage > dosmem)
187 goto too_big;
189 for (i = 0; i < ndata; i++) {
190 if (syslinux_add_movelist(&mlist, data[i].base,
191 (addr_t) data[i].data, data[i].size))
192 goto enomem;
195 if (opt.swap && driveno != swapdrive) {
196 static const uint8_t swapstub_master[] = {
197 /* The actual swap code */
198 0x53, /* 00: push bx */
199 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
200 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
201 0x5b, /* 08: pop bx */
202 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
203 0x90, 0x90, /* 0E: nop; nop */
204 /* Code to install this in the right location */
205 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
206 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
207 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
208 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
209 0x4f, /* 1F: dec di */
210 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
211 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
212 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
213 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
214 0x8e, 0xc7, /* 32: mov es,di */
215 0x31, 0xff, /* 34: xor di,di */
216 0xf3, 0x66, 0xa5, /* 36: rep movsd */
217 0xbe, 0, 0, /* 39: mov si,0 */
218 0xbf, 0, 0, /* 3C: mov di,0 */
219 0x8e, 0xde, /* 3F: mov ds,si */
220 0x8e, 0xc7, /* 41: mov es,di */
221 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
222 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
223 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
224 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
225 /* pad out to segment boundary */
226 0x90, 0x90, /* 5A: ... */
227 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
229 static uint8_t swapstub[1024];
230 uint8_t *p;
232 /* Note: we can't rely on either INT 13h nor the dosmem
233 vector to be correct at this stage, so we have to use an
234 installer stub to put things in the right place.
235 Round the installer location to a 1K boundary so the only
236 possible overlap is the identity mapping. */
237 endimage = (endimage + 1023u) & ~1023u;
239 /* Create swap stub */
240 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
241 *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
242 *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
243 *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
244 *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
245 *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
246 *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
247 *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
248 p = &swapstub[sizeof swapstub_master];
250 /* Mapping table; start out with identity mapping everything */
251 for (i = 0; i < 256; i++)
252 p[i] = (uint8_t)i;
254 /* And the actual swap */
255 p[driveno] = swapdrive;
256 p[swapdrive] = driveno;
258 /* Adjust registers */
259 opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
260 opt.regs.esi.l = opt.regs.es = 0;
261 opt.regs.ecx.l = sizeof swapstub >> 2;
262 opt.regs.ip = 0x10; /* Installer offset */
263 opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
265 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
266 sizeof swapstub))
267 goto enomem;
269 endimage += sizeof swapstub;
272 /* Tell the shuffler not to muck with this area... */
273 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
275 /* Force text mode */
276 syslinux_force_text_mode();
278 fputs("Booting...\n", stdout);
279 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
280 error("Chainboot failed!\n");
281 return;
283 too_big:
284 error("Loader file too large\n");
285 return;
287 enomem:
288 error("Out of memory\n");
289 return;
292 int find_dp(struct part_iter **_iter)
294 struct part_iter *iter = NULL;
295 struct disk_info diskinfo;
296 struct guid gpt_guid;
297 uint64_t fs_lba;
298 int drive, hd, partition;
299 const union syslinux_derivative_info *sdi;
301 sdi = syslinux_derivative_info();
303 if (!strncmp(opt.drivename, "mbr", 3)) {
304 if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
305 error("Unable to find requested MBR signature.\n");
306 goto bail;
308 } else if (!strncmp(opt.drivename, "guid", 4)) {
309 if (str_to_guid(opt.drivename + 5, &gpt_guid))
310 goto bail;
311 if (find_by_guid(&gpt_guid, &iter) < 0) {
312 error("Unable to find requested GPT disk or partition by guid.\n");
313 goto bail;
315 } else if (!strncmp(opt.drivename, "label", 5)) {
316 if (!opt.drivename[6]) {
317 error("No label specified.\n");
318 goto bail;
320 if (find_by_label(opt.drivename + 6, &iter) < 0) {
321 error("Unable to find requested GPT partition by label.\n");
322 goto bail;
324 } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
325 opt.drivename[1] == 'd') {
326 hd = opt.drivename[0] == 'h' ? 0x80 : 0;
327 opt.drivename += 2;
328 drive = hd | strtol(opt.drivename, NULL, 0);
330 if (disk_get_params(drive, &diskinfo))
331 goto bail;
332 /* this will start iteration over FDD, possibly raw */
333 if (!(iter = pi_begin(&diskinfo, 0)))
334 goto bail;
336 } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
337 if (!is_phys(sdi->c.filesystem)) {
338 error("When syslinux is not booted from physical disk (or its emulation),\n"
339 "'boot' and 'fs' are meaningless.\n");
340 goto bail;
342 /* offsets match, but in case it changes in the future */
343 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
344 drive = sdi->iso.drive_number;
345 fs_lba = *sdi->iso.partoffset;
346 } else {
347 drive = sdi->disk.drive_number;
348 fs_lba = *sdi->disk.partoffset;
350 if (disk_get_params(drive, &diskinfo))
351 goto bail;
352 /* this will start iteration over disk emulation, possibly raw */
353 if (!(iter = pi_begin(&diskinfo, 0)))
354 goto bail;
356 /* 'fs' => we should lookup the syslinux partition number and use it */
357 if (!strcmp(opt.drivename, "fs")) {
358 while (!pi_next(&iter)) {
359 if (iter->start_lba == fs_lba)
360 break;
362 /* broken part structure or other problems */
363 if (iter->status) {
364 error("Can't find myself on the drive I booted from.\n");
365 goto bail;
368 } else {
369 error("Unparsable drive specification.\n");
370 goto bail;
372 /* main options done - only thing left is explicit partition specification,
373 * if we're still at the disk stage with the iterator AND user supplied
374 * partition number (including disk pseudo-partition).
376 if (!iter->index && opt.partition) {
377 partition = strtol(opt.partition, NULL, 0);
378 /* search for matching part#, including disk */
379 do {
380 if (iter->index == partition)
381 break;
382 } while (!pi_next(&iter));
383 if (iter->status) {
384 error("Requested disk / partition combination not found.\n");
385 goto bail;
389 if (!(iter->di.disk & 0x80) && iter->index) {
390 error("WARNING: Partitions on floppy devices may not work.\n");
393 *_iter = iter;
395 return 0;
397 bail:
398 pi_del(&iter);
399 return -1;
402 static int setup_handover(const struct part_iter *iter,
403 struct data_area *data)
405 struct disk_dos_part_entry *ha;
406 uint32_t synth_size;
407 uint32_t *plen;
409 if (!iter->index) { /* implies typeraw or non-iterated */
410 uint32_t len;
411 /* RAW handover protocol */
412 synth_size = sizeof(struct disk_dos_part_entry);
413 ha = malloc(synth_size);
414 if (!ha) {
415 error("Could not build RAW hand-over record!\n");
416 goto bail;
418 len = ~0u;
419 if (iter->di.lbacnt < len)
420 len = (uint32_t)iter->di.lbacnt;
421 *(uint32_t *)ha->start = lba2chs(&iter->di, 0, l2c_cadd);
422 *(uint32_t *)ha->end = lba2chs(&iter->di, len - 1, l2c_cadd);
423 ha->active_flag = 0x80;
424 ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
425 ha->start_lba = 0;
426 ha->length = len;
427 } else if (iter->type == typegpt) {
428 const struct disk_gpt_part_entry *gp;
429 uint64_t lba_count;
430 /* GPT handover protocol */
431 gp = (const struct disk_gpt_part_entry *)iter->record;
432 lba_count = gp->lba_last - gp->lba_first + 1;
433 synth_size = sizeof(struct disk_dos_part_entry) +
434 sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
435 ha = malloc(synth_size);
436 if (!ha) {
437 error("Could not build GPT hand-over record!\n");
438 goto bail;
440 *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first, l2c_cadd);
441 *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last, l2c_cadd);
442 ha->active_flag = 0x80;
443 ha->ostype = 0xED;
444 /* All bits set by default */
445 ha->start_lba = ~0u;
446 ha->length = ~0u;
447 /* If these fit the precision, pass them on */
448 if (iter->start_lba < ha->start_lba)
449 ha->start_lba = (uint32_t)iter->start_lba;
450 if (lba_count < ha->length)
451 ha->length = (uint32_t)lba_count;
452 /* Next comes the GPT partition record length */
453 plen = (uint32_t *) (ha + 1);
454 plen[0] = (uint32_t)iter->sub.gpt.pe_size;
455 /* Next comes the GPT partition record copy */
456 memcpy(plen + 1, gp, plen[0]);
457 #ifdef DEBUG
458 dprintf("GPT handover:\n");
459 disk_dos_part_dump(ha);
460 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
461 #endif
462 } else if (iter->type == typedos) {
463 const struct disk_dos_part_entry *dp;
464 /* MBR handover protocol */
465 dp = (const struct disk_dos_part_entry *)iter->record;
466 synth_size = sizeof(struct disk_dos_part_entry);
467 ha = malloc(synth_size);
468 if (!ha) {
469 error("Could not build MBR hand-over record!\n");
470 goto bail;
472 *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba, l2c_cadd);
473 *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1, l2c_cadd);
474 ha->active_flag = dp->active_flag;
475 ha->ostype = dp->ostype;
476 ha->start_lba = (uint32_t)iter->start_lba; /* fine, we iterate over legacy scheme */
477 ha->length = dp->length;
478 #ifdef DEBUG
479 dprintf("MBR handover:\n");
480 disk_dos_part_dump(ha);
481 } else {
482 /* shouldn't ever happen */
483 goto bail;
484 #endif
487 data->base = 0x7be;
488 data->size = synth_size;
489 data->data = (void *)ha;
491 return 0;
492 bail:
493 return -1;
496 int main(int argc, char *argv[])
498 struct part_iter *iter = NULL;
500 void *sbck = NULL;
501 struct data_area fdat, hdat, sdat, data[3];
502 int ndata = 0;
504 console_ansi_raw();
505 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
507 /* Prepare and set defaults */
508 memset(&fdat, 0, sizeof(fdat));
509 memset(&hdat, 0, sizeof(hdat));
510 memset(&sdat, 0, sizeof(sdat));
511 memset(&opt, 0, sizeof(opt));
512 opt.sect = true; /* by def. load sector */
513 opt.maps = true; /* by def. map sector */
514 opt.hand = true; /* by def. prepare handover */
515 opt.chain = true; /* by def. do chainload */
516 opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
517 opt.drivename = "boot";
518 #ifdef DEBUG
519 opt.warn = true;
520 #endif
522 /* Parse arguments */
523 if (parse_args(argc, argv))
524 goto bail;
525 #if 0
526 /* Get max fixed disk number */
527 fixed_cnt = *(uint8_t *)(0x475);
530 * hmm, looks like we can't do that
531 * any better options than hardcoded 0x80 - 0xFF ?
533 #endif
535 /* Get disk/part iterator matching user supplied options */
536 if (find_dp(&iter))
537 goto bail;
539 /* Perform initial partition entry mangling */
540 if (manglepe_mbrchshide(iter))
541 goto bail;
543 /* Load the boot file */
544 if (opt.file) {
545 fdat.base = (opt.fseg << 4) + opt.foff;
547 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
548 error("Couldn't read the boot file.\n");
549 goto bail;
551 if (fdat.base + fdat.size - 1 > ADDRMAX) {
552 error("The boot file is too big to load at this address.\n");
553 goto bail;
557 /* Load the sector */
558 if (opt.sect) {
559 sdat.base = (opt.sseg << 4) + opt.soff;
560 sdat.size = iter->di.bps;
562 if (sdat.base + sdat.size - 1 > ADDRMAX) {
563 error("The sector cannot be loaded at such high address.\n");
564 goto bail;
566 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
567 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
568 opt.sect = false;
569 } else {
570 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
571 error("Couldn't read the sector.\n");
572 goto bail;
574 if (opt.save) {
575 if (!(sbck = malloc(sdat.size))) {
576 error("Couldn't allocate cmp-buf for option 'save'.\n");
577 goto bail;
579 memcpy(sbck, sdat.data, sdat.size);
584 /* Prep the handover */
585 if (opt.hand) {
586 if (setup_handover(iter, &hdat))
587 goto bail;
588 /* Verify possible conflicts */
589 if ( ( opt.file && overlap(&fdat, &hdat)) ||
590 ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
591 error("WARNING: Handover area won't be prepared,\n"
592 "as it would conflict with the boot file and/or the sector.\n");
593 opt.hand = false;
597 /* Adjust registers */
599 mangler_common(iter);
600 mangler_handover(iter, &hdat);
601 mangler_grldr(iter);
603 /* Patching functions */
605 if (manglef_isolinux(&fdat))
606 goto bail;
608 if (manglef_grub(iter, &fdat))
609 goto bail;
610 #if 0
611 if (manglef_drmk(&fdat))
612 goto bail;
613 #endif
614 if (manglef_bpb(iter, &fdat))
615 goto bail;
617 if (mangles_bpb(iter, &sdat))
618 goto bail;
620 if (mangles_save(iter, &sdat, sbck))
621 goto bail;
623 if (manglesf_bss(&sdat, &fdat))
624 goto bail;
626 /* This *must* be after BPB saving or copying */
627 if (mangles_cmldr(&sdat))
628 goto bail;
631 * Prepare boot-time mmap data. We should to it here, as manglers could
632 * potentially alter some of the data.
635 if (opt.file)
636 memcpy(data + ndata++, &fdat, sizeof(fdat));
637 if (opt.sect && opt.maps)
638 memcpy(data + ndata++, &sdat, sizeof(sdat));
639 if (opt.hand)
640 memcpy(data + ndata++, &hdat, sizeof(hdat));
642 #ifdef DEBUG
643 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
644 "iter->di C, H, S: %u, %u, %u\n",
645 iter->di.disk, iter->di.bps,
646 iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
647 iter->di.cyl, iter->di.head, iter->di.spt);
648 printf("iter idx: %d\n", iter->index);
649 printf("iter lba: %llu\n", iter->start_lba);
650 if (opt.hand)
651 printf("hand lba: %u\n",
652 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
653 #endif
655 if (opt.warn) {
656 puts("Press any key to continue booting...");
657 wait_key();
660 if (ndata && opt.chain) /* boot only if we actually chainload */
661 do_boot(data, ndata);
662 else
663 error("Service-only run completed, exiting.\n");
664 bail:
665 pi_del(&iter);
666 /* Free allocated areas */
667 free(fdat.data);
668 free(sdat.data);
669 free(hdat.data);
670 free(sbck);
671 return 255;
674 /* vim: set ts=8 sts=4 sw=4 noet: */