1 /* $NetBSD: installboot.c,v 1.4 1996/06/29 20:51:58 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 != 199606 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 install_wd(dn, &dl); 155 setNVpref(); 156 break; 157 case DTYPE_SCSI: 158 install_sd(dn, &dl); 159 setNVpref(); 160 break; 161 default: 162 errx(EXIT_FAILURE, 163 "%s: %s: Device type not supported.", 164 dn, dktypenames[dl.d_type]); 165 } 166 167 return(EXIT_SUCCESS); 168 } 169 170 static char * 171 lststr (lst, str, bra) 172 char *lst, *str, *bra; 173 { 174 char *p; 175 176 while ((p = strchr(lst, bra[0])) != NULL) { 177 lst = strchr(++p, bra[1]); 178 if (strncmp(str, p, lst - p)) 179 continue; 180 if ((p = strchr(lst, bra[0]))) 181 *p = 0; 182 return(++lst); 183 } 184 return(NULL); 185 } 186 187 static void 188 oscheck () 189 { 190 /* ideally, this would be a nested function... */ 191 static char *lststr __P((char *, char *, char *)); 192 static const char os_list[] = OS_LIST; 193 194 char *list, *type, *rel, *rev; 195 int mib[2], rvi; 196 size_t len; 197 198 list = alloca(sizeof(os_list)); 199 strcpy(list, os_list); 200 201 mib[0] = CTL_KERN; 202 mib[1] = KERN_OSTYPE; 203 sysctl(mib, 2, NULL, &len, NULL, 0); 204 type = alloca(len); 205 sysctl(mib, 2, type, &len, NULL, 0); 206 if ((list = lststr(list, type, BRA_TYPE)) == NULL) 207 errx(EXIT_FAILURE, 208 "%s: OS type not supported", type); 209 210 mib[0] = CTL_KERN; 211 mib[1] = KERN_OSRELEASE; 212 sysctl(mib, 2, NULL, &len, NULL, 0); 213 rel = alloca(len); 214 sysctl(mib, 2, rel, &len, NULL, 0); 215 if ((list = lststr(list, rel, BRA_RELEASE)) == NULL) 216 errx(EXIT_FAILURE, 217 "%s %s: OS release not supported", type, rel); 218 219 mib[0] = CTL_KERN; 220 mib[1] = KERN_OSREV; 221 len = sizeof(rvi); 222 sysctl(mib, 2, &rvi, &len, NULL, 0); 223 rev = alloca(3 * sizeof(rvi)); 224 sprintf(rev, "%u", rvi); 225 if ((list = lststr(list, rev, BRA_REVISION)) == NULL) 226 errx(EXIT_FAILURE, 227 "%s %s %s: OS revision not supported", type, rel, rev); 228 } 229 230 static void 231 install_fd (devnm, label) 232 char *devnm; 233 struct disklabel *label; 234 { 235 char *xxboot, *bootxx; 236 struct partition *rootpart; 237 238 if (label->d_secsize != 512) 239 errx(EXIT_FAILURE, 240 "%s: %u: Block size not supported.", devnm, 241 label->d_secsize); 242 if (label->d_ntracks != 2) 243 errx(EXIT_FAILURE, 244 "%s: Single sided floppy not supported.", devnm); 245 246 xxboot = alloca(strlen(mdecpath) + 8); 247 sprintf(xxboot, "%sfdboot", mdecpath); 248 bootxx = alloca(strlen(mdecpath) + 8); 249 sprintf(bootxx, "%sbootxx", mdecpath); 250 251 /* first used partition (a, b or c) */ /* XXX */ 252 for (rootpart = label->d_partitions; ; ++rootpart) { 253 if (rootpart->p_size) 254 break; 255 } 256 if (rootpart != label->d_partitions) { /* XXX */ 257 *(label->d_partitions) = *rootpart; 258 memset(rootpart, 0, sizeof(*rootpart)); 259 } 260 label->d_partitions->p_fstype = FS_BSDFFS; /* XXX */ 261 label->d_npartitions = 1; 262 label->d_checksum = 0; 263 label->d_checksum = dkcksum(label); 264 265 trackpercyl = secpertrack = 0; 266 mkbootblock(&bootarea, xxboot, bootxx, label, 0); 267 268 if (!nowrite) { 269 int fd; 270 if ((fd = open(devnm, O_WRONLY)) < 0) 271 err(EXIT_FAILURE, "%s", devnm); 272 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 273 err(EXIT_FAILURE, "%s", devnm); 274 if (close(fd)) 275 err(EXIT_FAILURE, "%s", devnm); 276 if (verbose) 277 printf("Boot block installed on %s\n", devnm); 278 } 279 } 280 281 static void 282 install_sd (devnm, label) 283 char *devnm; 284 struct disklabel *label; 285 { 286 char *xxb00t, *xxboot, *bootxx; 287 struct disklabel rawlabel; 288 daddr_t bbsec; 289 u_int magic; 290 291 if (label->d_partitions[0].p_size == 0) 292 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 293 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 294 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 295 devnm, fstypenames[label->d_partitions[0].p_fstype]); 296 297 bbsec = readdisklabel(devnm, &rawlabel); 298 if (bbsec == NO_BOOT_BLOCK) 299 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 300 if (memcmp(label, &rawlabel, sizeof(*label))) 301 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 302 303 if (bbsec) { 304 xxb00t = alloca(strlen(mdecpath) + 14); 305 sprintf(xxb00t, "%ssdb00t.ahdi", mdecpath); 306 xxboot = alloca(strlen(mdecpath) + 14); 307 sprintf(xxboot, "%sxxboot.ahdi", mdecpath); 308 magic = AHDIMAGIC; 309 } else { 310 xxb00t = NULL; 311 xxboot = alloca(strlen(mdecpath) + 8); 312 sprintf(xxboot, "%ssdboot", mdecpath); 313 magic = NBDAMAGIC; 314 } 315 bootxx = alloca(strlen(mdecpath) + 8); 316 sprintf(bootxx, "%sbootxx", mdecpath); 317 318 trackpercyl = secpertrack = 0; 319 if (xxb00t) 320 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 321 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 322 323 if (!nowrite) { 324 off_t bbo = bbsec * AHDI_BSIZE; 325 int fd; 326 327 if ((fd = open(devnm, O_WRONLY)) < 0) 328 err(EXIT_FAILURE, "%s", devnm); 329 if (lseek(fd, bbo, SEEK_SET) != bbo) 330 err(EXIT_FAILURE, "%s", devnm); 331 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 332 err(EXIT_FAILURE, "%s", devnm); 333 if (verbose) 334 printf("Boot block installed on %s (%u)\n", devnm, 335 bbsec); 336 if (xxb00t) { 337 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 338 err(EXIT_FAILURE, "%s", devnm); 339 if (write(fd, &ahdiboot, sizeof(ahdiboot)) != sizeof(ahdiboot)) 340 err(EXIT_FAILURE, "%s", devnm); 341 if (verbose) 342 printf("AHDI root installed on %s (0)\n", 343 devnm); 344 } 345 if (close(fd)) 346 err(EXIT_FAILURE, "%s", devnm); 347 } 348 } 349 350 static void 351 install_wd (devnm, label) 352 char *devnm; 353 struct disklabel *label; 354 { 355 char *xxb00t, *xxboot, *bootxx; 356 struct disklabel rawlabel; 357 daddr_t bbsec; 358 u_int magic; 359 360 if (label->d_partitions[0].p_size == 0) 361 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 362 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 363 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 364 devnm, fstypenames[label->d_partitions[0].p_fstype]); 365 366 bbsec = readdisklabel(devnm, &rawlabel); 367 if (bbsec == NO_BOOT_BLOCK) 368 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 369 if (memcmp(label, &rawlabel, sizeof(*label))) 370 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 371 372 if (bbsec) { 373 xxb00t = alloca(strlen(mdecpath) + 14); 374 sprintf(xxb00t, "%swdb00t.ahdi", mdecpath); 375 xxboot = alloca(strlen(mdecpath) + 14); 376 sprintf(xxboot, "%sxxboot.ahdi", mdecpath); 377 magic = AHDIMAGIC; 378 } else { 379 xxb00t = NULL; 380 xxboot = alloca(strlen(mdecpath) + 8); 381 sprintf(xxboot, "%swdboot", mdecpath); 382 magic = NBDAMAGIC; 383 } 384 bootxx = alloca(strlen(mdecpath) + 8); 385 sprintf(bootxx, "%sbootxx", mdecpath); 386 387 if (xxb00t) 388 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 389 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 390 391 if (!nowrite) { 392 int fd; 393 off_t bbo; 394 395 bbo = bbsec * AHDI_BSIZE; 396 if ((fd = open(devnm, O_WRONLY)) < 0) 397 err(EXIT_FAILURE, "%s", devnm); 398 if (lseek(fd, bbo, SEEK_SET) != bbo) 399 err(EXIT_FAILURE, "%s", devnm); 400 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 401 err(EXIT_FAILURE, "%s", devnm); 402 if (verbose) 403 printf("Boot block installed on %s (%u)\n", devnm, 404 bbsec); 405 if (xxb00t) { 406 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 407 err(EXIT_FAILURE, "%s", devnm); 408 if (write(fd, &ahdiboot, sizeof(ahdiboot)) 409 != sizeof(ahdiboot)) 410 err(EXIT_FAILURE, "%s", devnm); 411 if (verbose) 412 printf("AHDI root installed on %s (0)\n", 413 devnm); 414 } 415 if (close(fd)) 416 err(EXIT_FAILURE, "%s", devnm); 417 } 418 } 419 420 static void 421 mkahdiboot (newroot, xxb00t, devnm, bbsec) 422 struct ahdi_root *newroot; 423 char *xxb00t, 424 *devnm; 425 daddr_t bbsec; 426 { 427 struct ahdi_root tmproot; 428 struct ahdi_part *pd; 429 int fd; 430 431 /* read prototype root-sector */ 432 if ((fd = open(xxb00t, O_RDONLY)) < 0) 433 err(EXIT_FAILURE, "%s", xxb00t); 434 if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot)) 435 err(EXIT_FAILURE, "%s", xxb00t); 436 if (close(fd)) 437 err(EXIT_FAILURE, "%s", xxb00t); 438 439 /* set tracks/cylinder and sectors/track */ 440 setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill)); 441 442 /* read current root-sector */ 443 if ((fd = open(devnm, O_RDONLY)) < 0) 444 err(EXIT_FAILURE, "%s", devnm); 445 if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot)) 446 err(EXIT_FAILURE, "%s", devnm); 447 if (close(fd)) 448 err(EXIT_FAILURE, "%s", devnm); 449 450 /* set bootflags */ 451 for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) { 452 if (pd->ap_st == bbsec) { 453 pd->ap_flg = 0x21; /* bootable, pref = NetBSD */ 454 goto gotit; 455 } 456 } 457 errx(EXIT_FAILURE, 458 "%s: NetBSD boot block not on primary AHDI partition.", devnm); 459 460 gotit: /* copy code from prototype and set new checksum */ 461 memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill)); 462 newroot->ar_checksum = 0; 463 newroot->ar_checksum = 0x1234 - abcksum(newroot); 464 465 if (verbose) 466 printf("AHDI boot loader: %s\n", xxb00t); 467 } 468 469 static void 470 mkbootblock (bb, xxb, bxx, label, magic) 471 struct bootblock *bb; 472 char *xxb, 473 *bxx; 474 u_int magic; 475 struct disklabel *label; 476 { 477 int fd; 478 479 memset(bb, 0, sizeof(*bb)); 480 481 /* set boot block magic */ 482 bb->bb_magic = magic; 483 484 /* set disk pack label */ 485 BBSETLABEL(bb, label); 486 487 /* set second-stage boot loader */ 488 if ((fd = open(bxx, O_RDONLY)) < 0) 489 err(EXIT_FAILURE, "%s", bxx); 490 if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx)) 491 != sizeof(bb->bb_bootxx)) 492 err(EXIT_FAILURE, "%s", bxx); 493 if (close(fd)) 494 err(EXIT_FAILURE, "%s", bxx); 495 496 /* set first-stage bootloader */ 497 if ((fd = open(xxb, O_RDONLY)) < 0) 498 err(EXIT_FAILURE, "%s", xxb); 499 if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot)) 500 != sizeof(bb->bb_xxboot)) 501 err(EXIT_FAILURE, "%s", xxb); 502 if (close(fd)) 503 err(EXIT_FAILURE, "%s", xxb); 504 505 /* set tracks/cylinder and sectors/track */ 506 setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot)); 507 508 /* set AHDI checksum */ 509 *((u_int16_t *)bb->bb_xxboot + 255) = 0; 510 *((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot); 511 512 if (verbose) { 513 printf("Primary boot loader: %s\n", xxb); 514 printf("Secondary boot loader: %s\n", bxx); 515 } 516 } 517 518 static void 519 setIDEpar (start, size) 520 u_int8_t *start; 521 size_t size; 522 { 523 static const u_int8_t mark[] = { 'N', 'e', 't', 'B', 'S', 'D' }; 524 525 if ((u_int)trackpercyl > 255) 526 errx(EXIT_FAILURE, 527 "%d: Illegal tracks/cylinder value (1..255)", trackpercyl); 528 if ((u_int)secpertrack > 255) 529 errx(EXIT_FAILURE, 530 "%d: Illegal sectors/track value (1..255)", secpertrack); 531 532 if (trackpercyl || secpertrack) { 533 u_int8_t *p; 534 535 if (!trackpercyl) 536 errx(EXIT_FAILURE, "Need tracks/cylinder too."); 537 if (!secpertrack) 538 errx(EXIT_FAILURE, "Need sectors/track too."); 539 540 start += 2; 541 size -= sizeof(mark) + 2; 542 for (p = start + size; p >= start; --p) { 543 if (*p != *mark) 544 continue; 545 if (!memcmp(p, mark, sizeof(mark))) 546 break; 547 } 548 if (p < start) 549 errx(EXIT_FAILURE, 550 "Malformatted xxboot prototype."); 551 552 *--p = secpertrack; 553 *--p = trackpercyl; 554 555 if (verbose) { 556 printf("sectors/track : %d\n", secpertrack); 557 printf("tracks/cylinder: %d\n", trackpercyl); 558 } 559 } 560 } 561 562 static void 563 setNVpref () 564 { 565 static const u_char bootpref = BOOTPREF_NETBSD; 566 static const char nvrdev[] = PATH_NVRAM; 567 568 if (!nowrite) { 569 int fd; 570 571 if ((fd = open(nvrdev, O_RDWR)) < 0) 572 err(EXIT_FAILURE, "%s", nvrdev); 573 if (lseek(fd, (off_t)1, SEEK_SET) != 1) 574 err(EXIT_FAILURE, "%s", nvrdev); 575 if (write(fd, &bootpref, (size_t)1) != 1) 576 err(EXIT_FAILURE, "%s", nvrdev); 577 if (close(fd)) 578 err(EXIT_FAILURE, "%s", nvrdev); 579 if (verbose) 580 printf("Boot preference set to NetBSD.\n"); 581 } 582 } 583 584 static u_int 585 abcksum (bs) 586 void *bs; 587 { 588 u_int16_t sum = 0, 589 *st = (u_int16_t *)bs, 590 *end = (u_int16_t *)bs + 256; 591 592 while (st < end) 593 sum += *st++; 594 return(sum); 595 } 596