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