installboot.c revision 1.12 1 /* $NetBSD: installboot.c,v 1.12 2001/10/14 19:45:53 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
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
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