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