1/* $NetBSD: installboot.c,v 1.24 2009/03/14 21:04:07 dsl 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#include <limits.h> 46#include <nlist.h> 47#include <kvm.h> 48 49#define DKTYPENAMES 50#define FSTYPENAMES 51#include <sys/disklabel.h> 52#include <machine/ahdilabel.h> 53 54#include "installboot.h" 55 56static void usage(void); 57static void oscheck(void); 58static u_int abcksum(void *); 59static void setNVpref(void); 60static void setIDEpar(u_int8_t *, size_t); 61static void mkahdiboot(struct ahdi_root *, char *, 62 char *, u_int32_t); 63static void mkbootblock(struct bootblock *, char *, 64 char *, struct disklabel *, u_int); 65static void install_fd(char *, struct disklabel *); 66static void install_sd(char *, struct disklabel *); 67static void install_wd(char *, struct disklabel *); 68 69static struct bootblock bootarea; 70static struct ahdi_root ahdiboot; 71static const char mdecpath[] = PATH_MDEC; 72static const char stdpath[] = PATH_STD; 73static const char milanpath[] = PATH_MILAN; 74static int nowrite = 0; 75static int verbose = 0; 76static int trackpercyl = 0; 77static int secpertrack = 0; 78static int milan = 0; 79 80static void 81usage(void) 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-m use Milan boot blocks\n" 88 "\t-t number of tracks per cylinder (IDE disk)\n" 89 "\t-u number of sectors per track (IDE disk)\n" 90 "\t-v verbose mode\n"); 91 exit(EXIT_FAILURE); 92} 93 94int 95main(int argc, char *argv[]) 96{ 97 struct disklabel dl; 98 char *dn; 99 char *devchr; 100 int fd, c; 101 102 /* check OS bootversion */ 103 oscheck(); 104 105 /* parse options */ 106 while ((c = getopt(argc, argv, "Nmt:u:v")) != -1) { 107 switch (c) { 108 case 'N': 109 nowrite = 1; 110 break; 111 case 'm': 112 milan = 1; 113 break; 114 case 't': 115 trackpercyl = atoi(optarg); 116 break; 117 case 'u': 118 secpertrack = atoi(optarg); 119 break; 120 case 'v': 121 verbose = 1; 122 break; 123 default: 124 usage(); 125 } 126 } 127 argv += optind; 128 argc -= optind; 129 if (argc != 1) 130 usage(); 131 132 /* get disk label */ 133 dn = alloca(sizeof(_PATH_DEV) + strlen(argv[0]) + 8); 134 if (!strchr(argv[0], '/')) { 135 sprintf(dn, "%sr%s%c", _PATH_DEV, argv[0], RAW_PART + 'a'); 136 fd = open(dn, O_RDONLY); 137 if (fd < 0 && errno == ENOENT) { 138 sprintf(dn, "%sr%s", _PATH_DEV, argv[0]); 139 fd = open(dn, O_RDONLY); 140 } 141 } else { 142 sprintf(dn, "%s", argv[0]); 143 fd = open(dn, O_RDONLY); 144 } 145 if (fd < 0) 146 err(EXIT_FAILURE, "%s", dn); 147 if (ioctl(fd, DIOCGDINFO, &dl)) 148 err(EXIT_FAILURE, "%s: DIOCGDINFO", dn); 149 if (close(fd)) 150 err(EXIT_FAILURE, "%s", dn); 151 152 /* Eg: in /dev/fd0c, set devchr to point to the 'f' */ 153 devchr = strrchr(dn, '/') + 1; 154 if (*devchr == 'r') 155 ++devchr; 156 157 switch (*devchr) { 158 case 'f': /* fd */ 159 install_fd(dn, &dl); 160 break; 161 case 'w': /* wd */ 162 install_wd(dn, &dl); 163 setNVpref(); 164 break; 165 case 's': /* sd */ 166 install_sd(dn, &dl); 167 setNVpref(); 168 break; 169 default: 170 errx(EXIT_FAILURE, 171 "%s: '%c': Device type not supported.", 172 dn, *devchr); 173 } 174 175 return(EXIT_SUCCESS); 176} 177 178static void 179oscheck(void) 180{ 181 struct nlist kbv[] = { 182 { .n_name = "_bootversion" }, 183 { .n_name = NULL } 184 }; 185 kvm_t *kd_kern; 186 char errbuf[_POSIX2_LINE_MAX]; 187 u_short kvers; 188 struct stat sb; 189 190 if (stat(_PATH_UNIX, &sb) < 0) { 191 warnx("Cannot stat %s, no bootversion check done", _PATH_UNIX); 192 return; 193 } 194 195 kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); 196 if (kd_kern == NULL) 197 errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf); 198 if (kvm_nlist(kd_kern, kbv) == -1) 199 errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd_kern)); 200 if (kbv[0].n_value == 0) 201 errx(EXIT_FAILURE, "%s not in namelist", kbv[0].n_name); 202 if (kvm_read(kd_kern, kbv[0].n_value, &kvers, sizeof(kvers)) == -1) 203 errx(EXIT_FAILURE, "kvm_read: %s", kvm_geterr(kd_kern)); 204 kvm_close(kd_kern); 205 if (kvers != BOOTVERSION) 206 errx(EXIT_FAILURE, "Kern bootversion: %d, expected: %d", 207 kvers, BOOTVERSION); 208} 209 210static void 211install_fd(char *devnm, struct disklabel *label) 212{ 213 const char *machpath; 214 char *xxboot, *bootxx; 215 struct partition *rootpart; 216 217 if (label->d_secsize != 512) 218 errx(EXIT_FAILURE, 219 "%s: %u: Block size not supported.", devnm, 220 label->d_secsize); 221 if (label->d_ntracks != 2) 222 errx(EXIT_FAILURE, 223 "%s: Single sided floppy not supported.", devnm); 224 225 if (milan) 226 machpath = milanpath; 227 else 228 machpath = stdpath; 229 xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8); 230 sprintf(xxboot, "%s%sfdboot", mdecpath, machpath); 231 bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8); 232 sprintf(bootxx, "%s%sbootxx", mdecpath, machpath); 233 234 /* first used partition (a, b or c) */ /* XXX */ 235 for (rootpart = label->d_partitions; ; ++rootpart) { 236 if (rootpart->p_size) 237 break; 238 } 239 if (rootpart != label->d_partitions) { /* XXX */ 240 *(label->d_partitions) = *rootpart; 241 memset(rootpart, 0, sizeof(*rootpart)); 242 } 243 label->d_partitions->p_fstype = FS_BSDFFS; /* XXX */ 244 label->d_npartitions = 1; 245 label->d_checksum = 0; 246 label->d_checksum = dkcksum(label); 247 248 trackpercyl = secpertrack = 0; 249 mkbootblock(&bootarea, xxboot, bootxx, label, 0); 250 251 if (!nowrite) { 252 int fd; 253 if ((fd = open(devnm, O_WRONLY)) < 0) 254 err(EXIT_FAILURE, "%s", devnm); 255 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 256 err(EXIT_FAILURE, "%s", devnm); 257 if (close(fd)) 258 err(EXIT_FAILURE, "%s", devnm); 259 if (verbose) 260 printf("Boot block installed on %s\n", devnm); 261 } 262} 263 264static void 265install_sd(char *devnm, struct disklabel *label) 266{ 267 const char *machpath; 268 char *xxb00t, *xxboot, *bootxx; 269 struct disklabel rawlabel; 270 u_int32_t bbsec; 271 u_int magic; 272 273 if (label->d_partitions[0].p_size == 0) 274 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 275 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 276 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 277 devnm, fstypenames[label->d_partitions[0].p_fstype]); 278 279 bbsec = readdisklabel(devnm, &rawlabel); 280 if (bbsec == NO_BOOT_BLOCK) 281 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 282 if (memcmp(label, &rawlabel, sizeof(*label))) 283 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 284 285 if (milan) 286 machpath = milanpath; 287 else 288 machpath = stdpath; 289 if (bbsec) { 290 xxb00t = alloca(strlen(mdecpath) + strlen(machpath) + 14); 291 sprintf(xxb00t, "%s%ssdb00t.ahdi", mdecpath, machpath); 292 xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 14); 293 sprintf(xxboot, "%s%sxxboot.ahdi", mdecpath, machpath); 294 magic = AHDIMAGIC; 295 } else { 296 xxb00t = NULL; 297 xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8); 298 sprintf(xxboot, "%s%ssdboot", mdecpath, machpath); 299 magic = NBDAMAGIC; 300 } 301 bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8); 302 sprintf(bootxx, "%s%sbootxx", mdecpath, machpath); 303 304 trackpercyl = secpertrack = 0; 305 if (xxb00t) 306 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 307 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 308 309 if (!nowrite) { 310 off_t bbo = (off_t)bbsec * AHDI_BSIZE; 311 int fd; 312 313 if ((fd = open(devnm, O_WRONLY)) < 0) 314 err(EXIT_FAILURE, "%s", devnm); 315 if (lseek(fd, bbo, SEEK_SET) != bbo) 316 err(EXIT_FAILURE, "%s", devnm); 317 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 318 err(EXIT_FAILURE, "%s", devnm); 319 if (verbose) 320 printf("Boot block installed on %s (sector %d)\n", 321 devnm, bbsec); 322 if (xxb00t) { 323 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 324 err(EXIT_FAILURE, "%s", devnm); 325 if (write(fd, &ahdiboot, sizeof(ahdiboot)) != 326 sizeof(ahdiboot)) 327 err(EXIT_FAILURE, "%s", devnm); 328 if (verbose) 329 printf("AHDI root installed on %s (0)\n", 330 devnm); 331 } 332 if (close(fd)) 333 err(EXIT_FAILURE, "%s", devnm); 334 } 335} 336 337static void 338install_wd(char *devnm, struct disklabel *label) 339{ 340 const char *machpath; 341 char *xxb00t, *xxboot, *bootxx; 342 struct disklabel rawlabel; 343 u_int32_t bbsec; 344 u_int magic; 345 346 if (label->d_partitions[0].p_size == 0) 347 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm); 348 if (label->d_partitions[0].p_fstype != FS_BSDFFS) 349 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.", 350 devnm, fstypenames[label->d_partitions[0].p_fstype]); 351 352 bbsec = readdisklabel(devnm, &rawlabel); 353 if (bbsec == NO_BOOT_BLOCK) 354 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm); 355 if (memcmp(label, &rawlabel, sizeof(*label))) 356 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm); 357 358 if (milan) 359 machpath = milanpath; 360 else 361 machpath = stdpath; 362 if (bbsec) { 363 xxb00t = alloca(strlen(mdecpath) + strlen(machpath) + 14); 364 sprintf(xxb00t, "%s%swdb00t.ahdi", mdecpath, machpath); 365 xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 14); 366 sprintf(xxboot, "%s%sxxboot.ahdi", mdecpath, machpath); 367 magic = AHDIMAGIC; 368 } else { 369 xxb00t = NULL; 370 xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8); 371 sprintf(xxboot, "%s%swdboot", mdecpath, machpath); 372 magic = NBDAMAGIC; 373 } 374 bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8); 375 sprintf(bootxx, "%s%sbootxx", mdecpath, machpath); 376 377 if (xxb00t) 378 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec); 379 mkbootblock(&bootarea, xxboot, bootxx, label, magic); 380 381 if (!nowrite) { 382 int fd; 383 off_t bbo; 384 385 bbo = (off_t)bbsec * AHDI_BSIZE; 386 if ((fd = open(devnm, O_WRONLY)) < 0) 387 err(EXIT_FAILURE, "%s", devnm); 388 if (lseek(fd, bbo, SEEK_SET) != bbo) 389 err(EXIT_FAILURE, "%s", devnm); 390 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea)) 391 err(EXIT_FAILURE, "%s", devnm); 392 if (verbose) 393 printf("Boot block installed on %s (sector %d)\n", 394 devnm, bbsec); 395 if (xxb00t) { 396 if (lseek(fd, (off_t)0, SEEK_SET) != 0) 397 err(EXIT_FAILURE, "%s", devnm); 398 if (write(fd, &ahdiboot, sizeof(ahdiboot)) 399 != sizeof(ahdiboot)) 400 err(EXIT_FAILURE, "%s", devnm); 401 if (verbose) 402 printf("AHDI root installed on %s (sector 0)\n", 403 devnm); 404 } 405 if (close(fd)) 406 err(EXIT_FAILURE, "%s", devnm); 407 } 408} 409 410static void 411mkahdiboot(struct ahdi_root *newroot, char *xxb00t, char *devnm, 412 u_int32_t bbsec) 413{ 414 struct ahdi_root tmproot; 415 struct ahdi_part *pd; 416 int fd; 417 418 /* read prototype root-sector */ 419 if ((fd = open(xxb00t, O_RDONLY)) < 0) 420 err(EXIT_FAILURE, "%s", xxb00t); 421 if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot)) 422 err(EXIT_FAILURE, "%s", xxb00t); 423 if (close(fd)) 424 err(EXIT_FAILURE, "%s", xxb00t); 425 426 /* set tracks/cylinder and sectors/track */ 427 setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill)); 428 429 /* read current root-sector */ 430 if ((fd = open(devnm, O_RDONLY)) < 0) 431 err(EXIT_FAILURE, "%s", devnm); 432 if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot)) 433 err(EXIT_FAILURE, "%s", devnm); 434 if (close(fd)) 435 err(EXIT_FAILURE, "%s", devnm); 436 437 /* set bootflags */ 438 for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) { 439 if (pd->ap_st == bbsec) { 440 pd->ap_flg = 0x21; /* bootable, pref = NetBSD */ 441 goto gotit; 442 } 443 } 444 errx(EXIT_FAILURE, 445 "%s: NetBSD boot block not on primary AHDI partition.", devnm); 446 447gotit: /* copy code from prototype and set new checksum */ 448 memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill)); 449 newroot->ar_checksum = 0; 450 newroot->ar_checksum = 0x1234 - abcksum(newroot); 451 452 if (verbose) 453 printf("AHDI boot loader: %s\n", xxb00t); 454} 455 456static void 457mkbootblock(struct bootblock *bb, char *xxb, char *bxx, 458 struct disklabel *label, u_int magic) 459{ 460 int fd; 461 462 memset(bb, 0, sizeof(*bb)); 463 464 /* set boot block magic */ 465 bb->bb_magic = magic; 466 467 /* set disk pack label */ 468 BBSETLABEL(bb, label); 469 470 /* set second-stage boot loader */ 471 if ((fd = open(bxx, O_RDONLY)) < 0) 472 err(EXIT_FAILURE, "%s", bxx); 473 if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx)) 474 != sizeof(bb->bb_bootxx)) 475 err(EXIT_FAILURE, "%s", bxx); 476 if (close(fd)) 477 err(EXIT_FAILURE, "%s", bxx); 478 479 /* set first-stage bootloader */ 480 if ((fd = open(xxb, O_RDONLY)) < 0) 481 err(EXIT_FAILURE, "%s", xxb); 482 if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot)) 483 != sizeof(bb->bb_xxboot)) 484 err(EXIT_FAILURE, "%s", xxb); 485 if (close(fd)) 486 err(EXIT_FAILURE, "%s", xxb); 487 488 /* set tracks/cylinder and sectors/track */ 489 setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot)); 490 491 /* set AHDI checksum */ 492 *((u_int16_t *)bb->bb_xxboot + 255) = 0; 493 *((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot); 494 495 if (verbose) { 496 printf("Primary boot loader: %s\n", xxb); 497 printf("Secondary boot loader: %s\n", bxx); 498 } 499} 500 501static void 502setIDEpar (u_int8_t *start, size_t size) 503{ 504 static const u_int8_t mark[] = { 'N', 'e', 't', 'B', 'S', 'D' }; 505 506 if ((u_int)trackpercyl > 255) 507 errx(EXIT_FAILURE, 508 "%d: Illegal tracks/cylinder value (1..255)", trackpercyl); 509 if ((u_int)secpertrack > 255) 510 errx(EXIT_FAILURE, 511 "%d: Illegal sectors/track value (1..255)", secpertrack); 512 513 if (trackpercyl || secpertrack) { 514 u_int8_t *p; 515 516 if (!trackpercyl) 517 errx(EXIT_FAILURE, "Need tracks/cylinder too."); 518 if (!secpertrack) 519 errx(EXIT_FAILURE, "Need sectors/track too."); 520 521 start += 2; 522 size -= sizeof(mark) + 2; 523 for (p = start + size; p >= start; --p) { 524 if (*p != *mark) 525 continue; 526 if (!memcmp(p, mark, sizeof(mark))) 527 break; 528 } 529 if (p < start) 530 errx(EXIT_FAILURE, 531 "Malformatted xxboot prototype."); 532 533 *--p = secpertrack; 534 *--p = trackpercyl; 535 536 if (verbose) { 537 printf("sectors/track : %d\n", secpertrack); 538 printf("tracks/cylinder: %d\n", trackpercyl); 539 } 540 } 541} 542 543static void 544setNVpref(void) 545{ 546 static const u_char bootpref = BOOTPREF_NETBSD; 547 static const char nvrdev[] = PATH_NVRAM; 548 549 if (!nowrite) { 550 int fd; 551 552 if ((fd = open(nvrdev, O_RDWR)) < 0) 553 err(EXIT_FAILURE, "%s", nvrdev); 554 if (lseek(fd, (off_t)1, SEEK_SET) != 1) 555 err(EXIT_FAILURE, "%s", nvrdev); 556 if (write(fd, &bootpref, (size_t)1) != 1) 557 err(EXIT_FAILURE, "%s", nvrdev); 558 if (close(fd)) 559 err(EXIT_FAILURE, "%s", nvrdev); 560 if (verbose) 561 printf("Boot preference set to NetBSD.\n"); 562 } 563} 564 565static u_int 566abcksum (void *bs) 567{ 568 u_int16_t sum = 0, 569 *st = (u_int16_t *)bs, 570 *end = (u_int16_t *)bs + 256; 571 572 while (st < end) 573 sum += *st++; 574 return(sum); 575} 576