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