1 /* $NetBSD: installboot.c,v 1.6 1997/01/08 12:55:41 leo Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Waldi Ravens 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Waldi Ravens. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/sysctl.h> 36 #include <sys/ioctl.h> 37 #include <unistd.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <stdio.h> 41 #include <paths.h> 42 #include <fcntl.h> 43 #include <errno.h> 44 #include <err.h> 45 46 /* 47 * Small reminder for myself ;-) 48 */ 49 #if NetBSD != 199611 50 #error New NetBSD version! Update OS_LIST in installboot.h 51 #endif 52 53 #define DKTYPENAMES 54 #include <sys/disklabel.h> 55 #include <machine/ahdilabel.h> 56 57 #include "installboot.h" 58 59 static void usage __P((void)); 60 static void oscheck __P((void)); 61 static u_int abcksum __P((void *)); 62 static void setNVpref __P((void)); 63 static void setIDEpar __P((u_int8_t *, size_t)); 64 static void mkahdiboot __P((struct ahdi_root *, char *, 65 char *, daddr_t)); 66 static void mkbootblock __P((struct bootblock *, char *, 67 char *, struct disklabel *, u_int)); 68 static void install_fd __P((char *, struct disklabel *)); 69 static void install_sd __P((char *, struct disklabel *)); 70 static void install_wd __P((char *, struct disklabel *)); 71 72 static struct bootblock bootarea; 73 static struct ahdi_root ahdiboot; 74 static const char mdecpath[] = PATH_MDEC; 75 static int nowrite = 0; 76 static int verbose = 0; 77 static int trackpercyl = 0; 78 static int secpertrack = 0; 79 80 static void 81 usage () 82 { 83 fprintf(stderr, 84 "usage: installboot [options] device\n" 85 "where options are:\n" 86 "\t-N do not actually write anything on the disk\n" 87 "\t-t number of tracks per cylinder (IDE disk)\n" 88 "\t-u number of sectors per track (IDE disk)\n" 89 "\t-v verbose mode\n"); 90 exit(EXIT_FAILURE); 91 } 92 93 int 94 main (argc, argv) 95 int argc; 96 char *argv[]; 97 { 98 struct disklabel dl; 99 char *dn; 100 int fd, c; 101 102 /* check OS type, release and revision */ 103 oscheck(); 104 105 /* parse options */ 106 while ((c = getopt(argc, argv, "Nt:u:v")) != -1) { 107 switch (c) { 108 case 'N': 109 nowrite = 1; 110 break; 111 case 't': 112 trackpercyl = atoi(optarg); 113 break; 114 case 'u': 115 secpertrack = atoi(optarg); 116 break; 117 case 'v': 118 verbose = 1; 119 break; 120 default: 121 usage(); 122 } 123 } 124 argv += optind; 125 argc -= optind; 126 if (argc != 1) 127 usage(); 128 129 /* get disk label */ 130 dn = alloca(sizeof(_PATH_DEV) + strlen(argv[0]) + 8); 131 if (!strchr(argv[0], '/')) { 132 sprintf(dn, "%sr%s%c", _PATH_DEV, argv[0], RAW_PART + 'a'); 133 fd = open(dn, O_RDONLY); 134 if (fd < 0 && errno == ENOENT) { 135 sprintf(dn, "%sr%s", _PATH_DEV, argv[0]); 136 fd = open(dn, O_RDONLY); 137 } 138 } else { 139 sprintf(dn, "%s", argv[0]); 140 fd = open(dn, O_RDONLY); 141 } 142 if (fd < 0) 143 err(EXIT_FAILURE, "%s", dn); 144 if (ioctl(fd, DIOCGDINFO, &dl)) 145 err(EXIT_FAILURE, "%s: DIOCGDINFO", dn); 146 if (close(fd)) 147 err(EXIT_FAILURE, "%s", dn); 148 149 switch (dl.d_type) { 150 case DTYPE_FLOPPY: 151 install_fd(dn, &dl); 152 break; 153 case DTYPE_ST506: 154 case DTYPE_ESDI: 155 install_wd(dn, &dl); 156 setNVpref(); 157 break; 158 case DTYPE_SCSI: 159 install_sd(dn, &dl); 160 setNVpref(); 161 break; 162 default: 163 errx(EXIT_FAILURE, 164 "%s: %s: Device type not supported.", 165 dn, dktypenames[dl.d_type]); 166 } 167 168 return(EXIT_SUCCESS); 169 } 170 171 static char * 172 lststr (lst, str, bra) 173 char *lst, *str, *bra; 174 { 175 char *p; 176 177 while ((p = strchr(lst, bra[0])) != NULL) { 178 lst = strchr(++p, bra[1]); 179 if (strncmp(str, p, lst - p)) 180 continue; 181 if ((p = strchr(lst, bra[0]))) 182 *p = 0; 183 return(++lst); 184 } 185 return(NULL); 186 } 187 188 static void 189 oscheck () 190 { 191 /* ideally, this would be a nested function... */ 192 static char *lststr __P((char *, char *, char *)); 193 static const char os_list[] = OS_LIST; 194 195 char *list, *type, *rel, *rev; 196 int mib[2], rvi; 197 size_t len; 198 199 list = alloca(sizeof(os_list)); 200 strcpy(list, os_list); 201 202 mib[0] = CTL_KERN; 203 mib[1] = KERN_OSTYPE; 204 sysctl(mib, 2, NULL, &len, NULL, 0); 205 type = alloca(len); 206 sysctl(mib, 2, type, &len, NULL, 0); 207 if ((list = lststr(list, type, BRA_TYPE)) == NULL) 208 errx(EXIT_FAILURE, 209 "%s: OS type not supported", type); 210 211 mib[0] = CTL_KERN; 212 mib[1] = KERN_OSRELEASE; 213 sysctl(mib, 2, NULL, &len, NULL, 0); 214 rel = alloca(len); 215 sysctl(mib, 2, rel, &len, NULL, 0); 216 if ((list = lststr(list, rel, BRA_RELEASE)) == NULL) 217 errx(EXIT_FAILURE, 218 "%s %s: OS release not supported", type, rel); 219 220 mib[0] = CTL_KERN; 221 mib[1] = KERN_OSREV; 222 len = sizeof(rvi); 223 sysctl(mib, 2, &rvi, &len, NULL, 0); 224 rev = alloca(3 * sizeof(rvi)); 225 sprintf(rev, "%u", rvi); 226 if ((list = lststr(list, rev, BRA_REVISION)) == NULL) 227 errx(EXIT_FAILURE, 228 "%s %s %s: OS revision not supported", type, rel, rev); 229 } 230 231 static void 232 install_fd (devnm, label) 233 char *devnm; 234 struct disklabel *label; 235 { 236 char *xxboot, *bootxx; 237 struct partition *rootpart; 238 239 if (label->d_secsize != 512) 240 errx(EXIT_FAILURE, 241 "%s: %u: Block size not supported.", devnm, 242 label->d_secsize); 243 if (label->d_ntracks != 2) 244 errx(EXIT_FAILURE, 245 "%s: Single sided floppy not supported.", devnm); 246 247 xxboot = alloca(strlen(mdecpath) + 8); 248 sprintf(xxboot, "%sfdboot", mdecpath); 249 bootxx = alloca(strlen(mdecpath) + 8); 250 sprintf(bootxx, "%sbootxx", mdecpath); 251 252 /* first used partition (a, b or c) */ /* XXX */ 253 for (rootpart = label->d_partitions; ; ++rootpart) { 254 if (rootpart->p_size) 255 break; 256 } 257 if (rootpart != label->d_partitions) { /* XXX */ 258 *(label->d_partitions) = *rootpart; 259 memset(rootpart, 0, sizeof(*rootpart)); 260 } 261 label->d_partitions->p_fstype = FS_BSDFFS; /* XXX */ 262 label->d_npartitions = 1; 263 label->d_checksum = 0; 264 label->d_checksum = dkcksum(label); 265 266 trackpercyl = secpertrack = 0; 267 mkbootblock(&bootarea, xxboot, bootxx, label, 0); 268 269 if (!nowrite) { 270 int fd; 271 if ((fd = open(devnm, O_WRONLY)) < 0) 272 err(EXIT_FAILURE, "%s", devnm); 273 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 274 err(EXIT_FAILURE, "%s", devnm); 275 if (close(fd)) 276 err(EXIT_FAILURE, "%s", devnm); 277 if (verbose) 278 printf("Boot block installed on %s\n", devnm); 279 } 280 } 281 282 static void 283 install_sd (devnm, label) 284 char *devnm; 285 struct disklabel *label; 286 { 287 char *xxb00t, *xxboot, *bootxx; 288 struct disklabel rawlabel; 289 daddr_t bbsec; 290 u_int magic; 291 292 if (label->d_partitions[0].p_size == 0) 293 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 294 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 295 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 296 devnm, fstypenames[label->d_partitions[0].p_fstype]); 297 298 bbsec = readdisklabel(devnm, &rawlabel); 299 if (bbsec == NO_BOOT_BLOCK) 300 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 301 if (memcmp(label, &rawlabel, sizeof(*label))) 302 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 303 304 if (bbsec) { 305 xxb00t = alloca(strlen(mdecpath) + 14); 306 sprintf(xxb00t, "%ssdb00t.ahdi", mdecpath); 307 xxboot = alloca(strlen(mdecpath) + 14); 308 sprintf(xxboot, "%sxxboot.ahdi", mdecpath); 309 magic = AHDIMAGIC; 310 } else { 311 xxb00t = NULL; 312 xxboot = alloca(strlen(mdecpath) + 8); 313 sprintf(xxboot, "%ssdboot", mdecpath); 314 magic = NBDAMAGIC; 315 } 316 bootxx = alloca(strlen(mdecpath) + 8); 317 sprintf(bootxx, "%sbootxx", mdecpath); 318 319 trackpercyl = secpertrack = 0; 320 if (xxb00t) 321 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 322 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 323 324 if (!nowrite) { 325 off_t bbo = bbsec * AHDI_BSIZE; 326 int fd; 327 328 if ((fd = open(devnm, O_WRONLY)) < 0) 329 err(EXIT_FAILURE, "%s", devnm); 330 if (lseek(fd, bbo, SEEK_SET) != bbo) 331 err(EXIT_FAILURE, "%s", devnm); 332 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 333 err(EXIT_FAILURE, "%s", devnm); 334 if (verbose) 335 printf("Boot block installed on %s (%u)\n", devnm, 336 bbsec); 337 if (xxb00t) { 338 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 339 err(EXIT_FAILURE, "%s", devnm); 340 if (write(fd, &ahdiboot, sizeof(ahdiboot)) != sizeof(ahdiboot)) 341 err(EXIT_FAILURE, "%s", devnm); 342 if (verbose) 343 printf("AHDI root installed on %s (0)\n", 344 devnm); 345 } 346 if (close(fd)) 347 err(EXIT_FAILURE, "%s", devnm); 348 } 349 } 350 351 static void 352 install_wd (devnm, label) 353 char *devnm; 354 struct disklabel *label; 355 { 356 char *xxb00t, *xxboot, *bootxx; 357 struct disklabel rawlabel; 358 daddr_t bbsec; 359 u_int magic; 360 361 if (label->d_partitions[0].p_size == 0) 362 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 363 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 364 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 365 devnm, fstypenames[label->d_partitions[0].p_fstype]); 366 367 bbsec = readdisklabel(devnm, &rawlabel); 368 if (bbsec == NO_BOOT_BLOCK) 369 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 370 if (memcmp(label, &rawlabel, sizeof(*label))) 371 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 372 373 if (bbsec) { 374 xxb00t = alloca(strlen(mdecpath) + 14); 375 sprintf(xxb00t, "%swdb00t.ahdi", mdecpath); 376 xxboot = alloca(strlen(mdecpath) + 14); 377 sprintf(xxboot, "%sxxboot.ahdi", mdecpath); 378 magic = AHDIMAGIC; 379 } else { 380 xxb00t = NULL; 381 xxboot = alloca(strlen(mdecpath) + 8); 382 sprintf(xxboot, "%swdboot", mdecpath); 383 magic = NBDAMAGIC; 384 } 385 bootxx = alloca(strlen(mdecpath) + 8); 386 sprintf(bootxx, "%sbootxx", mdecpath); 387 388 if (xxb00t) 389 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 390 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 391 392 if (!nowrite) { 393 int fd; 394 off_t bbo; 395 396 bbo = bbsec * AHDI_BSIZE; 397 if ((fd = open(devnm, O_WRONLY)) < 0) 398 err(EXIT_FAILURE, "%s", devnm); 399 if (lseek(fd, bbo, SEEK_SET) != bbo) 400 err(EXIT_FAILURE, "%s", devnm); 401 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 402 err(EXIT_FAILURE, "%s", devnm); 403 if (verbose) 404 printf("Boot block installed on %s (%u)\n", devnm, 405 bbsec); 406 if (xxb00t) { 407 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 408 err(EXIT_FAILURE, "%s", devnm); 409 if (write(fd, &ahdiboot, sizeof(ahdiboot)) 410 != sizeof(ahdiboot)) 411 err(EXIT_FAILURE, "%s", devnm); 412 if (verbose) 413 printf("AHDI root installed on %s (0)\n", 414 devnm); 415 } 416 if (close(fd)) 417 err(EXIT_FAILURE, "%s", devnm); 418 } 419 } 420 421 static void 422 mkahdiboot (newroot, xxb00t, devnm, bbsec) 423 struct ahdi_root *newroot; 424 char *xxb00t, 425 *devnm; 426 daddr_t bbsec; 427 { 428 struct ahdi_root tmproot; 429 struct ahdi_part *pd; 430 int fd; 431 432 /* read prototype root-sector */ 433 if ((fd = open(xxb00t, O_RDONLY)) < 0) 434 err(EXIT_FAILURE, "%s", xxb00t); 435 if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot)) 436 err(EXIT_FAILURE, "%s", xxb00t); 437 if (close(fd)) 438 err(EXIT_FAILURE, "%s", xxb00t); 439 440 /* set tracks/cylinder and sectors/track */ 441 setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill)); 442 443 /* read current root-sector */ 444 if ((fd = open(devnm, O_RDONLY)) < 0) 445 err(EXIT_FAILURE, "%s", devnm); 446 if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot)) 447 err(EXIT_FAILURE, "%s", devnm); 448 if (close(fd)) 449 err(EXIT_FAILURE, "%s", devnm); 450 451 /* set bootflags */ 452 for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) { 453 if (pd->ap_st == bbsec) { 454 pd->ap_flg = 0x21; /* bootable, pref = NetBSD */ 455 goto gotit; 456 } 457 } 458 errx(EXIT_FAILURE, 459 "%s: NetBSD boot block not on primary AHDI partition.", devnm); 460 461 gotit: /* copy code from prototype and set new checksum */ 462 memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill)); 463 newroot->ar_checksum = 0; 464 newroot->ar_checksum = 0x1234 - abcksum(newroot); 465 466 if (verbose) 467 printf("AHDI boot loader: %s\n", xxb00t); 468 } 469 470 static void 471 mkbootblock (bb, xxb, bxx, label, magic) 472 struct bootblock *bb; 473 char *xxb, 474 *bxx; 475 u_int magic; 476 struct disklabel *label; 477 { 478 int fd; 479 480 memset(bb, 0, sizeof(*bb)); 481 482 /* set boot block magic */ 483 bb->bb_magic = magic; 484 485 /* set disk pack label */ 486 BBSETLABEL(bb, label); 487 488 /* set second-stage boot loader */ 489 if ((fd = open(bxx, O_RDONLY)) < 0) 490 err(EXIT_FAILURE, "%s", bxx); 491 if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx)) 492 != sizeof(bb->bb_bootxx)) 493 err(EXIT_FAILURE, "%s", bxx); 494 if (close(fd)) 495 err(EXIT_FAILURE, "%s", bxx); 496 497 /* set first-stage bootloader */ 498 if ((fd = open(xxb, O_RDONLY)) < 0) 499 err(EXIT_FAILURE, "%s", xxb); 500 if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot)) 501 != sizeof(bb->bb_xxboot)) 502 err(EXIT_FAILURE, "%s", xxb); 503 if (close(fd)) 504 err(EXIT_FAILURE, "%s", xxb); 505 506 /* set tracks/cylinder and sectors/track */ 507 setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot)); 508 509 /* set AHDI checksum */ 510 *((u_int16_t *)bb->bb_xxboot + 255) = 0; 511 *((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot); 512 513 if (verbose) { 514 printf("Primary boot loader: %s\n", xxb); 515 printf("Secondary boot loader: %s\n", bxx); 516 } 517 } 518 519 static void 520 setIDEpar (start, size) 521 u_int8_t *start; 522 size_t size; 523 { 524 static const u_int8_t mark[] = { 'N', 'e', 't', 'B', 'S', 'D' }; 525 526 if ((u_int)trackpercyl > 255) 527 errx(EXIT_FAILURE, 528 "%d: Illegal tracks/cylinder value (1..255)", trackpercyl); 529 if ((u_int)secpertrack > 255) 530 errx(EXIT_FAILURE, 531 "%d: Illegal sectors/track value (1..255)", secpertrack); 532 533 if (trackpercyl || secpertrack) { 534 u_int8_t *p; 535 536 if (!trackpercyl) 537 errx(EXIT_FAILURE, "Need tracks/cylinder too."); 538 if (!secpertrack) 539 errx(EXIT_FAILURE, "Need sectors/track too."); 540 541 start += 2; 542 size -= sizeof(mark) + 2; 543 for (p = start + size; p >= start; --p) { 544 if (*p != *mark) 545 continue; 546 if (!memcmp(p, mark, sizeof(mark))) 547 break; 548 } 549 if (p < start) 550 errx(EXIT_FAILURE, 551 "Malformatted xxboot prototype."); 552 553 *--p = secpertrack; 554 *--p = trackpercyl; 555 556 if (verbose) { 557 printf("sectors/track : %d\n", secpertrack); 558 printf("tracks/cylinder: %d\n", trackpercyl); 559 } 560 } 561 } 562 563 static void 564 setNVpref () 565 { 566 static const u_char bootpref = BOOTPREF_NETBSD; 567 static const char nvrdev[] = PATH_NVRAM; 568 569 if (!nowrite) { 570 int fd; 571 572 if ((fd = open(nvrdev, O_RDWR)) < 0) 573 err(EXIT_FAILURE, "%s", nvrdev); 574 if (lseek(fd, (off_t)1, SEEK_SET) != 1) 575 err(EXIT_FAILURE, "%s", nvrdev); 576 if (write(fd, &bootpref, (size_t)1) != 1) 577 err(EXIT_FAILURE, "%s", nvrdev); 578 if (close(fd)) 579 err(EXIT_FAILURE, "%s", nvrdev); 580 if (verbose) 581 printf("Boot preference set to NetBSD.\n"); 582 } 583 } 584 585 static u_int 586 abcksum (bs) 587 void *bs; 588 { 589 u_int16_t sum = 0, 590 *st = (u_int16_t *)bs, 591 *end = (u_int16_t *)bs + 256; 592 593 while (st < end) 594 sum += *st++; 595 return(sum); 596 } 597