1 /* $NetBSD: evboards.c,v 1.2 2019/05/12 13:47:09 maya Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if !defined(__lint) 38 __RCSID("$NetBSD: evboards.c,v 1.2 2019/05/12 13:47:09 maya Exp $"); 39 #endif /* !__lint */ 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <assert.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <fts.h> 48 #include <inttypes.h> 49 #include <limits.h> 50 #include <stdarg.h> 51 #include <stdlib.h> 52 #include <stdio.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #ifdef SUPPORT_FDT 57 #include "libfdt.h" 58 #endif 59 60 #if !HAVE_NBTOOL_CONFIG_H 61 #include <sys/utsname.h> 62 63 #ifdef SUPPORT_OPENFIRMWARE 64 #include <sys/ioctl.h> 65 #include <dev/ofw/openfirmio.h> 66 #endif 67 68 #endif /* ! HAVE_NBTOOL_CONFIG_H */ 69 70 #include "installboot.h" 71 #include "evboards.h" 72 73 /* 74 * The board database is implemented as a property list. The base 75 * system provides a set of known boards, keyed by their "compatible" 76 * device tree property. 77 * 78 * The database provided by the base system is meant to help guide 79 * the user as to which u-boot package needs to be installed on the 80 * system in order to write the boot loader to the boot media. The 81 * base board plist is specific to the $MACHINE (e.g. "evbarm"), and 82 * is installed along with the build tools, e.g.: 83 * 84 * (native location) 85 * /usr/sbin/installboot 86 * /usr/share/installboot/evbarm/boards.plist 87 * /usr/share/installboot/evbmips/boards.plist 88 * 89 * (example cross host tool location) 90 * /usr/local/xnbsd/bin/nbinstallboot 91 * /usr/local/xnbsd/share/installboot/evbarm/boards.plist 92 * /usr/local/xnbsd/share/installboot/evbmips/boards.plist 93 * 94 * The schema of the base board plist is as follows: 95 * 96 * <plist> 97 * <dict> 98 * <!-- 99 * -- Key: string matching a "compatible" DT property. 100 * -- Value: dictionary representing a board object. 101 * -- (required) 102 * --> 103 * <key>example,example-board</key> 104 * <dict> 105 * <!-- 106 * -- Key: "description". 107 * -- Value: string containing the board description. 108 * -- (required) 109 * --> 110 * <key>description</key> 111 * <string>Example Co. Example Board</string> 112 * 113 * <!-- 114 * -- Key: "u-boot-pkg". 115 * -- Value: string representing the board-specific 116 * -- portion of the u-boot package name. 117 * -- In this example, the package's full name 118 * -- is "u-boot-exampleboard". This is used 119 * -- to recommend to the user which u-boot 120 * -- package to install. If not present, then 121 * -- no package recommendation will be made. 122 * -- (optional) 123 * --> 124 * <key>u-boot-pkg</key> 125 * <string>exampleboard</string> 126 * </dict> 127 * </dict> 128 * </plist> 129 * 130 * Individual u-boot packages install their own overlay property list 131 * files that installboot(8) then scans for. These overlay files are 132 * named "installboot.plist", and are installed alongside the u-boot 133 * binaries by the individual u-boot packages, for example: 134 * 135 * /usr/pkg/share/u-boot/exampleboard/installboot.plist 136 * /usr/pkg/share/u-boot/exampleboard/u-boot-with-spl.bin 137 * 138 * installboot(8) scans a set of directories looking for "installboot.plist" 139 * overlay files one directory deep. For example: 140 * 141 * /usr/pkg/share/u-boot/ 142 * exampleboard/installboot.plist 143 * superarmdeluxe/installboot.plist 144 * dummy/ 145 * 146 * In this example, "/usr/pkg/share/u-boot" is scanned, it would identify 147 * "exampleboard" and "superarmdeluxe" as directories containing overlays 148 * and load them. 149 * 150 * The default path scanned for u-boot packages is: 151 * 152 * /usr/pkg/share/u-boot 153 * 154 * This can be overridden with the INSTALLBOOT_UBOOT_PATHS environment 155 * variable, which contains a colon-sparated list of directories, e.g.: 156 * 157 * /usr/pkg/share/u-boot:/home/jmcneill/hackityhack/u-boot 158 * 159 * The scan only consults the top-level children of the specified directory. 160 * 161 * Each overlay includes complete board objects that entirely replace 162 * the system-provided board objects in memory. Some of the keys in 163 * overlay board objects are computed at run-time and should not appear 164 * in the plists loaded from the file system. 165 * 166 * The schema of the overlay board plists are as follows: 167 * 168 * <plist> 169 * <dict> 170 * <!-- 171 * -- Key: string matching a "compatible" DT property. 172 * -- Value: dictionary representing a board object. 173 * -- (required) 174 * --> 175 * <key>example,example-board</key> 176 * <dict> 177 * <!-- 178 * -- Key: "description". 179 * -- Value: string containing the board description. 180 * -- (required) 181 * --> 182 * <key>description</key> 183 * <string>Example Co. Example Board</string> 184 * 185 * <!-- 186 * -- Key: "u-boot-install". 187 * -- (and variants; see discussion below) 188 * -- "u-boot-install-emmc", etc.). 189 * -- Value: Array of u-boot installation step objects, 190 * -- as described below. 191 * -- (required) 192 * -- 193 * -- At least one of these objects is required. If the 194 * -- board uses a single set of steps for all boot media 195 * -- types, then it should provide just "u-boot-install". 196 * -- Otherwise, it whould provide one or more objects 197 * -- with names reflecting the media type, e.g.: 198 * -- 199 * -- "u-boot-install-sdmmc" (for SD cards) 200 * -- "u-boot-install-emmc" (for eMMC modules) 201 * -- "u-boot-install-usb" (for USB block storage) 202 * -- 203 * -- These installation steps will be selectable using 204 * -- the "media=..." option to installboot(8). 205 * --> 206 * <key>u-boot-install</key> 207 * <array> 208 * <!-- see installation object discussion below. --> 209 * </array> 210 * 211 * <!-- 212 * -- Key: "runtime-u-boot-path" 213 * -- Value: A string representing the path to the u-boot 214 * -- binary files needed to install the boot loader. 215 * -- This value is computed at run-time and is the 216 * -- same directory in which the instalboot.plist 217 * -- file for that u-boot package is located. 218 * -- This key/value pair should never be included 219 * -- in an installboot.plist file, and including it 220 * -- will cause the overlay to be rejected. 221 * -- (computed at run-time) 222 * --> 223 * <key>runtime-u-boot-path</key> 224 * <string>/usr/pkg/share/u-boot/exampleboard</string> 225 * </dict> 226 * </dict> 227 * </plist> 228 * 229 * The installation objects provide a description of the steps needed 230 * to install u-boot on the boot media. Each installation object it 231 * itself an array of step object. 232 * 233 * A basic installation object has a single step that instructs 234 * installboot(8) to write a file to a specific offset onto the 235 * boot media. 236 * 237 * <key>u-boot-install</key> 238 * <!-- installation object --> 239 * <array> 240 * <!-- step object --> 241 * <dict> 242 * <!-- 243 * -- Key: "file-name". 244 * -- Value: a string naming the file to be 245 * -- written to the media. 246 * -- (required) 247 * --> 248 * <key>file-name</key> 249 * <string>u-boot-with-spl.bin</string> 250 * 251 * <!-- 252 * -- Key: "image-offset". 253 * -- Value: an integer specifying the offset 254 * -- into the output image or device 255 * -- where to write the file. Defaults 256 * -- to 0 if not specified. 257 * -- (optional) 258 * --> 259 * <key>image-offset</key> 260 * <integer>8192</integer> 261 * </dict> 262 * </array> 263 * 264 * Some installations require multiple steps with special handling. 265 * 266 * <key>u-boot-install</key> 267 * <array> 268 * <-- 269 * -- Step 1: Write the initial portion of the boot 270 * -- loader onto the media. The loader has a "hole" 271 * -- to leave room for the MBR partition table. Take 272 * -- care not to scribble over the table. 273 * --> 274 * <dict> 275 * <key>file-name</key> 276 * <string>u-boot-img.bin</string> 277 * 278 * <!-- 279 * -- Key: "file-size". 280 * -- Value: an integer specifying the amount of 281 * -- data from the file to be written to the 282 * -- output. Defaults to "to end of file" if 283 * -- not specified. 284 * -- (optional) 285 * --> 286 * <!-- Stop short of the MBR partition table. --> 287 * <key>file-size</key> 288 * <integer>442</integer> 289 * 290 * <!-- 291 * -- Key: "preserve". 292 * -- Value: a boolean indicating that any partial 293 * -- output block should preserve any pre- 294 * -- existing contents of that block for 295 * -- the portion of the of the block not 296 * -- overwritten by the input file. 297 * -- (read-modify-write) 298 * -- (optional) 299 * --> 300 * <!-- Preserve the MBR partition table. --> 301 * <key>preserve</key> 302 * <true/> 303 * </dict> 304 * <-- 305 * -- Step 2: Write the rest of the loader after the 306 * -- MBR partition table. 307 * --> 308 * <dict> 309 * <key>file-name</key> 310 * <string>u-boot-img.bin</string> 311 * 312 * <!-- 313 * -- Key: "file-offset". 314 * -- Value: an integer specifying the offset into 315 * -- the input file from where to start 316 * -- copying to the output. 317 * -- (optional) 318 * --> 319 * <key>file-offset</key> 320 * <integer>512</integer> 321 * 322 * <!-- ...just after the MBR partition talble. --> 323 * <key>image-offset</key> 324 * <integer>512</integer> 325 * </dict> 326 * </array> 327 */ 328 329 /* 330 * make_path -- 331 * Build a path into the given buffer with the specified 332 * format. Returns NULL if the path won't fit. 333 */ 334 static __printflike(3,4) const char * 335 make_path(char *buf, size_t bufsize, const char *fmt, ...) 336 { 337 va_list ap; 338 int ret; 339 340 va_start(ap, fmt); 341 ret = vsnprintf(buf, bufsize, fmt, ap); 342 va_end(ap); 343 344 if (ret < 0 || (size_t)ret >= bufsize) 345 return NULL; 346 347 return buf; 348 } 349 350 #ifndef EVBOARDS_PLIST_BASE 351 #define EVBOARDS_PLIST_BASE "/usr" 352 #endif 353 354 static const char evb_db_base_location[] = 355 EVBOARDS_PLIST_BASE "/share/installboot"; 356 357 #ifndef DEFAULT_UBOOT_PKG_PATH 358 #define DEFAULT_UBOOT_PKG_PATH "/usr/pkg/share/u-boot" 359 #endif 360 361 #ifndef UBOOT_PATHS_ENV_VAR 362 #define UBOOT_PATHS_ENV_VAR "INSTALLBOOT_UBOOT_PATHS" 363 #endif 364 365 static const char evb_uboot_pkg_path[] = DEFAULT_UBOOT_PKG_PATH; 366 367 /* 368 * evb_db_base_path -- 369 * Returns the path to the base board db file. 370 */ 371 static const char * 372 evb_db_base_path(ib_params *params, char *buf, size_t bufsize) 373 { 374 375 return make_path(buf, bufsize, "%s/%s/boards.plist", 376 evb_db_base_location, params->machine->name); 377 } 378 379 /* 380 * evb_uboot_pkg_paths -- 381 * Returns an array of u-boot package paths to scan for 382 * installboot.plist files. 383 * 384 * Number of array elements, not including the NULL terminator, 385 * is returned in *countp. 386 * 387 * The working buffer is returned in *bufp so that the caller 388 * can free it. 389 */ 390 static char ** 391 evb_uboot_pkg_paths(ib_params *params, int *countp, void **bufp) 392 { 393 char **ret_array = NULL; 394 char *buf = NULL; 395 const char *pathspec; 396 int i, count; 397 char *cp, *startcp;; 398 399 pathspec = getenv(UBOOT_PATHS_ENV_VAR); 400 if (pathspec == NULL) 401 pathspec = evb_uboot_pkg_path; 402 403 if (strlen(pathspec) == 0) 404 goto out; 405 406 /* Count the path elements. */ 407 for (cp = __UNCONST(pathspec), count = 0;;) { 408 count++; 409 cp = strchr(cp, ':'); 410 if (cp == NULL) 411 break; 412 cp++; 413 } 414 415 buf = malloc((sizeof(char *) * (count + 1)) + 416 strlen(pathspec) + 1); 417 if (buf == NULL) 418 goto out; 419 420 /* 421 * Because we want to follow the usual "paths are listed in priority 422 * order" semantics, we reverse the order of the paths when we put 423 * them into the array we feed to fts. This is because we always 424 * overwrite existing entries as we find them, thus the last board 425 * object found one a given key is the one that will be used. 426 */ 427 428 ret_array = (char **)buf; 429 startcp = buf + (sizeof(char *) * (count + 1)); 430 /* this is a safe strcpy(); don't replace it. */ 431 strcpy(startcp, pathspec); 432 433 cp = strrchr(startcp, ':'); 434 if (cp == NULL) 435 cp = startcp; 436 437 for (i = 0;;) { 438 if (*cp == ':') { 439 ret_array[i++] = cp+1; 440 *cp-- = '\0'; 441 } else 442 ret_array[i++] = cp; 443 if (cp == startcp) 444 break; 445 cp = strrchr(cp, ':'); 446 if (cp == NULL) 447 cp = startcp; 448 } 449 assert(i == count); 450 ret_array[i] = NULL; 451 452 out: 453 if (ret_array == NULL) { 454 if (buf != NULL) 455 free(buf); 456 } else { 457 if (countp != NULL) 458 *countp = count; 459 if (bufp != NULL) 460 *bufp = buf; 461 } 462 return ret_array; 463 } 464 465 static const char step_file_name_key[] = "file-name"; 466 static const char step_file_offset_key[] = "file-offset"; 467 static const char step_file_size_key[] = "file-size"; 468 static const char step_image_offset_key[] = "image-offset"; 469 static const char step_preserve_key[] = "preserve"; 470 471 static bool 472 validate_ubstep_object(evb_ubstep obj) 473 { 474 /* 475 * evb_ubstep is a dictionary with the following keys: 476 * 477 * "file-name" (string) (required) 478 * "file-offset" (number) (optional) 479 * "file-size" (number) (optional) 480 * "image-offset" (number) (optional) 481 * "preserve" (bool) (optional) 482 */ 483 if (prop_object_type(obj) != PROP_TYPE_DICTIONARY) 484 return false; 485 486 prop_object_t v; 487 488 v = prop_dictionary_get(obj, step_file_name_key); 489 if (v == NULL || 490 prop_object_type(v) != PROP_TYPE_STRING) 491 return false; 492 493 v = prop_dictionary_get(obj, step_file_offset_key); 494 if (v != NULL && 495 prop_object_type(v) != PROP_TYPE_NUMBER) 496 return false; 497 498 v = prop_dictionary_get(obj, step_file_size_key); 499 if (v != NULL && 500 prop_object_type(v) != PROP_TYPE_NUMBER) 501 return false; 502 503 v = prop_dictionary_get(obj, step_image_offset_key); 504 if (v != NULL && 505 prop_object_type(v) != PROP_TYPE_NUMBER) 506 return false; 507 508 v = prop_dictionary_get(obj, step_preserve_key); 509 if (v != NULL && 510 prop_object_type(v) != PROP_TYPE_BOOL) 511 return false; 512 513 return true; 514 } 515 516 static bool 517 validate_ubinstall_object(evb_ubinstall obj) 518 { 519 /* 520 * evb_ubinstall is an array with one or more evb_ubstep 521 * objects. 522 * 523 * (evb_ubsteps is just a convenience type for iterating 524 * over the steps.) 525 */ 526 if (prop_object_type(obj) != PROP_TYPE_ARRAY) 527 return false; 528 if (prop_array_count(obj) < 1) 529 return false; 530 531 prop_object_t v; 532 prop_object_iterator_t iter = prop_array_iterator(obj); 533 534 while ((v = prop_object_iterator_next(iter)) != NULL) { 535 if (!validate_ubstep_object(v)) 536 break; 537 } 538 539 prop_object_iterator_release(iter); 540 return v == NULL; 541 } 542 543 static const char board_description_key[] = "description"; 544 static const char board_u_boot_pkg_key[] = "u-boot-pkg"; 545 static const char board_u_boot_path_key[] = "runtime-u-boot-path"; 546 static const char board_u_boot_install_key[] = "u-boot-install"; 547 548 static bool 549 validate_board_object(evb_board obj, bool is_overlay) 550 { 551 /* 552 * evb_board is a dictionary with the following keys: 553 * 554 * "description" (string) (required) 555 * "u-boot-pkg" (string) (optional, base only) 556 * "runtime-u-boot-path" (string) (required, overlay only) 557 * 558 * With special consideration for these keys: 559 * 560 * Either this key and no other "u-boot-install*" keys: 561 * "u-boot-install" (string) (required, overlay only) 562 * 563 * Or one or more keys of the following pattern: 564 * "u-boot-install-*" (string) (required, overlay only) 565 */ 566 bool has_default_install = false; 567 bool has_media_install = false; 568 569 if (prop_object_type(obj) != PROP_TYPE_DICTIONARY) 570 return false; 571 572 prop_object_t v; 573 574 v = prop_dictionary_get(obj, board_description_key); 575 if (v == NULL || 576 prop_object_type(v) != PROP_TYPE_STRING) 577 return false; 578 579 v = prop_dictionary_get(obj, board_u_boot_pkg_key); 580 if (v != NULL && 581 (is_overlay || prop_object_type(v) != PROP_TYPE_STRING)) 582 return false; 583 584 /* 585 * "runtime-u-boot-path" is added to an overlay after we've 586 * validated the board object, so simply make sure it's not 587 * present. 588 */ 589 v = prop_dictionary_get(obj, board_u_boot_path_key); 590 if (v != NULL) 591 return false; 592 593 prop_object_iterator_t iter = prop_dictionary_iterator(obj); 594 prop_dictionary_keysym_t key; 595 while ((key = prop_object_iterator_next(iter)) != NULL) { 596 const char *cp = prop_dictionary_keysym_cstring_nocopy(key); 597 if (strcmp(cp, board_u_boot_install_key) == 0) { 598 has_default_install = true; 599 } else if (strncmp(cp, board_u_boot_install_key, 600 sizeof(board_u_boot_install_key) - 1) == 0 && 601 cp[sizeof(board_u_boot_install_key) - 1] == '-') { 602 has_media_install = true; 603 } else { 604 continue; 605 } 606 v = prop_dictionary_get_keysym(obj, key); 607 assert(v != NULL); 608 if (!is_overlay || !validate_ubinstall_object(v)) 609 break; 610 } 611 prop_object_iterator_release(iter); 612 if (key != NULL) 613 return false; 614 615 /* 616 * Overlays must have only a default install key OR one or more 617 * media install keys. 618 */ 619 if (is_overlay) 620 return has_default_install ^ has_media_install; 621 622 /* 623 * Base board objects must have neither. 624 */ 625 return (has_default_install | has_media_install) == false; 626 } 627 628 /* 629 * evb_db_load_overlay -- 630 * Load boards from an overlay file into the db. 631 */ 632 static void 633 evb_db_load_overlay(ib_params *params, const char *path, 634 const char *runtime_uboot_path) 635 { 636 prop_dictionary_t overlay; 637 struct stat sb; 638 639 if (params->flags & IB_VERBOSE) 640 printf("Loading '%s'.\n", path); 641 642 if (stat(path, &sb) < 0) { 643 warn("'%s'", path); 644 return; 645 } else { 646 overlay = prop_dictionary_internalize_from_file(path); 647 if (overlay == NULL) { 648 warnx("unable to parse overlay '%s'", path); 649 return; 650 } 651 } 652 653 /* 654 * Validate all of the board objects and add them to the board 655 * db, replacing any existing entries as we go. 656 */ 657 prop_object_iterator_t iter = prop_dictionary_iterator(overlay); 658 prop_dictionary_keysym_t key; 659 prop_dictionary_t board; 660 while ((key = prop_object_iterator_next(iter)) != NULL) { 661 board = prop_dictionary_get_keysym(overlay, key); 662 assert(board != NULL); 663 if (!validate_board_object(board, true)) { 664 warnx("invalid board object in '%s': '%s'", path, 665 prop_dictionary_keysym_cstring_nocopy(key)); 666 continue; 667 } 668 669 /* Add "runtime-u-boot-path". */ 670 prop_string_t string = 671 prop_string_create_cstring(runtime_uboot_path); 672 assert(string != NULL); 673 prop_dictionary_set(board, board_u_boot_path_key, string); 674 prop_object_release(string); 675 676 /* Insert into board db. */ 677 prop_dictionary_set_keysym(params->mach_data, key, board); 678 } 679 prop_object_iterator_release(iter); 680 prop_object_release(overlay); 681 } 682 683 /* 684 * evb_db_load_overlays -- 685 * Load the overlays from the search path. 686 */ 687 static void 688 evb_db_load_overlays(ib_params *params) 689 { 690 char overlay_pathbuf[PATH_MAX+1]; 691 const char *overlay_path; 692 char **paths; 693 void *pathsbuf = NULL; 694 FTS *fts; 695 FTSENT *chp, *p; 696 struct stat sb; 697 698 paths = evb_uboot_pkg_paths(params, NULL, &pathsbuf); 699 if (paths == NULL) { 700 warnx("No u-boot search path?"); 701 return; 702 } 703 704 fts = fts_open(paths, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR, NULL); 705 if (fts == NULL || 706 (chp = fts_children(fts, 0)) == NULL) { 707 warn("Unable to search u-boot path"); 708 if (fts != NULL) 709 fts_close(fts); 710 return; 711 } 712 713 chp = fts_children(fts, 0); 714 715 while ((p = fts_read(fts)) != NULL) { 716 if (p->fts_info != FTS_D) 717 continue; 718 overlay_path = make_path(overlay_pathbuf, 719 sizeof(overlay_pathbuf), "%s/installboot.plist", 720 p->fts_path); 721 if (overlay_path == NULL) 722 continue; 723 if (stat(overlay_path, &sb) < 0) 724 continue; 725 evb_db_load_overlay(params, overlay_path, p->fts_path); 726 } 727 728 fts_close(fts); 729 730 /* 731 * If the user specifed a stage1 loader, then consult it last 732 * for a possible u-boot package location. 733 */ 734 if (params->stage1 != NULL) { 735 overlay_path = make_path(overlay_pathbuf, 736 sizeof(overlay_pathbuf), "%s/installboot.plist", 737 params->stage1); 738 if (overlay_path != NULL) { 739 if (stat(overlay_path, &sb) == 0) { 740 evb_db_load_overlay(params, overlay_path, 741 params->stage1); 742 } 743 } 744 } 745 } 746 747 /* 748 * evb_db_load_base -- 749 * Load the base board db. 750 */ 751 static bool 752 evb_db_load_base(ib_params *params) 753 { 754 char buf[PATH_MAX+1]; 755 const char *path; 756 prop_dictionary_t board_db; 757 struct stat sb; 758 759 path = evb_db_base_path(params, buf, sizeof(buf)); 760 if (path == NULL) 761 return false; 762 763 if (params->flags & IB_VERBOSE) 764 printf("Loading '%s'.\n", path); 765 766 if (stat(path, &sb) < 0) { 767 if (errno != ENOENT) { 768 warn("'%s'", path); 769 return false; 770 } 771 board_db = prop_dictionary_create(); 772 assert(board_db != NULL); 773 } else { 774 board_db = prop_dictionary_internalize_from_file(path); 775 if (board_db == NULL) { 776 warnx("unable to parse board db '%s'", path); 777 return false; 778 } 779 } 780 781 if (prop_dictionary_count(board_db) == 0) { 782 /* 783 * Oh well, maybe we'll load some overlays. 784 */ 785 goto done; 786 } 787 788 /* 789 * Validate all of the board objects and remove any bad ones. 790 */ 791 prop_array_t all_board_keys = prop_dictionary_all_keys(board_db); 792 prop_object_iterator_t iter = prop_array_iterator(all_board_keys); 793 prop_dictionary_keysym_t key; 794 prop_dictionary_t board; 795 while ((key = prop_object_iterator_next(iter)) != NULL) { 796 board = prop_dictionary_get_keysym(board_db, key); 797 assert(board != NULL); 798 if (!validate_board_object(board, false)) { 799 warnx("invalid board object in '%s': '%s'", path, 800 prop_dictionary_keysym_cstring_nocopy(key)); 801 prop_dictionary_remove_keysym(board_db, key); 802 } 803 } 804 prop_object_iterator_release(iter); 805 prop_object_release(all_board_keys); 806 807 done: 808 params->mach_data = board_db; 809 return true; 810 } 811 812 /* 813 * evb_db_load -- 814 * Load the board database. 815 */ 816 bool 817 evb_db_load(ib_params *params) 818 { 819 if (!evb_db_load_base(params)) 820 return false; 821 evb_db_load_overlays(params); 822 823 return true; 824 } 825 826 #if !HAVE_NBTOOL_CONFIG_H 827 /* 828 * Native board name guessing methods. 829 */ 830 831 #ifdef SUPPORT_OPENFIRMWARE 832 static int 833 ofw_fd(void) 834 { 835 static const char openfirm_path[] = "/dev/openfirm"; 836 837 return open(openfirm_path, O_RDONLY); 838 } 839 840 static int 841 OF_finddevice(const char *name) 842 { 843 struct ofiocdesc ofio = { 844 .of_name = __UNCONST(name), 845 .of_namelen = strlen(name), 846 }; 847 int fd = ofw_fd(); 848 849 if (fd == -1) 850 return -1; 851 852 if (ioctl(fd, OFIOCFINDDEVICE, &ofio) < 0) { 853 if (errno != ENOENT) 854 warn("OFIOCFINDDEVICE('%s')", name); 855 ofio.of_nodeid = -1; 856 } 857 (void) close(fd); 858 859 return ofio.of_nodeid; 860 } 861 862 static int 863 OF_getprop(int phandle, const char *prop, void *buf, size_t buflen) 864 { 865 struct ofiocdesc ofio = { 866 .of_nodeid = phandle, 867 .of_name = __UNCONST(prop), 868 .of_namelen = strlen(prop), 869 .of_buf = buf, 870 .of_buflen = buflen, 871 }; 872 int fd = ofw_fd(); 873 874 if (fd == -1) 875 return -1; 876 877 int save_errno = 0; 878 879 if (ioctl(fd, OFIOCGET, &ofio) < 0) { 880 save_errno = errno; 881 if (errno != ENOMEM && errno != ENOENT) { 882 save_errno = errno; 883 warn("OFIOCGET('%s')", prop); 884 } 885 ofio.of_buflen = -1; 886 } 887 (void) close(fd); 888 errno = save_errno; 889 890 return ofio.of_buflen; 891 } 892 893 static void * 894 ofw_getprop(int phandle, const char *prop, int *lenp) 895 { 896 size_t buflen = 32; 897 void *buf = NULL; 898 int len; 899 900 for (;;) { 901 void *newbuf = realloc(buf, buflen); 902 if (newbuf == NULL) { 903 free(buf); 904 return NULL; 905 } 906 buf = newbuf; 907 switch (len = OF_getprop(phandle, prop, buf, buflen)) { 908 case -1: 909 if (errno != ENOMEM) { 910 free(buf); 911 return NULL; 912 } 913 buflen *= 2; 914 break; 915 916 default: 917 if (lenp) 918 *lenp = len; 919 return buf; 920 } 921 } 922 } 923 924 static evb_board 925 evb_db_get_board_from_ofw(ib_params *params, const char **board_namep) 926 { 927 int phandle; 928 int compatible_len = 0; 929 char *compatible_buf; 930 const char *sp, *nsp; 931 evb_board board; 932 933 phandle = OF_finddevice("/"); 934 if (phandle == -1) { 935 /* No OpenFirmware available. */ 936 return NULL; 937 } 938 939 compatible_buf = ofw_getprop(phandle, "compatible", &compatible_len); 940 941 /* 942 * We just leak compatible_buf on success. Not a big deal since 943 * we are not a long-running process. 944 */ 945 946 sp = compatible_buf; 947 while (compatible_len && 948 (nsp = memchr(sp, 0, compatible_len)) != NULL) { 949 if (params->flags & IB_VERBOSE) 950 printf("Checking OFW compatible string '%s'.\n", sp); 951 board = prop_dictionary_get(params->mach_data, sp); 952 if (board != NULL) { 953 if (board_namep) 954 *board_namep = sp; 955 return board; 956 } 957 nsp++; /* skip over NUL */ 958 compatible_len -= (nsp - sp); 959 sp = nsp; 960 } 961 962 free(compatible_buf); 963 return NULL; 964 } 965 #endif /* SUPPORT_OPENFIRMWARE */ 966 967 #endif /* ! HAVE_NBTOOL_CONFIG_H */ 968 969 /* 970 * Host-tool and native board name guessing methods. 971 */ 972 973 #ifdef SUPPORT_FDT 974 static void * 975 load_dtb(ib_params *params) 976 { 977 struct stat sb; 978 void *buf; 979 int fd; 980 981 if (stat(params->dtb, &sb) < 0) { 982 warn("%s", params->dtb); 983 return NULL; 984 } 985 986 buf = malloc((size_t)sb.st_size); 987 assert(buf != NULL); 988 989 if ((fd = open(params->dtb, O_RDONLY)) < 0) { 990 warn("%s", params->dtb); 991 free(buf); 992 return NULL; 993 } 994 995 if (read(fd, buf, (size_t)sb.st_size) != (ssize_t)sb.st_size) { 996 warn("read '%s'", params->dtb); 997 free(buf); 998 buf = NULL; 999 } 1000 (void) close(fd); 1001 1002 return buf; 1003 } 1004 1005 static evb_board 1006 evb_db_get_board_from_dtb(ib_params *params, const char **board_namep) 1007 { 1008 evb_board board = NULL; 1009 void *fdt = NULL; 1010 int error; 1011 1012 fdt = load_dtb(params); 1013 if (fdt == NULL) 1014 return NULL; 1015 1016 error = fdt_check_header(fdt); 1017 if (error) { 1018 warnx("%s: %s", params->dtb, fdt_strerror(error)); 1019 goto bad; 1020 } 1021 1022 const int system_root = fdt_path_offset(fdt, "/"); 1023 if (system_root < 0) { 1024 warnx("%s: unable to find node '/'", params->dtb); 1025 goto bad; 1026 } 1027 1028 const int system_ncompat = fdt_stringlist_count(fdt, system_root, 1029 "compatible"); 1030 if (system_ncompat <= 0) { 1031 warnx("%s: no 'compatible' property on node '/'", params->dtb); 1032 goto bad; 1033 } 1034 1035 const char *compatible; 1036 int si; 1037 for (si = 0; si < system_ncompat; si++) { 1038 compatible = fdt_stringlist_get(fdt, system_root, 1039 "compatible", si, NULL); 1040 if (compatible == NULL) 1041 continue; 1042 if (params->flags & IB_VERBOSE) 1043 printf("Checking FDT compatible string '%s'.\n", 1044 compatible); 1045 board = prop_dictionary_get(params->mach_data, compatible); 1046 if (board != NULL) { 1047 /* 1048 * We just leak compatible on success. Not a big 1049 * deal since we are not a long-running process. 1050 */ 1051 if (board_namep) { 1052 *board_namep = strdup(compatible); 1053 assert(*board_namep != NULL); 1054 } 1055 free(fdt); 1056 return board; 1057 } 1058 } 1059 1060 bad: 1061 if (fdt != NULL) 1062 free(fdt); 1063 return NULL; 1064 } 1065 #endif /* SUPPORT_FDT */ 1066 1067 /* 1068 * evb_db_get_board -- 1069 * Return the specified board object from the database. 1070 */ 1071 evb_board 1072 evb_db_get_board(ib_params *params) 1073 { 1074 const char *board_name = NULL; 1075 evb_board board = NULL; 1076 1077 #if !HAVE_NBTOOL_CONFIG_H 1078 /* 1079 * If we're not a host tool, determine if we're running "natively". 1080 */ 1081 bool is_native = false; 1082 struct utsname utsname; 1083 1084 if (uname(&utsname) < 0) { 1085 warn("uname"); 1086 } else if (strcmp(utsname.machine, params->machine->name) == 0) { 1087 is_native = true; 1088 } 1089 #endif /* ! HAVE_NBTOOL_CONFIG_H */ 1090 1091 /* 1092 * Logic for determing board type that can be shared by host-tool 1093 * and native builds goes here. 1094 */ 1095 1096 /* 1097 * Command-line argument trumps all. 1098 */ 1099 if (params->flags & IB_BOARD) { 1100 board_name = params->board; 1101 } 1102 1103 #ifdef SUPPORT_FDT 1104 if (board_name == NULL && (params->flags & IB_DTB)) { 1105 board = evb_db_get_board_from_dtb(params, &board_name); 1106 if ((params->flags & IB_VERBOSE) && board != NULL) 1107 printf("Found board '%s' from DTB data.\n", board_name); 1108 #if !HAVE_NBTOOL_CONFIG_H 1109 /* 1110 * If the user specified a DTB, then regardless of the 1111 * outcome, this is like specifying the board directly, 1112 * so native checks should be skipped. 1113 */ 1114 is_native = false; 1115 #endif /* ! HAVE_NBTOOL_CONFIG_H */ 1116 } 1117 #endif /* SUPPORT_FDT */ 1118 1119 #if !HAVE_NBTOOL_CONFIG_H 1120 /* 1121 * Non-host-tool logic for determining the board type goes here. 1122 */ 1123 1124 #ifdef SUPPORT_OPENFIRMWARE 1125 if (board_name == NULL && is_native) { 1126 board = evb_db_get_board_from_ofw(params, &board_name); 1127 if ((params->flags & IB_VERBOSE) && board != NULL) 1128 printf("Found board '%s' from OFW data.\n", board_name); 1129 } 1130 #endif /* SUPPORT_OPENFIRMWARE */ 1131 1132 /* Ensure is_native is consumed. */ 1133 if (is_native == false) 1134 is_native = false; 1135 1136 #endif /* ! HAVE_NBTOOL_CONFIG_H */ 1137 1138 /* 1139 * If all else fails, we can always rely on the user, right? 1140 */ 1141 if (board_name == NULL) { 1142 if (!(params->flags & IB_BOARD)) { 1143 warnx("Must specify board=..."); 1144 return NULL; 1145 } 1146 board_name = params->board; 1147 } 1148 1149 assert(board_name != NULL); 1150 1151 if (board == NULL) 1152 board = prop_dictionary_get(params->mach_data, board_name); 1153 if (board == NULL) 1154 warnx("Unknown board '%s'", board_name); 1155 1156 /* Ensure params->board is always valid. */ 1157 params->board = board_name; 1158 1159 if (params->flags & IB_VERBOSE) { 1160 printf("Board: %s\n", evb_board_get_description(params, board)); 1161 } 1162 1163 return board; 1164 } 1165 1166 /* 1167 * evb_db_list_boards -- 1168 * Print the list of known boards to the specified output stream. 1169 */ 1170 void 1171 evb_db_list_boards(ib_params *params, FILE *out) 1172 { 1173 prop_object_iterator_t iter; 1174 prop_dictionary_keysym_t key; 1175 evb_board board; 1176 const char *uboot_pkg; 1177 const char *uboot_path; 1178 1179 /* 1180 * By default, we only list boards that we have a u-boot 1181 * package installed for, or if we know which package you 1182 * need to install. You get the full monty in verbose mode. 1183 */ 1184 1185 iter = prop_dictionary_iterator(params->mach_data); 1186 while ((key = prop_object_iterator_next(iter)) != NULL) { 1187 board = prop_dictionary_get_keysym(params->mach_data, key); 1188 assert(board != NULL); 1189 uboot_pkg = evb_board_get_uboot_pkg(params, board); 1190 uboot_path = evb_board_get_uboot_path(params, board); 1191 1192 if (uboot_pkg == NULL && uboot_path == NULL && 1193 !(params->flags & IB_VERBOSE)) 1194 continue; 1195 1196 fprintf(out, "%-30s %s\n", 1197 prop_dictionary_keysym_cstring_nocopy(key), 1198 evb_board_get_description(params, board)); 1199 1200 if ((params->flags & IB_VERBOSE) && uboot_path) { 1201 fprintf(out, "\t(u-boot package found at %s)\n", 1202 uboot_path); 1203 } else if ((params->flags & IB_VERBOSE) && uboot_pkg) { 1204 fprintf(out, 1205 "\t(install the sysutils/u-boot-%s package)\n", 1206 uboot_pkg); 1207 } 1208 } 1209 prop_object_iterator_release(iter); 1210 } 1211 1212 /* 1213 * evb_board_get_description -- 1214 * Return the description for the specified board. 1215 */ 1216 const char * 1217 evb_board_get_description(ib_params *params, evb_board board) 1218 { 1219 prop_string_t string; 1220 1221 string = prop_dictionary_get(board, board_description_key); 1222 return prop_string_cstring_nocopy(string); 1223 } 1224 1225 /* 1226 * evb_board_get_uboot_pkg -- 1227 * Return the u-boot package name for the specified board. 1228 */ 1229 const char * 1230 evb_board_get_uboot_pkg(ib_params *params, evb_board board) 1231 { 1232 prop_string_t string; 1233 1234 string = prop_dictionary_get(board, board_u_boot_pkg_key); 1235 if (string == NULL) 1236 return NULL; 1237 return prop_string_cstring_nocopy(string); 1238 } 1239 1240 /* 1241 * evb_board_get_uboot_path -- 1242 * Return the u-boot installed package path for the specified board. 1243 */ 1244 const char * 1245 evb_board_get_uboot_path(ib_params *params, evb_board board) 1246 { 1247 prop_string_t string; 1248 1249 string = prop_dictionary_get(board, board_u_boot_path_key); 1250 if (string == NULL) 1251 return NULL; 1252 return prop_string_cstring_nocopy(string); 1253 } 1254 1255 /* 1256 * evb_board_get_uboot_install -- 1257 * Return the u-boot install object for the specified board, 1258 * corresponding to the media specified by the user. 1259 */ 1260 evb_ubinstall 1261 evb_board_get_uboot_install(ib_params *params, evb_board board) 1262 { 1263 evb_ubinstall install; 1264 1265 install = prop_dictionary_get(board, board_u_boot_install_key); 1266 1267 if (!(params->flags & IB_MEDIA)) { 1268 if (install == NULL) { 1269 warnx("Must specify media=... for board '%s'", 1270 params->board); 1271 goto list_media; 1272 } 1273 return install; 1274 } 1275 1276 /* media=... was specified by the user. */ 1277 1278 if (install) { 1279 warnx("media=... is not a valid option for board '%s'", 1280 params->board); 1281 return NULL; 1282 } 1283 1284 char install_key[128]; 1285 int n = snprintf(install_key, sizeof(install_key), "%s-%s", 1286 board_u_boot_install_key, params->media); 1287 if (n < 0 || (size_t)n >= sizeof(install_key)) 1288 goto invalid_media;; 1289 install = prop_dictionary_get(board, install_key); 1290 if (install != NULL) 1291 return install; 1292 invalid_media: 1293 warnx("invalid media specification: '%s'", params->media); 1294 list_media: 1295 fprintf(stderr, "Valid media types:"); 1296 prop_array_t array = evb_board_copy_uboot_media(params, board); 1297 assert(array != NULL); 1298 prop_object_iterator_t iter = prop_array_iterator(array); 1299 prop_string_t string; 1300 while ((string = prop_object_iterator_next(iter)) != NULL) 1301 fprintf(stderr, " %s", prop_string_cstring_nocopy(string)); 1302 fprintf(stderr, "\n"); 1303 prop_object_iterator_release(iter); 1304 prop_object_release(array); 1305 1306 return NULL; 1307 } 1308 1309 /* 1310 * evb_board_copy_uboot_media -- 1311 * Return the valid media types for the given board as an array 1312 * of strings. 1313 * 1314 * Follows the create rule; caller is responsible for releasing 1315 * the array. 1316 */ 1317 prop_array_t 1318 evb_board_copy_uboot_media(ib_params *params, evb_board board) 1319 { 1320 prop_array_t array = prop_array_create(); 1321 prop_object_iterator_t iter = prop_dictionary_iterator(board); 1322 prop_string_t string; 1323 prop_dictionary_keysym_t key; 1324 const char *cp; 1325 1326 assert(array != NULL); 1327 assert(iter != NULL); 1328 1329 while ((key = prop_object_iterator_next(iter)) != NULL) { 1330 cp = prop_dictionary_keysym_cstring_nocopy(key); 1331 if (strcmp(cp, board_u_boot_install_key) == 0 || 1332 strncmp(cp, board_u_boot_install_key, 1333 sizeof(board_u_boot_install_key) - 1) != 0) 1334 continue; 1335 string = prop_string_create_cstring(strrchr(cp, '-')+1); 1336 assert(string != NULL); 1337 prop_array_add(array, string); 1338 prop_object_release(string); 1339 } 1340 prop_object_iterator_release(iter); 1341 return array; 1342 } 1343 1344 /* 1345 * evb_ubinstall_get_steps -- 1346 * Get the install steps for a given install object. 1347 */ 1348 evb_ubsteps 1349 evb_ubinstall_get_steps(ib_params *params, evb_ubinstall install) 1350 { 1351 return prop_array_iterator(install); 1352 } 1353 1354 /* 1355 * evb_ubsteps_next_step -- 1356 * Return the next step in the install object. 1357 * 1358 * N.B. The iterator is released upon termination. 1359 */ 1360 evb_ubstep 1361 evb_ubsteps_next_step(ib_params *params, evb_ubsteps steps) 1362 { 1363 prop_dictionary_t step = prop_object_iterator_next(steps); 1364 1365 /* If we are out of steps, release the iterator. */ 1366 if (step == NULL) 1367 prop_object_iterator_release(steps); 1368 1369 return step; 1370 } 1371 1372 /* 1373 * evb_ubstep_get_file_name -- 1374 * Returns the input file name for the step. 1375 */ 1376 const char * 1377 evb_ubstep_get_file_name(ib_params *params, evb_ubstep step) 1378 { 1379 prop_string_t string = prop_dictionary_get(step, step_file_name_key); 1380 return prop_string_cstring_nocopy(string); 1381 } 1382 1383 /* 1384 * evb_ubstep_get_file_offset -- 1385 * Returns the input file offset for the step. 1386 */ 1387 uint64_t 1388 evb_ubstep_get_file_offset(ib_params *params, evb_ubstep step) 1389 { 1390 prop_number_t number = prop_dictionary_get(step, step_file_offset_key); 1391 if (number != NULL) 1392 return prop_number_unsigned_integer_value(number); 1393 return 0; 1394 } 1395 1396 /* 1397 * evb_ubstep_get_file_size -- 1398 * Returns the size of the input file to copy for this step, or 1399 * zero if the remainder of the file should be copied. 1400 */ 1401 uint64_t 1402 evb_ubstep_get_file_size(ib_params *params, evb_ubstep step) 1403 { 1404 prop_number_t number = prop_dictionary_get(step, step_file_size_key); 1405 if (number != NULL) 1406 return prop_number_unsigned_integer_value(number); 1407 return 0; 1408 } 1409 1410 /* 1411 * evb_ubstep_get_image_offset -- 1412 * Returns the offset into the destination image / device to 1413 * copy the input file. 1414 */ 1415 uint64_t 1416 evb_ubstep_get_image_offset(ib_params *params, evb_ubstep step) 1417 { 1418 prop_number_t number = prop_dictionary_get(step, step_image_offset_key); 1419 if (number != NULL) 1420 return prop_number_unsigned_integer_value(number); 1421 return 0; 1422 } 1423 1424 /* 1425 * evb_ubstep_preserves_partial_block -- 1426 * Returns true if the step preserves a partial block. 1427 */ 1428 bool 1429 evb_ubstep_preserves_partial_block(ib_params *params, evb_ubstep step) 1430 { 1431 prop_bool_t val = prop_dictionary_get(step, step_preserve_key); 1432 if (val != NULL) 1433 return prop_bool_true(val); 1434 return false; 1435 } 1436 1437 /* 1438 * evb_uboot_file_path -- 1439 * Build a file path from the u-boot base path in the board object 1440 * and the file name in the step object. 1441 */ 1442 static const char * 1443 evb_uboot_file_path(ib_params *params, evb_board board, evb_ubstep step, 1444 char *buf, size_t bufsize) 1445 { 1446 const char *base_path = evb_board_get_uboot_path(params, board); 1447 const char *file_name = evb_ubstep_get_file_name(params, step); 1448 1449 if (base_path == NULL || file_name == NULL) 1450 return NULL; 1451 1452 return make_path(buf, bufsize, "%s/%s", base_path, file_name); 1453 } 1454 1455 /* 1456 * evb_uboot_do_step -- 1457 * Given a evb_ubstep, do the deed. 1458 */ 1459 static int 1460 evb_uboot_do_step(ib_params *params, const char *uboot_file, evb_ubstep step) 1461 { 1462 struct stat sb; 1463 int ifd = -1; 1464 char *blockbuf; 1465 size_t thisblock; 1466 off_t curoffset; 1467 off_t remaining; 1468 bool rv = false; 1469 1470 uint64_t file_size = evb_ubstep_get_file_size(params, step); 1471 uint64_t file_offset = evb_ubstep_get_file_offset(params, step); 1472 uint64_t image_offset = evb_ubstep_get_image_offset(params, step); 1473 1474 blockbuf = malloc(params->sectorsize); 1475 if (blockbuf == NULL) 1476 goto out; 1477 1478 ifd = open(uboot_file, O_RDONLY); 1479 if (ifd < 0) { 1480 warn("open '%s'", uboot_file); 1481 goto out; 1482 } 1483 if (fstat(ifd, &sb) < 0) { 1484 warn("fstat '%s'", uboot_file); 1485 goto out; 1486 } 1487 1488 if (file_size) 1489 remaining = (off_t)file_size; 1490 else 1491 remaining = sb.st_size - (off_t)file_offset; 1492 1493 if (params->flags & IB_VERBOSE) { 1494 if (file_offset) { 1495 printf("Writing '%s' -- %lld @ %" PRIu64 " ==> %" PRIu64 "\n", 1496 evb_ubstep_get_file_name(params, step), 1497 (long long)remaining, file_offset, image_offset); 1498 } else { 1499 printf("Writing '%s' -- %lld ==> %" PRIu64 "\n", 1500 evb_ubstep_get_file_name(params, step), 1501 (long long)remaining, image_offset); 1502 } 1503 } 1504 1505 if (lseek(ifd, (off_t)file_offset, SEEK_SET) < 0) { 1506 warn("lseek '%s' @ %" PRIu64, uboot_file, 1507 file_offset); 1508 goto out; 1509 } 1510 1511 for (curoffset = (off_t)image_offset; remaining > 0; 1512 remaining -= thisblock, curoffset += params->sectorsize) { 1513 thisblock = params->sectorsize; 1514 if ((off_t)thisblock > remaining) 1515 thisblock = (size_t)remaining; 1516 if ((thisblock % params->sectorsize) != 0) { 1517 memset(blockbuf, 0, params->sectorsize); 1518 if (evb_ubstep_preserves_partial_block(params, step)) { 1519 if (params->flags & IB_VERBOSE) { 1520 printf("(Reading '%s' -- %u @ %lld)\n", 1521 params->filesystem, 1522 params->sectorsize, 1523 (long long)curoffset); 1524 } 1525 if (pread(params->fsfd, blockbuf, 1526 params->sectorsize, curoffset) < 0) { 1527 warn("pread '%s'", params->filesystem); 1528 goto out; 1529 } 1530 } 1531 } 1532 if (read(ifd, blockbuf, thisblock) != (ssize_t)thisblock) { 1533 warn("read '%s'", uboot_file); 1534 goto out; 1535 } 1536 if (!(params->flags & IB_NOWRITE) && 1537 pwrite(params->fsfd, blockbuf, params->sectorsize, 1538 curoffset) != (ssize_t)params->sectorsize) { 1539 warn("pwrite '%s'", params->filesystem); 1540 goto out; 1541 } 1542 } 1543 1544 /* Success! */ 1545 rv = true; 1546 1547 out: 1548 if (ifd != -1 && close(ifd) == -1) 1549 warn("close '%s'", uboot_file); 1550 if (blockbuf) 1551 free(blockbuf); 1552 return rv; 1553 } 1554 1555 int 1556 evb_uboot_setboot(ib_params *params, evb_board board) 1557 { 1558 char uboot_filebuf[PATH_MAX+1]; 1559 const char *uboot_file; 1560 struct stat sb; 1561 off_t max_offset = 0; 1562 1563 /* 1564 * If we don't have a u-boot path for this board, it means 1565 * that a u-boot package wasn't found. Prompt the user to 1566 * install it. 1567 */ 1568 if (evb_board_get_uboot_path(params, board) == NULL) { 1569 warnx("No u-boot package found for board '%s'", 1570 params->board); 1571 uboot_file = evb_board_get_uboot_pkg(params, board); 1572 if (uboot_file != NULL) 1573 warnx("Please install the sysutils/u-boot-%s package.", 1574 uboot_file); 1575 return 0; 1576 } 1577 1578 evb_ubinstall install = evb_board_get_uboot_install(params, board); 1579 evb_ubsteps steps; 1580 evb_ubstep step; 1581 1582 if (install == NULL) 1583 return 0; 1584 1585 /* 1586 * First, make sure the files are all there. While we're 1587 * at it, calculate the largest byte offset that we will 1588 * be writing. 1589 */ 1590 steps = evb_ubinstall_get_steps(params, install); 1591 while ((step = evb_ubsteps_next_step(params, steps)) != NULL) { 1592 uint64_t file_offset = evb_ubstep_get_file_offset(params, step); 1593 uint64_t file_size = evb_ubstep_get_file_size(params, step); 1594 uint64_t image_offset = 1595 evb_ubstep_get_image_offset(params, step); 1596 uboot_file = evb_uboot_file_path(params, board, step, 1597 uboot_filebuf, sizeof(uboot_filebuf)); 1598 if (uboot_file == NULL) 1599 return 0; 1600 if (stat(uboot_file, &sb) < 0) { 1601 warn("%s", uboot_file); 1602 return 0; 1603 } 1604 if (!S_ISREG(sb.st_mode)) { 1605 warnx("%s: %s", uboot_file, strerror(EFTYPE)); 1606 return 0; 1607 } 1608 off_t this_max; 1609 if (file_size) 1610 this_max = file_size; 1611 else 1612 this_max = sb.st_size - file_offset; 1613 this_max += image_offset; 1614 if (max_offset < this_max) 1615 max_offset = this_max; 1616 } 1617 1618 /* 1619 * Ok, we've verified that all of the files are there, and now 1620 * max_offset points to the first byte that's available for a 1621 * partition containing a file system. 1622 */ 1623 1624 off_t rounded_max_offset = (off_t)(max_offset / params->sectorsize) * 1625 params->sectorsize; 1626 if (rounded_max_offset != max_offset) 1627 rounded_max_offset += params->sectorsize; 1628 1629 if (params->flags & IB_VERBOSE) { 1630 printf("Max u-boot offset (rounded): %lld (%lld)\n", 1631 (long long)max_offset, (long long)rounded_max_offset); 1632 printf("First free block available for file systems: " 1633 "%lld (0x%llx)\n", 1634 (long long)rounded_max_offset / params->sectorsize, 1635 (long long)rounded_max_offset / params->sectorsize); 1636 } 1637 1638 /* XXX Check MBR table for overlapping partitions. */ 1639 1640 /* 1641 * Now write each binary component to the appropriate location 1642 * on disk. 1643 */ 1644 steps = evb_ubinstall_get_steps(params, install); 1645 while ((step = evb_ubsteps_next_step(params, steps)) != NULL) { 1646 uboot_file = evb_uboot_file_path(params, board, step, 1647 uboot_filebuf, sizeof(uboot_filebuf)); 1648 if (uboot_file == NULL) 1649 return 0; 1650 if (!evb_uboot_do_step(params, uboot_file, step)) 1651 return 0; 1652 } 1653 1654 return 1; 1655 } 1656