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