1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <libgen.h> 29 #include <malloc.h> 30 #include <string.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <strings.h> 36 #include <sys/mount.h> 37 #include <sys/mnttab.h> 38 #include <sys/dktp/fdisk.h> 39 #include <sys/dkio.h> 40 #include <sys/vtoc.h> 41 42 #include <libintl.h> 43 #include <locale.h> 44 #include "message.h" 45 #include <errno.h> 46 47 #ifndef TEXT_DOMAIN 48 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 49 #endif 50 51 #define SECTOR_SIZE 0x200 52 #define STAGE2_MEMADDR 0x8000 /* loading addr of stage2 */ 53 54 #define STAGE1_BPB_OFFSET 0x3 55 #define STAGE1_BPB_SIZE 0x3B 56 #define STAGE1_BOOT_DRIVE 0x40 57 #define STAGE1_FORCE_LBA 0x41 58 #define STAGE1_STAGE2_ADDRESS 0x42 59 #define STAGE1_STAGE2_SECTOR 0x44 60 #define STAGE1_STAGE2_SEGMENT 0x48 61 62 #define STAGE2_BLOCKLIST (SECTOR_SIZE - 0x8) 63 #define STAGE2_INSTALLPART (SECTOR_SIZE + 0x8) 64 #define STAGE2_FORCE_LBA (SECTOR_SIZE + 0x11) 65 #define STAGE2_VER_STRING (SECTOR_SIZE + 0x12) 66 #define STAGE2_BLKOFF 50 /* offset from start of fdisk part */ 67 68 static int nowrite = 0; 69 static int write_mboot = 0; 70 static int force_mboot = 0; 71 static int is_floppy = 0; 72 static int is_bootpar = 0; 73 static int stage2_fd; 74 static int partition, slice = 0xff; 75 static unsigned int stage2_first_sector, stage2_second_sector; 76 77 78 static char bpb_sect[SECTOR_SIZE]; 79 static char boot_sect[SECTOR_SIZE]; 80 static char stage1_buffer[SECTOR_SIZE]; 81 static char stage2_buffer[2 * SECTOR_SIZE]; 82 static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)]; 83 84 static int open_device(char *); 85 static void read_bpb_sect(int); 86 static void read_boot_sect(char *); 87 static void write_boot_sect(char *); 88 static void read_stage1_stage2(char *, char *); 89 static void modify_and_write_stage1(int); 90 static void modify_and_write_stage2(int); 91 static unsigned int get_start_sector(int); 92 static void copy_stage2(int, char *); 93 static char *get_raw_partition(char *); 94 static void usage(char *); 95 96 extern int read_stage2_blocklist(int, unsigned int *); 97 98 int 99 main(int argc, char *argv[]) 100 { 101 int dev_fd, opt; 102 char *stage1, *stage2, *device; 103 104 (void) setlocale(LC_ALL, ""); 105 (void) textdomain(TEXT_DOMAIN); 106 107 while ((opt = getopt(argc, argv, "fmn")) != EOF) { 108 switch (opt) { 109 case 'm': 110 write_mboot = 1; 111 break; 112 case 'n': 113 nowrite = 1; 114 break; 115 case 'f': 116 force_mboot = 1; 117 break; 118 default: 119 /* fall through to process non-optional args */ 120 break; 121 } 122 } 123 124 /* check arguments */ 125 if (argc != optind + 3) { 126 usage(argv[0]); 127 } 128 129 if (nowrite) { 130 (void) fprintf(stdout, DRY_RUN); 131 } 132 133 stage1 = strdup(argv[optind]); 134 stage2 = strdup(argv[optind + 1]); 135 device = strdup(argv[optind + 2]); 136 137 if (!stage1 || !stage2 || !device) { 138 usage(argv[0]); 139 } 140 141 /* open and check device type */ 142 dev_fd = open_device(device); 143 144 /* read in stage1 and stage2 into buffer */ 145 read_stage1_stage2(stage1, stage2); 146 147 /* In the pcfs case, write a fresh stage2 */ 148 if (is_floppy || is_bootpar) { 149 copy_stage2(dev_fd, device); 150 read_bpb_sect(dev_fd); 151 } 152 153 /* read in boot sector */ 154 if (!is_floppy) 155 read_boot_sect(device); 156 157 /* modify stage1 based on grub needs */ 158 modify_and_write_stage1(dev_fd); 159 160 /* modify stage2 and write to media */ 161 modify_and_write_stage2(dev_fd); 162 163 if (!is_floppy && write_mboot) 164 write_boot_sect(device); 165 (void) close(dev_fd); 166 167 return (0); 168 } 169 170 static unsigned int 171 get_start_sector(int fd) 172 { 173 static unsigned int start_sect = 0; 174 175 int i; 176 struct mboot *mboot; 177 struct ipart *part; 178 179 if (start_sect) 180 return (start_sect); 181 182 mboot = (struct mboot *)boot_sect; 183 for (i = 0; i < FD_NUMPART; i++) { 184 part = (struct ipart *)mboot->parts + i; 185 if (is_bootpar) { 186 if (part->systid == 0xbe) 187 break; 188 } 189 } 190 191 /* 192 * If there is no boot partition, find the solaris partition 193 */ 194 195 if (i == FD_NUMPART) { 196 struct part_info dkpi; 197 struct extpart_info edkpi; 198 199 /* 200 * Get the solaris partition information from the device 201 * and compare the offset of S2 with offset of solaris partition 202 * from fdisk partition table. 203 */ 204 if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) { 205 if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) { 206 (void) fprintf(stderr, PART_FAIL); 207 exit(-1); 208 } else { 209 edkpi.p_start = dkpi.p_start; 210 } 211 } 212 213 for (i = 0; i < FD_NUMPART; i++) { 214 part = (struct ipart *)mboot->parts + i; 215 216 if (part->relsect == 0) { 217 (void) fprintf(stderr, BAD_PART, i); 218 exit(-1); 219 } 220 if (edkpi.p_start >= part->relsect && 221 edkpi.p_start < (part->relsect + part->numsect)) { 222 /* Found the partition */ 223 break; 224 } 225 } 226 } 227 228 if (i == FD_NUMPART) { 229 (void) fprintf(stderr, BOOTPAR); 230 exit(-1); 231 } 232 233 /* get confirmation for -m */ 234 if (write_mboot && !force_mboot) { 235 (void) fprintf(stdout, MBOOT_PROMPT); 236 if (getchar() != 'y') { 237 write_mboot = 0; 238 (void) fprintf(stdout, MBOOT_NOT_UPDATED); 239 } 240 } 241 242 start_sect = part->relsect; 243 if (part->bootid != 128 && write_mboot == 0) { 244 (void) fprintf(stdout, BOOTPAR_INACTIVE, i + 1); 245 } 246 247 partition = i; 248 return (start_sect); 249 } 250 251 static void 252 usage(char *progname) 253 { 254 (void) fprintf(stderr, USAGE, basename(progname)); 255 exit(-1); 256 } 257 258 static int 259 open_device(char *device) 260 { 261 int dev_fd; 262 struct stat stat; 263 char *raw_part; 264 265 is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) && 266 strncmp(device, "/dev/dsk", strlen("/dev/dsk")); 267 268 /* handle boot partition specification */ 269 if (!is_floppy && strstr(device, "p0:boot")) { 270 is_bootpar = 1; 271 } 272 273 raw_part = get_raw_partition(device); 274 275 if (nowrite) 276 dev_fd = open(raw_part, O_RDONLY); 277 else 278 dev_fd = open(raw_part, O_RDWR); 279 280 if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) { 281 (void) fprintf(stderr, OPEN_FAIL, raw_part); 282 exit(-1); 283 } 284 if (S_ISCHR(stat.st_mode) == 0) { 285 (void) fprintf(stderr, NOT_RAW_DEVICE, raw_part); 286 exit(-1); 287 } 288 289 return (dev_fd); 290 } 291 292 static void 293 read_stage1_stage2(char *stage1, char *stage2) 294 { 295 int fd; 296 297 /* read the stage1 file from filesystem */ 298 fd = open(stage1, O_RDONLY); 299 if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) { 300 (void) fprintf(stderr, READ_FAIL_STAGE1, stage1); 301 exit(-1); 302 } 303 (void) close(fd); 304 305 /* read first two blocks of stage 2 from filesystem */ 306 stage2_fd = open(stage2, O_RDONLY); 307 if (stage2_fd == -1 || 308 read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE) 309 != 2 * SECTOR_SIZE) { 310 (void) fprintf(stderr, READ_FAIL_STAGE2, stage2); 311 exit(-1); 312 } 313 /* leave the stage2 file open for later */ 314 } 315 316 static void 317 read_bpb_sect(int dev_fd) 318 { 319 if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) { 320 (void) fprintf(stderr, READ_FAIL_BPB); 321 exit(-1); 322 } 323 } 324 325 static void 326 read_boot_sect(char *device) 327 { 328 static int read_mbr = 0; 329 int i, fd; 330 char save[2]; 331 332 if (read_mbr) 333 return; 334 read_mbr = 1; 335 336 /* get the whole disk (p0) */ 337 i = strlen(device); 338 save[0] = device[i - 2]; 339 save[1] = device[i - 1]; 340 device[i - 2] = 'p'; 341 device[i - 1] = '0'; 342 343 fd = open(device, O_RDONLY); 344 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 345 (void) fprintf(stderr, READ_FAIL_MBR, device); 346 if (fd == -1) 347 perror("open"); 348 else 349 perror("read"); 350 exit(-1); 351 } 352 (void) close(fd); 353 device[i - 2] = save[0]; 354 device[i - 1] = save[1]; 355 } 356 357 static void 358 write_boot_sect(char *device) 359 { 360 int fd, len; 361 char *raw, *end; 362 struct stat stat; 363 364 /* make a copy and chop off ":boot" */ 365 raw = strdup(device); 366 end = strstr(raw, "p0:boot"); 367 if (end) 368 end[2] = 0; 369 370 /* open p0 (whole disk) */ 371 len = strlen(raw); 372 raw[len - 2] = 'p'; 373 raw[len - 1] = '0'; 374 fd = open(raw, O_WRONLY); 375 if (fd == -1 || fstat(fd, &stat) != 0) { 376 (void) fprintf(stderr, OPEN_FAIL, raw); 377 exit(-1); 378 } 379 if (!nowrite && 380 pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { 381 (void) fprintf(stderr, WRITE_FAIL_BOOTSEC); 382 exit(-1); 383 } 384 (void) fprintf(stdout, WRITE_MBOOT); 385 (void) close(fd); 386 } 387 388 static void 389 modify_and_write_stage1(int dev_fd) 390 { 391 if (is_floppy) { 392 stage2_first_sector = blocklist[0]; 393 /* copy bios parameter block (for fat fs) */ 394 bcopy(bpb_sect + STAGE1_BPB_OFFSET, 395 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); 396 } else if (is_bootpar) { 397 stage2_first_sector = get_start_sector(dev_fd) + blocklist[0]; 398 /* copy bios parameter block (for fat fs) and MBR */ 399 bcopy(bpb_sect + STAGE1_BPB_OFFSET, 400 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); 401 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); 402 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; 403 } else { 404 stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF; 405 /* copy MBR to stage1 in case of overwriting MBR sector */ 406 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); 407 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; 408 } 409 410 /* modify default stage1 file generated by GRUB */ 411 *((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR)) 412 = stage2_first_sector; 413 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS)) 414 = STAGE2_MEMADDR; 415 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT)) 416 = STAGE2_MEMADDR >> 4; 417 418 /* 419 * XXX the default grub distribution also: 420 * - Copy the possible MBR/extended part table 421 * - Set the boot drive of stage1 422 */ 423 424 /* write stage1/pboot to 1st sector */ 425 if (!nowrite && 426 pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { 427 (void) fprintf(stderr, WRITE_FAIL_PBOOT); 428 exit(-1); 429 } 430 431 if (is_floppy) { 432 (void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY); 433 } else { 434 (void) fprintf(stdout, WRITE_PBOOT, 435 partition, get_start_sector(dev_fd)); 436 } 437 } 438 439 #define START_BLOCK(pos) (*(ulong_t *)(pos)) 440 #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4)) 441 #define START_SEG(pos) (*(ushort_t *)((pos) + 6)) 442 443 static void 444 modify_and_write_stage2(int dev_fd) 445 { 446 int nrecord; 447 off_t offset; 448 449 if (is_floppy || is_bootpar) { 450 int i = 0; 451 uint_t partition_offset; 452 uint_t install_addr = 0x8200; 453 uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST; 454 455 stage2_first_sector = blocklist[0]; 456 457 /* figure out the second sector */ 458 if (blocklist[1] > 1) { 459 blocklist[0]++; 460 blocklist[1]--; 461 } else { 462 i += 2; 463 } 464 stage2_second_sector = blocklist[i]; 465 466 if (is_floppy) 467 partition_offset = 0; 468 else /* solaris boot partition */ 469 partition_offset = get_start_sector(dev_fd); 470 471 /* install the blocklist at the end of stage2_buffer */ 472 while (blocklist[i]) { 473 if (START_BLOCK(pos - 8) != 0 && 474 START_BLOCK(pos - 8) != blocklist[i + 2]) { 475 (void) fprintf(stderr, PCFS_FRAGMENTED); 476 exit(-1); 477 } 478 START_BLOCK(pos) = blocklist[i] + partition_offset; 479 START_SEG(pos) = (ushort_t)(install_addr >> 4); 480 NUM_BLOCK(pos) = blocklist[i + 1]; 481 install_addr += blocklist[i + 1] * SECTOR_SIZE; 482 pos -= 8; 483 i += 2; 484 } 485 486 } else { 487 /* 488 * In a solaris partition, stage2 is written to contiguous 489 * blocks. So we update the starting block only. 490 */ 491 *((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) = 492 stage2_first_sector + 1; 493 } 494 495 if (is_floppy) { 496 /* modify the config file to add (fd0) */ 497 char *config_file = stage2_buffer + STAGE2_VER_STRING; 498 while (*config_file++) 499 ; 500 strcpy(config_file, "(fd0)/boot/grub/menu.lst"); 501 } else { 502 /* force lba and set disk partition */ 503 *((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1; 504 *((long *)(stage2_buffer + STAGE2_INSTALLPART)) 505 = (partition << 16) | (slice << 8) | 0xff; 506 } 507 508 /* modification done, now do the writing */ 509 if (is_floppy || is_bootpar) { 510 /* we rewrite block 0 and 1 and that's it */ 511 if (!nowrite && 512 (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, 513 stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE || 514 pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE, 515 stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) { 516 (void) fprintf(stderr, WRITE_FAIL_STAGE2); 517 exit(-1); 518 } 519 (void) fprintf(stdout, WRITE_STAGE2_PCFS); 520 return; 521 } 522 523 /* for disk, write stage2 starting at STAGE2_BLKOFF sector */ 524 offset = STAGE2_BLKOFF; 525 526 /* write the modified first two sectors */ 527 if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE, 528 offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) { 529 (void) fprintf(stderr, WRITE_FAIL_STAGE2); 530 exit(-1); 531 } 532 533 /* write the remaining sectors */ 534 nrecord = 2; 535 offset += 2; 536 for (;;) { 537 int nread, nwrite; 538 nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE, 539 nrecord * SECTOR_SIZE); 540 if (nread > 0 && !nowrite) 541 nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, 542 offset * SECTOR_SIZE); 543 else 544 nwrite = SECTOR_SIZE; 545 if (nread < 0 || nwrite != SECTOR_SIZE) { 546 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, 547 nread, nwrite); 548 break; 549 } 550 if (nread > 0) { 551 nrecord ++; 552 offset ++; 553 } 554 if (nread < SECTOR_SIZE) 555 break; /* end of file */ 556 } 557 (void) fprintf(stdout, WRITE_STAGE2_DISK, 558 partition, nrecord, STAGE2_BLKOFF, stage2_first_sector); 559 } 560 561 static char * 562 get_raw_partition(char *device) 563 { 564 int len; 565 struct mboot *mboot; 566 static char *raw = NULL; 567 568 if (raw) 569 return (raw); 570 raw = strdup(device); 571 572 if (is_floppy) 573 return (raw); 574 575 if (is_bootpar) { 576 int i; 577 char *end = strstr(raw, "p0:boot"); 578 579 end[2] = 0; /* chop off :boot */ 580 read_boot_sect(raw); 581 mboot = (struct mboot *)boot_sect; 582 for (i = 0; i < FD_NUMPART; i++) { 583 struct ipart *part = (struct ipart *)mboot->parts + i; 584 if (part->systid == 0xbe) /* solaris boot part */ 585 break; 586 } 587 588 if (i == FD_NUMPART) { 589 (void) fprintf(stderr, BOOTPAR_NOTFOUND, device); 590 exit(-1); 591 } 592 end[1] = '1' + i; /* set partition name */ 593 return (raw); 594 } 595 596 /* For disk, remember slice and return whole fdisk partition */ 597 len = strlen(raw); 598 if (raw[len - 2] != 's' || raw[len - 1] == '2') { 599 (void) fprintf(stderr, NOT_ROOT_SLICE); 600 exit(-1); 601 } 602 slice = atoi(&raw[len - 1]); 603 604 raw[len - 2] = 's'; 605 raw[len - 1] = '2'; 606 return (raw); 607 } 608 609 #define TMP_MNTPT "/tmp/installgrub_pcfs" 610 static void 611 copy_stage2(int dev_fd, char *device) 612 { 613 FILE *mntfp; 614 int i, pcfs_fp; 615 char buf[SECTOR_SIZE]; 616 char *cp; 617 struct mnttab mp = {0}, mpref = {0}; 618 619 /* convert raw to block device name by removing the first 'r' */ 620 (void) strncpy(buf, device, sizeof (buf)); 621 buf[sizeof (buf) - 1] = 0; 622 cp = strchr(buf, 'r'); 623 if (cp == NULL) { 624 (void) fprintf(stderr, CONVERT_FAIL, device); 625 exit(-1); 626 } 627 do { 628 *cp = *(cp + 1); 629 } while (*(++cp)); 630 631 /* get the mount point, if any */ 632 mntfp = fopen("/etc/mnttab", "r"); 633 if (mntfp == NULL) { 634 (void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab"); 635 exit(-1); 636 } 637 638 mpref.mnt_special = buf; 639 if (getmntany(mntfp, &mp, &mpref) != 0) { 640 char cmd[128]; 641 642 /* not mounted, try remount */ 643 (void) mkdir(TMP_MNTPT, S_IRWXU); 644 (void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s", 645 buf, TMP_MNTPT); 646 (void) system(cmd); 647 rewind(mntfp); 648 bzero(&mp, sizeof (mp)); 649 if (getmntany(mntfp, &mp, &mpref) != 0) { 650 (void) fprintf(stderr, MOUNT_FAIL, buf); 651 exit(-1); 652 } 653 } 654 655 (void) snprintf(buf, sizeof (buf), 656 "%s/boot", mp.mnt_mountp); 657 (void) mkdir(buf, S_IRWXU); 658 (void) strcat(buf, "/grub"); 659 (void) mkdir(buf, S_IRWXU); 660 661 (void) strcat(buf, "/stage2"); 662 pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU); 663 if (pcfs_fp == -1) { 664 (void) fprintf(stderr, OPEN_FAIL_FILE, buf); 665 perror("open:"); 666 (void) umount(TMP_MNTPT); 667 exit(-1); 668 } 669 670 /* write stage2 to pcfs */ 671 for (i = 0; ; i++) { 672 int nread, nwrite; 673 nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE); 674 if (nowrite) 675 nwrite = nread; 676 else 677 nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE); 678 if (nread < 0 || nwrite != nread) { 679 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, 680 nread, nwrite); 681 break; 682 } 683 if (nread < SECTOR_SIZE) 684 break; /* end of file */ 685 } 686 (void) close(pcfs_fp); 687 (void) umount(TMP_MNTPT); 688 689 /* 690 * Now, get the blocklist from the device. 691 */ 692 bzero(blocklist, sizeof (blocklist)); 693 if (read_stage2_blocklist(dev_fd, blocklist) != 0) 694 exit(-1); 695 } 696