com32/chain: use disk_guid for part_guid for 0th partition
[syslinux.git] / com32 / chain / chain.c
blob85ec723cc71016749211a7d2c02245a6e440a4d5
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-2012 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->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/partition guid */
114 do {
115 if (!memcmp(&boot_part->gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
116 goto ok;
117 } while (!pi_next(boot_part));
119 drive = -1;
121 *_boot_part = boot_part;
122 return drive;
126 * Search for a specific drive/partition, based on the GPT label.
127 * Return drive and iterator at proper position.
129 static int find_by_label(const char *label, struct part_iter **_boot_part)
131 struct part_iter *boot_part = NULL;
132 struct disk_info diskinfo;
133 int drive;
135 for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
136 if (disk_get_params(drive, &diskinfo))
137 continue; /* Drive doesn't exist */
138 if (!(boot_part = pi_begin(&diskinfo, 0)))
139 continue;
140 /* Check for a GPT disk */
141 if (!(boot_part->type == typegpt)) {
142 pi_del(&boot_part);
143 continue;
145 /* Check for a matching partition */
146 while (!pi_next(boot_part)) {
147 if (!strcmp(label, boot_part->gpt.part_label))
148 goto ok;
151 drive = -1;
153 *_boot_part = boot_part;
154 return drive;
157 static void do_boot(struct data_area *data, int ndata)
159 uint16_t *const bios_fbm = (uint16_t *) 0x413;
160 addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
161 struct syslinux_memmap *mmap;
162 struct syslinux_movelist *mlist = NULL;
163 addr_t endimage;
164 uint8_t driveno = opt.regs.edx.b[0];
165 uint8_t swapdrive = driveno & 0x80;
166 int i;
168 mmap = syslinux_memory_map();
170 if (!mmap) {
171 error("Cannot read system memory map\n");
172 return;
175 endimage = 0;
176 for (i = 0; i < ndata; i++) {
177 if (data[i].base + data[i].size > endimage)
178 endimage = data[i].base + data[i].size;
180 if (endimage > dosmem)
181 goto too_big;
183 for (i = 0; i < ndata; i++) {
184 if (syslinux_add_movelist(&mlist, data[i].base,
185 (addr_t) data[i].data, data[i].size))
186 goto enomem;
189 if (opt.swap && driveno != swapdrive) {
190 static const uint8_t swapstub_master[] = {
191 /* The actual swap code */
192 0x53, /* 00: push bx */
193 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
194 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
195 0x5b, /* 08: pop bx */
196 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
197 0x90, 0x90, /* 0E: nop; nop */
198 /* Code to install this in the right location */
199 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
200 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
201 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
202 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
203 0x4f, /* 1F: dec di */
204 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
205 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
206 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
207 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
208 0x8e, 0xc7, /* 32: mov es,di */
209 0x31, 0xff, /* 34: xor di,di */
210 0xf3, 0x66, 0xa5, /* 36: rep movsd */
211 0xbe, 0, 0, /* 39: mov si,0 */
212 0xbf, 0, 0, /* 3C: mov di,0 */
213 0x8e, 0xde, /* 3F: mov ds,si */
214 0x8e, 0xc7, /* 41: mov es,di */
215 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
216 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
217 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
218 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
219 /* pad out to segment boundary */
220 0x90, 0x90, /* 5A: ... */
221 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
223 static uint8_t swapstub[1024];
224 uint8_t *p;
226 /* Note: we can't rely on either INT 13h nor the dosmem
227 vector to be correct at this stage, so we have to use an
228 installer stub to put things in the right place.
229 Round the installer location to a 1K boundary so the only
230 possible overlap is the identity mapping. */
231 endimage = (endimage + 1023u) & ~1023u;
233 /* Create swap stub */
234 memcpy(swapstub, swapstub_master, sizeof swapstub_master);
235 *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
236 *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
237 *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
238 *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
239 *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
240 *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
241 *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
242 p = &swapstub[sizeof swapstub_master];
244 /* Mapping table; start out with identity mapping everything */
245 for (i = 0; i < 256; i++)
246 p[i] = i;
248 /* And the actual swap */
249 p[driveno] = swapdrive;
250 p[swapdrive] = driveno;
252 /* Adjust registers */
253 opt.regs.ds = opt.regs.cs = endimage >> 4;
254 opt.regs.esi.l = opt.regs.es = 0;
255 opt.regs.ecx.l = sizeof swapstub >> 2;
256 opt.regs.ip = 0x10; /* Installer offset */
257 opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
259 if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
260 sizeof swapstub))
261 goto enomem;
263 endimage += sizeof swapstub;
266 /* Tell the shuffler not to muck with this area... */
267 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
269 /* Force text mode */
270 syslinux_force_text_mode();
272 fputs("Booting...\n", stdout);
273 syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
274 error("Chainboot failed!\n");
275 return;
277 too_big:
278 error("Loader file too large\n");
279 return;
281 enomem:
282 error("Out of memory\n");
283 return;
286 int find_dp(struct part_iter **_iter)
288 struct part_iter *iter = NULL;
289 struct disk_info diskinfo;
290 struct guid gpt_guid;
291 uint64_t fs_lba;
292 int drive, hd, partition;
293 const union syslinux_derivative_info *sdi;
295 sdi = syslinux_derivative_info();
297 if (!strncmp(opt.drivename, "mbr", 3)) {
298 if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
299 error("Unable to find requested MBR signature.\n");
300 goto bail;
302 } else if (!strncmp(opt.drivename, "guid", 4)) {
303 if (str_to_guid(opt.drivename + 5, &gpt_guid))
304 goto bail;
305 if (find_by_guid(&gpt_guid, &iter) < 0) {
306 error("Unable to find requested GPT disk or partition by guid.\n");
307 goto bail;
309 } else if (!strncmp(opt.drivename, "label", 5)) {
310 if (!opt.drivename[6]) {
311 error("No label specified.\n");
312 goto bail;
314 if (find_by_label(opt.drivename + 6, &iter) < 0) {
315 error("Unable to find requested GPT partition by label.\n");
316 goto bail;
318 } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
319 opt.drivename[1] == 'd') {
320 hd = opt.drivename[0] == 'h' ? 0x80 : 0;
321 opt.drivename += 2;
322 drive = hd | strtol(opt.drivename, NULL, 0);
324 if (disk_get_params(drive, &diskinfo))
325 goto bail;
326 /* this will start iteration over FDD, possibly raw */
327 if (!(iter = pi_begin(&diskinfo, 0)))
328 goto bail;
330 } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
331 if (!is_phys(sdi->c.filesystem)) {
332 error("When syslinux is not booted from physical disk (or its emulation),\n"
333 "'boot' and 'fs' are meaningless.\n");
334 goto bail;
336 /* offsets match, but in case it changes in the future */
337 if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
338 drive = sdi->iso.drive_number;
339 fs_lba = *sdi->iso.partoffset;
340 } else {
341 drive = sdi->disk.drive_number;
342 fs_lba = *sdi->disk.partoffset;
344 if (disk_get_params(drive, &diskinfo))
345 goto bail;
346 /* this will start iteration over disk emulation, possibly raw */
347 if (!(iter = pi_begin(&diskinfo, 0)))
348 goto bail;
350 /* 'fs' => we should lookup the syslinux partition number and use it */
351 if (!strcmp(opt.drivename, "fs")) {
352 while (!pi_next(iter)) {
353 if (iter->start_lba == fs_lba)
354 break;
356 /* broken part structure or other problems */
357 if (iter->status) {
358 error("Can't find myself on the drive I booted from.\n");
359 goto bail;
362 } else {
363 error("Unparsable drive specification.\n");
364 goto bail;
366 /* main options done - only thing left is explicit partition specification,
367 * if we're still at the disk stage with the iterator AND user supplied
368 * partition number (including disk pseudo-partition).
370 if (!iter->index && opt.partition) {
371 partition = strtol(opt.partition, NULL, 0);
372 /* search for matching part#, including disk */
373 do {
374 if (iter->index == partition)
375 break;
376 } while (!pi_next(iter));
377 if (iter->status) {
378 error("Requested disk / partition combination not found.\n");
379 goto bail;
383 if (!(iter->di.disk & 0x80) && iter->index) {
384 error("WARNING: Partitions on floppy devices may not work.\n");
387 *_iter = iter;
389 return 0;
391 bail:
392 pi_del(&iter);
393 return -1;
396 static int setup_handover(const struct part_iter *iter,
397 struct data_area *data)
399 struct disk_dos_part_entry *ha;
400 uint32_t synth_size = sizeof *ha;
403 * we have to cover both non-iterated but otherwise properly detected
404 * gpt/dos schemes as well as raw disks; checking index for 0 covers both
406 if (iter->index == 0) {
407 uint32_t len;
408 /* RAW handover protocol */
409 ha = malloc(synth_size);
410 if (!ha) {
411 error("Could not build RAW hand-over record!\n");
412 goto bail;
414 len = ~0u;
415 if (iter->length < len)
416 len = iter->length;
417 lba2chs(&ha->start, &iter->di, 0, L2C_CADD);
418 lba2chs(&ha->end, &iter->di, len - 1, L2C_CADD);
419 ha->active_flag = 0x80;
420 ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
421 ha->start_lba = 0;
422 ha->length = len;
423 } else if (iter->type == typegpt) {
424 uint32_t *plen;
425 /* GPT handover protocol */
426 synth_size += sizeof *plen + iter->gpt.pe_size;
427 ha = malloc(synth_size);
428 if (!ha) {
429 error("Could not build GPT hand-over record!\n");
430 goto bail;
432 lba2chs(&ha->start, &iter->di, iter->start_lba, L2C_CADD);
433 lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, L2C_CADD);
434 ha->active_flag = 0x80;
435 ha->ostype = 0xED;
436 /* All bits set by default */
437 ha->start_lba = ~0u;
438 ha->length = ~0u;
439 /* If these fit the precision, pass them on */
440 if (iter->start_lba < ha->start_lba)
441 ha->start_lba = iter->start_lba;
442 if (iter->length < ha->length)
443 ha->length = iter->length;
444 /* Next comes the GPT partition record length */
445 plen = (uint32_t *)(ha + 1);
446 plen[0] = iter->gpt.pe_size;
447 /* Next comes the GPT partition record copy */
448 memcpy(plen + 1, iter->record, plen[0]);
449 #ifdef DEBUG
450 dprintf("GPT handover:\n");
451 disk_dos_part_dump(ha);
452 disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
453 #endif
454 /* the only possible case left is dos scheme */
455 } else if (iter->type == typedos) {
456 /* MBR handover protocol */
457 ha = malloc(synth_size);
458 if (!ha) {
459 error("Could not build MBR hand-over record!\n");
460 goto bail;
462 memcpy(ha, iter->record, synth_size);
463 /* make sure these match bios imaginations and are ebr agnostic */
464 lba2chs(&ha->start, &iter->di, iter->start_lba, L2C_CADD);
465 lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, L2C_CADD);
466 ha->start_lba = iter->start_lba;
467 ha->length = iter->length;
469 #ifdef DEBUG
470 dprintf("MBR handover:\n");
471 disk_dos_part_dump(ha);
472 #endif
473 } else {
474 /* shouldn't ever happen */
475 goto bail;
478 data->base = 0x7be;
479 data->size = synth_size;
480 data->data = (void *)ha;
482 return 0;
483 bail:
484 return -1;
487 int main(int argc, char *argv[])
489 struct part_iter *iter = NULL;
490 void *sbck = NULL;
491 struct data_area fdat, hdat, sdat, data[3];
492 int ndata = 0;
494 console_ansi_raw();
496 memset(&fdat, 0, sizeof(fdat));
497 memset(&hdat, 0, sizeof(hdat));
498 memset(&sdat, 0, sizeof(sdat));
500 opt_set_defs();
501 if (opt_parse_args(argc, argv))
502 goto bail;
504 #if 0
505 /* Get max fixed disk number */
506 fixed_cnt = *(uint8_t *)(0x475);
509 * hmm, looks like we can't do that -
510 * some bioses/vms just set it to 1
511 * and go on living happily
512 * any better options than hardcoded 0x80 - 0xFF ?
514 #endif
516 /* Get disk/part iterator matching user supplied options */
517 if (find_dp(&iter))
518 goto bail;
520 /* Perform initial partition entry mangling */
521 if (manglepe_fixchs(iter))
522 goto bail;
523 if (manglepe_hide(iter))
524 goto bail;
526 /* Load the boot file */
527 if (opt.file) {
528 fdat.base = (opt.fseg << 4) + opt.foff;
530 if (loadfile(opt.file, &fdat.data, &fdat.size)) {
531 error("Couldn't read the boot file.\n");
532 goto bail;
534 if (fdat.base + fdat.size - 1 > ADDRMAX) {
535 error("The boot file is too big to load at this address.\n");
536 goto bail;
540 /* Load the sector */
541 if (opt.sect) {
542 sdat.base = (opt.sseg << 4) + opt.soff;
543 sdat.size = iter->di.bps;
545 if (sdat.base + sdat.size - 1 > ADDRMAX) {
546 error("The sector cannot be loaded at such high address.\n");
547 goto bail;
549 if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
550 error("Couldn't read the sector.\n");
551 goto bail;
553 if (opt.save) {
554 if (!(sbck = malloc(sdat.size))) {
555 error("Couldn't allocate cmp-buf for option 'save'.\n");
556 goto bail;
558 memcpy(sbck, sdat.data, sdat.size);
560 if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
561 error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n");
562 opt.maps = false;
566 /* Prep the handover */
567 if (opt.hand) {
568 if (setup_handover(iter, &hdat))
569 goto bail;
570 /* Verify possible conflicts */
571 if ( ( opt.file && overlap(&fdat, &hdat)) ||
572 ( opt.maps && overlap(&sdat, &hdat)) ) {
573 error("WARNING: Handover area won't be prepared,\n"
574 "as it would conflict with the boot file and/or the sector.\n");
575 opt.hand = false;
579 /* Adjust registers */
581 mangler_init(iter);
582 mangler_handover(iter, &hdat);
583 mangler_grldr(iter);
585 /* Patching functions */
587 if (manglef_isolinux(&fdat))
588 goto bail;
590 if (manglef_grub(iter, &fdat))
591 goto bail;
592 #if 0
593 if (manglef_drmk(&fdat))
594 goto bail;
595 #endif
596 if (manglef_bpb(iter, &fdat))
597 goto bail;
599 if (mangles_bpb(iter, &sdat))
600 goto bail;
602 if (mangles_save(iter, &sdat, sbck))
603 goto bail;
605 if (manglesf_bss(&sdat, &fdat))
606 goto bail;
608 /* This *must* be after BPB saving or copying */
609 if (mangles_cmldr(&sdat))
610 goto bail;
613 * Prepare boot-time mmap data. We should to it here, as manglers could
614 * potentially alter some of the data.
617 if (opt.file)
618 memcpy(data + ndata++, &fdat, sizeof(fdat));
619 if (opt.maps)
620 memcpy(data + ndata++, &sdat, sizeof(sdat));
621 if (opt.hand)
622 memcpy(data + ndata++, &hdat, sizeof(hdat));
624 #ifdef DEBUG
625 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
626 "iter->di C, H, S: %u, %u, %u\n",
627 iter->di.disk, iter->di.bps,
628 iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
629 iter->di.cyl, iter->di.head, iter->di.spt);
630 printf("iter idx: %d\n", iter->index);
631 printf("iter lba: %"PRIu64"\n", iter->start_lba);
632 if (opt.hand)
633 printf("hand lba: %u\n",
634 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
635 #endif
637 if (opt.warn) {
638 puts("Press any key to continue booting...");
639 wait_key();
642 if (ndata && !opt.brkchain) /* boot only if we actually chainload */
643 do_boot(data, ndata);
644 else
645 error("Service-only run completed, exiting.\n");
646 bail:
647 pi_del(&iter);
648 /* Free allocated areas */
649 free(fdat.data);
650 free(sdat.data);
651 free(hdat.data);
652 free(sbck);
653 return 255;
656 /* vim: set ts=8 sts=4 sw=4 noet: */