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