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