boot0cfg.c revision 227876
1202719Sgabor/* 2265533Sdelphij * Copyright (c) 2008 Luigi Rizzo 3202719Sgabor * Copyright (c) 1999 Robert Nordier 4202719Sgabor * All rights reserved. 5202719Sgabor * 6202719Sgabor * Redistribution and use in source and binary forms, with or without 7202719Sgabor * modification, are permitted provided that the following conditions 8202719Sgabor * are met: 9202719Sgabor * 1. Redistributions of source code must retain the above copyright 10202719Sgabor * notice, this list of conditions and the following disclaimer. 11202719Sgabor * 2. Redistributions in binary form must reproduce the above copyright 12202719Sgabor * notice, this list of conditions and the following disclaimer in the 13202719Sgabor * documentation and/or other materials provided with the distribution. 14202719Sgabor * 15202719Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND 16202719Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17202719Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18202719Sgabor * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 19202719Sgabor * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 20202719Sgabor * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21202719Sgabor * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 22202719Sgabor * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23202719Sgabor * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 24202719Sgabor * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 25203498Sdelphij * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26202719Sgabor */ 27265533Sdelphij 28202719Sgabor#include <sys/cdefs.h> 29202719Sgabor__FBSDID("$FreeBSD: head/usr.sbin/boot0cfg/boot0cfg.c 227876 2011-11-23 10:27:18Z kevlo $"); 30202719Sgabor 31202719Sgabor#include <sys/param.h> 32202719Sgabor#include <sys/disklabel.h> 33202719Sgabor#include <sys/diskmbr.h> 34202719Sgabor#include <sys/stat.h> 35265533Sdelphij 36265533Sdelphij#include <err.h> 37202719Sgabor#include <errno.h> 38203498Sdelphij#include <fcntl.h> 39203498Sdelphij#include <libgeom.h> 40203498Sdelphij#include <paths.h> 41203498Sdelphij#include <stdio.h> 42202719Sgabor#include <stdlib.h> 43265533Sdelphij#include <string.h> 44265533Sdelphij#include <unistd.h> 45265533Sdelphij 46265533Sdelphij#define MBRSIZE 512 /* master boot record size */ 47202719Sgabor 48265533Sdelphij#define OFF_VERSION 0x1b0 /* offset: version number, only boot0version */ 49265533Sdelphij#define OFF_SERIAL 0x1b8 /* offset: volume serial number */ 50265533Sdelphij#define OFF_PTBL 0x1be /* offset: partition table */ 51203498Sdelphij#define OFF_MAGIC 0x1fe /* offset: magic number */ 52202719Sgabor/* 53250926Sjkim * Offsets to the parameters of the 512-byte boot block. 54215704Sbrucec * For historical reasons they are set as macros 55203498Sdelphij */ 56203498Sdelphijstruct opt_offsets { 57203498Sdelphij int opt; 58265533Sdelphij int drive; 59202719Sgabor int flags; 60202719Sgabor int ticks; 61202719Sgabor}; 62202719Sgabor 63202719Sgaborstatic struct opt_offsets b0_ofs[] = { 64202719Sgabor { 0x0, 0x0, 0x0, 0x0 }, /* no boot block */ 65202719Sgabor { 0x1b9, 0x1ba, 0x1bb, 0x1bc }, /* original block */ 66202719Sgabor { 0x1b5, 0x1b6, 0x1b7, 0x1bc }, /* NT_SERIAL block */ 67202719Sgabor}; 68202719Sgabor 69202719Sgaborstatic int b0_ver; /* boot block version set by boot0bs */ 70202719Sgabor 71202719Sgabor#define OFF_OPT (b0_ofs[b0_ver].opt) /* default boot option */ 72202719Sgabor#define OFF_DRIVE (b0_ofs[b0_ver].drive) /* setdrv drive */ 73202719Sgabor#define OFF_FLAGS (b0_ofs[b0_ver].flags) /* option flags */ 74202719Sgabor#define OFF_TICKS (b0_ofs[b0_ver].ticks) /* clock ticks */ 75202719Sgabor 76202719Sgabor 77202719Sgabor#define cv2(p) ((p)[0] | (p)[1] << 010) 78202719Sgabor 79202719Sgabor#define mk2(p, x) \ 80202719Sgabor (p)[0] = (u_int8_t)(x), \ 81202719Sgabor (p)[1] = (u_int8_t)((x) >> 010) 82202719Sgabor 83202719Sgaborstatic const struct { 84202719Sgabor const char *tok; 85202719Sgabor int def; 86202719Sgabor} opttbl[] = { 87202719Sgabor {"packet", 0}, 88202719Sgabor {"update", 1}, 89202719Sgabor {"setdrv", 0} 90202719Sgabor}; 91202719Sgaborstatic const int nopt = sizeof(opttbl) / sizeof(opttbl[0]); 92202719Sgabor 93202719Sgaborstatic const char fmt0[] = "# flag start chs type" 94202719Sgabor " end chs offset size\n"; 95202719Sgabor 96202719Sgaborstatic const char fmt1[] = "%d 0x%02x %4u:%3u:%2u 0x%02x" 97202719Sgabor " %4u:%3u:%2u %10u %10u\n"; 98202719Sgabor 99202719Sgaborstatic int geom_class_available(const char *); 100202719Sgaborstatic int read_mbr(const char *, u_int8_t **, int); 101202719Sgaborstatic void write_mbr(const char *, int, u_int8_t *, int); 102202719Sgaborstatic void display_mbr(u_int8_t *); 103202719Sgaborstatic int boot0version(const u_int8_t *); 104202719Sgaborstatic int boot0bs(const u_int8_t *); 105202719Sgaborstatic void stropt(const char *, int *, int *); 106202719Sgaborstatic int argtoi(const char *, int, int, int); 107202719Sgaborstatic int set_bell(u_int8_t *, int, int); 108202719Sgaborstatic void usage(void); 109202719Sgabor 110265533Sdelphijstatic unsigned vol_id[5]; /* 4 plus 1 for flag */ 111202719Sgabor 112202719Sgaborstatic int v_flag; 113202719Sgabor/* 114202719Sgabor * Boot manager installation/configuration utility. 115202719Sgabor */ 116202719Sgaborint 117202719Sgabormain(int argc, char *argv[]) 118202719Sgabor{ 119202719Sgabor u_int8_t *mbr, *boot0; 120202719Sgabor int boot0_size, mbr_size; 121265533Sdelphij const char *bpath, *fpath; 122202719Sgabor char *disk; 123202719Sgabor int B_flag, o_flag; 124265533Sdelphij int d_arg, m_arg, s_arg, t_arg; 125202719Sgabor int o_and, o_or, o_e = -1; 126202719Sgabor int up, c; 127202719Sgabor 128202719Sgabor bpath = "/boot/boot0"; 129265533Sdelphij fpath = NULL; 130265533Sdelphij B_flag = v_flag = o_flag = 0; 131265533Sdelphij d_arg = m_arg = s_arg = t_arg = -1; 132265533Sdelphij o_and = 0xff; 133265533Sdelphij o_or = 0; 134265533Sdelphij while ((c = getopt(argc, argv, "Bvb:d:e:f:i:m:o:s:t:")) != -1) 135265533Sdelphij switch (c) { 136265533Sdelphij case 'B': 137265533Sdelphij B_flag = 1; 138265533Sdelphij break; 139265533Sdelphij case 'v': 140265533Sdelphij v_flag = 1; 141265533Sdelphij break; 142265533Sdelphij case 'b': 143265533Sdelphij bpath = optarg; 144265533Sdelphij break; 145265533Sdelphij case 'd': 146202719Sgabor d_arg = argtoi(optarg, 0, 0xff, 'd'); 147265533Sdelphij break; 148265533Sdelphij case 'e': 149265533Sdelphij if (optarg[0] == '0' && optarg[1] == 'x') 150265533Sdelphij sscanf(optarg, "0x%02x", &o_e); 151202719Sgabor else 152265533Sdelphij o_e = optarg[0]; 153265533Sdelphij break; 154265533Sdelphij case 'f': 155202719Sgabor fpath = optarg; 156265533Sdelphij break; 157265533Sdelphij case 'i': 158202719Sgabor if (sscanf(optarg, "%02x%02x-%02x%02x", 159265533Sdelphij vol_id, vol_id+1, vol_id+2, vol_id+3) == 4) 160265533Sdelphij vol_id[4] = 1; 161202719Sgabor else 162265533Sdelphij errx(1, "bad argument %s", optarg); 163265533Sdelphij break; 164265533Sdelphij case 'm': 165265533Sdelphij m_arg = argtoi(optarg, 0, 0xf, 'm'); 166265533Sdelphij break; 167265533Sdelphij case 'o': 168265533Sdelphij stropt(optarg, &o_and, &o_or); 169202719Sgabor o_flag = 1; 170265533Sdelphij break; 171265533Sdelphij case 's': 172265533Sdelphij s_arg = argtoi(optarg, 1, 5, 's'); 173265533Sdelphij break; 174265533Sdelphij case 't': 175265533Sdelphij t_arg = argtoi(optarg, 1, 0xffff, 't'); 176202719Sgabor break; 177265533Sdelphij default: 178265533Sdelphij usage(); 179202719Sgabor } 180265533Sdelphij argc -= optind; 181265533Sdelphij argv += optind; 182202719Sgabor if (argc != 1) 183265533Sdelphij usage(); 184265533Sdelphij disk = g_device_path(*argv); 185202719Sgabor if (disk == NULL) 186265533Sdelphij errx(1, "Unable to get providername for %s\n", *argv); 187265533Sdelphij up = B_flag || d_arg != -1 || m_arg != -1 || o_flag || s_arg != -1 188202719Sgabor || t_arg != -1; 189202719Sgabor 190202719Sgabor /* open the disk and read in the existing mbr. Either here or 191202719Sgabor * when reading the block from disk, we do check for the version 192202719Sgabor * and abort if a suitable block is not found. 193202719Sgabor */ 194202719Sgabor mbr_size = read_mbr(disk, &mbr, !B_flag); 195202719Sgabor 196265533Sdelphij /* save the existing MBR if we are asked to do so */ 197202719Sgabor if (fpath) 198202719Sgabor write_mbr(fpath, O_CREAT | O_TRUNC, mbr, mbr_size); 199202719Sgabor 200265533Sdelphij /* 201202719Sgabor * If we are installing the boot loader, read it from disk and copy the 202202719Sgabor * slice table over from the existing MBR. If not, then point boot0 203202719Sgabor * back at the MBR we just read in. After this, boot0 is the data to 204265533Sdelphij * write back to disk if we are going to do a write. 205202719Sgabor */ 206202719Sgabor if (B_flag) { 207202719Sgabor boot0_size = read_mbr(bpath, &boot0, 1); 208202719Sgabor memcpy(boot0 + OFF_PTBL, mbr + OFF_PTBL, 209202719Sgabor sizeof(struct dos_partition) * NDOSPART); 210202719Sgabor if (b0_ver == 2) /* volume serial number support */ 211202719Sgabor memcpy(boot0 + OFF_SERIAL, mbr + OFF_SERIAL, 4); 212202719Sgabor } else { 213202719Sgabor boot0 = mbr; 214202719Sgabor boot0_size = mbr_size; 215202719Sgabor } 216202719Sgabor 217202719Sgabor /* set the drive */ 218202719Sgabor if (d_arg != -1) 219202719Sgabor boot0[OFF_DRIVE] = d_arg; 220202719Sgabor 221202719Sgabor /* set various flags */ 222202719Sgabor if (m_arg != -1) { 223203443Sgabor boot0[OFF_FLAGS] &= 0xf0; 224202719Sgabor boot0[OFF_FLAGS] |= m_arg; 225202719Sgabor } 226202719Sgabor if (o_flag) { 227202719Sgabor boot0[OFF_FLAGS] &= o_and; 228265533Sdelphij boot0[OFF_FLAGS] |= o_or; 229265533Sdelphij } 230202719Sgabor 231202719Sgabor /* set the default boot selection */ 232202719Sgabor if (s_arg != -1) 233202719Sgabor boot0[OFF_OPT] = s_arg - 1; 234202719Sgabor 235202719Sgabor /* set the timeout */ 236202719Sgabor if (t_arg != -1) 237202719Sgabor mk2(boot0 + OFF_TICKS, t_arg); 238202719Sgabor 239202719Sgabor /* set the bell char */ 240202719Sgabor if (o_e != -1 && set_bell(boot0, o_e, 0) != -1) 241202719Sgabor up = 1; 242202719Sgabor 243265533Sdelphij if (vol_id[4]) { 244265533Sdelphij if (b0_ver != 2) 245265533Sdelphij errx(1, "incompatible boot block, cannot set volume ID"); 246265533Sdelphij boot0[OFF_SERIAL] = vol_id[0]; 247265533Sdelphij boot0[OFF_SERIAL+1] = vol_id[1]; 248265533Sdelphij boot0[OFF_SERIAL+2] = vol_id[2]; 249265533Sdelphij boot0[OFF_SERIAL+3] = vol_id[3]; 250265533Sdelphij up = 1; /* force update */ 251265533Sdelphij } 252265533Sdelphij /* write the MBR back to disk */ 253265533Sdelphij if (up) 254265533Sdelphij write_mbr(disk, 0, boot0, boot0_size); 255265533Sdelphij 256265533Sdelphij /* display the MBR */ 257265533Sdelphij if (v_flag) 258265533Sdelphij display_mbr(boot0); 259265533Sdelphij 260265533Sdelphij /* clean up */ 261265533Sdelphij if (mbr != boot0) 262265533Sdelphij free(boot0); 263265533Sdelphij free(mbr); 264265533Sdelphij free(disk); 265265533Sdelphij 266265533Sdelphij return 0; 267265533Sdelphij} 268265533Sdelphij 269265533Sdelphij/* get or set the 'bell' character to be used in case of errors. 270265533Sdelphij * Lookup for a certain code sequence, return -1 if not found. 271265533Sdelphij */ 272265533Sdelphijstatic int 273265533Sdelphijset_bell(u_int8_t *mbr, int new_bell, int report) 274265533Sdelphij{ 275265533Sdelphij /* lookup sequence: 0x100 means skip, 0x200 means done */ 276265533Sdelphij static unsigned seq[] = 277265533Sdelphij { 0xb0, 0x100, 0xe8, 0x100, 0x100, 0x30, 0xe4, 0x200 }; 278265533Sdelphij int ofs, i, c; 279202719Sgabor for (ofs = 0x60; ofs < 0x180; ofs++) { /* search range */ 280202719Sgabor if (mbr[ofs] != seq[0]) /* search initial pattern */ 281202719Sgabor continue; 282265533Sdelphij for (i=0;; i++) { 283203443Sgabor if (seq[i] == 0x200) { /* found */ 284202719Sgabor c = mbr[ofs+1]; 285202719Sgabor if (!report) 286202719Sgabor mbr[ofs+1] = c = new_bell; 287202719Sgabor else 288202719Sgabor printf(" bell=%c (0x%x)", 289202719Sgabor (c >= ' ' && c < 0x7f) ? c : ' ', c); 290202719Sgabor return c; 291202719Sgabor } 292202719Sgabor if (seq[i] != 0x100 && seq[i] != mbr[ofs+i]) 293202719Sgabor break; 294202719Sgabor } 295202719Sgabor } 296202719Sgabor warn("bell not found"); 297202719Sgabor return -1; 298202719Sgabor} 299202719Sgabor/* 300202719Sgabor * Read in the MBR of the disk. If it is boot0, then use the version to 301202719Sgabor * read in all of it if necessary. Use pointers to return a malloc'd 302202719Sgabor * buffer containing the MBR and then return its size. 303202719Sgabor */ 304202719Sgaborstatic int 305202719Sgaborread_mbr(const char *disk, u_int8_t **mbr, int check_version) 306202719Sgabor{ 307202719Sgabor u_int8_t buf[MBRSIZE]; 308202719Sgabor int mbr_size, fd; 309202719Sgabor int ver; 310202719Sgabor ssize_t n; 311202719Sgabor 312202719Sgabor if ((fd = open(disk, O_RDONLY)) == -1) 313202719Sgabor err(1, "open %s", disk); 314202719Sgabor if ((n = read(fd, buf, MBRSIZE)) == -1) 315202719Sgabor err(1, "read %s", disk); 316265533Sdelphij if (n != MBRSIZE) 317265533Sdelphij errx(1, "%s: short read", disk); 318265533Sdelphij if (cv2(buf + OFF_MAGIC) != 0xaa55) 319265533Sdelphij errx(1, "%s: bad magic", disk); 320202719Sgabor 321202719Sgabor if (! (ver = boot0bs(buf))) { 322202719Sgabor if (check_version) 323202719Sgabor errx(1, "%s: unknown or incompatible boot code", disk); 324202719Sgabor } else if (boot0version(buf) == 0x101) { 325202719Sgabor mbr_size = 1024; 326203498Sdelphij if ((*mbr = malloc(mbr_size)) == NULL) 327203498Sdelphij errx(1, "%s: unable to allocate read buffer", disk); 328203498Sdelphij if (lseek(fd, 0, SEEK_SET) == -1 || 329203498Sdelphij (n = read(fd, *mbr, mbr_size)) == -1) 330203498Sdelphij err(1, "%s", disk); 331265533Sdelphij if (n != mbr_size) 332265533Sdelphij errx(1, "%s: short read", disk); 333265533Sdelphij close(fd); 334265533Sdelphij return (mbr_size); 335265533Sdelphij } 336203498Sdelphij *mbr = malloc(sizeof(buf)); 337265533Sdelphij memcpy(*mbr, buf, sizeof(buf)); 338203498Sdelphij close(fd); 339203498Sdelphij 340203498Sdelphij return sizeof(buf); 341265533Sdelphij} 342265533Sdelphij 343265533Sdelphijstatic int 344265533Sdelphijgeom_class_available(const char *name) 345265533Sdelphij{ 346265533Sdelphij struct gclass *class; 347265533Sdelphij struct gmesh mesh; 348265533Sdelphij int error; 349265533Sdelphij 350203498Sdelphij error = geom_gettree(&mesh); 351265533Sdelphij if (error != 0) 352203498Sdelphij errc(1, error, "Cannot get GEOM tree"); 353203498Sdelphij 354203498Sdelphij LIST_FOREACH(class, &mesh.lg_class, lg_class) { 355203498Sdelphij if (strcmp(class->lg_name, name) == 0) { 356265533Sdelphij geom_deletetree(&mesh); 357203498Sdelphij return (1); 358203498Sdelphij } 359203498Sdelphij } 360203498Sdelphij 361203498Sdelphij geom_deletetree(&mesh); 362203498Sdelphij return (0); 363203498Sdelphij} 364203498Sdelphij 365203498Sdelphij/* 366203498Sdelphij * Write out the mbr to the specified file. 367203498Sdelphij */ 368203498Sdelphijstatic void 369203498Sdelphijwrite_mbr(const char *fname, int flags, u_int8_t *mbr, int mbr_size) 370265533Sdelphij{ 371 struct gctl_req *grq; 372 const char *errmsg; 373 char *pname; 374 ssize_t n; 375 int fd; 376 377 fd = open(fname, O_WRONLY | flags, 0666); 378 if (fd != -1) { 379 n = write(fd, mbr, mbr_size); 380 close(fd); 381 if (n != mbr_size) 382 errx(1, "%s: short write", fname); 383 return; 384 } 385 386 /* 387 * If we're called to write to a backup file, don't try to 388 * write through GEOM. 389 */ 390 if (flags != 0) 391 err(1, "can't open file %s to write backup", fname); 392 393 /* Try open it read only. */ 394 fd = open(fname, O_RDONLY); 395 if (fd == -1) { 396 warn("error opening %s", fname); 397 return; 398 } 399 400 pname = g_providername(fd); 401 if (pname == NULL) { 402 warn("error getting providername for %s", fname); 403 return; 404 } 405 406 /* First check that GEOM_PART is available */ 407 if (geom_class_available("PART") != 0) { 408 grq = gctl_get_handle(); 409 gctl_ro_param(grq, "class", -1, "PART"); 410 gctl_ro_param(grq, "arg0", -1, pname); 411 gctl_ro_param(grq, "verb", -1, "bootcode"); 412 gctl_ro_param(grq, "bootcode", mbr_size, mbr); 413 gctl_ro_param(grq, "flags", -1, "C"); 414 errmsg = gctl_issue(grq); 415 if (errmsg != NULL && errmsg[0] != '\0') 416 errx(1, "GEOM_PART: write bootcode to %s failed: %s", 417 fname, errmsg); 418 gctl_free(grq); 419 } else if (geom_class_available("MBR") != 0) { 420 grq = gctl_get_handle(); 421 gctl_ro_param(grq, "verb", -1, "write MBR"); 422 gctl_ro_param(grq, "class", -1, "MBR"); 423 gctl_ro_param(grq, "geom", -1, pname); 424 gctl_ro_param(grq, "data", mbr_size, mbr); 425 errmsg = gctl_issue(grq); 426 if (errmsg != NULL) 427 err(1, "GEOM_MBR: write MBR to %s failed", fname); 428 gctl_free(grq); 429 } else 430 errx(1, "can't write MBR to %s", fname); 431 free(pname); 432} 433 434/* 435 * Outputs an informative dump of the data in the MBR to stdout. 436 */ 437static void 438display_mbr(u_int8_t *mbr) 439{ 440 struct dos_partition *part; 441 int i, version; 442 443 part = (struct dos_partition *)(mbr + DOSPARTOFF); 444 printf(fmt0); 445 for (i = 0; i < NDOSPART; i++) 446 if (part[i].dp_typ) 447 printf(fmt1, 1 + i, part[i].dp_flag, 448 part[i].dp_scyl + ((part[i].dp_ssect & 0xc0) << 2), 449 part[i].dp_shd, part[i].dp_ssect & 0x3f, part[i].dp_typ, 450 part[i].dp_ecyl + ((part[i].dp_esect & 0xc0) << 2), 451 part[i].dp_ehd, part[i].dp_esect & 0x3f, part[i].dp_start, 452 part[i].dp_size); 453 printf("\n"); 454 version = boot0version(mbr); 455 printf("version=%d.%d drive=0x%x mask=0x%x ticks=%u", 456 version >> 8, version & 0xff, mbr[OFF_DRIVE], 457 mbr[OFF_FLAGS] & 0xf, cv2(mbr + OFF_TICKS)); 458 set_bell(mbr, 0, 1); 459 printf("\noptions="); 460 for (i = 0; i < nopt; i++) { 461 if (i) 462 printf(","); 463 if (!(mbr[OFF_FLAGS] & 1 << (7 - i)) ^ opttbl[i].def) 464 printf("no"); 465 printf("%s", opttbl[i].tok); 466 } 467 printf("\n"); 468 if (b0_ver == 2) 469 printf("volume serial ID %02x%02x-%02x%02x\n", 470 mbr[OFF_SERIAL], mbr[OFF_SERIAL+1], 471 mbr[OFF_SERIAL+2], mbr[OFF_SERIAL+3]); 472 printf("default_selection=F%d (", mbr[OFF_OPT] + 1); 473 if (mbr[OFF_OPT] < 4) 474 printf("Slice %d", mbr[OFF_OPT] + 1); 475 else 476 printf("Drive 1"); 477 printf(")\n"); 478} 479 480/* 481 * Return the boot0 version with the minor revision in the low byte, and 482 * the major revision in the next higher byte. 483 */ 484static int 485boot0version(const u_int8_t *bs) 486{ 487 /* Check for old version, and return 0x100 if found. */ 488 int v = boot0bs(bs); 489 if (v != 0) 490 return v << 8; 491 492 /* We have a newer boot0, so extract the version number and return it. */ 493 return *(const int *)(bs + OFF_VERSION) & 0xffff; 494} 495 496/* descriptor of a pattern to match. 497 * Start from the first entry trying to match the chunk of bytes, 498 * if you hit an entry with len=0 terminate the search and report 499 * off as the version. Otherwise skip to the next block after len=0 500 * An entry with len=0, off=0 is the end marker. 501 */ 502struct byte_pattern { 503 unsigned off; 504 unsigned len; 505 u_int8_t *key; 506}; 507 508/* 509 * Decide if we have valid boot0 boot code by looking for 510 * characteristic byte sequences at fixed offsets. 511 */ 512static int 513boot0bs(const u_int8_t *bs) 514{ 515 /* the initial code sequence */ 516 static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8, 517 0x8e, 0xd0, 0xbc, 0x00, 0x7c }; 518 /* the drive id */ 519 static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '}; 520 static struct byte_pattern patterns[] = { 521 {0x0, sizeof(id0), id0}, 522 {0x1b2, sizeof(id1), id1}, 523 {1, 0, NULL}, 524 {0x0, sizeof(id0), id0}, /* version with NT support */ 525 {0x1ae, sizeof(id1), id1}, 526 {2, 0, NULL}, 527 {0, 0, NULL}, 528 }; 529 struct byte_pattern *p = patterns; 530 531 for (; p->off || p->len; p++) { 532 if (p->len == 0) 533 break; 534 if (!memcmp(bs + p->off, p->key, p->len)) /* match */ 535 continue; 536 while (p->len) /* skip to next block */ 537 p++; 538 } 539 b0_ver = p->off; /* XXX ugly side effect */ 540 return p->off; 541} 542 543/* 544 * Adjust "and" and "or" masks for a -o option argument. 545 */ 546static void 547stropt(const char *arg, int *xa, int *xo) 548{ 549 const char *q; 550 char *s, *s1; 551 int inv, i, x; 552 553 if (!(s = strdup(arg))) 554 err(1, NULL); 555 for (s1 = s; (q = strtok(s1, ",")); s1 = NULL) { 556 if ((inv = !strncmp(q, "no", 2))) 557 q += 2; 558 for (i = 0; i < nopt; i++) 559 if (!strcmp(q, opttbl[i].tok)) 560 break; 561 if (i == nopt) 562 errx(1, "%s: Unknown -o option", q); 563 if (opttbl[i].def) 564 inv ^= 1; 565 x = 1 << (7 - i); 566 if (inv) 567 *xa &= ~x; 568 else 569 *xo |= x; 570 } 571 free(s); 572} 573 574/* 575 * Convert and check an option argument. 576 */ 577static int 578argtoi(const char *arg, int lo, int hi, int opt) 579{ 580 char *s; 581 long x; 582 583 errno = 0; 584 x = strtol(arg, &s, 0); 585 if (errno || !*arg || *s || x < lo || x > hi) 586 errx(1, "%s: Bad argument to -%c option", arg, opt); 587 return x; 588} 589 590/* 591 * Display usage information. 592 */ 593static void 594usage(void) 595{ 596 fprintf(stderr, "%s\n%s\n", 597 "usage: boot0cfg [-Bv] [-b boot0] [-d drive] [-f file] [-m mask]", 598 " [-o options] [-s slice] [-t ticks] disk"); 599 exit(1); 600} 601