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 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 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 int blocklist[SECTOR_SIZE / sizeof (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 int get_start_sector(); 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, 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 int 171 get_start_sector() 172 { 173 static 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 } else { 189 if (part->systid == 0x82 || part->systid == 0xbf) 190 break; 191 } 192 } 193 194 if (i == FD_NUMPART) { 195 (void) fprintf(stderr, BOOTPAR); 196 exit(-1); 197 } 198 199 /* get confirmation for -m */ 200 if (write_mboot && !force_mboot) { 201 (void) fprintf(stdout, MBOOT_PROMPT); 202 if (getchar() != 'y') { 203 write_mboot = 0; 204 (void) fprintf(stdout, MBOOT_NOT_UPDATED); 205 } 206 } 207 208 start_sect = part->relsect; 209 if (part->bootid != 128 && write_mboot == 0) { 210 (void) fprintf(stdout, BOOTPAR_INACTIVE, i + 1); 211 } 212 213 partition = i; 214 return (start_sect); 215 } 216 217 static void 218 usage(char *progname) 219 { 220 (void) fprintf(stderr, USAGE, basename(progname)); 221 exit(-1); 222 } 223 224 static int 225 open_device(char *device) 226 { 227 int dev_fd; 228 struct stat stat; 229 char *raw_part; 230 231 is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) && 232 strncmp(device, "/dev/dsk", strlen("/dev/dsk")); 233 234 /* handle boot partition specification */ 235 if (!is_floppy && strstr(device, "p0:boot")) { 236 is_bootpar = 1; 237 } 238 239 raw_part = get_raw_partition(device); 240 241 if (nowrite) 242 dev_fd = open(raw_part, O_RDONLY); 243 else 244 dev_fd = open(raw_part, O_RDWR); 245 246 if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) { 247 (void) fprintf(stderr, OPEN_FAIL, raw_part); 248 exit(-1); 249 } 250 if (S_ISCHR(stat.st_mode) == 0) { 251 (void) fprintf(stderr, NOT_RAW_DEVICE, raw_part); 252 exit(-1); 253 } 254 255 return (dev_fd); 256 } 257 258 static void 259 read_stage1_stage2(char *stage1, char *stage2) 260 { 261 int fd; 262 263 /* read the stage1 file from filesystem */ 264 fd = open(stage1, O_RDONLY); 265 if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) { 266 (void) fprintf(stderr, READ_FAIL_STAGE1, stage1); 267 exit(-1); 268 } 269 (void) close(fd); 270 271 /* read first two blocks of stage 2 from filesystem */ 272 stage2_fd = open(stage2, O_RDONLY); 273 if (stage2_fd == -1 || 274 read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE) 275 != 2 * SECTOR_SIZE) { 276 (void) fprintf(stderr, READ_FAIL_STAGE2, stage2); 277 exit(-1); 278 } 279 /* leave the stage2 file open for later */ 280 } 281 282 static void 283 read_bpb_sect(int dev_fd) 284 { 285 if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) { 286 (void) fprintf(stderr, READ_FAIL_BPB); 287 exit(-1); 288 } 289 } 290 291 static void 292 read_boot_sect(char *device) 293 { 294 static int read_mbr = 0; 295 int i, fd; 296 char save[2]; 297 298 if (read_mbr) 299 return; 300 read_mbr = 1; 301 302 /* get the whole disk (p0) */ 303 i = strlen(device); 304 save[0] = device[i - 2]; 305 save[1] = device[i - 1]; 306 device[i - 2] = 'p'; 307 device[i - 1] = '0'; 308 309 fd = open(device, O_RDONLY); 310 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 311 (void) fprintf(stderr, READ_FAIL_MBR, device); 312 if (fd == -1) 313 perror("open"); 314 else 315 perror("read"); 316 exit(-1); 317 } 318 (void) close(fd); 319 device[i - 2] = save[0]; 320 device[i - 1] = save[1]; 321 } 322 323 static void 324 write_boot_sect(char *device) 325 { 326 int fd, len; 327 char *raw, *end; 328 struct stat stat; 329 330 /* make a copy and chop off ":boot" */ 331 raw = strdup(device); 332 end = strstr(raw, "p0:boot"); 333 if (end) 334 end[2] = 0; 335 336 /* open p0 (whole disk) */ 337 len = strlen(raw); 338 raw[len - 2] = 'p'; 339 raw[len - 1] = '0'; 340 fd = open(raw, O_WRONLY); 341 if (fd == -1 || fstat(fd, &stat) != 0) { 342 (void) fprintf(stderr, OPEN_FAIL, raw); 343 exit(-1); 344 } 345 if (!nowrite && 346 pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { 347 (void) fprintf(stderr, WRITE_FAIL_BOOTSEC); 348 exit(-1); 349 } 350 (void) fprintf(stdout, WRITE_MBOOT); 351 (void) close(fd); 352 } 353 354 static void 355 modify_and_write_stage1(int dev_fd) 356 { 357 if (is_floppy) { 358 stage2_first_sector = blocklist[0]; 359 /* copy bios parameter block (for fat fs) */ 360 bcopy(bpb_sect + STAGE1_BPB_OFFSET, 361 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); 362 } else if (is_bootpar) { 363 stage2_first_sector = get_start_sector() + blocklist[0]; 364 /* copy bios parameter block (for fat fs) and MBR */ 365 bcopy(bpb_sect + STAGE1_BPB_OFFSET, 366 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); 367 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); 368 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; 369 } else { 370 stage2_first_sector = get_start_sector() + STAGE2_BLKOFF; 371 /* copy MBR to stage1 in case of overwriting MBR sector */ 372 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); 373 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; 374 } 375 376 /* modify default stage1 file generated by GRUB */ 377 *((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR)) 378 = stage2_first_sector; 379 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS)) 380 = STAGE2_MEMADDR; 381 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT)) 382 = STAGE2_MEMADDR >> 4; 383 384 /* 385 * XXX the default grub distribution also: 386 * - Copy the possible MBR/extended part table 387 * - Set the boot drive of stage1 388 */ 389 390 /* write stage1/pboot to 1st sector */ 391 if (!nowrite && 392 pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { 393 (void) fprintf(stderr, WRITE_FAIL_PBOOT); 394 exit(-1); 395 } 396 397 if (is_floppy) { 398 (void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY); 399 } else { 400 (void) fprintf(stdout, WRITE_PBOOT, 401 partition, get_start_sector()); 402 } 403 } 404 405 #define START_BLOCK(pos) (*(ulong_t *)(pos)) 406 #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4)) 407 #define START_SEG(pos) (*(ushort_t *)((pos) + 6)) 408 409 static void 410 modify_and_write_stage2(int dev_fd) 411 { 412 int nrecord; 413 off_t offset; 414 415 if (is_floppy || is_bootpar) { 416 int i = 0; 417 uint_t partition_offset; 418 uint_t install_addr = 0x8200; 419 uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST; 420 421 stage2_first_sector = blocklist[0]; 422 423 /* figure out the second sector */ 424 if (blocklist[1] > 1) { 425 blocklist[0]++; 426 blocklist[1]--; 427 } else { 428 i += 2; 429 } 430 stage2_second_sector = blocklist[i]; 431 432 if (is_floppy) 433 partition_offset = 0; 434 else /* solaris boot partition */ 435 partition_offset = get_start_sector(); 436 437 /* install the blocklist at the end of stage2_buffer */ 438 while (blocklist[i]) { 439 if (START_BLOCK(pos - 8) != 0 && 440 START_BLOCK(pos - 8) != blocklist[i + 2]) { 441 (void) fprintf(stderr, PCFS_FRAGMENTED); 442 exit(-1); 443 } 444 START_BLOCK(pos) = blocklist[i] + partition_offset; 445 START_SEG(pos) = (ushort_t)(install_addr >> 4); 446 NUM_BLOCK(pos) = blocklist[i + 1]; 447 install_addr += blocklist[i + 1] * SECTOR_SIZE; 448 pos -= 8; 449 i += 2; 450 } 451 452 } else { 453 /* 454 * In a solaris partition, stage2 is written to contiguous 455 * blocks. So we update the starting block only. 456 */ 457 *((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) = 458 stage2_first_sector + 1; 459 } 460 461 if (is_floppy) { 462 /* modify the config file to add (fd0) */ 463 char *config_file = stage2_buffer + STAGE2_VER_STRING; 464 while (*config_file++) 465 ; 466 strcpy(config_file, "(fd0)/boot/grub/menu.lst"); 467 } else { 468 /* force lba and set disk partition */ 469 *((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1; 470 *((long *)(stage2_buffer + STAGE2_INSTALLPART)) 471 = (partition << 16) | (slice << 8) | 0xff; 472 } 473 474 /* modification done, now do the writing */ 475 if (is_floppy || is_bootpar) { 476 /* we rewrite block 0 and 1 and that's it */ 477 if (!nowrite && 478 (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, 479 stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE || 480 pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE, 481 stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) { 482 (void) fprintf(stderr, WRITE_FAIL_STAGE2); 483 exit(-1); 484 } 485 (void) fprintf(stdout, WRITE_STAGE2_PCFS); 486 return; 487 } 488 489 /* for disk, write stage2 starting at STAGE2_BLKOFF sector */ 490 offset = STAGE2_BLKOFF; 491 492 /* write the modified first two sectors */ 493 if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE, 494 offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) { 495 (void) fprintf(stderr, WRITE_FAIL_STAGE2); 496 exit(-1); 497 } 498 499 /* write the remaining sectors */ 500 nrecord = 2; 501 offset += 2; 502 for (;;) { 503 int nread, nwrite; 504 nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE, 505 nrecord * SECTOR_SIZE); 506 if (nread > 0 && !nowrite) 507 nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, 508 offset * SECTOR_SIZE); 509 else 510 nwrite = SECTOR_SIZE; 511 if (nread < 0 || nwrite != SECTOR_SIZE) { 512 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, 513 nread, nwrite); 514 break; 515 } 516 nrecord ++; 517 offset ++; 518 if (nread < SECTOR_SIZE) 519 break; /* end of file */ 520 } 521 (void) fprintf(stdout, WRITE_STAGE2_DISK, 522 partition, nrecord, STAGE2_BLKOFF, stage2_first_sector); 523 } 524 525 static char * 526 get_raw_partition(char *device) 527 { 528 int len; 529 struct mboot *mboot; 530 static char *raw = NULL; 531 532 if (raw) 533 return (raw); 534 raw = strdup(device); 535 536 if (is_floppy) 537 return (raw); 538 539 if (is_bootpar) { 540 int i; 541 char *end = strstr(raw, "p0:boot"); 542 543 end[2] = 0; /* chop off :boot */ 544 read_boot_sect(raw); 545 mboot = (struct mboot *)boot_sect; 546 for (i = 0; i < FD_NUMPART; i++) { 547 struct ipart *part = (struct ipart *)mboot->parts + i; 548 if (part->systid == 0xbe) /* solaris boot part */ 549 break; 550 } 551 552 if (i == FD_NUMPART) { 553 (void) fprintf(stderr, BOOTPAR_NOTFOUND, device); 554 exit(-1); 555 } 556 end[1] = '1' + i; /* set partition name */ 557 return (raw); 558 } 559 560 /* For disk, remember slice and return whole fdisk partition */ 561 len = strlen(raw); 562 if (raw[len - 2] != 's' || raw[len - 1] == '2') { 563 (void) fprintf(stderr, NOT_ROOT_SLICE); 564 exit(-1); 565 } 566 slice = atoi(&raw[len - 1]); 567 568 raw[len - 2] = 's'; 569 raw[len - 1] = '2'; 570 return (raw); 571 } 572 573 #define TMP_MNTPT "/tmp/installgrub_pcfs" 574 static void 575 copy_stage2(int dev_fd, char *device) 576 { 577 FILE *mntfp; 578 int i, pcfs_fp; 579 char buf[SECTOR_SIZE]; 580 char *cp; 581 struct mnttab mp = {0}, mpref = {0}; 582 583 /* convert raw to block device name by removing the first 'r' */ 584 (void) strncpy(buf, device, sizeof (buf)); 585 buf[sizeof (buf) - 1] = 0; 586 cp = strchr(buf, 'r'); 587 if (cp == NULL) { 588 (void) fprintf(stderr, CONVERT_FAIL, device); 589 exit(-1); 590 } 591 do { 592 *cp = *(cp + 1); 593 } while (*(++cp)); 594 595 /* get the mount point, if any */ 596 mntfp = fopen("/etc/mnttab", "r"); 597 if (mntfp == NULL) { 598 (void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab"); 599 exit(-1); 600 } 601 602 mpref.mnt_special = buf; 603 if (getmntany(mntfp, &mp, &mpref) != 0) { 604 char cmd[128]; 605 606 /* not mounted, try remount */ 607 (void) mkdir(TMP_MNTPT, S_IRWXU); 608 (void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s", 609 buf, TMP_MNTPT); 610 (void) system(cmd); 611 rewind(mntfp); 612 bzero(&mp, sizeof (mp)); 613 if (getmntany(mntfp, &mp, &mpref) != 0) { 614 (void) fprintf(stderr, MOUNT_FAIL, buf); 615 exit(-1); 616 } 617 } 618 619 (void) snprintf(buf, sizeof (buf), 620 "%s/boot", mp.mnt_mountp); 621 (void) mkdir(buf, S_IRWXU); 622 (void) strcat(buf, "/grub"); 623 (void) mkdir(buf, S_IRWXU); 624 625 (void) strcat(buf, "/stage2"); 626 pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU); 627 if (pcfs_fp == -1) { 628 (void) fprintf(stderr, OPEN_FAIL_FILE, buf); 629 perror("open:"); 630 (void) umount(TMP_MNTPT); 631 exit(-1); 632 } 633 634 /* write stage2 to pcfs */ 635 for (i = 0; ; i++) { 636 int nread, nwrite; 637 nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE); 638 if (nowrite) 639 nwrite = nread; 640 else 641 nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE); 642 if (nread < 0 || nwrite != nread) { 643 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, 644 nread, nwrite); 645 break; 646 } 647 if (nread < SECTOR_SIZE) 648 break; /* end of file */ 649 } 650 (void) close(pcfs_fp); 651 (void) umount(TMP_MNTPT); 652 653 /* 654 * Now, get the blocklist from the device. 655 */ 656 bzero(blocklist, sizeof (blocklist)); 657 if (read_stage2_blocklist(dev_fd, blocklist) != 0) 658 exit(-1); 659 } 660