chain.c: Fix misplaced #endif breaking non-DEBUG builds
[syslinux/sherbszt.git] / com32 / chain / chain.c
blob30153c4df923bd54a8ed10f9c5d85df5e4338298
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 static int fixed_cnt = 128; /* see comments in main() */
46 static int overlap(const struct data_area *a, const struct data_area *b)
48 return
49 a->base + a->size > b->base &&
50 b->base + b->size > a->base;
53 static int is_phys(uint8_t sdifs)
55 return
56 sdifs == SYSLINUX_FS_SYSLINUX ||
57 sdifs == SYSLINUX_FS_EXTLINUX ||
58 sdifs == SYSLINUX_FS_ISOLINUX;
62 * Search for a specific drive, based on the MBR signature.
63 * Return drive and iterator at 0th position.
65 static int find_by_sig(uint32_t mbr_sig,
66 struct part_iter **_boot_part)
68 struct part_iter *boot_part = NULL;
69 struct disk_info diskinfo;
70 int drive;
72 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
73 if (disk_get_params(drive, &diskinfo))
74 continue; /* Drive doesn't exist */
75 if (!(boot_part = pi_begin(&diskinfo, 0)))
76 continue;
77 /* Check for a MBR disk */
78 if (boot_part->type != typedos) {
79 pi_del(&boot_part);
80 continue;
82 if (boot_part->sub.dos.disk_sig == mbr_sig) {
83 goto ok;
86 drive = -1;
87 ok:
88 *_boot_part = boot_part;
89 return drive;
93 * Search for a specific drive/partition, based on the GPT GUID.
94 * Return drive and iterator at proper position.
96 static int find_by_guid(const struct guid *gpt_guid,
97 struct part_iter **_boot_part)
99 struct part_iter *boot_part = NULL;
100 struct disk_info diskinfo;
101 int drive;
103 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
104 if (disk_get_params(drive, &diskinfo))
105 continue; /* Drive doesn't exist */
106 if (!(boot_part = pi_begin(&diskinfo, 0)))
107 continue;
108 /* Check for a GPT disk */
109 if (boot_part->type != typegpt) {
110 pi_del(&boot_part);
111 continue;
113 /* Check for a matching GPT disk guid */
114 if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
115 goto ok;
117 /* disk guid doesn't match, maybe partition guid will */
118 while (!pi_next(&boot_part)) {
119 if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
120 goto ok;
123 drive = -1;
125 *_boot_part = boot_part;
126 return drive;
130 * Search for a specific drive/partition, based on the GPT label.
131 * Return drive and iterator at proper position.
133 static int find_by_label(const char *label, struct part_iter **_boot_part)
135 struct part_iter *boot_part = NULL;
136 struct disk_info diskinfo;
137 int drive;
139 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
140 if (disk_get_params(drive, &diskinfo))
141 continue; /* Drive doesn't exist */
142 if (!(boot_part = pi_begin(&diskinfo, 0)))
143 continue;
144 /* Check for a GPT disk */
145 if (!(boot_part->type == typegpt)) {
146 pi_del(&boot_part);
147 continue;
149 /* Check for a matching partition */
150 while (!pi_next(&boot_part)) {
151 if (!strcmp(label, boot_part->sub.gpt.part_label))
152 goto ok;
155 drive = -1;
157 *_boot_part = boot_part;
158 return drive;
161 static void do_boot(struct data_area *data, int ndata)
163 uint16_t *const bios_fbm = (uint16_t *) 0x413;
164 addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
165 struct syslinux_memmap *mmap;
166 struct syslinux_movelist *mlist = NULL;
167 addr_t endimage;
168 uint8_t driveno = opt.regs.edx.b[0];
169 uint8_t swapdrive = driveno & 0x80;
170 int i;
172 mmap = syslinux_memory_map();
174 if (!mmap) {
175 error("Cannot read system memory map\n");
176 return;
179 endimage = 0;
180 for (i = 0; i < ndata; i++) {
181 if (data[i].base + data[i].size > endimage)
182 endimage = data[i].base + data[i].size;
184 if (endimage > dosmem)
185 goto too_big;
187 for (i = 0; i < ndata; i++) {
188 if (syslinux_add_movelist(&mlist, data[i].base,
189 (addr_t) data[i].data, data[i].size))
190 goto enomem;
193 if (opt.swap && driveno != swapdrive) {
194 static const uint8_t swapstub_master[] = {
195 /* The actual swap code */
196 0x53, /* 00: push bx */
197 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
198 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
199 0x5b, /* 08: pop bx */
200 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
201 0x90, 0x90, /* 0E: nop; nop */
202 /* Code to install this in the right location */
203 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
204 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
205 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
206 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
207 0x4f, /* 1F: dec di */
208 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
209 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
210 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
211 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
212 0x8e, 0xc7, /* 32: mov es,di */
213 0x31, 0xff, /* 34: xor di,di */
214 0xf3, 0x66, 0xa5, /* 36: rep movsd */
215 0xbe, 0, 0, /* 39: mov si,0 */
216 0xbf, 0, 0, /* 3C: mov di,0 */
217 0x8e, 0xde, /* 3F: mov ds,si */
218 0x8e, 0xc7, /* 41: mov es,di */
219 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
220 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
221 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
222 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
223 /* pad out to segment boundary */
224 0x90, 0x90, /* 5A: ... */
225 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
227 static uint8_t swapstub[1024];
228 uint8_t *p;
230 /* Note: we can't rely on either INT 13h nor the dosmem
231 vector to be correct at this stage, so we have to use an
232 installer stub to put things in the right place.
233 Round the installer location to a 1K boundary so the only
234 possible overlap is the identity mapping. */
235 endimage = (endimage + 1023u) & ~1023u;
237 /* Create swap stub */
238 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
239 *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
240 *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
241 *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
242 *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
243 *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
244 *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
245 *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
246 p = &swapstub[sizeof swapstub_master];
248 /* Mapping table; start out with identity mapping everything */
249 for (i = 0; i < 256; i++)
250 p[i] = (uint8_t)i;
252 /* And the actual swap */
253 p[driveno] = swapdrive;
254 p[swapdrive] = driveno;
256 /* Adjust registers */
257 opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
258 opt.regs.esi.l = opt.regs.es = 0;
259 opt.regs.ecx.l = sizeof swapstub >> 2;
260 opt.regs.ip = 0x10; /* Installer offset */
261 opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
263 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
264 sizeof swapstub))
265 goto enomem;
267 endimage += sizeof swapstub;
270 /* Tell the shuffler not to muck with this area... */
271 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
273 /* Force text mode */
274 syslinux_force_text_mode();
276 fputs("Booting...\n", stdout);
277 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
278 error("Chainboot failed!\n");
279 return;
281 too_big:
282 error("Loader file too large\n");
283 return;
285 enomem:
286 error("Out of memory\n");
287 return;
290 int find_dp(struct part_iter **_iter)
292 struct part_iter *iter = NULL;
293 struct disk_info diskinfo;
294 struct guid gpt_guid;
295 uint64_t fs_lba;
296 int drive, hd, partition;
297 const union syslinux_derivative_info *sdi;
299 sdi = syslinux_derivative_info();
301 if (!strncmp(opt.drivename, "mbr", 3)) {
302 if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
303 error("Unable to find requested MBR signature.\n");
304 goto bail;
306 } else if (!strncmp(opt.drivename, "guid", 4)) {
307 if (str_to_guid(opt.drivename + 5, &gpt_guid))
308 goto bail;
309 if (find_by_guid(&gpt_guid, &iter) < 0) {
310 error("Unable to find requested GPT disk or partition by guid.\n");
311 goto bail;
313 } else if (!strncmp(opt.drivename, "label", 5)) {
314 if (!opt.drivename[6]) {
315 error("No label specified.\n");
316 goto bail;
318 if (find_by_label(opt.drivename + 6, &iter) < 0) {
319 error("Unable to find requested GPT partition by label.\n");
320 goto bail;
322 } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
323 opt.drivename[1] == 'd') {
324 hd = opt.drivename[0] == 'h' ? 0x80 : 0;
325 opt.drivename += 2;
326 drive = hd | strtol(opt.drivename, NULL, 0);
328 if (disk_get_params(drive, &diskinfo))
329 goto bail;
330 /* this will start iteration over FDD, possibly raw */
331 if (!(iter = pi_begin(&diskinfo, 0)))
332 goto bail;
334 } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
335 if (!is_phys(sdi->c.filesystem)) {
336 error("When syslinux is not booted from physical disk (or its emulation),\n"
337 "'boot' and 'fs' are meaningless.\n");
338 goto bail;
340 /* offsets match, but in case it changes in the future */
341 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
342 drive = sdi->iso.drive_number;
343 fs_lba = *sdi->iso.partoffset;
344 } else {
345 drive = sdi->disk.drive_number;
346 fs_lba = *sdi->disk.partoffset;
348 if (disk_get_params(drive, &diskinfo))
349 goto bail;
350 /* this will start iteration over disk emulation, possibly raw */
351 if (!(iter = pi_begin(&diskinfo, 0)))
352 goto bail;
354 /* 'fs' => we should lookup the syslinux partition number and use it */
355 if (!strcmp(opt.drivename, "fs")) {
356 while (!pi_next(&iter)) {
357 if (iter->start_lba == fs_lba)
358 break;
360 /* broken part structure or other problems */
361 if (iter->status) {
362 error("Can't find myself on the drive I booted from.\n");
363 goto bail;
366 } else {
367 error("Unparsable drive specification.\n");
368 goto bail;
370 /* main options done - only thing left is explicit partition specification,
371 * if we're still at the disk stage with the iterator AND user supplied
372 * partition number (including disk pseudo-partition).
374 if (!iter->index && opt.partition) {
375 partition = strtol(opt.partition, NULL, 0);
376 /* search for matching part#, including disk */
377 do {
378 if (iter->index == partition)
379 break;
380 } while (!pi_next(&iter));
381 if (iter->status) {
382 error("Requested disk / partition combination not found.\n");
383 goto bail;
387 if (!(iter->di.disk & 0x80) && iter->index) {
388 error("WARNING: Partitions on floppy devices may not work.\n");
391 *_iter = iter;
393 return 0;
395 bail:
396 pi_del(&iter);
397 return -1;
400 static int setup_handover(const struct part_iter *iter,
401 struct data_area *data)
403 struct disk_dos_part_entry *ha;
404 uint32_t synth_size;
405 uint32_t *plen;
407 if (!iter->index) { /* implies typeraw or non-iterated */
408 uint32_t len;
409 /* RAW handover protocol */
410 synth_size = sizeof(struct disk_dos_part_entry);
411 ha = malloc(synth_size);
412 if (!ha) {
413 error("Could not build RAW hand-over record!\n");
414 goto bail;
416 len = ~0u;
417 if (iter->length < len)
418 len = (uint32_t)iter->length;
419 lba2chs(&ha->start, &iter->di, 0, l2c_cadd);
420 lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd);
421 ha->active_flag = 0x80;
422 ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
423 ha->start_lba = 0;
424 ha->length = len;
425 } else if (iter->type == typegpt) {
426 /* GPT handover protocol */
427 synth_size = sizeof(struct disk_dos_part_entry) +
428 sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
429 ha = malloc(synth_size);
430 if (!ha) {
431 error("Could not build GPT hand-over record!\n");
432 goto bail;
434 lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
435 lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
436 ha->active_flag = 0x80;
437 ha->ostype = 0xED;
438 /* All bits set by default */
439 ha->start_lba = ~0u;
440 ha->length = ~0u;
441 /* If these fit the precision, pass them on */
442 if (iter->start_lba < ha->start_lba)
443 ha->start_lba = (uint32_t)iter->start_lba;
444 if (iter->length < ha->length)
445 ha->length = (uint32_t)iter->length;
446 /* Next comes the GPT partition record length */
447 plen = (uint32_t *) (ha + 1);
448 plen[0] = (uint32_t)iter->sub.gpt.pe_size;
449 /* Next comes the GPT partition record copy */
450 memcpy(plen + 1, iter->record, plen[0]);
451 #ifdef DEBUG
452 dprintf("GPT handover:\n");
453 disk_dos_part_dump(ha);
454 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
455 #endif
456 } else if (iter->type == typedos) {
457 /* MBR handover protocol */
458 synth_size = sizeof(struct disk_dos_part_entry);
459 ha = malloc(synth_size);
460 if (!ha) {
461 error("Could not build MBR hand-over record!\n");
462 goto bail;
464 memcpy(ha, iter->record, synth_size);
465 /* make sure these match bios imaginations and are ebr agnostic */
466 lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
467 lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
468 ha->start_lba = (uint32_t)iter->start_lba;
469 ha->length = (uint32_t)iter->length;
471 #ifdef DEBUG
472 dprintf("MBR handover:\n");
473 disk_dos_part_dump(ha);
474 #endif
475 } else {
476 /* shouldn't ever happen */
477 goto bail;
480 data->base = 0x7be;
481 data->size = synth_size;
482 data->data = (void *)ha;
484 return 0;
485 bail:
486 return -1;
489 int main(int argc, char *argv[])
491 struct part_iter *iter = NULL;
492 void *sbck = NULL;
493 struct data_area fdat, hdat, sdat, data[3];
494 int ndata = 0;
496 console_ansi_raw();
498 memset(&fdat, 0, sizeof(fdat));
499 memset(&hdat, 0, sizeof(hdat));
500 memset(&sdat, 0, sizeof(sdat));
502 opt_set_defs();
503 if (opt_parse_args(argc, argv))
504 goto bail;
506 #if 0
507 /* Get max fixed disk number */
508 fixed_cnt = *(uint8_t *)(0x475);
511 * hmm, looks like we can't do that -
512 * some bioses/vms just set it to 1
513 * and go on living happily
514 * any better options than hardcoded 0x80 - 0xFF ?
516 #endif
518 /* Get disk/part iterator matching user supplied options */
519 if (find_dp(&iter))
520 goto bail;
522 /* Perform initial partition entry mangling */
523 if (manglepe_fixchs(iter))
524 goto bail;
525 if (manglepe_hide(iter))
526 goto bail;
528 /* Load the boot file */
529 if (opt.file) {
530 fdat.base = (opt.fseg << 4) + opt.foff;
532 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
533 error("Couldn't read the boot file.\n");
534 goto bail;
536 if (fdat.base + fdat.size - 1 > ADDRMAX) {
537 error("The boot file is too big to load at this address.\n");
538 goto bail;
542 /* Load the sector */
543 if (opt.sect) {
544 sdat.base = (opt.sseg << 4) + opt.soff;
545 sdat.size = iter->di.bps;
547 if (sdat.base + sdat.size - 1 > ADDRMAX) {
548 error("The sector cannot be loaded at such high address.\n");
549 goto bail;
551 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
552 error("Couldn't read the sector.\n");
553 goto bail;
555 if (opt.save) {
556 if (!(sbck = malloc(sdat.size))) {
557 error("Couldn't allocate cmp-buf for option 'save'.\n");
558 goto bail;
560 memcpy(sbck, sdat.data, sdat.size);
562 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
563 error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n");
564 opt.maps = false;
568 /* Prep the handover */
569 if (opt.hand) {
570 if (setup_handover(iter, &hdat))
571 goto bail;
572 /* Verify possible conflicts */
573 if ( ( opt.file && overlap(&fdat, &hdat)) ||
574 ( opt.maps && overlap(&sdat, &hdat)) ) {
575 error("WARNING: Handover area won't be prepared,\n"
576 "as it would conflict with the boot file and/or the sector.\n");
577 opt.hand = false;
581 /* Adjust registers */
583 mangler_init(iter);
584 mangler_handover(iter, &hdat);
585 mangler_grldr(iter);
587 /* Patching functions */
589 if (manglef_isolinux(&fdat))
590 goto bail;
592 if (manglef_grub(iter, &fdat))
593 goto bail;
594 #if 0
595 if (manglef_drmk(&fdat))
596 goto bail;
597 #endif
598 if (manglef_bpb(iter, &fdat))
599 goto bail;
601 if (mangles_bpb(iter, &sdat))
602 goto bail;
604 if (mangles_save(iter, &sdat, sbck))
605 goto bail;
607 if (manglesf_bss(&sdat, &fdat))
608 goto bail;
610 /* This *must* be after BPB saving or copying */
611 if (mangles_cmldr(&sdat))
612 goto bail;
615 * Prepare boot-time mmap data. We should to it here, as manglers could
616 * potentially alter some of the data.
619 if (opt.file)
620 memcpy(data + ndata++, &fdat, sizeof(fdat));
621 if (opt.maps)
622 memcpy(data + ndata++, &sdat, sizeof(sdat));
623 if (opt.hand)
624 memcpy(data + ndata++, &hdat, sizeof(hdat));
626 #ifdef DEBUG
627 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
628 "iter->di C, H, S: %u, %u, %u\n",
629 iter->di.disk, iter->di.bps,
630 iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
631 iter->di.cyl, iter->di.head, iter->di.spt);
632 printf("iter idx: %d\n", iter->index);
633 printf("iter lba: %"PRIu64"\n", iter->start_lba);
634 if (opt.hand)
635 printf("hand lba: %u\n",
636 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
637 #endif
639 if (opt.warn) {
640 puts("Press any key to continue booting...");
641 wait_key();
644 if (ndata && !opt.brkchain) /* boot only if we actually chainload */
645 do_boot(data, ndata);
646 else
647 error("Service-only run completed, exiting.\n");
648 bail:
649 pi_del(&iter);
650 /* Free allocated areas */
651 free(fdat.data);
652 free(sdat.data);
653 free(hdat.data);
654 free(sbck);
655 return 255;
658 /* vim: set ts=8 sts=4 sw=4 noet: */