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
usage(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
main(int argc,char * argv[])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
oscheck(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
install_fd(char * devnm,struct disklabel * label)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
install_hd(char * devnm,struct disklabel * label,bool use_wd)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
mkahdiboot(struct ahdi_root * newroot,char * xxb00t,char * devnm,u_int32_t bbsec)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
mkbootblock(struct bootblock * bb,char * xxb,char * bxx,struct disklabel * label,u_int magic)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
setIDEpar(u_int8_t * start,size_t size)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
setNVpref(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
abcksum(void * bs)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