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