2 * Copyright (c) 2008 Luigi Rizzo
3 * Copyright (c) 1999 Robert Nordier
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/disklabel.h>
33 #include <sys/diskmbr.h>
46 #define MBRSIZE 512 /* master boot record size */
48 #define OFF_VERSION 0x1b0 /* offset: version number, only boot0version */
49 #define OFF_SERIAL 0x1b8 /* offset: volume serial number */
50 #define OFF_PTBL 0x1be /* offset: partition table */
51 #define OFF_MAGIC 0x1fe /* offset: magic number */
53 * Offsets to the parameters of the 512-byte boot block.
54 * For historical reasons they are set as macros
63 static struct opt_offsets b0_ofs
[] = {
64 { 0x0, 0x0, 0x0, 0x0 }, /* no boot block */
65 { 0x1b9, 0x1ba, 0x1bb, 0x1bc }, /* original block */
66 { 0x1b5, 0x1b6, 0x1b7, 0x1bc }, /* NT_SERIAL block */
69 static int b0_ver
; /* boot block version set by boot0bs */
71 #define OFF_OPT (b0_ofs[b0_ver].opt) /* default boot option */
72 #define OFF_DRIVE (b0_ofs[b0_ver].drive) /* setdrv drive */
73 #define OFF_FLAGS (b0_ofs[b0_ver].flags) /* option flags */
74 #define OFF_TICKS (b0_ofs[b0_ver].ticks) /* clock ticks */
77 #define cv2(p) ((p)[0] | (p)[1] << 010)
80 (p)[0] = (u_int8_t)(x), \
81 (p)[1] = (u_int8_t)((x) >> 010)
91 static const int nopt
= sizeof(opttbl
) / sizeof(opttbl
[0]);
93 static const char fmt0
[] = "# flag start chs type"
94 " end chs offset size\n";
96 static const char fmt1
[] = "%d 0x%02x %4u:%3u:%2u 0x%02x"
97 " %4u:%3u:%2u %10u %10u\n";
99 static int geom_class_available(const char *);
100 static int read_mbr(const char *, u_int8_t
**, int);
101 static void write_mbr(const char *, int, u_int8_t
*, int);
102 static void display_mbr(u_int8_t
*);
103 static int boot0version(const u_int8_t
*);
104 static int boot0bs(const u_int8_t
*);
105 static void stropt(const char *, int *, int *);
106 static int argtoi(const char *, int, int, int);
107 static int set_bell(u_int8_t
*, int, int);
108 static void usage(void);
110 static unsigned vol_id
[5]; /* 4 plus 1 for flag */
114 * Boot manager installation/configuration utility.
117 main(int argc
, char *argv
[])
119 u_int8_t
*mbr
, *boot0
;
120 int boot0_size
, mbr_size
;
121 const char *bpath
, *fpath
;
124 int d_arg
, m_arg
, s_arg
, t_arg
;
125 int o_and
, o_or
, o_e
= -1;
128 bpath
= "/boot/boot0";
130 B_flag
= v_flag
= o_flag
= 0;
131 d_arg
= m_arg
= s_arg
= t_arg
= -1;
134 while ((c
= getopt(argc
, argv
, "Bvb:d:e:f:i:m:o:s:t:")) != -1)
146 d_arg
= argtoi(optarg
, 0, 0xff, 'd');
149 if (optarg
[0] == '0' && optarg
[1] == 'x')
150 sscanf(optarg
, "0x%02x", &o_e
);
158 if (sscanf(optarg
, "%02x%02x-%02x%02x",
159 vol_id
, vol_id
+1, vol_id
+2, vol_id
+3) == 4)
162 errx(1, "bad argument %s", optarg
);
165 m_arg
= argtoi(optarg
, 0, 0xf, 'm');
168 stropt(optarg
, &o_and
, &o_or
);
172 if (strcasecmp(optarg
, "pxe") == 0)
175 s_arg
= argtoi(optarg
, 1, 6, 's');
178 t_arg
= argtoi(optarg
, 1, 0xffff, 't');
187 disk
= g_device_path(*argv
);
189 errx(1, "Unable to get providername for %s\n", *argv
);
190 up
= B_flag
|| d_arg
!= -1 || m_arg
!= -1 || o_flag
|| s_arg
!= -1
193 /* open the disk and read in the existing mbr. Either here or
194 * when reading the block from disk, we do check for the version
195 * and abort if a suitable block is not found.
197 mbr_size
= read_mbr(disk
, &mbr
, !B_flag
);
199 /* save the existing MBR if we are asked to do so */
201 write_mbr(fpath
, O_CREAT
| O_TRUNC
, mbr
, mbr_size
);
204 * If we are installing the boot loader, read it from disk and copy the
205 * slice table over from the existing MBR. If not, then point boot0
206 * back at the MBR we just read in. After this, boot0 is the data to
207 * write back to disk if we are going to do a write.
210 boot0_size
= read_mbr(bpath
, &boot0
, 1);
211 memcpy(boot0
+ OFF_PTBL
, mbr
+ OFF_PTBL
,
212 sizeof(struct dos_partition
) * NDOSPART
);
213 if (b0_ver
== 2) /* volume serial number support */
214 memcpy(boot0
+ OFF_SERIAL
, mbr
+ OFF_SERIAL
, 4);
217 boot0_size
= mbr_size
;
222 boot0
[OFF_DRIVE
] = d_arg
;
224 /* set various flags */
226 boot0
[OFF_FLAGS
] &= 0xf0;
227 boot0
[OFF_FLAGS
] |= m_arg
;
230 boot0
[OFF_FLAGS
] &= o_and
;
231 boot0
[OFF_FLAGS
] |= o_or
;
234 /* set the default boot selection */
236 boot0
[OFF_OPT
] = s_arg
- 1;
238 /* set the timeout */
240 mk2(boot0
+ OFF_TICKS
, t_arg
);
242 /* set the bell char */
243 if (o_e
!= -1 && set_bell(boot0
, o_e
, 0) != -1)
248 errx(1, "incompatible boot block, cannot set volume ID");
249 boot0
[OFF_SERIAL
] = vol_id
[0];
250 boot0
[OFF_SERIAL
+1] = vol_id
[1];
251 boot0
[OFF_SERIAL
+2] = vol_id
[2];
252 boot0
[OFF_SERIAL
+3] = vol_id
[3];
253 up
= 1; /* force update */
255 /* write the MBR back to disk */
257 write_mbr(disk
, 0, boot0
, boot0_size
);
259 /* display the MBR */
272 /* get or set the 'bell' character to be used in case of errors.
273 * Lookup for a certain code sequence, return -1 if not found.
276 set_bell(u_int8_t
*mbr
, int new_bell
, int report
)
278 /* lookup sequence: 0x100 means skip, 0x200 means done */
279 static unsigned seq
[] =
280 { 0xb0, 0x100, 0xe8, 0x100, 0x100, 0x30, 0xe4, 0x200 };
282 for (ofs
= 0x60; ofs
< 0x180; ofs
++) { /* search range */
283 if (mbr
[ofs
] != seq
[0]) /* search initial pattern */
286 if (seq
[i
] == 0x200) { /* found */
289 mbr
[ofs
+1] = c
= new_bell
;
291 printf(" bell=%c (0x%x)",
292 (c
>= ' ' && c
< 0x7f) ? c
: ' ', c
);
295 if (seq
[i
] != 0x100 && seq
[i
] != mbr
[ofs
+i
])
299 warn("bell not found");
303 * Read in the MBR of the disk. If it is boot0, then use the version to
304 * read in all of it if necessary. Use pointers to return a malloc'd
305 * buffer containing the MBR and then return its size.
308 read_mbr(const char *disk
, u_int8_t
**mbr
, int check_version
)
310 u_int8_t buf
[MBRSIZE
];
315 if ((fd
= open(disk
, O_RDONLY
)) == -1)
316 err(1, "open %s", disk
);
317 if ((n
= read(fd
, buf
, MBRSIZE
)) == -1)
318 err(1, "read %s", disk
);
320 errx(1, "%s: short read", disk
);
321 if (cv2(buf
+ OFF_MAGIC
) != 0xaa55)
322 errx(1, "%s: bad magic", disk
);
324 if (! (ver
= boot0bs(buf
))) {
326 errx(1, "%s: unknown or incompatible boot code", disk
);
327 } else if (boot0version(buf
) == 0x101) {
329 if ((*mbr
= malloc(mbr_size
)) == NULL
)
330 errx(1, "%s: unable to allocate read buffer", disk
);
331 if (lseek(fd
, 0, SEEK_SET
) == -1 ||
332 (n
= read(fd
, *mbr
, mbr_size
)) == -1)
335 errx(1, "%s: short read", disk
);
339 if ((*mbr
= malloc(sizeof(buf
))) == NULL
)
340 errx(1, "%s: unable to allocate MBR buffer", disk
);
341 memcpy(*mbr
, buf
, sizeof(buf
));
348 geom_class_available(const char *name
)
350 struct gclass
*class;
354 error
= geom_gettree(&mesh
);
356 errc(1, error
, "Cannot get GEOM tree");
358 LIST_FOREACH(class, &mesh
.lg_class
, lg_class
) {
359 if (strcmp(class->lg_name
, name
) == 0) {
360 geom_deletetree(&mesh
);
365 geom_deletetree(&mesh
);
370 * Write out the mbr to the specified file.
373 write_mbr(const char *fname
, int flags
, u_int8_t
*mbr
, int mbr_size
)
375 struct gctl_req
*grq
;
381 fd
= open(fname
, O_WRONLY
| flags
, 0666);
383 n
= write(fd
, mbr
, mbr_size
);
386 errx(1, "%s: short write", fname
);
391 * If we're called to write to a backup file, don't try to
392 * write through GEOM.
395 err(1, "can't open file %s to write backup", fname
);
397 /* Try open it read only. */
398 fd
= open(fname
, O_RDONLY
);
400 warn("error opening %s", fname
);
404 pname
= g_providername(fd
);
406 warn("error getting providername for %s", fname
);
410 /* First check that GEOM_PART is available */
411 if (geom_class_available("PART") != 0) {
412 grq
= gctl_get_handle();
413 gctl_ro_param(grq
, "class", -1, "PART");
414 gctl_ro_param(grq
, "arg0", -1, pname
);
415 gctl_ro_param(grq
, "verb", -1, "bootcode");
416 gctl_ro_param(grq
, "bootcode", mbr_size
, mbr
);
417 gctl_ro_param(grq
, "flags", -1, "C");
418 errmsg
= gctl_issue(grq
);
419 if (errmsg
!= NULL
&& errmsg
[0] != '\0')
420 errx(1, "GEOM_PART: write bootcode to %s failed: %s",
423 } else if (geom_class_available("MBR") != 0) {
424 grq
= gctl_get_handle();
425 gctl_ro_param(grq
, "verb", -1, "write MBR");
426 gctl_ro_param(grq
, "class", -1, "MBR");
427 gctl_ro_param(grq
, "geom", -1, pname
);
428 gctl_ro_param(grq
, "data", mbr_size
, mbr
);
429 errmsg
= gctl_issue(grq
);
431 err(1, "GEOM_MBR: write MBR to %s failed", fname
);
434 errx(1, "can't write MBR to %s", fname
);
439 * Outputs an informative dump of the data in the MBR to stdout.
442 display_mbr(u_int8_t
*mbr
)
444 struct dos_partition
*part
;
447 part
= (struct dos_partition
*)(mbr
+ DOSPARTOFF
);
449 for (i
= 0; i
< NDOSPART
; i
++)
451 printf(fmt1
, 1 + i
, part
[i
].dp_flag
,
452 part
[i
].dp_scyl
+ ((part
[i
].dp_ssect
& 0xc0) << 2),
453 part
[i
].dp_shd
, part
[i
].dp_ssect
& 0x3f, part
[i
].dp_typ
,
454 part
[i
].dp_ecyl
+ ((part
[i
].dp_esect
& 0xc0) << 2),
455 part
[i
].dp_ehd
, part
[i
].dp_esect
& 0x3f, part
[i
].dp_start
,
458 version
= boot0version(mbr
);
459 printf("version=%d.%d drive=0x%x mask=0x%x ticks=%u",
460 version
>> 8, version
& 0xff, mbr
[OFF_DRIVE
],
461 mbr
[OFF_FLAGS
] & 0xf, cv2(mbr
+ OFF_TICKS
));
463 printf("\noptions=");
464 for (i
= 0; i
< nopt
; i
++) {
467 if (!(mbr
[OFF_FLAGS
] & 1 << (7 - i
)) ^ opttbl
[i
].def
)
469 printf("%s", opttbl
[i
].tok
);
473 printf("volume serial ID %02x%02x-%02x%02x\n",
474 mbr
[OFF_SERIAL
], mbr
[OFF_SERIAL
+1],
475 mbr
[OFF_SERIAL
+2], mbr
[OFF_SERIAL
+3]);
476 printf("default_selection=F%d (", mbr
[OFF_OPT
] + 1);
477 if (mbr
[OFF_OPT
] < 4)
478 printf("Slice %d", mbr
[OFF_OPT
] + 1);
479 else if (mbr
[OFF_OPT
] == 4)
487 * Return the boot0 version with the minor revision in the low byte, and
488 * the major revision in the next higher byte.
491 boot0version(const u_int8_t
*bs
)
493 /* Check for old version, and return 0x100 if found. */
498 /* We have a newer boot0, so extract the version number and return it. */
499 return *(const int *)(bs
+ OFF_VERSION
) & 0xffff;
502 /* descriptor of a pattern to match.
503 * Start from the first entry trying to match the chunk of bytes,
504 * if you hit an entry with len=0 terminate the search and report
505 * off as the version. Otherwise skip to the next block after len=0
506 * An entry with len=0, off=0 is the end marker.
508 struct byte_pattern
{
515 * Decide if we have valid boot0 boot code by looking for
516 * characteristic byte sequences at fixed offsets.
519 boot0bs(const u_int8_t
*bs
)
521 /* the initial code sequence */
522 static u_int8_t id0
[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
523 0x8e, 0xd0, 0xbc, 0x00, 0x7c };
525 static u_int8_t id1
[] = {'D', 'r', 'i', 'v', 'e', ' '};
526 static struct byte_pattern patterns
[] = {
527 {0x0, sizeof(id0
), id0
},
528 {0x1b2, sizeof(id1
), id1
},
530 {0x0, sizeof(id0
), id0
}, /* version with NT support */
531 {0x1ae, sizeof(id1
), id1
},
535 struct byte_pattern
*p
= patterns
;
537 for (; p
->off
|| p
->len
; p
++) {
540 if (!memcmp(bs
+ p
->off
, p
->key
, p
->len
)) /* match */
542 while (p
->len
) /* skip to next block */
545 b0_ver
= p
->off
; /* XXX ugly side effect */
550 * Adjust "and" and "or" masks for a -o option argument.
553 stropt(const char *arg
, int *xa
, int *xo
)
559 if (!(s
= strdup(arg
)))
561 for (s1
= s
; (q
= strtok(s1
, ",")); s1
= NULL
) {
562 if ((inv
= !strncmp(q
, "no", 2)))
564 for (i
= 0; i
< nopt
; i
++)
565 if (!strcmp(q
, opttbl
[i
].tok
))
568 errx(1, "%s: Unknown -o option", q
);
581 * Convert and check an option argument.
584 argtoi(const char *arg
, int lo
, int hi
, int opt
)
590 x
= strtol(arg
, &s
, 0);
591 if (errno
|| !*arg
|| *s
|| x
< lo
|| x
> hi
)
592 errx(1, "%s: Bad argument to -%c option", arg
, opt
);
597 * Display usage information.
602 fprintf(stderr
, "%s\n%s\n",
603 "usage: boot0cfg [-Bv] [-b boot0] [-d drive] [-f file] [-m mask]",
604 " [-o options] [-s slice] [-t ticks] disk");