installboot.c revision 1.9 1 /* $NetBSD: installboot.c,v 1.9 2000/07/08 14:41:04 jdolecek 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[] = { { _C_LABEL("bootversion") }, { NULL } };
172 kvm_t *kd_kern;
173 char errbuf[_POSIX2_LINE_MAX];
174 u_short kvers;
175
176 kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
177 if (kd_kern == NULL)
178 errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf);
179 if (kvm_nlist(kd_kern, kbv) == -1)
180 errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd_kern));
181 if (kbv[0].n_value == 0)
182 errx(EXIT_FAILURE, "%s not in namelist", kbv[0].n_name);
183 if (kvm_read(kd_kern, kbv[0].n_value, (char *)&kvers,
184 sizeof(kvers)) == -1)
185 errx(EXIT_FAILURE, "kvm_read: %s", kvm_geterr(kd_kern));
186 kvm_close(kd_kern);
187 if (kvers != BOOTVERSION)
188 errx(EXIT_FAILURE, "Kern bootversion: %d, expected: %d\n",
189 kvers, BOOTVERSION);
190 }
191
192 static void
193 install_fd (devnm, label)
194 char *devnm;
195 struct disklabel *label;
196 {
197 char *xxboot, *bootxx;
198 struct partition *rootpart;
199
200 if (label->d_secsize != 512)
201 errx(EXIT_FAILURE,
202 "%s: %u: Block size not supported.", devnm,
203 label->d_secsize);
204 if (label->d_ntracks != 2)
205 errx(EXIT_FAILURE,
206 "%s: Single sided floppy not supported.", devnm);
207
208 xxboot = alloca(strlen(mdecpath) + 8);
209 sprintf(xxboot, "%sfdboot", mdecpath);
210 bootxx = alloca(strlen(mdecpath) + 8);
211 sprintf(bootxx, "%sbootxx", mdecpath);
212
213 /* first used partition (a, b or c) */ /* XXX */
214 for (rootpart = label->d_partitions; ; ++rootpart) {
215 if (rootpart->p_size)
216 break;
217 }
218 if (rootpart != label->d_partitions) { /* XXX */
219 *(label->d_partitions) = *rootpart;
220 memset(rootpart, 0, sizeof(*rootpart));
221 }
222 label->d_partitions->p_fstype = FS_BSDFFS; /* XXX */
223 label->d_npartitions = 1;
224 label->d_checksum = 0;
225 label->d_checksum = dkcksum(label);
226
227 trackpercyl = secpertrack = 0;
228 mkbootblock(&bootarea, xxboot, bootxx, label, 0);
229
230 if (!nowrite) {
231 int fd;
232 if ((fd = open(devnm, O_WRONLY)) < 0)
233 err(EXIT_FAILURE, "%s", devnm);
234 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
235 err(EXIT_FAILURE, "%s", devnm);
236 if (close(fd))
237 err(EXIT_FAILURE, "%s", devnm);
238 if (verbose)
239 printf("Boot block installed on %s\n", devnm);
240 }
241 }
242
243 static void
244 install_sd (devnm, label)
245 char *devnm;
246 struct disklabel *label;
247 {
248 char *xxb00t, *xxboot, *bootxx;
249 struct disklabel rawlabel;
250 daddr_t bbsec;
251 u_int magic;
252
253 if (label->d_partitions[0].p_size == 0)
254 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
255 if (label->d_partitions[0].p_fstype != FS_BSDFFS)
256 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
257 devnm, fstypenames[label->d_partitions[0].p_fstype]);
258
259 bbsec = readdisklabel(devnm, &rawlabel);
260 if (bbsec == NO_BOOT_BLOCK)
261 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
262 if (memcmp(label, &rawlabel, sizeof(*label)))
263 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
264
265 if (bbsec) {
266 xxb00t = alloca(strlen(mdecpath) + 14);
267 sprintf(xxb00t, "%ssdb00t.ahdi", mdecpath);
268 xxboot = alloca(strlen(mdecpath) + 14);
269 sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
270 magic = AHDIMAGIC;
271 } else {
272 xxb00t = NULL;
273 xxboot = alloca(strlen(mdecpath) + 8);
274 sprintf(xxboot, "%ssdboot", mdecpath);
275 magic = NBDAMAGIC;
276 }
277 bootxx = alloca(strlen(mdecpath) + 8);
278 sprintf(bootxx, "%sbootxx", mdecpath);
279
280 trackpercyl = secpertrack = 0;
281 if (xxb00t)
282 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
283 mkbootblock(&bootarea, xxboot, bootxx, label, magic);
284
285 if (!nowrite) {
286 off_t bbo = bbsec * AHDI_BSIZE;
287 int fd;
288
289 if ((fd = open(devnm, O_WRONLY)) < 0)
290 err(EXIT_FAILURE, "%s", devnm);
291 if (lseek(fd, bbo, SEEK_SET) != bbo)
292 err(EXIT_FAILURE, "%s", devnm);
293 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
294 err(EXIT_FAILURE, "%s", devnm);
295 if (verbose)
296 printf("Boot block installed on %s (%u)\n", devnm,
297 bbsec);
298 if (xxb00t) {
299 if (lseek(fd, (off_t)0, SEEK_SET) != 0)
300 err(EXIT_FAILURE, "%s", devnm);
301 if (write(fd, &ahdiboot, sizeof(ahdiboot)) != sizeof(ahdiboot))
302 err(EXIT_FAILURE, "%s", devnm);
303 if (verbose)
304 printf("AHDI root installed on %s (0)\n",
305 devnm);
306 }
307 if (close(fd))
308 err(EXIT_FAILURE, "%s", devnm);
309 }
310 }
311
312 static void
313 install_wd (devnm, label)
314 char *devnm;
315 struct disklabel *label;
316 {
317 char *xxb00t, *xxboot, *bootxx;
318 struct disklabel rawlabel;
319 daddr_t bbsec;
320 u_int magic;
321
322 if (label->d_partitions[0].p_size == 0)
323 errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
324 if (label->d_partitions[0].p_fstype != FS_BSDFFS)
325 errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
326 devnm, fstypenames[label->d_partitions[0].p_fstype]);
327
328 bbsec = readdisklabel(devnm, &rawlabel);
329 if (bbsec == NO_BOOT_BLOCK)
330 errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
331 if (memcmp(label, &rawlabel, sizeof(*label)))
332 errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
333
334 if (bbsec) {
335 xxb00t = alloca(strlen(mdecpath) + 14);
336 sprintf(xxb00t, "%swdb00t.ahdi", mdecpath);
337 xxboot = alloca(strlen(mdecpath) + 14);
338 sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
339 magic = AHDIMAGIC;
340 } else {
341 xxb00t = NULL;
342 xxboot = alloca(strlen(mdecpath) + 8);
343 sprintf(xxboot, "%swdboot", mdecpath);
344 magic = NBDAMAGIC;
345 }
346 bootxx = alloca(strlen(mdecpath) + 8);
347 sprintf(bootxx, "%sbootxx", mdecpath);
348
349 if (xxb00t)
350 mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
351 mkbootblock(&bootarea, xxboot, bootxx, label, magic);
352
353 if (!nowrite) {
354 int fd;
355 off_t bbo;
356
357 bbo = bbsec * AHDI_BSIZE;
358 if ((fd = open(devnm, O_WRONLY)) < 0)
359 err(EXIT_FAILURE, "%s", devnm);
360 if (lseek(fd, bbo, SEEK_SET) != bbo)
361 err(EXIT_FAILURE, "%s", devnm);
362 if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
363 err(EXIT_FAILURE, "%s", devnm);
364 if (verbose)
365 printf("Boot block installed on %s (%u)\n", devnm,
366 bbsec);
367 if (xxb00t) {
368 if (lseek(fd, (off_t)0, SEEK_SET) != 0)
369 err(EXIT_FAILURE, "%s", devnm);
370 if (write(fd, &ahdiboot, sizeof(ahdiboot))
371 != sizeof(ahdiboot))
372 err(EXIT_FAILURE, "%s", devnm);
373 if (verbose)
374 printf("AHDI root installed on %s (0)\n",
375 devnm);
376 }
377 if (close(fd))
378 err(EXIT_FAILURE, "%s", devnm);
379 }
380 }
381
382 static void
383 mkahdiboot (newroot, xxb00t, devnm, bbsec)
384 struct ahdi_root *newroot;
385 char *xxb00t,
386 *devnm;
387 daddr_t bbsec;
388 {
389 struct ahdi_root tmproot;
390 struct ahdi_part *pd;
391 int fd;
392
393 /* read prototype root-sector */
394 if ((fd = open(xxb00t, O_RDONLY)) < 0)
395 err(EXIT_FAILURE, "%s", xxb00t);
396 if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot))
397 err(EXIT_FAILURE, "%s", xxb00t);
398 if (close(fd))
399 err(EXIT_FAILURE, "%s", xxb00t);
400
401 /* set tracks/cylinder and sectors/track */
402 setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill));
403
404 /* read current root-sector */
405 if ((fd = open(devnm, O_RDONLY)) < 0)
406 err(EXIT_FAILURE, "%s", devnm);
407 if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot))
408 err(EXIT_FAILURE, "%s", devnm);
409 if (close(fd))
410 err(EXIT_FAILURE, "%s", devnm);
411
412 /* set bootflags */
413 for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) {
414 if (pd->ap_st == bbsec) {
415 pd->ap_flg = 0x21; /* bootable, pref = NetBSD */
416 goto gotit;
417 }
418 }
419 errx(EXIT_FAILURE,
420 "%s: NetBSD boot block not on primary AHDI partition.", devnm);
421
422 gotit: /* copy code from prototype and set new checksum */
423 memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill));
424 newroot->ar_checksum = 0;
425 newroot->ar_checksum = 0x1234 - abcksum(newroot);
426
427 if (verbose)
428 printf("AHDI boot loader: %s\n", xxb00t);
429 }
430
431 static void
432 mkbootblock (bb, xxb, bxx, label, magic)
433 struct bootblock *bb;
434 char *xxb,
435 *bxx;
436 u_int magic;
437 struct disklabel *label;
438 {
439 int fd;
440
441 memset(bb, 0, sizeof(*bb));
442
443 /* set boot block magic */
444 bb->bb_magic = magic;
445
446 /* set disk pack label */
447 BBSETLABEL(bb, label);
448
449 /* set second-stage boot loader */
450 if ((fd = open(bxx, O_RDONLY)) < 0)
451 err(EXIT_FAILURE, "%s", bxx);
452 if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx))
453 != sizeof(bb->bb_bootxx))
454 err(EXIT_FAILURE, "%s", bxx);
455 if (close(fd))
456 err(EXIT_FAILURE, "%s", bxx);
457
458 /* set first-stage bootloader */
459 if ((fd = open(xxb, O_RDONLY)) < 0)
460 err(EXIT_FAILURE, "%s", xxb);
461 if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot))
462 != sizeof(bb->bb_xxboot))
463 err(EXIT_FAILURE, "%s", xxb);
464 if (close(fd))
465 err(EXIT_FAILURE, "%s", xxb);
466
467 /* set tracks/cylinder and sectors/track */
468 setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot));
469
470 /* set AHDI checksum */
471 *((u_int16_t *)bb->bb_xxboot + 255) = 0;
472 *((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot);
473
474 if (verbose) {
475 printf("Primary boot loader: %s\n", xxb);
476 printf("Secondary boot loader: %s\n", bxx);
477 }
478 }
479
480 static void
481 setIDEpar (start, size)
482 u_int8_t *start;
483 size_t size;
484 {
485 static const u_int8_t mark[] = { 'N', 'e', 't', 'B', 'S', 'D' };
486
487 if ((u_int)trackpercyl > 255)
488 errx(EXIT_FAILURE,
489 "%d: Illegal tracks/cylinder value (1..255)", trackpercyl);
490 if ((u_int)secpertrack > 255)
491 errx(EXIT_FAILURE,
492 "%d: Illegal sectors/track value (1..255)", secpertrack);
493
494 if (trackpercyl || secpertrack) {
495 u_int8_t *p;
496
497 if (!trackpercyl)
498 errx(EXIT_FAILURE, "Need tracks/cylinder too.");
499 if (!secpertrack)
500 errx(EXIT_FAILURE, "Need sectors/track too.");
501
502 start += 2;
503 size -= sizeof(mark) + 2;
504 for (p = start + size; p >= start; --p) {
505 if (*p != *mark)
506 continue;
507 if (!memcmp(p, mark, sizeof(mark)))
508 break;
509 }
510 if (p < start)
511 errx(EXIT_FAILURE,
512 "Malformatted xxboot prototype.");
513
514 *--p = secpertrack;
515 *--p = trackpercyl;
516
517 if (verbose) {
518 printf("sectors/track : %d\n", secpertrack);
519 printf("tracks/cylinder: %d\n", trackpercyl);
520 }
521 }
522 }
523
524 static void
525 setNVpref ()
526 {
527 static const u_char bootpref = BOOTPREF_NETBSD;
528 static const char nvrdev[] = PATH_NVRAM;
529
530 if (!nowrite) {
531 int fd;
532
533 if ((fd = open(nvrdev, O_RDWR)) < 0)
534 err(EXIT_FAILURE, "%s", nvrdev);
535 if (lseek(fd, (off_t)1, SEEK_SET) != 1)
536 err(EXIT_FAILURE, "%s", nvrdev);
537 if (write(fd, &bootpref, (size_t)1) != 1)
538 err(EXIT_FAILURE, "%s", nvrdev);
539 if (close(fd))
540 err(EXIT_FAILURE, "%s", nvrdev);
541 if (verbose)
542 printf("Boot preference set to NetBSD.\n");
543 }
544 }
545
546 static u_int
547 abcksum (bs)
548 void *bs;
549 {
550 u_int16_t sum = 0,
551 *st = (u_int16_t *)bs,
552 *end = (u_int16_t *)bs + 256;
553
554 while (st < end)
555 sum += *st++;
556 return(sum);
557 }
558