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