diff options
author | Simon Glass <sjg@chromium.org> | 2021-10-14 12:47:54 -0600 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2021-11-11 19:01:56 -0500 |
commit | 19a91f2464a89402a925fd4a2d8b7e28c804c7cc (patch) | |
tree | 35d48b06f479acac201447cb32fea47a78c87eb5 /boot | |
parent | 1e72ad6b387c599f477f83cda67ab525c089a9b0 (diff) | |
download | u-boot-19a91f2464a89402a925fd4a2d8b7e28c804c7cc.zip u-boot-19a91f2464a89402a925fd4a2d8b7e28c804c7cc.tar.gz u-boot-19a91f2464a89402a925fd4a2d8b7e28c804c7cc.tar.bz2 |
Create a new boot/ directory
Quite a lot of the code in common/relates to booting and images. Before
adding more it seems like a good time to move the code into its own
directory.
Most files with 'boot' or 'image' in them are moved, except:
- autoboot.c which relates to U-Boot automatically running a script
- bootstage.c which relates to U-Boot timing
Drop the removal of boot* files from the output directory, since this
interfers with the symlinks created by tools and there does not appear
to be any such file from my brief testing.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Artem Lapkin <email2tema@gmail.com>
Tested-by: Artem Lapkin <email2tema@gmail.com>
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Kconfig | 1036 | ||||
-rw-r--r-- | boot/Makefile | 34 | ||||
-rw-r--r-- | boot/android_ab.c | 305 | ||||
-rw-r--r-- | boot/boot_fit.c | 80 | ||||
-rw-r--r-- | boot/bootm.c | 1038 | ||||
-rw-r--r-- | boot/bootm_os.c | 653 | ||||
-rw-r--r-- | boot/bootretry.c | 60 | ||||
-rw-r--r-- | boot/common_fit.c | 86 | ||||
-rw-r--r-- | boot/fdt_region.c | 671 | ||||
-rw-r--r-- | boot/image-android-dt.c | 157 | ||||
-rw-r--r-- | boot/image-android.c | 539 | ||||
-rw-r--r-- | boot/image-board.c | 956 | ||||
-rw-r--r-- | boot/image-cipher.c | 176 | ||||
-rw-r--r-- | boot/image-fdt.c | 666 | ||||
-rw-r--r-- | boot/image-fit-sig.c | 486 | ||||
-rw-r--r-- | boot/image-fit.c | 2448 | ||||
-rw-r--r-- | boot/image-host.c | 27 | ||||
-rw-r--r-- | boot/image-sig.c | 136 | ||||
-rw-r--r-- | boot/image.c | 738 |
19 files changed, 10292 insertions, 0 deletions
diff --git a/boot/Kconfig b/boot/Kconfig new file mode 100644 index 0000000..a8d4be2 --- /dev/null +++ b/boot/Kconfig @@ -0,0 +1,1036 @@ +menu "Boot options" + +menu "Boot images" + +config ANDROID_BOOT_IMAGE + bool "Enable support for Android Boot Images" + default y if FASTBOOT + help + This enables support for booting images which use the Android + image format header. + +config FIT + bool "Support Flattened Image Tree" + select HASH + select MD5 + select SHA1 + imply SHA256 + help + This option allows you to boot the new uImage structure, + Flattened Image Tree. FIT is formally a FDT, which can include + images of various types (kernel, FDT blob, ramdisk, etc.) + in a single blob. To boot this new uImage structure, + pass the address of the blob to the "bootm" command. + FIT is very flexible, supporting compression, multiple images, + multiple configurations, verification through hashing and also + verified boot (secure boot using RSA). + +if FIT + +config FIT_EXTERNAL_OFFSET + hex "FIT external data offset" + default 0x0 + help + This specifies a data offset in fit image. + The offset is from data payload offset to the beginning of + fit image header. When specifies a offset, specific data + could be put in the hole between data payload and fit image + header, such as CSF data on i.MX platform. + +config FIT_FULL_CHECK + bool "Do a full check of the FIT before using it" + default y + help + Enable this do a full check of the FIT to make sure it is valid. This + helps to protect against carefully crafted FITs which take advantage + of bugs or omissions in the code. This includes a bad structure, + multiple root nodes and the like. + +config FIT_SIGNATURE + bool "Enable signature verification of FIT uImages" + depends on DM + select HASH + imply RSA + imply RSA_VERIFY + select IMAGE_SIGN_INFO + select FIT_FULL_CHECK + help + This option enables signature verification of FIT uImages, + using a hash signed and verified using RSA. If + CONFIG_SHA_PROG_HW_ACCEL is defined, i.e support for progressive + hashing is available using hardware, then the RSA library will use + it. See doc/uImage.FIT/signature.txt for more details. + + WARNING: When relying on signed FIT images with a required signature + check the legacy image format is disabled by default, so that + unsigned images cannot be loaded. If a board needs the legacy image + format support in this case, enable it using + CONFIG_LEGACY_IMAGE_FORMAT. + +config FIT_SIGNATURE_MAX_SIZE + hex "Max size of signed FIT structures" + depends on FIT_SIGNATURE + default 0x10000000 + help + This option sets a max size in bytes for verified FIT uImages. + A sane value of 256MB protects corrupted DTB structures from overlapping + device memory. Assure this size does not extend past expected storage + space. + +config FIT_RSASSA_PSS + bool "Support rsassa-pss signature scheme of FIT image contents" + depends on FIT_SIGNATURE + help + Enable this to support the pss padding algorithm as described + in the rfc8017 (https://tools.ietf.org/html/rfc8017). + +config FIT_CIPHER + bool "Enable ciphering data in a FIT uImages" + depends on DM + select AES + help + Enable the feature of data ciphering/unciphering in the tool mkimage + and in the u-boot support of the FIT image. + +config FIT_VERBOSE + bool "Show verbose messages when FIT images fail" + help + Generally a system will have valid FIT images so debug messages + are a waste of code space. If you are debugging your images then + you can enable this option to get more verbose information about + failures. + +config FIT_BEST_MATCH + bool "Select the best match for the kernel device tree" + help + When no configuration is explicitly selected, default to the + one whose fdt's compatibility field best matches that of + U-Boot itself. A match is considered "best" if it matches the + most specific compatibility entry of U-Boot's fdt's root node. + The order of entries in the configuration's fdt is ignored. + +config FIT_IMAGE_POST_PROCESS + bool "Enable post-processing of FIT artifacts after loading by U-Boot" + depends on TI_SECURE_DEVICE || SOCFPGA_SECURE_VAB_AUTH + help + Allows doing any sort of manipulation to blobs after they got extracted + from FIT images like stripping off headers or modifying the size of the + blob, verification, authentication, decryption etc. in a platform or + board specific way. In order to use this feature a platform or board- + specific implementation of board_fit_image_post_process() must be + provided. Also, anything done during this post-processing step would + need to be comprehended in how the images were prepared before being + injected into the FIT creation (i.e. the blobs would have been pre- + processed before being added to the FIT image). + +config FIT_PRINT + bool "Support FIT printing" + default y + help + Support printing the content of the fitImage in a verbose manner. + +if SPL + +config SPL_FIT + bool "Support Flattened Image Tree within SPL" + depends on SPL + select SPL_HASH + select SPL_OF_LIBFDT + +config SPL_FIT_PRINT + bool "Support FIT printing within SPL" + depends on SPL_FIT + help + Support printing the content of the fitImage in a verbose manner in SPL. + +config SPL_FIT_FULL_CHECK + bool "Do a full check of the FIT before using it" + help + Enable this do a full check of the FIT to make sure it is valid. This + helps to protect against carefully crafted FITs which take advantage + of bugs or omissions in the code. This includes a bad structure, + multiple root nodes and the like. + + +config SPL_FIT_SIGNATURE + bool "Enable signature verification of FIT firmware within SPL" + depends on SPL_DM + depends on SPL_LOAD_FIT || SPL_LOAD_FIT_FULL + select FIT_SIGNATURE + select SPL_FIT + select SPL_CRYPTO + select SPL_HASH + imply SPL_RSA + imply SPL_RSA_VERIFY + select SPL_IMAGE_SIGN_INFO + select SPL_FIT_FULL_CHECK + +config SPL_FIT_SIGNATURE_MAX_SIZE + hex "Max size of signed FIT structures in SPL" + depends on SPL_FIT_SIGNATURE + default 0x10000000 + help + This option sets a max size in bytes for verified FIT uImages. + A sane value of 256MB protects corrupted DTB structures from overlapping + device memory. Assure this size does not extend past expected storage + space. + +config SPL_FIT_RSASSA_PSS + bool "Support rsassa-pss signature scheme of FIT image contents in SPL" + depends on SPL_FIT_SIGNATURE + help + Enable this to support the pss padding algorithm as described + in the rfc8017 (https://tools.ietf.org/html/rfc8017) in SPL. + +config SPL_LOAD_FIT + bool "Enable SPL loading U-Boot as a FIT (basic fitImage features)" + select SPL_FIT + help + Normally with the SPL framework a legacy image is generated as part + of the build. This contains U-Boot along with information as to + where it should be loaded. This option instead enables generation + of a FIT (Flat Image Tree) which provides more flexibility. In + particular it can handle selecting from multiple device tree + and passing the correct one to U-Boot. + + This path has the following limitations: + + 1. "loadables" images, other than FDTs, which do not have a "load" + property will not be loaded. This limitation also applies to FPGA + images with the correct "compatible" string. + 2. For FPGA images, only the "compatible" = "u-boot,fpga-legacy" + loading method is supported. + 3. FDTs are only loaded for images with an "os" property of "u-boot". + "linux" images are also supported with Falcon boot mode. + +config SPL_LOAD_FIT_ADDRESS + hex "load address of fit image" + depends on SPL_LOAD_FIT + default 0x0 + help + Specify the load address of the fit image that will be loaded + by SPL. + +config SPL_LOAD_FIT_APPLY_OVERLAY + bool "Enable SPL applying DT overlays from FIT" + depends on SPL_LOAD_FIT + select OF_LIBFDT_OVERLAY + help + The device tree is loaded from the FIT image. Allow the SPL is to + also load device-tree overlays from the FIT image an apply them + over the device tree. + +config SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ + depends on SPL_LOAD_FIT_APPLY_OVERLAY + default 0x10000 + hex "size of temporary buffer used to load the overlays" + help + The size of the area where the overlays will be loaded and + uncompress. Must be at least as large as biggest overlay + (uncompressed) + +config SPL_LOAD_FIT_FULL + bool "Enable SPL loading U-Boot as a FIT (full fitImage features)" + select SPL_FIT + help + Normally with the SPL framework a legacy image is generated as part + of the build. This contains U-Boot along with information as to + where it should be loaded. This option instead enables generation + of a FIT (Flat Image Tree) which provides more flexibility. In + particular it can handle selecting from multiple device tree + and passing the correct one to U-Boot. + +config SPL_FIT_IMAGE_POST_PROCESS + bool "Enable post-processing of FIT artifacts after loading by the SPL" + depends on SPL_LOAD_FIT + help + Allows doing any sort of manipulation to blobs after they got extracted + from the U-Boot FIT image like stripping off headers or modifying the + size of the blob, verification, authentication, decryption etc. in a + platform or board specific way. In order to use this feature a platform + or board-specific implementation of board_fit_image_post_process() must + be provided. Also, anything done during this post-processing step would + need to be comprehended in how the images were prepared before being + injected into the FIT creation (i.e. the blobs would have been pre- + processed before being added to the FIT image). + +config SPL_FIT_SOURCE + string ".its source file for U-Boot FIT image" + depends on SPL_FIT + help + Specifies a (platform specific) FIT source file to generate the + U-Boot FIT image. This could specify further image to load and/or + execute. + +config USE_SPL_FIT_GENERATOR + bool "Use a script to generate the .its script" + default y if SPL_FIT && (!ARCH_SUNXI && !RISCV) + +config SPL_FIT_GENERATOR + string ".its file generator script for U-Boot FIT image" + depends on USE_SPL_FIT_GENERATOR + default "arch/arm/mach-rockchip/make_fit_atf.py" if SPL_LOAD_FIT && ARCH_ROCKCHIP + default "arch/arm/mach-zynqmp/mkimage_fit_atf.sh" if SPL_LOAD_FIT && ARCH_ZYNQMP + help + Specifies a (platform specific) script file to generate the FIT + source file used to build the U-Boot FIT image file. This gets + passed a list of supported device tree file stub names to + include in the generated image. + +endif # SPL + +endif # FIT + +config LEGACY_IMAGE_FORMAT + bool "Enable support for the legacy image format" + default y if !FIT_SIGNATURE + help + This option enables the legacy image format. It is enabled by + default for backward compatibility, unless FIT_SIGNATURE is + set where it is disabled so that unsigned images cannot be + loaded. If a board needs the legacy image format support in this + case, enable it here. + +config SUPPORT_RAW_INITRD + bool "Enable raw initrd images" + help + Note, defining the SUPPORT_RAW_INITRD allows user to supply + kernel with raw initrd images. The syntax is slightly different, the + address of the initrd must be augmented by it's size, in the following + format: "<initrd address>:<initrd size>". + +config OF_BOARD_SETUP + bool "Set up board-specific details in device tree before boot" + depends on OF_LIBFDT + help + This causes U-Boot to call ft_board_setup() before booting into + the Operating System. This function can set up various + board-specific information in the device tree for use by the OS. + The device tree is then passed to the OS. + +config OF_SYSTEM_SETUP + bool "Set up system-specific details in device tree before boot" + depends on OF_LIBFDT + help + This causes U-Boot to call ft_system_setup() before booting into + the Operating System. This function can set up various + system-specific information in the device tree for use by the OS. + The device tree is then passed to the OS. + +config OF_STDOUT_VIA_ALIAS + bool "Update the device-tree stdout alias from U-Boot" + depends on OF_LIBFDT + help + This uses U-Boot's serial alias from the aliases node to update + the device tree passed to the OS. The "linux,stdout-path" property + in the chosen node is set to point to the correct serial node. + This option currently references CONFIG_CONS_INDEX, which is + incorrect when used with device tree as this option does not + exist / should not be used. + +config SYS_EXTRA_OPTIONS + string "Extra Options (DEPRECATED)" + help + The old configuration infrastructure (= mkconfig + boards.cfg) + provided the extra options field. If you have something like + "HAS_BAR,BAZ=64", the optional options + #define CONFIG_HAS + #define CONFIG_BAZ 64 + will be defined in include/config.h. + This option was prepared for the smooth migration from the old + configuration to Kconfig. Since this option will be removed sometime, + new boards should not use this option. + +config HAVE_SYS_TEXT_BASE + bool + depends on !NIOS2 && !XTENSA + depends on !EFI_APP + default y + +config SYS_TEXT_BASE + depends on HAVE_SYS_TEXT_BASE + default 0x0 if POSITION_INDEPENDENT + default 0x80800000 if ARCH_OMAP2PLUS || ARCH_K3 + default 0x4a000000 if ARCH_SUNXI && !MACH_SUN9I && !MACH_SUN8I_V3S + default 0x2a000000 if ARCH_SUNXI && MACH_SUN9I + default 0x42e00000 if ARCH_SUNXI && MACH_SUN8I_V3S + hex "Text Base" + help + The address in memory that U-Boot will be running from, initially. + +config SYS_CLK_FREQ + depends on ARC || ARCH_SUNXI || MPC83xx + int "CPU clock frequency" + help + TODO: Move CONFIG_SYS_CLK_FREQ for all the architecture + +config ARCH_FIXUP_FDT_MEMORY + bool "Enable arch_fixup_memory_banks() call" + default y + help + Enable FDT memory map syncup before OS boot. This feature can be + used for booting OS with different memory setup where the part of + the memory location should be used for different purpose. + +config CHROMEOS + bool "Support booting Chrome OS" + help + Chrome OS requires U-Boot to set up a table indicating the boot mode + (e.g. Developer mode) and a few other things. Enable this if you are + booting on a Chromebook to avoid getting an error about an invalid + firmware ID. + +config CHROMEOS_VBOOT + bool "Support Chrome OS verified boot" + help + This is intended to enable the full Chrome OS verified boot support + in U-Boot. It is not actually implemented in the U-Boot source code + at present, so this option is always set to 'n'. It allows + distinguishing between booting Chrome OS in a basic way (developer + mode) and a full boot. + +config RAMBOOT_PBL + bool "Freescale PBL(pre-boot loader) image format support" + help + Some SoCs use PBL to load RCW and/or pre-initialization instructions. + For more details refer to doc/README.pblimage + +config SYS_FSL_PBL_PBI + string "PBI(pre-boot instructions) commands for the PBL image" + depends on RAMBOOT_PBL + help + PBI commands can be used to configure SoC before it starts the execution. + Please refer doc/README.pblimage for more details. + +config SYS_FSL_PBL_RCW + string "Aadditional RCW (Power on reset configuration) for the PBL image" + depends on RAMBOOT_PBL + help + Enables addition of RCW (Power on reset configuration) in built image. + Please refer doc/README.pblimage for more details. + +endmenu # Boot images + +menu "Boot timing" + +config BOOTSTAGE + bool "Boot timing and reporting" + help + Enable recording of boot time while booting. To use it, insert + calls to bootstage_mark() with a suitable BOOTSTAGE_ID from + bootstage.h. Only a single entry is recorded for each ID. You can + give the entry a name with bootstage_mark_name(). You can also + record elapsed time in a particular stage using bootstage_start() + before starting and bootstage_accum() when finished. Bootstage will + add up all the accumulated time and report it. + + Normally, IDs are defined in bootstage.h but a small number of + additional 'user' IDs can be used by passing BOOTSTAGE_ID_ALLOC + as the ID. + + Calls to show_boot_progress() will also result in log entries but + these will not have names. + +config SPL_BOOTSTAGE + bool "Boot timing and reported in SPL" + depends on BOOTSTAGE + help + Enable recording of boot time in SPL. To make this visible to U-Boot + proper, enable BOOTSTAGE_STASH as well. This will stash the timing + information when SPL finishes and load it when U-Boot proper starts + up. + +config TPL_BOOTSTAGE + bool "Boot timing and reported in TPL" + depends on BOOTSTAGE + help + Enable recording of boot time in SPL. To make this visible to U-Boot + proper, enable BOOTSTAGE_STASH as well. This will stash the timing + information when TPL finishes and load it when U-Boot proper starts + up. + +config BOOTSTAGE_REPORT + bool "Display a detailed boot timing report before booting the OS" + depends on BOOTSTAGE + help + Enable output of a boot time report just before the OS is booted. + This shows how long it took U-Boot to go through each stage of the + boot process. The report looks something like this: + + Timer summary in microseconds: + Mark Elapsed Stage + 0 0 reset + 3,575,678 3,575,678 board_init_f start + 3,575,695 17 arch_cpu_init A9 + 3,575,777 82 arch_cpu_init done + 3,659,598 83,821 board_init_r start + 3,910,375 250,777 main_loop + 29,916,167 26,005,792 bootm_start + 30,361,327 445,160 start_kernel + +config BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store" + depends on BOOTSTAGE + default 30 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config SPL_BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store for SPL" + depends on SPL_BOOTSTAGE + default 5 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config TPL_BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store for TPL" + depends on TPL_BOOTSTAGE + default 5 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config BOOTSTAGE_FDT + bool "Store boot timing information in the OS device tree" + depends on BOOTSTAGE + help + Stash the bootstage information in the FDT. A root 'bootstage' + node is created with each bootstage id as a child. Each child + has a 'name' property and either 'mark' containing the + mark time in microseconds, or 'accum' containing the + accumulated time for that bootstage id in microseconds. + For example: + + bootstage { + 154 { + name = "board_init_f"; + mark = <3575678>; + }; + 170 { + name = "lcd"; + accum = <33482>; + }; + }; + + Code in the Linux kernel can find this in /proc/devicetree. + +config BOOTSTAGE_STASH + bool "Stash the boot timing information in memory before booting OS" + depends on BOOTSTAGE + help + Some OSes do not support device tree. Bootstage can instead write + the boot timing information in a binary format at a given address. + This happens through a call to bootstage_stash(), typically in + the CPU's cleanup_before_linux() function. You can use the + 'bootstage stash' and 'bootstage unstash' commands to do this on + the command line. + +config BOOTSTAGE_STASH_ADDR + hex "Address to stash boot timing information" + default 0 + help + Provide an address which will not be overwritten by the OS when it + starts, so that it can read this information when ready. + +config BOOTSTAGE_STASH_SIZE + hex "Size of boot timing stash region" + default 0x1000 + help + This should be large enough to hold the bootstage stash. A value of + 4096 (4KiB) is normally plenty. + +config SHOW_BOOT_PROGRESS + bool "Show boot progress in a board-specific manner" + help + Defining this option allows to add some board-specific code (calling + a user-provided function show_boot_progress(int) that enables you to + show the system's boot progress on some display (for example, some + LEDs) on your board. At the moment, the following checkpoints are + implemented: + + Legacy uImage format: + + Arg Where When + 1 common/cmd_bootm.c before attempting to boot an image + -1 common/cmd_bootm.c Image header has bad magic number + 2 common/cmd_bootm.c Image header has correct magic number + -2 common/cmd_bootm.c Image header has bad checksum + 3 common/cmd_bootm.c Image header has correct checksum + -3 common/cmd_bootm.c Image data has bad checksum + 4 common/cmd_bootm.c Image data has correct checksum + -4 common/cmd_bootm.c Image is for unsupported architecture + 5 common/cmd_bootm.c Architecture check OK + -5 common/cmd_bootm.c Wrong Image Type (not kernel, multi) + 6 common/cmd_bootm.c Image Type check OK + -6 common/cmd_bootm.c gunzip uncompression error + -7 common/cmd_bootm.c Unimplemented compression type + 7 common/cmd_bootm.c Uncompression OK + 8 common/cmd_bootm.c No uncompress/copy overwrite error + -9 common/cmd_bootm.c Unsupported OS (not Linux, BSD, VxWorks, QNX) + + 9 common/image.c Start initial ramdisk verification + -10 common/image.c Ramdisk header has bad magic number + -11 common/image.c Ramdisk header has bad checksum + 10 common/image.c Ramdisk header is OK + -12 common/image.c Ramdisk data has bad checksum + 11 common/image.c Ramdisk data has correct checksum + 12 common/image.c Ramdisk verification complete, start loading + -13 common/image.c Wrong Image Type (not PPC Linux ramdisk) + 13 common/image.c Start multifile image verification + 14 common/image.c No initial ramdisk, no multifile, continue. + + 15 arch/<arch>/lib/bootm.c All preparation done, transferring control to OS + + -30 arch/powerpc/lib/board.c Fatal error, hang the system + -31 post/post.c POST test failed, detected by post_output_backlog() + -32 post/post.c POST test failed, detected by post_run_single() + + 34 common/cmd_doc.c before loading a Image from a DOC device + -35 common/cmd_doc.c Bad usage of "doc" command + 35 common/cmd_doc.c correct usage of "doc" command + -36 common/cmd_doc.c No boot device + 36 common/cmd_doc.c correct boot device + -37 common/cmd_doc.c Unknown Chip ID on boot device + 37 common/cmd_doc.c correct chip ID found, device available + -38 common/cmd_doc.c Read Error on boot device + 38 common/cmd_doc.c reading Image header from DOC device OK + -39 common/cmd_doc.c Image header has bad magic number + 39 common/cmd_doc.c Image header has correct magic number + -40 common/cmd_doc.c Error reading Image from DOC device + 40 common/cmd_doc.c Image header has correct magic number + 41 common/cmd_ide.c before loading a Image from a IDE device + -42 common/cmd_ide.c Bad usage of "ide" command + 42 common/cmd_ide.c correct usage of "ide" command + -43 common/cmd_ide.c No boot device + 43 common/cmd_ide.c boot device found + -44 common/cmd_ide.c Device not available + 44 common/cmd_ide.c Device available + -45 common/cmd_ide.c wrong partition selected + 45 common/cmd_ide.c partition selected + -46 common/cmd_ide.c Unknown partition table + 46 common/cmd_ide.c valid partition table found + -47 common/cmd_ide.c Invalid partition type + 47 common/cmd_ide.c correct partition type + -48 common/cmd_ide.c Error reading Image Header on boot device + 48 common/cmd_ide.c reading Image Header from IDE device OK + -49 common/cmd_ide.c Image header has bad magic number + 49 common/cmd_ide.c Image header has correct magic number + -50 common/cmd_ide.c Image header has bad checksum + 50 common/cmd_ide.c Image header has correct checksum + -51 common/cmd_ide.c Error reading Image from IDE device + 51 common/cmd_ide.c reading Image from IDE device OK + 52 common/cmd_nand.c before loading a Image from a NAND device + -53 common/cmd_nand.c Bad usage of "nand" command + 53 common/cmd_nand.c correct usage of "nand" command + -54 common/cmd_nand.c No boot device + 54 common/cmd_nand.c boot device found + -55 common/cmd_nand.c Unknown Chip ID on boot device + 55 common/cmd_nand.c correct chip ID found, device available + -56 common/cmd_nand.c Error reading Image Header on boot device + 56 common/cmd_nand.c reading Image Header from NAND device OK + -57 common/cmd_nand.c Image header has bad magic number + 57 common/cmd_nand.c Image header has correct magic number + -58 common/cmd_nand.c Error reading Image from NAND device + 58 common/cmd_nand.c reading Image from NAND device OK + + -60 common/env_common.c Environment has a bad CRC, using default + + 64 net/eth.c starting with Ethernet configuration. + -64 net/eth.c no Ethernet found. + 65 net/eth.c Ethernet found. + + -80 common/cmd_net.c usage wrong + 80 common/cmd_net.c before calling net_loop() + -81 common/cmd_net.c some error in net_loop() occurred + 81 common/cmd_net.c net_loop() back without error + -82 common/cmd_net.c size == 0 (File with size 0 loaded) + 82 common/cmd_net.c trying automatic boot + 83 common/cmd_net.c running "source" command + -83 common/cmd_net.c some error in automatic boot or "source" command + 84 common/cmd_net.c end without errors + + FIT uImage format: + + Arg Where When + 100 common/cmd_bootm.c Kernel FIT Image has correct format + -100 common/cmd_bootm.c Kernel FIT Image has incorrect format + 101 common/cmd_bootm.c No Kernel subimage unit name, using configuration + -101 common/cmd_bootm.c Can't get configuration for kernel subimage + 102 common/cmd_bootm.c Kernel unit name specified + -103 common/cmd_bootm.c Can't get kernel subimage node offset + 103 common/cmd_bootm.c Found configuration node + 104 common/cmd_bootm.c Got kernel subimage node offset + -104 common/cmd_bootm.c Kernel subimage hash verification failed + 105 common/cmd_bootm.c Kernel subimage hash verification OK + -105 common/cmd_bootm.c Kernel subimage is for unsupported architecture + 106 common/cmd_bootm.c Architecture check OK + -106 common/cmd_bootm.c Kernel subimage has wrong type + 107 common/cmd_bootm.c Kernel subimage type OK + -107 common/cmd_bootm.c Can't get kernel subimage data/size + 108 common/cmd_bootm.c Got kernel subimage data/size + -108 common/cmd_bootm.c Wrong image type (not legacy, FIT) + -109 common/cmd_bootm.c Can't get kernel subimage type + -110 common/cmd_bootm.c Can't get kernel subimage comp + -111 common/cmd_bootm.c Can't get kernel subimage os + -112 common/cmd_bootm.c Can't get kernel subimage load address + -113 common/cmd_bootm.c Image uncompress/copy overwrite error + + 120 common/image.c Start initial ramdisk verification + -120 common/image.c Ramdisk FIT image has incorrect format + 121 common/image.c Ramdisk FIT image has correct format + 122 common/image.c No ramdisk subimage unit name, using configuration + -122 common/image.c Can't get configuration for ramdisk subimage + 123 common/image.c Ramdisk unit name specified + -124 common/image.c Can't get ramdisk subimage node offset + 125 common/image.c Got ramdisk subimage node offset + -125 common/image.c Ramdisk subimage hash verification failed + 126 common/image.c Ramdisk subimage hash verification OK + -126 common/image.c Ramdisk subimage for unsupported architecture + 127 common/image.c Architecture check OK + -127 common/image.c Can't get ramdisk subimage data/size + 128 common/image.c Got ramdisk subimage data/size + 129 common/image.c Can't get ramdisk load address + -129 common/image.c Got ramdisk load address + + -130 common/cmd_doc.c Incorrect FIT image format + 131 common/cmd_doc.c FIT image format OK + + -140 common/cmd_ide.c Incorrect FIT image format + 141 common/cmd_ide.c FIT image format OK + + -150 common/cmd_nand.c Incorrect FIT image format + 151 common/cmd_nand.c FIT image format OK + +config SPL_SHOW_BOOT_PROGRESS + bool "Show boot progress in a board-specific manner" + depends on SPL + help + Defining this option allows to add some board-specific code (calling + a user-provided function show_boot_progress(int) that enables you to + show the system's boot progress on some display (for example, some + LEDs) on your board. For details see SHOW_BOOT_PROGRESS. + +endmenu + +menu "Boot media" + +config NOR_BOOT + bool "Support for booting from NOR flash" + depends on NOR + help + Enabling this will make a U-Boot binary that is capable of being + booted via NOR. In this case we will enable certain pinmux early + as the ROM only partially sets up pinmux. We also default to using + NOR for environment. + +config NAND_BOOT + bool "Support for booting from NAND flash" + imply MTD_RAW_NAND + help + Enabling this will make a U-Boot binary that is capable of being + booted via NAND flash. This is not a must, some SoCs need this, + some not. + +config ONENAND_BOOT + bool "Support for booting from ONENAND" + imply MTD_RAW_NAND + help + Enabling this will make a U-Boot binary that is capable of being + booted via ONENAND. This is not a must, some SoCs need this, + some not. + +config QSPI_BOOT + bool "Support for booting from QSPI flash" + help + Enabling this will make a U-Boot binary that is capable of being + booted via QSPI flash. This is not a must, some SoCs need this, + some not. + +config SATA_BOOT + bool "Support for booting from SATA" + help + Enabling this will make a U-Boot binary that is capable of being + booted via SATA. This is not a must, some SoCs need this, + some not. + +config SD_BOOT + bool "Support for booting from SD/EMMC" + help + Enabling this will make a U-Boot binary that is capable of being + booted via SD/EMMC. This is not a must, some SoCs need this, + some not. + +config SPI_BOOT + bool "Support for booting from SPI flash" + help + Enabling this will make a U-Boot binary that is capable of being + booted via SPI flash. This is not a must, some SoCs need this, + some not. + +endmenu + +menu "Autoboot options" + +config AUTOBOOT + bool "Autoboot" + default y + help + This enables the autoboot. See doc/README.autoboot for detail. + +config BOOTDELAY + int "delay in seconds before automatically booting" + default 2 + depends on AUTOBOOT + help + Delay before automatically running bootcmd; + set to 0 to autoboot with no delay, but you can stop it by key input. + set to -1 to disable autoboot. + set to -2 to autoboot with no delay and not check for abort + + If this value is >= 0 then it is also used for the default delay + before starting the default entry in bootmenu. If it is < 0 then + a default value of 10s is used. + + See doc/README.autoboot for details. + +config AUTOBOOT_KEYED + bool "Stop autobooting via specific input key / string" + help + This option enables stopping (aborting) of the automatic + boot feature only by issuing a specific input key or + string. If not enabled, any input key will abort the + U-Boot automatic booting process and bring the device + to the U-Boot prompt for user input. + +config AUTOBOOT_FLUSH_STDIN + bool "Enable flushing stdin before starting to read the password" + depends on AUTOBOOT_KEYED && !SANDBOX + help + When this option is enabled stdin buffer will be flushed before + starting to read the password. + This can't be enabled for the sandbox as flushing stdin would + break the autoboot unit tests. + +config AUTOBOOT_PROMPT + string "Autoboot stop prompt" + depends on AUTOBOOT_KEYED + default "Autoboot in %d seconds\\n" + help + This string is displayed before the boot delay selected by + CONFIG_BOOTDELAY starts. If it is not defined there is no + output indicating that autoboot is in progress. + + Note that this define is used as the (only) argument to a + printf() call, so it may contain '%' format specifications, + provided that it also includes, sepearated by commas exactly + like in a printf statement, the required arguments. It is + the responsibility of the user to select only such arguments + that are valid in the given context. + +config AUTOBOOT_ENCRYPTION + bool "Enable encryption in autoboot stopping" + depends on AUTOBOOT_KEYED + help + This option allows a string to be entered into U-Boot to stop the + autoboot. + The behavior depends whether CONFIG_CRYPT_PW from lib is enabled + or not. + In case CONFIG_CRYPT_PW is enabled, the string will be forwarded + to the crypt-based functionality and be compared against the + string in the environment variable 'bootstopkeycrypt'. + In case CONFIG_CRYPT_PW is disabled the string itself is hashed + and compared against the hash in the environment variable + 'bootstopkeysha256'. + If it matches in either case then boot stops and + a command-line prompt is presented. + This provides a way to ship a secure production device which can also + be accessed at the U-Boot command line. + +config AUTOBOOT_SHA256_FALLBACK + bool "Allow fallback from crypt-hashed password to sha256" + depends on AUTOBOOT_ENCRYPTION && CRYPT_PW + help + This option adds support to fall back from crypt-hashed + passwords to checking a SHA256 hashed password in case the + 'bootstopusesha256' environment variable is set to 'true'. + +config AUTOBOOT_DELAY_STR + string "Delay autobooting via specific input key / string" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + help + This option delays the automatic boot feature by issuing + a specific input key or string. If CONFIG_AUTOBOOT_DELAY_STR + or the environment variable "bootdelaykey" is specified + and this string is received from console input before + autoboot starts booting, U-Boot gives a command prompt. The + U-Boot prompt will time out if CONFIG_BOOT_RETRY_TIME is + used, otherwise it never times out. + +config AUTOBOOT_STOP_STR + string "Stop autobooting via specific input key / string" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + help + This option enables stopping (aborting) of the automatic + boot feature only by issuing a specific input key or + string. If CONFIG_AUTOBOOT_STOP_STR or the environment + variable "bootstopkey" is specified and this string is + received from console input before autoboot starts booting, + U-Boot gives a command prompt. The U-Boot prompt never + times out, even if CONFIG_BOOT_RETRY_TIME is used. + +config AUTOBOOT_KEYED_CTRLC + bool "Enable Ctrl-C autoboot interruption" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + help + This option allows for the boot sequence to be interrupted + by ctrl-c, in addition to the "bootdelaykey" and "bootstopkey". + Setting this variable provides an escape sequence from the + limited "password" strings. + +config AUTOBOOT_NEVER_TIMEOUT + bool "Make the password entry never time-out" + depends on AUTOBOOT_KEYED && AUTOBOOT_ENCRYPTION && CRYPT_PW + help + This option removes the timeout from the password entry + when the user first presses the <Enter> key before entering + any other character. + +config AUTOBOOT_STOP_STR_ENABLE + bool "Enable fixed string to stop autobooting" + depends on AUTOBOOT_KEYED && AUTOBOOT_ENCRYPTION + help + This option enables the feature to add a fixed stop + string that is defined at compile time. + In every case it will be tried to load the stop + string from the environment. + In case this is enabled and there is no stop string + in the environment, this will be used as default value. + +config AUTOBOOT_STOP_STR_CRYPT + string "Stop autobooting via crypt-hashed password" + depends on AUTOBOOT_STOP_STR_ENABLE && CRYPT_PW + help + This option adds the feature to only stop the autobooting, + and therefore boot into the U-Boot prompt, when the input + string / password matches a values that is hashed via + one of the supported crypt-style password hashing options + and saved in the environment variable "bootstopkeycrypt". + +config AUTOBOOT_STOP_STR_SHA256 + string "Stop autobooting via SHA256 hashed password" + depends on AUTOBOOT_STOP_STR_ENABLE + help + This option adds the feature to only stop the autobooting, + and therefore boot into the U-Boot prompt, when the input + string / password matches a values that is encypted via + a SHA256 hash and saved in the environment variable + "bootstopkeysha256". If the value in that variable + includes a ":", the portion prior to the ":" will be treated + as a salt value. + +config AUTOBOOT_USE_MENUKEY + bool "Allow a specify key to run a menu from the environment" + depends on !AUTOBOOT_KEYED + help + If a specific key is pressed to stop autoboot, then the commands in + the environment variable 'menucmd' are executed before boot starts. + +config AUTOBOOT_MENUKEY + int "ASCII value of boot key to show a menu" + default 0 + depends on AUTOBOOT_USE_MENUKEY + help + If this key is pressed to stop autoboot, then the commands in the + environment variable 'menucmd' will be executed before boot starts. + For example, 33 means "!" in ASCII, so pressing ! at boot would take + this action. + +config AUTOBOOT_MENU_SHOW + bool "Show a menu on boot" + depends on CMD_BOOTMENU + help + This enables the boot menu, controlled by environment variables + defined by the board. The menu starts after running the 'preboot' + environmnent variable (if enabled) and before handling the boot delay. + See README.bootmenu for more details. + +endmenu + +config USE_BOOTARGS + bool "Enable boot arguments" + help + Provide boot arguments to bootm command. Boot arguments are specified + in CONFIG_BOOTARGS option. Enable this option to be able to specify + CONFIG_BOOTARGS string. If this option is disabled, CONFIG_BOOTARGS + will be undefined and won't take any space in U-Boot image. + +config BOOTARGS + string "Boot arguments" + depends on USE_BOOTARGS && !USE_DEFAULT_ENV_FILE + help + This can be used to pass arguments to the bootm command. The value of + CONFIG_BOOTARGS goes into the environment value "bootargs". Note that + this value will also override the "chosen" node in FDT blob. + +config BOOTARGS_SUBST + bool "Support substituting strings in boot arguments" + help + This allows substituting string values in the boot arguments. These + are applied after the commandline has been built. + + One use for this is to insert the root-disk UUID into the command + line where bootargs contains "root=${uuid}" + + setenv bootargs "console= root=${uuid}" + # Set the 'uuid' environment variable + part uuid mmc 2:2 uuid + + # Command-line substitution will put the real uuid into the + # kernel command line + bootm + +config USE_BOOTCOMMAND + bool "Enable a default value for bootcmd" + help + Provide a default value for the bootcmd entry in the environment. If + autoboot is enabled this is what will be run automatically. Enable + this option to be able to specify CONFIG_BOOTCOMMAND as a string. If + this option is disabled, CONFIG_BOOTCOMMAND will be undefined and + won't take any space in U-Boot image. + +config BOOTCOMMAND + string "bootcmd value" + depends on USE_BOOTCOMMAND && !USE_DEFAULT_ENV_FILE + default "run distro_bootcmd" if DISTRO_DEFAULTS + help + This is the string of commands that will be used as bootcmd and if + AUTOBOOT is set, automatically run. + +config USE_PREBOOT + bool "Enable preboot" + help + When this option is enabled, the existence of the environment + variable "preboot" will be checked immediately before starting the + CONFIG_BOOTDELAY countdown and/or running the auto-boot command resp. + entering interactive mode. + + This feature is especially useful when "preboot" is automatically + generated or modified. For example, the boot code can modify the + "preboot" when a user holds down a certain combination of keys. + +config PREBOOT + string "preboot default value" + depends on USE_PREBOOT && !USE_DEFAULT_ENV_FILE + default "usb start" if USB_KEYBOARD + default "" + help + This is the default of "preboot" environment variable. + +config DEFAULT_FDT_FILE + string "Default fdt file" + help + This option is used to set the default fdt file to boot OS. + +endmenu # Booting diff --git a/boot/Makefile b/boot/Makefile new file mode 100644 index 0000000..a19e85c --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2004-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +ifndef CONFIG_SPL_BUILD + +# This option is not just y/n - it can have a numeric value +ifdef CONFIG_BOOT_RETRY_TIME +obj-y += bootretry.o +endif + +obj-$(CONFIG_CMD_BOOTM) += bootm.o bootm_os.o +obj-$(CONFIG_CMD_BOOTZ) += bootm.o bootm_os.o +obj-$(CONFIG_CMD_BOOTI) += bootm.o bootm_os.o + +endif + +obj-y += image.o image-board.o +obj-$(CONFIG_ANDROID_AB) += android_ab.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o +obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o +obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o +obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o +obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o +obj-$(CONFIG_$(SPL_TPL_)IMAGE_SIGN_INFO) += image-sig.o +obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o +obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o + +obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o +endif diff --git a/boot/android_ab.c b/boot/android_ab.c new file mode 100644 index 0000000..4943f26 --- /dev/null +++ b/boot/android_ab.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2017 The Android Open Source Project + */ +#include <common.h> +#include <android_ab.h> +#include <android_bootloader_message.h> +#include <blk.h> +#include <log.h> +#include <malloc.h> +#include <part.h> +#include <memalign.h> +#include <linux/err.h> +#include <u-boot/crc.h> +#include <u-boot/crc.h> + +/** + * Compute the CRC-32 of the bootloader control struct. + * + * Only the bytes up to the crc32_le field are considered for the CRC-32 + * calculation. + * + * @param[in] abc bootloader control block + * + * @return crc32 sum + */ +static uint32_t ab_control_compute_crc(struct bootloader_control *abc) +{ + return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le)); +} + +/** + * Initialize bootloader_control to the default value. + * + * It allows us to boot all slots in order from the first one. This value + * should be used when the bootloader message is corrupted, but not when + * a valid message indicates that all slots are unbootable. + * + * @param[in] abc bootloader control block + * + * @return 0 on success and a negative on error + */ +static int ab_control_default(struct bootloader_control *abc) +{ + int i; + const struct slot_metadata metadata = { + .priority = 15, + .tries_remaining = 7, + .successful_boot = 0, + .verity_corrupted = 0, + .reserved = 0 + }; + + if (!abc) + return -EFAULT; + + memcpy(abc->slot_suffix, "a\0\0\0", 4); + abc->magic = BOOT_CTRL_MAGIC; + abc->version = BOOT_CTRL_VERSION; + abc->nb_slot = NUM_SLOTS; + memset(abc->reserved0, 0, sizeof(abc->reserved0)); + for (i = 0; i < abc->nb_slot; ++i) + abc->slot_info[i] = metadata; + + memset(abc->reserved1, 0, sizeof(abc->reserved1)); + abc->crc32_le = ab_control_compute_crc(abc); + + return 0; +} + +/** + * Load the boot_control struct from disk into newly allocated memory. + * + * This function allocates and returns an integer number of disk blocks, + * based on the block size of the passed device to help performing a + * read-modify-write operation on the boot_control struct. + * The boot_control struct offset (2 KiB) must be a multiple of the device + * block size, for simplicity. + * + * @param[in] dev_desc Device where to read the boot_control struct from + * @param[in] part_info Partition in 'dev_desc' where to read from, normally + * the "misc" partition should be used + * @param[out] pointer to pointer to bootloader_control data + * @return 0 on success and a negative on error + */ +static int ab_control_create_from_disk(struct blk_desc *dev_desc, + const struct disk_partition *part_info, + struct bootloader_control **abc) +{ + ulong abc_offset, abc_blocks, ret; + + abc_offset = offsetof(struct bootloader_message_ab, slot_suffix); + if (abc_offset % part_info->blksz) { + log_err("ANDROID: Boot control block not block aligned.\n"); + return -EINVAL; + } + abc_offset /= part_info->blksz; + + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + if (abc_offset + abc_blocks > part_info->size) { + log_err("ANDROID: boot control partition too small. Need at"); + log_err(" least %lu blocks but have %lu blocks.\n", + abc_offset + abc_blocks, part_info->size); + return -EINVAL; + } + *abc = malloc_cache_aligned(abc_blocks * part_info->blksz); + if (!*abc) + return -ENOMEM; + + ret = blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks, + *abc); + if (IS_ERR_VALUE(ret)) { + log_err("ANDROID: Could not read from boot ctrl partition\n"); + free(*abc); + return -EIO; + } + + log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks); + + return 0; +} + +/** + * Store the loaded boot_control block. + * + * Store back to the same location it was read from with + * ab_control_create_from_misc(). + * + * @param[in] dev_desc Device where we should write the boot_control struct + * @param[in] part_info Partition on the 'dev_desc' where to write + * @param[in] abc Pointer to the boot control struct and the extra bytes after + * it up to the nearest block boundary + * @return 0 on success and a negative on error + */ +static int ab_control_store(struct blk_desc *dev_desc, + const struct disk_partition *part_info, + struct bootloader_control *abc) +{ + ulong abc_offset, abc_blocks, ret; + + abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) / + part_info->blksz; + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + ret = blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks, + abc); + if (IS_ERR_VALUE(ret)) { + log_err("ANDROID: Could not write back the misc partition\n"); + return -EIO; + } + + return 0; +} + +/** + * Compare two slots. + * + * The function determines slot which is should we boot from among the two. + * + * @param[in] a The first bootable slot metadata + * @param[in] b The second bootable slot metadata + * @return Negative if the slot "a" is better, positive of the slot "b" is + * better or 0 if they are equally good. + */ +static int ab_compare_slots(const struct slot_metadata *a, + const struct slot_metadata *b) +{ + /* Higher priority is better */ + if (a->priority != b->priority) + return b->priority - a->priority; + + /* Higher successful_boot value is better, in case of same priority */ + if (a->successful_boot != b->successful_boot) + return b->successful_boot - a->successful_boot; + + /* Higher tries_remaining is better to ensure round-robin */ + if (a->tries_remaining != b->tries_remaining) + return b->tries_remaining - a->tries_remaining; + + return 0; +} + +int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info) +{ + struct bootloader_control *abc = NULL; + u32 crc32_le; + int slot, i, ret; + bool store_needed = false; + char slot_suffix[4]; + + ret = ab_control_create_from_disk(dev_desc, part_info, &abc); + if (ret < 0) { + /* + * This condition represents an actual problem with the code or + * the board setup, like an invalid partition information. + * Signal a repair mode and do not try to boot from either slot. + */ + return ret; + } + + crc32_le = ab_control_compute_crc(abc); + if (abc->crc32_le != crc32_le) { + log_err("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x),", + crc32_le, abc->crc32_le); + log_err("re-initializing A/B metadata.\n"); + + ret = ab_control_default(abc); + if (ret < 0) { + free(abc); + return -ENODATA; + } + store_needed = true; + } + + if (abc->magic != BOOT_CTRL_MAGIC) { + log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic); + free(abc); + return -ENODATA; + } + + if (abc->version > BOOT_CTRL_VERSION) { + log_err("ANDROID: Unsupported A/B metadata version: %.8x\n", + abc->version); + free(abc); + return -ENODATA; + } + + /* + * At this point a valid boot control metadata is stored in abc, + * followed by other reserved data in the same block. We select a with + * the higher priority slot that + * - is not marked as corrupted and + * - either has tries_remaining > 0 or successful_boot is true. + * If the selected slot has a false successful_boot, we also decrement + * the tries_remaining until it eventually becomes unbootable because + * tries_remaining reaches 0. This mechanism produces a bootloader + * induced rollback, typically right after a failed update. + */ + + /* Safety check: limit the number of slots. */ + if (abc->nb_slot > ARRAY_SIZE(abc->slot_info)) { + abc->nb_slot = ARRAY_SIZE(abc->slot_info); + store_needed = true; + } + + slot = -1; + for (i = 0; i < abc->nb_slot; ++i) { + if (abc->slot_info[i].verity_corrupted || + !abc->slot_info[i].tries_remaining) { + log_debug("ANDROID: unbootable slot %d tries: %d, ", + i, abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d\n", + abc->slot_info[i].verity_corrupted); + continue; + } + log_debug("ANDROID: bootable slot %d pri: %d, tries: %d, ", + i, abc->slot_info[i].priority, + abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d, successful: %d\n", + abc->slot_info[i].verity_corrupted, + abc->slot_info[i].successful_boot); + + if (slot < 0 || + ab_compare_slots(&abc->slot_info[i], + &abc->slot_info[slot]) < 0) { + slot = i; + } + } + + if (slot >= 0 && !abc->slot_info[slot].successful_boot) { + log_err("ANDROID: Attempting slot %c, tries remaining %d\n", + BOOT_SLOT_NAME(slot), + abc->slot_info[slot].tries_remaining); + abc->slot_info[slot].tries_remaining--; + store_needed = true; + } + + if (slot >= 0) { + /* + * Legacy user-space requires this field to be set in the BCB. + * Newer releases load this slot suffix from the command line + * or the device tree. + */ + memset(slot_suffix, 0, sizeof(slot_suffix)); + slot_suffix[0] = BOOT_SLOT_NAME(slot); + if (memcmp(abc->slot_suffix, slot_suffix, + sizeof(slot_suffix))) { + memcpy(abc->slot_suffix, slot_suffix, + sizeof(slot_suffix)); + store_needed = true; + } + } + + if (store_needed) { + abc->crc32_le = ab_control_compute_crc(abc); + ab_control_store(dev_desc, part_info, abc); + } + free(abc); + + if (slot < 0) + return -EINVAL; + + return slot; +} diff --git a/boot/boot_fit.c b/boot/boot_fit.c new file mode 100644 index 0000000..dfc2a31 --- /dev/null +++ b/boot/boot_fit.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Texas Instruments, <www.ti.com> + * + * Franklin S Cooper Jr. <fcooper@ti.com> + */ + +#include <boot_fit.h> +#include <common.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <linux/libfdt.h> + +static int fdt_offset(const void *fit) +{ + int images, node, fdt_len, fdt_node, fdt_offset; + const char *fdt_name; + + node = fit_find_config_node(fit); + if (node < 0) + return node; + + images = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, images); + return -EINVAL; + } + + fdt_name = fdt_getprop(fit, node, FIT_FDT_PROP, &fdt_len); + if (!fdt_name) { + debug("%s: Cannot find fdt name property: %d\n", + __func__, fdt_len); + return -EINVAL; + } + + fdt_node = fdt_subnode_offset(fit, images, fdt_name); + if (fdt_node < 0) { + debug("%s: Cannot find fdt node '%s': %d\n", + __func__, fdt_name, fdt_node); + return -EINVAL; + } + + fdt_offset = fdt_getprop_u32(fit, fdt_node, "data-offset"); + + if (fdt_offset == FDT_ERROR) + return -ENOENT; + + fdt_len = fdt_getprop_u32(fit, fdt_node, "data-size"); + + if (fdt_len < 0) + return fdt_len; + + return fdt_offset; +} + +void *locate_dtb_in_fit(const void *fit) +{ + struct image_header *header; + int size; + int ret; + + size = fdt_totalsize(fit); + size = (size + 3) & ~3; + + header = (struct image_header *)fit; + + if (image_get_magic(header) != FDT_MAGIC) { + debug("No FIT image appended to U-boot\n"); + return NULL; + } + + ret = fdt_offset(fit); + + if (ret < 0) + return NULL; + else + return (void *)fit+size+ret; +} diff --git a/boot/bootm.c b/boot/bootm.c new file mode 100644 index 0000000..4482f84 --- /dev/null +++ b/boot/bootm.c @@ -0,0 +1,1038 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <bootstage.h> +#include <cli.h> +#include <cpu_func.h> +#include <env.h> +#include <errno.h> +#include <fdt_support.h> +#include <irq_func.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/sizes.h> +#if defined(CONFIG_CMD_USB) +#include <usb.h> +#endif +#else +#include "mkimage.h" +#endif + +#include <command.h> +#include <bootm.h> +#include <image.h> + +#ifndef CONFIG_SYS_BOOTM_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN 0x800000 +#endif + +#define MAX_CMDLINE_SIZE SZ_4K + +#define IH_INITRD_ARCH IH_ARCH_DEFAULT + +#ifndef USE_HOSTCC + +DECLARE_GLOBAL_DATA_PTR; + +bootm_headers_t images; /* pointers to os/initrd/fdt images */ + +static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len); + +__weak void board_quiesce_devices(void) +{ +} + +#ifdef CONFIG_LMB +static void boot_start_lmb(bootm_headers_t *images) +{ + ulong mem_start; + phys_size_t mem_size; + + mem_start = env_get_bootm_low(); + mem_size = env_get_bootm_size(); + + lmb_init_and_reserve_range(&images->lmb, (phys_addr_t)mem_start, + mem_size, NULL); +} +#else +#define lmb_reserve(lmb, base, size) +static inline void boot_start_lmb(bootm_headers_t *images) { } +#endif + +static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + memset((void *)&images, 0, sizeof(images)); + images.verify = env_get_yesno("verify"); + + boot_start_lmb(&images); + + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); + images.state = BOOTM_STATE_START; + + return 0; +} + +static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const void *os_hdr; + bool ep_found = false; + int ret; + + /* get kernel image header, start address and length */ + os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, + &images, &images.os.image_start, &images.os.image_len); + if (images.os.image_len == 0) { + puts("ERROR: can't get kernel image!\n"); + return 1; + } + + /* get image parameters */ + switch (genimg_get_format(os_hdr)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + images.os.type = image_get_type(os_hdr); + images.os.comp = image_get_comp(os_hdr); + images.os.os = image_get_os(os_hdr); + + images.os.end = image_get_image_end(os_hdr); + images.os.load = image_get_load(os_hdr); + images.os.arch = image_get_arch(os_hdr); + break; +#endif +#if CONFIG_IS_ENABLED(FIT) + case IMAGE_FORMAT_FIT: + if (fit_image_get_type(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.type)) { + puts("Can't get image type!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_TYPE); + return 1; + } + + if (fit_image_get_comp(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.comp)) { + puts("Can't get image compression!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_COMPRESSION); + return 1; + } + + if (fit_image_get_os(images.fit_hdr_os, images.fit_noffset_os, + &images.os.os)) { + puts("Can't get image OS!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_OS); + return 1; + } + + if (fit_image_get_arch(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.arch)) { + puts("Can't get image ARCH!\n"); + return 1; + } + + images.os.end = fit_get_end(images.fit_hdr_os); + + if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os, + &images.os.load)) { + puts("Can't get image load address!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR); + return 1; + } + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = android_image_get_kcomp(os_hdr); + images.os.os = IH_OS_LINUX; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + images.ep = images.os.load; + ep_found = true; + break; +#endif + default: + puts("ERROR: unknown image format type!\n"); + return 1; + } + + /* If we have a valid setup.bin, we will use that for entry (x86) */ + if (images.os.arch == IH_ARCH_I386 || + images.os.arch == IH_ARCH_X86_64) { + ulong len; + + ret = boot_get_setup(&images, IH_ARCH_I386, &images.ep, &len); + if (ret < 0 && ret != -ENOENT) { + puts("Could not find a valid setup.bin for x86\n"); + return 1; + } + /* Kernel entry point is the setup.bin */ + } else if (images.legacy_hdr_valid) { + images.ep = image_get_ep(&images.legacy_hdr_os_copy); +#if CONFIG_IS_ENABLED(FIT) + } else if (images.fit_uname_os) { + int ret; + + ret = fit_image_get_entry(images.fit_hdr_os, + images.fit_noffset_os, &images.ep); + if (ret) { + puts("Can't get entry point property!\n"); + return 1; + } +#endif + } else if (!ep_found) { + puts("Could not find kernel entry point!\n"); + return 1; + } + + if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { + if (CONFIG_IS_ENABLED(CMD_BOOTI) && + images.os.arch == IH_ARCH_ARM64) { + ulong image_addr; + ulong image_size; + + ret = booti_setup(images.os.image_start, &image_addr, + &image_size, true); + if (ret != 0) + return 1; + + images.os.type = IH_TYPE_KERNEL; + images.os.load = image_addr; + images.ep = image_addr; + } else { + images.os.load = images.os.image_start; + images.ep += images.os.image_start; + } + } + + images.os.start = map_to_sysmem(os_hdr); + + return 0; +} + +/** + * bootm_find_images - wrapper to find and locate various images + * @flag: Ignored Argument + * @argc: command argument count + * @argv: command argument list + * @start: OS image start address + * @size: OS image size + * + * boot_find_images() will attempt to load an available ramdisk, + * flattened device tree, as well as specifically marked + * "loadable" images (loadables are FIT only) + * + * Note: bootm_find_images will skip an image if it is not found + * + * @return: + * 0, if all existing images were loaded correctly + * 1, if an image is found but corrupted, or invalid + */ +int bootm_find_images(int flag, int argc, char *const argv[], ulong start, + ulong size) +{ + int ret; + + /* find ramdisk */ + ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH, + &images.rd_start, &images.rd_end); + if (ret) { + puts("Ramdisk image is corrupt or invalid\n"); + return 1; + } + + /* check if ramdisk overlaps OS image */ + if (images.rd_start && (((ulong)images.rd_start >= start && + (ulong)images.rd_start < start + size) || + ((ulong)images.rd_end > start && + (ulong)images.rd_end <= start + size) || + ((ulong)images.rd_start < start && + (ulong)images.rd_end >= start + size))) { + printf("ERROR: RD image overlaps OS image (OS=0x%lx..0x%lx)\n", + start, start + size); + return 1; + } + +#if CONFIG_IS_ENABLED(OF_LIBFDT) + /* find flattened device tree */ + ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images, + &images.ft_addr, &images.ft_len); + if (ret) { + puts("Could not find a valid device tree\n"); + return 1; + } + + /* check if FDT overlaps OS image */ + if (images.ft_addr && + (((ulong)images.ft_addr >= start && + (ulong)images.ft_addr <= start + size) || + ((ulong)images.ft_addr + images.ft_len >= start && + (ulong)images.ft_addr + images.ft_len <= start + size))) { + printf("ERROR: FDT image overlaps OS image (OS=0x%lx..0x%lx)\n", + start, start + size); + return 1; + } + + if (CONFIG_IS_ENABLED(CMD_FDT)) + set_working_fdt_addr(map_to_sysmem(images.ft_addr)); +#endif + +#if CONFIG_IS_ENABLED(FIT) + if (IS_ENABLED(CONFIG_FPGA)) { + /* find bitstreams */ + ret = boot_get_fpga(argc, argv, &images, IH_ARCH_DEFAULT, + NULL, NULL); + if (ret) { + printf("FPGA image is corrupted or invalid\n"); + return 1; + } + } + + /* find all of the loadables */ + ret = boot_get_loadable(argc, argv, &images, IH_ARCH_DEFAULT, + NULL, NULL); + if (ret) { + printf("Loadable(s) is corrupt or invalid\n"); + return 1; + } +#endif + + return 0; +} + +static int bootm_find_other(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (((images.os.type == IH_TYPE_KERNEL) || + (images.os.type == IH_TYPE_KERNEL_NOLOAD) || + (images.os.type == IH_TYPE_MULTI)) && + (images.os.os == IH_OS_LINUX || + images.os.os == IH_OS_VXWORKS)) + return bootm_find_images(flag, argc, argv, 0, 0); + + return 0; +} +#endif /* USE_HOSTC */ + +#if !defined(USE_HOSTCC) || defined(CONFIG_FIT_SIGNATURE) +/** + * handle_decomp_error() - display a decompression error + * + * This function tries to produce a useful message. In the case where the + * uncompressed size is the same as the available space, we can assume that + * the image is too large for the buffer. + * + * @comp_type: Compression type being used (IH_COMP_...) + * @uncomp_size: Number of bytes uncompressed + * @ret: errno error code received from compression library + * @return Appropriate BOOTM_ERR_ error code + */ +static int handle_decomp_error(int comp_type, size_t uncomp_size, int ret) +{ + const char *name = genimg_get_comp_name(comp_type); + + /* ENOSYS means unimplemented compression type, don't reset. */ + if (ret == -ENOSYS) + return BOOTM_ERR_UNIMPLEMENTED; + + if (uncomp_size >= CONFIG_SYS_BOOTM_LEN) + printf("Image too large: increase CONFIG_SYS_BOOTM_LEN\n"); + else + printf("%s: uncompress error %d\n", name, ret); + + /* + * The decompression routines are now safe, so will not write beyond + * their bounds. Probably it is not necessary to reset, but maintain + * the current behaviour for now. + */ + printf("Must RESET board to recover\n"); +#ifndef USE_HOSTCC + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); +#endif + + return BOOTM_ERR_RESET; +} +#endif + +#ifndef USE_HOSTCC +static int bootm_load_os(bootm_headers_t *images, int boot_progress) +{ + image_info_t os = images->os; + ulong load = os.load; + ulong load_end; + ulong blob_start = os.start; + ulong blob_end = os.end; + ulong image_start = os.image_start; + ulong image_len = os.image_len; + ulong flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN); + bool no_overlap; + void *load_buf, *image_buf; + int err; + + load_buf = map_sysmem(load, 0); + image_buf = map_sysmem(os.image_start, image_len); + err = image_decomp(os.comp, load, os.image_start, os.type, + load_buf, image_buf, image_len, + CONFIG_SYS_BOOTM_LEN, &load_end); + if (err) { + err = handle_decomp_error(os.comp, load_end - load, err); + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return err; + } + /* We need the decompressed image size in the next steps */ + images->os.image_len = load_end - load; + + flush_cache(flush_start, ALIGN(load_end, ARCH_DMA_MINALIGN) - flush_start); + + debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, load_end); + bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); + + no_overlap = (os.comp == IH_COMP_NONE && load == image_start); + + if (!no_overlap && load < blob_end && load_end > blob_start) { + debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n", + blob_start, blob_end); + debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load, + load_end); + + /* Check what type of image this is. */ + if (images->legacy_hdr_valid) { + if (image_get_type(&images->legacy_hdr_os_copy) + == IH_TYPE_MULTI) + puts("WARNING: legacy format multi component image overwritten\n"); + return BOOTM_ERR_OVERLAP; + } else { + puts("ERROR: new format image overwritten - must RESET the board to recover\n"); + bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); + return BOOTM_ERR_RESET; + } + } + + lmb_reserve(&images->lmb, images->os.load, (load_end - + images->os.load)); + return 0; +} + +/** + * bootm_disable_interrupts() - Disable interrupts in preparation for load/boot + * + * @return interrupt flag (0 if interrupts were disabled, non-zero if they were + * enabled) + */ +ulong bootm_disable_interrupts(void) +{ + ulong iflag; + + /* + * We have reached the point of no return: we are going to + * overwrite all exception vector code, so we cannot easily + * recover from any failures any more... + */ + iflag = disable_interrupts(); +#ifdef CONFIG_NETCONSOLE + /* Stop the ethernet stack if NetConsole could have left it up */ + eth_halt(); +# ifndef CONFIG_DM_ETH + eth_unregister(eth_get_dev()); +# endif +#endif + +#if defined(CONFIG_CMD_USB) + /* + * turn off USB to prevent the host controller from writing to the + * SDRAM while Linux is booting. This could happen (at least for OHCI + * controller), because the HCCA (Host Controller Communication Area) + * lies within the SDRAM and the host controller writes continously to + * this area (as busmaster!). The HccaFrameNumber is for example + * updated every 1 ms within the HCCA structure in SDRAM! For more + * details see the OpenHCI specification. + */ + usb_stop(); +#endif + return iflag; +} + +#define CONSOLE_ARG "console=" +#define CONSOLE_ARG_SIZE sizeof(CONSOLE_ARG) + +/** + * fixup_silent_linux() - Handle silencing the linux boot if required + * + * This uses the silent_linux envvar to control whether to add/set a "console=" + * parameter to the command line + * + * @buf: Buffer containing the string to process + * @maxlen: Maximum length of buffer + * @return 0 if OK, -ENOSPC if @maxlen is too small + */ +static int fixup_silent_linux(char *buf, int maxlen) +{ + int want_silent; + char *cmdline; + int size; + + /* + * Move the input string to the end of buffer. The output string will be + * built up at the start. + */ + size = strlen(buf) + 1; + if (size * 2 > maxlen) + return -ENOSPC; + cmdline = buf + maxlen - size; + memmove(cmdline, buf, size); + /* + * Only fix cmdline when requested. The environment variable can be: + * + * no - we never fixup + * yes - we always fixup + * unset - we rely on the console silent flag + */ + want_silent = env_get_yesno("silent_linux"); + if (want_silent == 0) + return 0; + else if (want_silent == -1 && !(gd->flags & GD_FLG_SILENT)) + return 0; + + debug("before silent fix-up: %s\n", cmdline); + if (*cmdline) { + char *start = strstr(cmdline, CONSOLE_ARG); + + /* Check space for maximum possible new command line */ + if (size + CONSOLE_ARG_SIZE > maxlen) + return -ENOSPC; + + if (start) { + char *end = strchr(start, ' '); + int start_bytes; + + start_bytes = start - cmdline + CONSOLE_ARG_SIZE - 1; + strncpy(buf, cmdline, start_bytes); + if (end) + strcpy(buf + start_bytes, end); + else + buf[start_bytes] = '\0'; + } else { + sprintf(buf, "%s %s", cmdline, CONSOLE_ARG); + } + if (buf + strlen(buf) >= cmdline) + return -ENOSPC; + } else { + if (maxlen < sizeof(CONSOLE_ARG)) + return -ENOSPC; + strcpy(buf, CONSOLE_ARG); + } + debug("after silent fix-up: %s\n", buf); + + return 0; +} + +/** + * process_subst() - Handle substitution of ${...} fields in the environment + * + * Handle variable substitution in the provided buffer + * + * @buf: Buffer containing the string to process + * @maxlen: Maximum length of buffer + * @return 0 if OK, -ENOSPC if @maxlen is too small + */ +static int process_subst(char *buf, int maxlen) +{ + char *cmdline; + int size; + int ret; + + /* Move to end of buffer */ + size = strlen(buf) + 1; + cmdline = buf + maxlen - size; + if (buf + size > cmdline) + return -ENOSPC; + memmove(cmdline, buf, size); + + ret = cli_simple_process_macros(cmdline, buf, cmdline - buf); + + return ret; +} + +int bootm_process_cmdline(char *buf, int maxlen, int flags) +{ + int ret; + + /* Check config first to enable compiler to eliminate code */ + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && + !IS_ENABLED(CONFIG_SILENT_U_BOOT_ONLY) && + (flags & BOOTM_CL_SILENT)) { + ret = fixup_silent_linux(buf, maxlen); + if (ret) + return log_msg_ret("silent", ret); + } + if (IS_ENABLED(CONFIG_BOOTARGS_SUBST) && IS_ENABLED(CONFIG_CMDLINE) && + (flags & BOOTM_CL_SUBST)) { + ret = process_subst(buf, maxlen); + if (ret) + return log_msg_ret("subst", ret); + } + + return 0; +} + +int bootm_process_cmdline_env(int flags) +{ + const int maxlen = MAX_CMDLINE_SIZE; + bool do_silent; + const char *env; + char *buf; + int ret; + + /* First check if any action is needed */ + do_silent = IS_ENABLED(CONFIG_SILENT_CONSOLE) && + !IS_ENABLED(CONFIG_SILENT_U_BOOT_ONLY) && (flags & BOOTM_CL_SILENT); + if (!do_silent && !IS_ENABLED(CONFIG_BOOTARGS_SUBST)) + return 0; + + env = env_get("bootargs"); + if (env && strlen(env) >= maxlen) + return -E2BIG; + buf = malloc(maxlen); + if (!buf) + return -ENOMEM; + if (env) + strcpy(buf, env); + else + *buf = '\0'; + ret = bootm_process_cmdline(buf, maxlen, flags); + if (!ret) { + ret = env_set("bootargs", buf); + + /* + * If buf is "" and bootargs does not exist, this will produce + * an error trying to delete bootargs. Ignore it + */ + if (ret == -ENOENT) + ret = 0; + } + free(buf); + if (ret) + return log_msg_ret("env", ret); + + return 0; +} + +/** + * Execute selected states of the bootm command. + * + * Note the arguments to this state must be the first argument, Any 'bootm' + * or sub-command arguments must have already been taken. + * + * Note that if states contains more than one flag it MUST contain + * BOOTM_STATE_START, since this handles and consumes the command line args. + * + * Also note that aside from boot_os_fn functions and bootm_load_os no other + * functions we store the return value of in 'ret' may use a negative return + * value, without special handling. + * + * @param cmdtp Pointer to bootm command table entry + * @param flag Command flags (CMD_FLAG_...) + * @param argc Number of subcommand arguments (0 = no arguments) + * @param argv Arguments + * @param states Mask containing states to run (BOOTM_STATE_...) + * @param images Image header information + * @param boot_progress 1 to show boot progress, 0 to not do this + * @return 0 if ok, something else on error. Some errors will cause this + * function to perform a reboot! If states contains BOOTM_STATE_OS_GO + * then the intent is to boot an OS, so this function will not return + * unless the image type is standalone. + */ +int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int states, bootm_headers_t *images, + int boot_progress) +{ + boot_os_fn *boot_fn; + ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; + + /* + * Work through the states and see how far we get. We stop on + * any error. + */ + if (states & BOOTM_STATE_START) + ret = bootm_start(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOS)) + ret = bootm_find_os(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOTHER)) + ret = bootm_find_other(cmdtp, flag, argc, argv); + + /* Load the OS */ + if (!ret && (states & BOOTM_STATE_LOADOS)) { + iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, 0); + if (ret && ret != BOOTM_ERR_OVERLAP) + goto err; + else if (ret == BOOTM_ERR_OVERLAP) + ret = 0; + } + + /* Relocate the ramdisk */ +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + if (!ret && (states & BOOTM_STATE_RAMDISK)) { + ulong rd_len = images->rd_end - images->rd_start; + + ret = boot_ramdisk_high(&images->lmb, images->rd_start, + rd_len, &images->initrd_start, &images->initrd_end); + if (!ret) { + env_set_hex("initrd_start", images->initrd_start); + env_set_hex("initrd_end", images->initrd_end); + } + } +#endif +#if CONFIG_IS_ENABLED(OF_LIBFDT) && defined(CONFIG_LMB) + if (!ret && (states & BOOTM_STATE_FDT)) { + boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); + ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, + &images->ft_len); + } +#endif + + /* From now on, we need the OS boot function */ + if (ret) + return ret; + boot_fn = bootm_os_get_boot_func(images->os.os); + need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { + if (iflag) + enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); + return 1; + } + + + /* Call various other states that are not generally used */ + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_BD_T)) + ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_PREP)) { + ret = bootm_process_cmdline_env(images->os.os == IH_OS_LINUX); + if (ret) { + printf("Cmdline setup failed (err=%d)\n", ret); + ret = CMD_RET_FAILURE; + goto err; + } + ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); + } + +#ifdef CONFIG_TRACE + /* Pretend to run the OS, then run a user command */ + if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + char *cmd_list = env_get("fakegocmd"); + + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, + images, boot_fn); + if (!ret && cmd_list) + ret = run_command_list(cmd_list, -1, flag); + } +#endif + + /* Check for unsupported subcommand. */ + if (ret) { + puts("subcommand not supported\n"); + return ret; + } + + /* Now run the OS! We hope this doesn't return */ + if (!ret && (states & BOOTM_STATE_OS_GO)) + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, + images, boot_fn); + + /* Deal with any fallout */ +err: + if (iflag) + enable_interrupts(); + + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + + return ret; +} + +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +/** + * image_get_kernel - verify legacy format kernel image + * @img_addr: in RAM address of the legacy format image to be verified + * @verify: data CRC verification flag + * + * image_get_kernel() verifies legacy image integrity and returns pointer to + * legacy image header if image verification was completed successfully. + * + * returns: + * pointer to a legacy image header if valid image was found + * otherwise return NULL + */ +static image_header_t *image_get_kernel(ulong img_addr, int verify) +{ + image_header_t *hdr = (image_header_t *)img_addr; + + if (!image_check_magic(hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC); + return NULL; + } + bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER); + + if (!image_check_hcrc(hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_HEADER); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM); + image_print_contents(hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH); + + if (!image_check_target_arch(hdr)) { + printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr)); + bootstage_error(BOOTSTAGE_ID_CHECK_ARCH); + return NULL; + } + return hdr; +} +#endif + +/** + * boot_get_kernel - find kernel image + * @os_data: pointer to a ulong variable, will hold os data start address + * @os_len: pointer to a ulong variable, will hold os data length + * + * boot_get_kernel() tries to find a kernel image, verifies its integrity + * and locates kernel data. + * + * returns: + * pointer to image header if valid image was found, plus kernel start + * address and length, otherwise NULL + */ +static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len) +{ +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + image_header_t *hdr; +#endif + ulong img_addr; + const void *buf; + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; +#if CONFIG_IS_ENABLED(FIT) + int os_noffset; +#endif + + img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], + &fit_uname_config, + &fit_uname_kernel); + + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); + + /* check image type, for FIT images get FIT kernel node */ + *os_data = *os_len = 0; + buf = map_sysmem(img_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + printf("## Booting kernel from Legacy Image at %08lx ...\n", + img_addr); + hdr = image_get_kernel(img_addr, images->verify); + if (!hdr) + return NULL; + bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE); + + /* get os_data and os_len */ + switch (image_get_type(hdr)) { + case IH_TYPE_KERNEL: + case IH_TYPE_KERNEL_NOLOAD: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + case IH_TYPE_MULTI: + image_multi_getimg(hdr, 0, os_data, os_len); + break; + case IH_TYPE_STANDALONE: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + default: + printf("Wrong Image Type for %s command\n", + cmdtp->name); + bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); + return NULL; + } + + /* + * copy image header to allow for image overwrites during + * kernel decompression. + */ + memmove(&images->legacy_hdr_os_copy, hdr, + sizeof(image_header_t)); + + /* save pointer to image header */ + images->legacy_hdr_os = hdr; + + images->legacy_hdr_valid = 1; + bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE); + break; +#endif +#if CONFIG_IS_ENABLED(FIT) + case IMAGE_FORMAT_FIT: + os_noffset = fit_image_load(images, img_addr, + &fit_uname_kernel, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_KERNEL, + BOOTSTAGE_ID_FIT_KERNEL_START, + FIT_LOAD_IGNORED, os_data, os_len); + if (os_noffset < 0) + return NULL; + + images->fit_hdr_os = map_sysmem(img_addr, 0); + images->fit_uname_os = fit_uname_kernel; + images->fit_uname_cfg = fit_uname_config; + images->fit_noffset_os = os_noffset; + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel(buf, images->verify, + os_data, os_len)) + return NULL; + break; +#endif + default: + printf("Wrong Image Format for %s command\n", cmdtp->name); + bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); + return NULL; + } + + debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", + *os_data, *os_len, *os_len); + + return buf; +} + +/** + * switch_to_non_secure_mode() - switch to non-secure mode + * + * This routine is overridden by architectures requiring this feature. + */ +void __weak switch_to_non_secure_mode(void) +{ +} + +#else /* USE_HOSTCC */ + +#if defined(CONFIG_FIT_SIGNATURE) +static int bootm_host_load_image(const void *fit, int req_image_type, + int cfg_noffset) +{ + const char *fit_uname_config = NULL; + ulong data, len; + bootm_headers_t images; + int noffset; + ulong load_end; + uint8_t image_type; + uint8_t imape_comp; + void *load_buf; + int ret; + + fit_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + memset(&images, '\0', sizeof(images)); + images.verify = 1; + noffset = fit_image_load(&images, (ulong)fit, + NULL, &fit_uname_config, + IH_ARCH_DEFAULT, req_image_type, -1, + FIT_LOAD_IGNORED, &data, &len); + if (noffset < 0) + return noffset; + if (fit_image_get_type(fit, noffset, &image_type)) { + puts("Can't get image type!\n"); + return -EINVAL; + } + + if (fit_image_get_comp(fit, noffset, &imape_comp)) { + puts("Can't get image compression!\n"); + return -EINVAL; + } + + /* Allow the image to expand by a factor of 4, should be safe */ + load_buf = malloc((1 << 20) + len * 4); + ret = image_decomp(imape_comp, 0, data, image_type, load_buf, + (void *)data, len, CONFIG_SYS_BOOTM_LEN, + &load_end); + free(load_buf); + + if (ret) { + ret = handle_decomp_error(imape_comp, load_end - 0, ret); + if (ret != BOOTM_ERR_UNIMPLEMENTED) + return ret; + } + + return 0; +} + +int bootm_host_load_images(const void *fit, int cfg_noffset) +{ + static uint8_t image_types[] = { + IH_TYPE_KERNEL, + IH_TYPE_FLATDT, + IH_TYPE_RAMDISK, + }; + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(image_types); i++) { + int ret; + + ret = bootm_host_load_image(fit, image_types[i], cfg_noffset); + if (!err && ret && ret != -ENOENT) + err = ret; + } + + /* Return the first error we found */ + return err; +} +#endif + +#endif /* ndef USE_HOSTCC */ diff --git a/boot/bootm_os.c b/boot/bootm_os.c new file mode 100644 index 0000000..39623f9 --- /dev/null +++ b/boot/bootm_os.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <bootm.h> +#include <bootstage.h> +#include <cpu_func.h> +#include <efi_loader.h> +#include <env.h> +#include <fdt_support.h> +#include <image.h> +#include <lmb.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <mapmem.h> +#include <vxworks.h> +#include <tee/optee.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int do_bootm_standalone(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *s; + int (*appl)(int, char *const[]); + + /* Don't start if "autostart" is set to "no" */ + s = env_get("autostart"); + if ((s != NULL) && !strcmp(s, "no")) { + env_set_hex("filesize", images->os.image_len); + return 0; + } + appl = (int (*)(int, char * const []))images->ep; + appl(argc, argv); + return 0; +} + +/*******************************************************************/ +/* OS booting routines */ +/*******************************************************************/ + +#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) +static void copy_args(char *dest, int argc, char *const argv[], char delim) +{ + int i; + + for (i = 0; i < argc; i++) { + if (i > 0) + *dest++ = delim; + strcpy(dest, argv[i]); + dest += strlen(argv[i]); + } +} +#endif + +static void __maybe_unused fit_unsupported_reset(const char *msg) +{ + if (CONFIG_IS_ENABLED(FIT_VERBOSE)) { + printf("! FIT images not supported for '%s' - must reset board to recover!\n", + msg); + } +} + +#ifdef CONFIG_BOOTM_NETBSD +static int do_bootm_netbsd(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*loader)(struct bd_info *, image_header_t *, char *, char *); + image_header_t *os_hdr, *hdr; + ulong kernel_data, kernel_len; + char *cmdline; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("NetBSD"); + return 1; + } +#endif + hdr = images->legacy_hdr_os; + + /* + * Booting a (NetBSD) kernel image + * + * This process is pretty similar to a standalone application: + * The (first part of an multi-) image must be a stage-2 loader, + * which in turn is responsible for loading & invoking the actual + * kernel. The only differences are the parameters being passed: + * besides the board info strucure, the loader expects a command + * line, the name of the console device, and (optionally) the + * address of the original image header. + */ + os_hdr = NULL; + if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { + image_multi_getimg(hdr, 1, &kernel_data, &kernel_len); + if (kernel_len) + os_hdr = hdr; + } + + if (argc > 0) { + ulong len; + int i; + + for (i = 0, len = 0; i < argc; i += 1) + len += strlen(argv[i]) + 1; + cmdline = malloc(len); + copy_args(cmdline, argc, argv, ' '); + } else { + cmdline = env_get("bootargs"); + if (cmdline == NULL) + cmdline = ""; + } + + loader = (void (*)(struct bd_info *, image_header_t *, char *, char *))images->ep; + + printf("## Transferring control to NetBSD stage-2 loader (at address %08lx) ...\n", + (ulong)loader); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * NetBSD Stage-2 Loader Parameters: + * arg[0]: pointer to board info data + * arg[1]: image load address + * arg[2]: char pointer to the console device to use + * arg[3]: char pointer to the boot arguments + */ + (*loader)(gd->bd, os_hdr, "", cmdline); + + return 1; +} +#endif /* CONFIG_BOOTM_NETBSD*/ + +#ifdef CONFIG_LYNXKDI +static int do_bootm_lynxkdi(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + image_header_t *hdr = &images->legacy_hdr_os_copy; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Lynx"); + return 1; + } +#endif + + lynxkdi_boot((image_header_t *)hdr); + + return 1; +} +#endif /* CONFIG_LYNXKDI */ + +#ifdef CONFIG_BOOTM_RTEMS +static int do_bootm_rtems(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(struct bd_info *); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("RTEMS"); + return 1; + } +#endif + + entry_point = (void (*)(struct bd_info *))images->ep; + + printf("## Transferring control to RTEMS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * RTEMS Parameters: + * r3: ptr to board info data + */ + (*entry_point)(gd->bd); + + return 1; +} +#endif /* CONFIG_BOOTM_RTEMS */ + +#if defined(CONFIG_BOOTM_OSE) +static int do_bootm_ose(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("OSE"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OSE (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OSE Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_OSE */ + +#if defined(CONFIG_BOOTM_PLAN9) +static int do_bootm_plan9(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + char *s; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Plan 9"); + return 1; + } +#endif + + /* See README.plan9 */ + s = env_get("confaddr"); + if (s != NULL) { + char *confaddr = (char *)hextoul(s, NULL); + + if (argc > 0) { + copy_args(confaddr, argc, argv, '\n'); + } else { + s = env_get("bootargs"); + if (s != NULL) + strcpy(confaddr, s); + } + } + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to Plan 9 (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * Plan 9 Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_PLAN9 */ + +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + +static void do_bootvx_fdt(bootm_headers_t *images) +{ +#if defined(CONFIG_OF_LIBFDT) + int ret; + char *bootline; + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + + if (*of_flat_tree) { + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return; + + /* Update ethernet nodes */ + fdt_fixup_ethernet(*of_flat_tree); + + ret = fdt_add_subnode(*of_flat_tree, 0, "chosen"); + if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) { + bootline = env_get("bootargs"); + if (bootline) { + ret = fdt_find_and_setprop(*of_flat_tree, + "/chosen", "bootargs", + bootline, + strlen(bootline) + 1, 1); + if (ret < 0) { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } + } else { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } +#endif + + boot_prep_vxworks(images); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) + printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n", + (ulong)images->ep, (ulong)*of_flat_tree); +#else + printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep); +#endif + + boot_jump_vxworks(images); + + puts("## vxWorks terminated\n"); +} + +static int do_bootm_vxworks_legacy(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("VxWorks"); + return 1; + } +#endif + + do_bootvx_fdt(images); + + return 1; +} + +int do_bootm_vxworks(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *bootargs; + int pos; + unsigned long vxflags; + bool std_dtb = false; + + /* get bootargs env */ + bootargs = env_get("bootargs"); + + if (bootargs != NULL) { + for (pos = 0; pos < strlen(bootargs); pos++) { + /* find f=0xnumber flag */ + if ((bootargs[pos] == '=') && (pos >= 1) && + (bootargs[pos - 1] == 'f')) { + vxflags = hextoul(&bootargs[pos + 1], NULL); + if (vxflags & VXWORKS_SYSFLG_STD_DTB) + std_dtb = true; + } + } + } + + if (std_dtb) { + if (flag & BOOTM_STATE_OS_PREP) + printf(" Using standard DTB\n"); + return do_bootm_linux(flag, argc, argv, images); + } else { + if (flag & BOOTM_STATE_OS_PREP) + printf(" !!! WARNING !!! Using legacy DTB\n"); + return do_bootm_vxworks_legacy(flag, argc, argv, images); + } +} +#endif + +#if defined(CONFIG_CMD_ELF) +static int do_bootm_qnxelf(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *local_args[2]; + char str[16]; + int dcache; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("QNX"); + return 1; + } +#endif + + sprintf(str, "%lx", images->ep); /* write entry-point into string */ + local_args[0] = argv[0]; + local_args[1] = str; /* and provide it via the arguments */ + + /* + * QNX images require the data cache is disabled. + */ + dcache = dcache_status(); + if (dcache) + dcache_disable(); + + do_bootelf(NULL, 0, 2, local_args); + + if (dcache) + dcache_enable(); + + return 1; +} +#endif + +#ifdef CONFIG_INTEGRITY +static int do_bootm_integrity(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("INTEGRITY"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to INTEGRITY (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * INTEGRITY Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +#ifdef CONFIG_BOOTM_OPENRTOS +static int do_bootm_openrtos(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OpenRTOS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OpenRTOS Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +#ifdef CONFIG_BOOTM_OPTEE +static int do_bootm_tee(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + int ret; + + /* Verify OS type */ + if (images->os.os != IH_OS_TEE) { + return 1; + }; + + /* Validate OPTEE header */ + ret = optee_verify_bootm_image(images->os.image_start, + images->os.load, + images->os.image_len); + if (ret) + return ret; + + /* Locate FDT etc */ + ret = bootm_find_images(flag, argc, argv, 0, 0); + if (ret) + return ret; + + /* From here we can run the regular linux boot path */ + return do_bootm_linux(flag, argc, argv, images); +} +#endif + +#ifdef CONFIG_BOOTM_EFI +static int do_bootm_efi(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + int ret; + efi_status_t efi_ret; + void *image_buf; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + /* Locate FDT, if provided */ + ret = bootm_find_images(flag, argc, argv, 0, 0); + if (ret) + return ret; + + /* Initialize EFI drivers */ + efi_ret = efi_init_obj_list(); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to initialize UEFI sub-system: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Install device tree */ + efi_ret = efi_install_fdt(images->ft_len + ? images->ft_addr : EFI_FDT_USE_INTERNAL); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to install device tree: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Run EFI image */ + printf("## Transferring control to EFI (at address %08lx) ...\n", + images->ep); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* We expect to return */ + images->os.type = IH_TYPE_STANDALONE; + + image_buf = map_sysmem(images->ep, images->os.image_len); + + efi_ret = efi_run_image(image_buf, images->os.image_len); + if (efi_ret != EFI_SUCCESS) + return 1; + return 0; +} +#endif + +static boot_os_fn *boot_os[] = { + [IH_OS_U_BOOT] = do_bootm_standalone, +#ifdef CONFIG_BOOTM_LINUX + [IH_OS_LINUX] = do_bootm_linux, +#endif +#ifdef CONFIG_BOOTM_NETBSD + [IH_OS_NETBSD] = do_bootm_netbsd, +#endif +#ifdef CONFIG_LYNXKDI + [IH_OS_LYNXOS] = do_bootm_lynxkdi, +#endif +#ifdef CONFIG_BOOTM_RTEMS + [IH_OS_RTEMS] = do_bootm_rtems, +#endif +#if defined(CONFIG_BOOTM_OSE) + [IH_OS_OSE] = do_bootm_ose, +#endif +#if defined(CONFIG_BOOTM_PLAN9) + [IH_OS_PLAN9] = do_bootm_plan9, +#endif +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM) || defined(CONFIG_RISCV)) + [IH_OS_VXWORKS] = do_bootm_vxworks, +#endif +#if defined(CONFIG_CMD_ELF) + [IH_OS_QNX] = do_bootm_qnxelf, +#endif +#ifdef CONFIG_INTEGRITY + [IH_OS_INTEGRITY] = do_bootm_integrity, +#endif +#ifdef CONFIG_BOOTM_OPENRTOS + [IH_OS_OPENRTOS] = do_bootm_openrtos, +#endif +#ifdef CONFIG_BOOTM_OPTEE + [IH_OS_TEE] = do_bootm_tee, +#endif +#ifdef CONFIG_BOOTM_EFI + [IH_OS_EFI] = do_bootm_efi, +#endif +}; + +/* Allow for arch specific config before we boot */ +__weak void arch_preboot_os(void) +{ + /* please define platform specific arch_preboot_os() */ +} + +/* Allow for board specific config before we boot */ +__weak void board_preboot_os(void) +{ + /* please define board specific board_preboot_os() */ +} + +int boot_selected_os(int argc, char *const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) +{ + arch_preboot_os(); + board_preboot_os(); + boot_fn(state, argc, argv, images); + + /* Stand-alone may return when 'autostart' is 'no' */ + if (images->os.type == IH_TYPE_STANDALONE || + IS_ENABLED(CONFIG_SANDBOX) || + state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ + return 0; + bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); + debug("\n## Control returned to monitor - resetting...\n"); + + return BOOTM_ERR_RESET; +} + +boot_os_fn *bootm_os_get_boot_func(int os) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static bool relocated; + + if (!relocated) { + int i; + + /* relocate boot function table */ + for (i = 0; i < ARRAY_SIZE(boot_os); i++) + if (boot_os[i] != NULL) + boot_os[i] += gd->reloc_off; + + relocated = true; + } +#endif + return boot_os[os]; +} diff --git a/boot/bootretry.c b/boot/bootretry.c new file mode 100644 index 0000000..dac891f --- /dev/null +++ b/boot/bootretry.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <env.h> +#include <errno.h> +#include <time.h> +#include <watchdog.h> + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +static uint64_t endtime; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ + +/*************************************************************************** + * initialize command line timeout + */ +void bootretry_init_cmd_timeout(void) +{ + char *s = env_get("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void bootretry_reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} + +int bootretry_tstc_timeout(void) +{ + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return -ETIMEDOUT; + WATCHDOG_RESET(); + } + + return 0; +} + +void bootretry_dont_retry(void) +{ + retry_time = -1; +} diff --git a/boot/common_fit.c b/boot/common_fit.c new file mode 100644 index 0000000..cde2dc4 --- /dev/null +++ b/boot/common_fit.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <linux/libfdt.h> + +ulong fdt_getprop_u32(const void *fdt, int node, const char *prop) +{ + const u32 *cell; + int len; + + cell = fdt_getprop(fdt, node, prop, &len); + if (!cell || len != sizeof(*cell)) + return FDT_ERROR; + + return fdt32_to_cpu(*cell); +} + +__weak int board_fit_config_name_match(const char *name) +{ + return -EINVAL; +} + +/* + * Iterate over all /configurations subnodes and call a platform specific + * function to find the matching configuration. + * Returns the node offset or a negative error number. + */ +int fit_find_config_node(const void *fdt) +{ + const char *name; + int conf, node, len; + const char *dflt_conf_name; + const char *dflt_conf_desc = NULL; + int dflt_conf_node = -ENOENT; + + conf = fdt_path_offset(fdt, FIT_CONFS_PATH); + if (conf < 0) { + debug("%s: Cannot find /configurations node: %d\n", __func__, + conf); + return -EINVAL; + } + + dflt_conf_name = fdt_getprop(fdt, conf, "default", &len); + + for (node = fdt_first_subnode(fdt, conf); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + name = fdt_getprop(fdt, node, "description", &len); + if (!name) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: Missing FDT description in DTB\n", + __func__); +#endif + return -EINVAL; + } + + if (dflt_conf_name) { + const char *node_name = fdt_get_name(fdt, node, NULL); + if (strcmp(dflt_conf_name, node_name) == 0) { + dflt_conf_node = node; + dflt_conf_desc = name; + } + } + + if (board_fit_config_name_match(name)) + continue; + + debug("Selecting config '%s'\n", name); + + return node; + } + + if (dflt_conf_node != -ENOENT) { + debug("Selecting default config '%s'\n", dflt_conf_desc); + return dflt_conf_node; + } + + return -ENOENT; +} diff --git a/boot/fdt_region.c b/boot/fdt_region.c new file mode 100644 index 0000000..e4ef0ca --- /dev/null +++ b/boot/fdt_region.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2013 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <fdt_support.h> +#include <linux/libfdt_env.h> +#include <fdt_region.h> + +#ifndef USE_HOSTCC +#include <fdt.h> +#include <linux/libfdt.h> +#else +#include "fdt_host.h" +#endif + +#define FDT_MAX_DEPTH 32 + +static int str_in_list(const char *str, char * const list[], int count) +{ + int i; + + for (i = 0; i < count; i++) + if (!strcmp(list[i], str)) + return 1; + + return 0; +} + +int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + char * const exc_prop[], int exc_prop_count, + struct fdt_region region[], int max_regions, + char *path, int path_len, int add_string_tab) +{ + int stack[FDT_MAX_DEPTH] = { 0 }; + char *end; + int nextoffset = 0; + uint32_t tag; + int count = 0; + int start = -1; + int depth = -1; + int want = 0; + int base = fdt_off_dt_struct(fdt); + bool expect_end = false; + + end = path; + *end = '\0'; + do { + const struct fdt_property *prop; + const char *name; + const char *str; + int include = 0; + int stop_at = 0; + int offset; + int len; + + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + stop_at = nextoffset; + + /* If we see two root nodes, something is wrong */ + if (expect_end && tag != FDT_END) + return -FDT_ERR_BADLAYOUT; + + switch (tag) { + case FDT_PROP: + include = want >= 2; + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + if (!str) + return -FDT_ERR_BADSTRUCTURE; + if (str_in_list(str, exc_prop, exc_prop_count)) + include = 0; + break; + + case FDT_NOP: + include = want >= 2; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + depth++; + if (depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); + + /* The root node must have an empty name */ + if (!depth && *name) + return -FDT_ERR_BADLAYOUT; + if (end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + if (end != path + 1) + *end++ = '/'; + strcpy(end, name); + end += len; + stack[depth] = want; + if (want == 1) + stop_at = offset; + if (str_in_list(path, inc, inc_count)) + want = 2; + else if (want) + want--; + else + stop_at = offset; + include = want; + break; + + case FDT_END_NODE: + /* Depth must never go below -1 */ + if (depth < 0) + return -FDT_ERR_BADSTRUCTURE; + include = want; + want = stack[depth--]; + while (end > path && *--end != '/') + ; + *end = '\0'; + if (depth == -1) + expect_end = true; + break; + + case FDT_END: + include = 1; + break; + } + + if (include && start == -1) { + /* Should we merge with previous? */ + if (count && count <= max_regions && + offset == region[count - 1].offset + + region[count - 1].size - base) + start = region[--count].offset - base; + else + start = offset; + } + + if (!include && start != -1) { + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = stop_at - start; + } + count++; + start = -1; + } + } while (tag != FDT_END); + + if (nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADLAYOUT; + + /* Add a region for the END tag and the string table */ + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = nextoffset - start; + if (add_string_tab) + region[count].size += fdt_size_dt_strings(fdt); + } + count++; + + return count; +} + +/** + * fdt_add_region() - Add a new region to our list + * @info: State information + * @offset: Start offset of region + * @size: Size of region + * + * The region is added if there is space, but in any case we increment the + * count. If permitted, and the new region overlaps the last one, we merge + * them. + */ +static int fdt_add_region(struct fdt_region_state *info, int offset, int size) +{ + struct fdt_region *reg; + + reg = info->region ? &info->region[info->count - 1] : NULL; + if (info->can_merge && info->count && + info->count <= info->max_regions && + reg && offset <= reg->offset + reg->size) { + reg->size = offset + size - reg->offset; + } else if (info->count++ < info->max_regions) { + if (reg) { + reg++; + reg->offset = offset; + reg->size = size; + } + } else { + return -1; + } + + return 0; +} + +static int region_list_contains_offset(struct fdt_region_state *info, + const void *fdt, int target) +{ + struct fdt_region *reg; + int num; + + target += fdt_off_dt_struct(fdt); + for (reg = info->region, num = 0; num < info->count; reg++, num++) { + if (target >= reg->offset && target < reg->offset + reg->size) + return 1; + } + + return 0; +} + +/** + * fdt_add_alias_regions() - Add regions covering the aliases that we want + * + * The /aliases node is not automatically included by fdtgrep unless the + * command-line arguments cause to be included (or not excluded). However + * aliases are special in that we generally want to include those which + * reference a node that fdtgrep includes. + * + * In fact we want to include only aliases for those nodes still included in + * the fdt, and drop the other aliases since they point to nodes that will not + * be present. + * + * This function scans the aliases and adds regions for those which we want + * to keep. + * + * @fdt: Device tree to scan + * @region: List of regions + * @count: Number of regions in the list so far (i.e. starting point for this + * function) + * @max_regions: Maximum number of regions in @region list + * @info: Place to put the region state + * @return number of regions after processing, or -FDT_ERR_NOSPACE if we did + * not have enough room in the regions table for the regions we wanted to add. + */ +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int node, node_end, offset; + int did_alias_header; + + node = fdt_subnode_offset(fdt, 0, "aliases"); + if (node < 0) + return -FDT_ERR_NOTFOUND; + + /* + * Find the next node so that we know where the /aliases node ends. We + * need special handling if /aliases is the last node. + */ + node_end = fdt_next_subnode(fdt, node); + if (node_end == -FDT_ERR_NOTFOUND) + /* Move back to the FDT_END_NODE tag of '/' */ + node_end = fdt_size_dt_struct(fdt) - sizeof(fdt32_t) * 2; + else if (node_end < 0) /* other error */ + return node_end; + node_end -= sizeof(fdt32_t); /* Move to FDT_END_NODE tag of /aliases */ + + did_alias_header = 0; + info->region = region; + info->count = count; + info->can_merge = 0; + info->max_regions = max_regions; + + for (offset = fdt_first_property_offset(fdt, node); + offset >= 0; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *name; + int target, next; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + target = fdt_path_offset(fdt, name); + if (!region_list_contains_offset(info, fdt, target)) + continue; + next = fdt_next_property_offset(fdt, offset); + if (next < 0) + next = node_end; + + if (!did_alias_header) { + fdt_add_region(info, base + node, 12); + did_alias_header = 1; + } + fdt_add_region(info, base + offset, next - offset); + } + + /* Add the FDT_END_NODE tag */ + if (did_alias_header) + fdt_add_region(info, base + node_end, sizeof(fdt32_t)); + + return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE; +} + +/** + * fdt_include_supernodes() - Include supernodes required by this node + * @info: State information + * @depth: Current stack depth + * + * When we decided to include a node or property which is not at the top + * level, this function forces the inclusion of higher level nodes. For + * example, given this tree: + * + * / { + * testing { + * } + * } + * + * If we decide to include testing then we need the root node to have a valid + * tree. This function adds those regions. + */ +static int fdt_include_supernodes(struct fdt_region_state *info, int depth) +{ + int base = fdt_off_dt_struct(info->fdt); + int start, stop_at; + int i; + + /* + * Work down the stack looking for supernodes that we didn't include. + * The algortihm here is actually pretty simple, since we know that + * no previous subnode had to include these nodes, or if it did, we + * marked them as included (on the stack) already. + */ + for (i = 0; i <= depth; i++) { + if (!info->stack[i].included) { + start = info->stack[i].offset; + + /* Add the FDT_BEGIN_NODE tag of this supernode */ + fdt_next_tag(info->fdt, start, &stop_at); + if (fdt_add_region(info, base + start, stop_at - start)) + return -1; + + /* Remember that this supernode is now included */ + info->stack[i].included = 1; + info->can_merge = 1; + } + + /* Force (later) generation of the FDT_END_NODE tag */ + if (!info->stack[i].want) + info->stack[i].want = WANT_NODES_ONLY; + } + + return 0; +} + +enum { + FDT_DONE_NOTHING, + FDT_DONE_MEM_RSVMAP, + FDT_DONE_STRUCT, + FDT_DONE_END, + FDT_DONE_STRINGS, + FDT_DONE_ALL, +}; + +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + struct fdt_region_ptrs *p = &info->ptrs; + + /* Set up our state */ + info->fdt = fdt; + info->can_merge = 1; + info->max_regions = 1; + info->start = -1; + p->want = WANT_NOTHING; + p->end = path; + *p->end = '\0'; + p->nextoffset = 0; + p->depth = -1; + p->done = FDT_DONE_NOTHING; + + return fdt_next_region(fdt, h_include, priv, region, + path, path_len, flags, info); +} + +/*********************************************************************** + * + * Theory of operation + * + * Note: in this description 'included' means that a node (or other part + * of the tree) should be included in the region list, i.e. it will have + * a region which covers its part of the tree. + * + * This function maintains some state from the last time it is called. + * It checks the next part of the tree that it is supposed to look at + * (p.nextoffset) to see if that should be included or not. When it + * finds something to include, it sets info->start to its offset. This + * marks the start of the region we want to include. + * + * Once info->start is set to the start (i.e. not -1), we continue + * scanning until we find something that we don't want included. This + * will be the end of a region. At this point we can close off the + * region and add it to the list. So we do so, and reset info->start + * to -1. + * + * One complication here is that we want to merge regions. So when we + * come to add another region later, we may in fact merge it with the + * previous one if one ends where the other starts. + * + * The function fdt_add_region() will return -1 if it fails to add the + * region, because we already have a region ready to be returned, and + * the new one cannot be merged in with it. In this case, we must return + * the region we found, and wait for another call to this function. + * When it comes, we will repeat the processing of the tag and again + * try to add a region. This time it will succeed. + * + * The current state of the pointers (stack, offset, etc.) is maintained + * in a ptrs member. At the start of every loop iteration we make a copy + * of it. The copy is then updated as the tag is processed. Only if we + * get to the end of the loop iteration (and successfully call + * fdt_add_region() if we need to) can we commit the changes we have + * made to these pointers. For example, if we see an FDT_END_NODE tag, + * we will decrement the depth value. But if we need to add a region + * for this tag (let's say because the previous tag is included and this + * FDT_END_NODE tag is not included) then we will only commit the result + * if we were able to add the region. That allows us to retry again next + * time. + * + * We keep track of a variable called 'want' which tells us what we want + * to include when there is no specific information provided by the + * h_include function for a particular property. This basically handles + * the inclusion of properties which are pulled in by virtue of the node + * they are in. So if you include a node, its properties are also + * included. In this case 'want' will be WANT_NODES_AND_PROPS. The + * FDT_REG_DIRECT_SUBNODES feature also makes use of 'want'. While we + * are inside the subnode, 'want' will be set to WANT_NODES_ONLY, so + * that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE tags will be + * included, and properties will be skipped. If WANT_NOTHING is + * selected, then we will just rely on what the h_include() function + * tells us. + * + * Using 'want' we work out 'include', which tells us whether this + * current tag should be included or not. As you can imagine, if the + * value of 'include' changes, that means we are on a boundary between + * nodes to include and nodes to exclude. At this point we either close + * off a previous region and add it to the list, or mark the start of a + * new region. + * + * Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the + * string list. Each of these dealt with as a whole (i.e. we create a + * region for each if it is to be included). For mem_rsvmap we don't + * allow it to merge with the first struct region. For the stringlist, + * we don't allow it to merge with the last struct region (which + * contains at minimum the FDT_END tag). + * + *********************************************************************/ + +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int last_node = 0; + const char *str; + + info->region = region; + info->count = 0; + if (info->ptrs.done < FDT_DONE_MEM_RSVMAP && + (flags & FDT_REG_ADD_MEM_RSVMAP)) { + /* Add the memory reserve map into its own region */ + if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt), + fdt_off_dt_struct(fdt) - + fdt_off_mem_rsvmap(fdt))) + return 0; + info->can_merge = 0; /* Don't allow merging with this */ + info->ptrs.done = FDT_DONE_MEM_RSVMAP; + } + + /* + * Work through the tags one by one, deciding whether each needs to + * be included or not. We set the variable 'include' to indicate our + * decision. 'want' is used to track what we want to include - it + * allows us to pick up all the properties (and/or subnode tags) of + * a node. + */ + while (info->ptrs.done < FDT_DONE_STRUCT) { + const struct fdt_property *prop; + struct fdt_region_ptrs p; + const char *name; + int include = 0; + int stop_at = 0; + uint32_t tag; + int offset; + int val; + int len; + + /* + * Make a copy of our pointers. If we make it to the end of + * this block then we will commit them back to info->ptrs. + * Otherwise we can try again from the same starting state + * next time we are called. + */ + p = info->ptrs; + + /* + * Find the tag, and the offset of the next one. If we need to + * stop including tags, then by default we stop *after* + * including the current tag + */ + offset = p.nextoffset; + tag = fdt_next_tag(fdt, offset, &p.nextoffset); + stop_at = p.nextoffset; + + switch (tag) { + case FDT_PROP: + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + val = h_include(priv, fdt, last_node, FDT_IS_PROP, str, + strlen(str) + 1); + if (val == -1) { + include = p.want >= WANT_NODES_AND_PROPS; + } else { + include = val; + /* + * Make sure we include the } for this block. + * It might be more correct to have this done + * by the call to fdt_include_supernodes() in + * the case where it adds the node we are + * currently in, but this is equivalent. + */ + if ((flags & FDT_REG_SUPERNODES) && val && + !p.want) + p.want = WANT_NODES_ONLY; + } + + /* Value grepping is not yet supported */ + break; + + case FDT_NOP: + include = p.want >= WANT_NODES_AND_PROPS; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + last_node = offset; + p.depth++; + if (p.depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); + if (p.end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + + /* Build the full path of this node */ + if (p.end != path + 1) + *p.end++ = '/'; + strcpy(p.end, name); + p.end += len; + info->stack[p.depth].want = p.want; + info->stack[p.depth].offset = offset; + + /* + * If we are not intending to include this node unless + * it matches, make sure we stop *before* its tag. + */ + if (p.want == WANT_NODES_ONLY || + !(flags & (FDT_REG_DIRECT_SUBNODES | + FDT_REG_ALL_SUBNODES))) { + stop_at = offset; + p.want = WANT_NOTHING; + } + val = h_include(priv, fdt, offset, FDT_IS_NODE, path, + p.end - path + 1); + + /* Include this if requested */ + if (val) { + p.want = (flags & FDT_REG_ALL_SUBNODES) ? + WANT_ALL_NODES_AND_PROPS : + WANT_NODES_AND_PROPS; + } + + /* If not requested, decay our 'p.want' value */ + else if (p.want) { + if (p.want != WANT_ALL_NODES_AND_PROPS) + p.want--; + + /* Not including this tag, so stop now */ + } else { + stop_at = offset; + } + + /* + * Decide whether to include this tag, and update our + * stack with the state for this node + */ + include = p.want; + info->stack[p.depth].included = include; + break; + + case FDT_END_NODE: + include = p.want; + if (p.depth < 0) + return -FDT_ERR_BADSTRUCTURE; + + /* + * If we don't want this node, stop right away, unless + * we are including subnodes + */ + if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES)) + stop_at = offset; + p.want = info->stack[p.depth].want; + p.depth--; + while (p.end > path && *--p.end != '/') + ; + *p.end = '\0'; + break; + + case FDT_END: + /* We always include the end tag */ + include = 1; + p.done = FDT_DONE_STRUCT; + break; + } + + /* If this tag is to be included, mark it as region start */ + if (include && info->start == -1) { + /* Include any supernodes required by this one */ + if (flags & FDT_REG_SUPERNODES) { + if (fdt_include_supernodes(info, p.depth)) + return 0; + } + info->start = offset; + } + + /* + * If this tag is not to be included, finish up the current + * region. + */ + if (!include && info->start != -1) { + if (fdt_add_region(info, base + info->start, + stop_at - info->start)) + return 0; + info->start = -1; + info->can_merge = 1; + } + + /* If we have made it this far, we can commit our pointers */ + info->ptrs = p; + } + + /* Add a region for the END tag and a separate one for string table */ + if (info->ptrs.done < FDT_DONE_END) { + if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTRUCTURE; + + if (fdt_add_region(info, base + info->start, + info->ptrs.nextoffset - info->start)) + return 0; + info->ptrs.done++; + } + if (info->ptrs.done < FDT_DONE_STRINGS) { + if (flags & FDT_REG_ADD_STRING_TAB) { + info->can_merge = 0; + if (fdt_off_dt_strings(fdt) < + base + info->ptrs.nextoffset) + return -FDT_ERR_BADLAYOUT; + if (fdt_add_region(info, fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return 0; + } + info->ptrs.done++; + } + + return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND; +} diff --git a/boot/image-android-dt.c b/boot/image-android-dt.c new file mode 100644 index 0000000..a2d52df --- /dev/null +++ b/boot/image-android-dt.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + */ + +#include <image-android-dt.h> +#include <dt_table.h> +#include <common.h> +#include <linux/libfdt.h> +#include <mapmem.h> + +/** + * Check if image header is correct. + * + * @param hdr_addr Start address of DT image + * @return true if header is correct or false if header is incorrect + */ +bool android_dt_check_header(ulong hdr_addr) +{ + const struct dt_table_header *hdr; + u32 magic; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + magic = fdt32_to_cpu(hdr->magic); + unmap_sysmem(hdr); + + return magic == DT_TABLE_MAGIC; +} + +/** + * Get the address of FDT (dtb or dtbo) in memory by its index in image. + * + * @param hdr_addr Start address of DT image + * @param index Index of desired FDT in image (starting from 0) + * @param[out] addr If not NULL, will contain address to specified FDT + * @param[out] size If not NULL, will contain size of specified FDT + * + * @return true on success or false on error + */ +bool android_dt_get_fdt_by_index(ulong hdr_addr, u32 index, ulong *addr, + u32 *size) +{ + const struct dt_table_header *hdr; + const struct dt_table_entry *e; + u32 entry_count, entries_offset, entry_size; + ulong e_addr; + u32 dt_offset, dt_size; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + entry_count = fdt32_to_cpu(hdr->dt_entry_count); + entries_offset = fdt32_to_cpu(hdr->dt_entries_offset); + entry_size = fdt32_to_cpu(hdr->dt_entry_size); + unmap_sysmem(hdr); + + if (index >= entry_count) { + printf("Error: index >= dt_entry_count (%u >= %u)\n", index, + entry_count); + return false; + } + + e_addr = hdr_addr + entries_offset + index * entry_size; + e = map_sysmem(e_addr, sizeof(*e)); + dt_offset = fdt32_to_cpu(e->dt_offset); + dt_size = fdt32_to_cpu(e->dt_size); + unmap_sysmem(e); + + if (addr) + *addr = hdr_addr + dt_offset; + if (size) + *size = dt_size; + + return true; +} + +#if !defined(CONFIG_SPL_BUILD) +static void android_dt_print_fdt_info(const struct fdt_header *fdt) +{ + u32 fdt_size; + int root_node_off; + const char *compatible; + + root_node_off = fdt_path_offset(fdt, "/"); + if (root_node_off < 0) { + printf("Error: Root node not found\n"); + return; + } + + fdt_size = fdt_totalsize(fdt); + compatible = fdt_getprop(fdt, root_node_off, "compatible", + NULL); + + printf(" (FDT)size = %d\n", fdt_size); + printf(" (FDT)compatible = %s\n", + compatible ? compatible : "(unknown)"); +} + +/** + * Print information about DT image structure. + * + * @param hdr_addr Start address of DT image + */ +void android_dt_print_contents(ulong hdr_addr) +{ + const struct dt_table_header *hdr; + u32 entry_count, entries_offset, entry_size; + u32 i; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + entry_count = fdt32_to_cpu(hdr->dt_entry_count); + entries_offset = fdt32_to_cpu(hdr->dt_entries_offset); + entry_size = fdt32_to_cpu(hdr->dt_entry_size); + + /* Print image header info */ + printf("dt_table_header:\n"); + printf(" magic = %08x\n", fdt32_to_cpu(hdr->magic)); + printf(" total_size = %d\n", fdt32_to_cpu(hdr->total_size)); + printf(" header_size = %d\n", fdt32_to_cpu(hdr->header_size)); + printf(" dt_entry_size = %d\n", entry_size); + printf(" dt_entry_count = %d\n", entry_count); + printf(" dt_entries_offset = %d\n", entries_offset); + printf(" page_size = %d\n", fdt32_to_cpu(hdr->page_size)); + printf(" version = %d\n", fdt32_to_cpu(hdr->version)); + + unmap_sysmem(hdr); + + /* Print image entries info */ + for (i = 0; i < entry_count; ++i) { + const ulong e_addr = hdr_addr + entries_offset + i * entry_size; + const struct dt_table_entry *e; + const struct fdt_header *fdt; + u32 dt_offset, dt_size; + u32 j; + + e = map_sysmem(e_addr, sizeof(*e)); + dt_offset = fdt32_to_cpu(e->dt_offset); + dt_size = fdt32_to_cpu(e->dt_size); + + printf("dt_table_entry[%d]:\n", i); + printf(" dt_size = %d\n", dt_size); + printf(" dt_offset = %d\n", dt_offset); + printf(" id = %08x\n", fdt32_to_cpu(e->id)); + printf(" rev = %08x\n", fdt32_to_cpu(e->rev)); + for (j = 0; j < 4; ++j) { + printf(" custom[%d] = %08x\n", j, + fdt32_to_cpu(e->custom[j])); + } + + unmap_sysmem(e); + + /* Print FDT info for this entry */ + fdt = map_sysmem(hdr_addr + dt_offset, sizeof(*fdt)); + android_dt_print_fdt_info(fdt); + unmap_sysmem(fdt); + } +} +#endif diff --git a/boot/image-android.c b/boot/image-android.c new file mode 100644 index 0000000..1fbbbba --- /dev/null +++ b/boot/image-android.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> + */ + +#include <common.h> +#include <env.h> +#include <image.h> +#include <image-android-dt.h> +#include <android_image.h> +#include <malloc.h> +#include <errno.h> +#include <asm/unaligned.h> +#include <mapmem.h> +#include <linux/libfdt.h> + +#define ANDROID_IMAGE_DEFAULT_KERNEL_ADDR 0x10008000 + +static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; + +static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) +{ + /* + * All the Android tools that generate a boot.img use this + * address as the default. + * + * Even though it doesn't really make a lot of sense, and it + * might be valid on some platforms, we treat that adress as + * the default value for this field, and try to execute the + * kernel in place in such a case. + * + * Otherwise, we will return the actual value set by the user. + */ + if (hdr->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) + return (ulong)hdr + hdr->page_size; + + /* + * abootimg creates images where all load addresses are 0 + * and we need to fix them. + */ + if (hdr->kernel_addr == 0 && hdr->ramdisk_addr == 0) + return env_get_ulong("kernel_addr_r", 16, 0); + + return hdr->kernel_addr; +} + +/** + * android_image_get_kernel() - processes kernel part of Android boot images + * @hdr: Pointer to image header, which is at the start + * of the image. + * @verify: Checksum verification flag. Currently unimplemented. + * @os_data: Pointer to a ulong variable, will hold os data start + * address. + * @os_len: Pointer to a ulong variable, will hold os data length. + * + * This function returns the os image's start address and length. Also, + * it appends the kernel command line to the bootargs env variable. + * + * Return: Zero, os start address and length on success, + * otherwise on failure. + */ +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len) +{ + u32 kernel_addr = android_image_get_kernel_addr(hdr); + const struct image_header *ihdr = (const struct image_header *) + ((uintptr_t)hdr + hdr->page_size); + + /* + * Not all Android tools use the id field for signing the image with + * sha1 (or anything) so we don't check it. It is not obvious that the + * string is null terminated so we take care of this. + */ + strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; + if (strlen(andr_tmp_str)) + printf("Android's image name: %s\n", andr_tmp_str); + + printf("Kernel load addr 0x%08x size %u KiB\n", + kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + + int len = 0; + if (*hdr->cmdline) { + printf("Kernel command line: %s\n", hdr->cmdline); + len += strlen(hdr->cmdline); + } + + char *bootargs = env_get("bootargs"); + if (bootargs) + len += strlen(bootargs); + + char *newbootargs = malloc(len + 2); + if (!newbootargs) { + puts("Error: malloc in android_image_get_kernel failed!\n"); + return -ENOMEM; + } + *newbootargs = '\0'; + + if (bootargs) { + strcpy(newbootargs, bootargs); + strcat(newbootargs, " "); + } + if (*hdr->cmdline) + strcat(newbootargs, hdr->cmdline); + + env_set("bootargs", newbootargs); + + if (os_data) { + if (image_get_magic(ihdr) == IH_MAGIC) { + *os_data = image_get_data(ihdr); + } else { + *os_data = (ulong)hdr; + *os_data += hdr->page_size; + } + } + if (os_len) { + if (image_get_magic(ihdr) == IH_MAGIC) + *os_len = image_get_data_size(ihdr); + else + *os_len = hdr->kernel_size; + } + return 0; +} + +int android_image_check_header(const struct andr_img_hdr *hdr) +{ + return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); +} + +ulong android_image_get_end(const struct andr_img_hdr *hdr) +{ + ulong end; + + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + end = (ulong)hdr; + end += hdr->page_size; + end += ALIGN(hdr->kernel_size, hdr->page_size); + end += ALIGN(hdr->ramdisk_size, hdr->page_size); + end += ALIGN(hdr->second_size, hdr->page_size); + + if (hdr->header_version >= 1) + end += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + + if (hdr->header_version >= 2) + end += ALIGN(hdr->dtb_size, hdr->page_size); + + return end; +} + +ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{ + return android_image_get_kernel_addr(hdr); +} + +ulong android_image_get_kcomp(const struct andr_img_hdr *hdr) +{ + const void *p = (void *)((uintptr_t)hdr + hdr->page_size); + + if (image_get_magic((image_header_t *)p) == IH_MAGIC) + return image_get_comp((image_header_t *)p); + else if (get_unaligned_le32(p) == LZ4F_MAGIC) + return IH_COMP_LZ4; + else + return image_decomp_type(p, sizeof(u32)); +} + +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len) +{ + if (!hdr->ramdisk_size) { + *rd_data = *rd_len = 0; + return -1; + } + + printf("RAM disk load addr 0x%08x size %u KiB\n", + hdr->ramdisk_addr, DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + + *rd_data = (unsigned long)hdr; + *rd_data += hdr->page_size; + *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + + *rd_len = hdr->ramdisk_size; + return 0; +} + +int android_image_get_second(const struct andr_img_hdr *hdr, + ulong *second_data, ulong *second_len) +{ + if (!hdr->second_size) { + *second_data = *second_len = 0; + return -1; + } + + *second_data = (unsigned long)hdr; + *second_data += hdr->page_size; + *second_data += ALIGN(hdr->kernel_size, hdr->page_size); + *second_data += ALIGN(hdr->ramdisk_size, hdr->page_size); + + printf("second address is 0x%lx\n",*second_data); + + *second_len = hdr->second_size; + return 0; +} + +/** + * android_image_get_dtbo() - Get address and size of recovery DTBO image. + * @hdr_addr: Boot image header address + * @addr: If not NULL, will contain address of recovery DTBO image + * @size: If not NULL, will contain size of recovery DTBO image + * + * Get the address and size of DTBO image in "Recovery DTBO" area of Android + * Boot Image in RAM. The format of this image is Android DTBO (see + * corresponding "DTB/DTBO Partitions" AOSP documentation for details). Once + * the address is obtained from this function, one can use 'adtimg' U-Boot + * command or android_dt_*() functions to extract desired DTBO blob. + * + * This DTBO (included in boot image) is only needed for non-A/B devices, and it + * only can be found in recovery image. On A/B devices we can always rely on + * "dtbo" partition. See "Including DTBO in Recovery for Non-A/B Devices" in + * AOSP documentation for details. + * + * Return: true on success or false on error. + */ +bool android_image_get_dtbo(ulong hdr_addr, ulong *addr, u32 *size) +{ + const struct andr_img_hdr *hdr; + ulong dtbo_img_addr; + bool ret = true; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + ret = false; + goto exit; + } + + if (hdr->header_version < 1) { + printf("Error: header_version must be >= 1 to get dtbo\n"); + ret = false; + goto exit; + } + + if (hdr->recovery_dtbo_size == 0) { + printf("Error: recovery_dtbo_size is 0\n"); + ret = false; + goto exit; + } + + /* Calculate the address of DTB area in boot image */ + dtbo_img_addr = hdr_addr; + dtbo_img_addr += hdr->page_size; + dtbo_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); + dtbo_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); + dtbo_img_addr += ALIGN(hdr->second_size, hdr->page_size); + + if (addr) + *addr = dtbo_img_addr; + if (size) + *size = hdr->recovery_dtbo_size; + +exit: + unmap_sysmem(hdr); + return ret; +} + +/** + * android_image_get_dtb_img_addr() - Get the address of DTB area in boot image. + * @hdr_addr: Boot image header address + * @addr: Will contain the address of DTB area in boot image + * + * Return: true on success or false on fail. + */ +static bool android_image_get_dtb_img_addr(ulong hdr_addr, ulong *addr) +{ + const struct andr_img_hdr *hdr; + ulong dtb_img_addr; + bool ret = true; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + ret = false; + goto exit; + } + + if (hdr->header_version < 2) { + printf("Error: header_version must be >= 2 to get dtb\n"); + ret = false; + goto exit; + } + + if (hdr->dtb_size == 0) { + printf("Error: dtb_size is 0\n"); + ret = false; + goto exit; + } + + /* Calculate the address of DTB area in boot image */ + dtb_img_addr = hdr_addr; + dtb_img_addr += hdr->page_size; + dtb_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->second_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + + *addr = dtb_img_addr; + +exit: + unmap_sysmem(hdr); + return ret; +} + +/** + * android_image_get_dtb_by_index() - Get address and size of blob in DTB area. + * @hdr_addr: Boot image header address + * @index: Index of desired DTB in DTB area (starting from 0) + * @addr: If not NULL, will contain address to specified DTB + * @size: If not NULL, will contain size of specified DTB + * + * Get the address and size of DTB blob by its index in DTB area of Android + * Boot Image in RAM. + * + * Return: true on success or false on error. + */ +bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, + u32 *size) +{ + const struct andr_img_hdr *hdr; + bool res; + ulong dtb_img_addr; /* address of DTB part in boot image */ + u32 dtb_img_size; /* size of DTB payload in boot image */ + ulong dtb_addr; /* address of DTB blob with specified index */ + u32 i; /* index iterator */ + + res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); + if (!res) + return false; + + /* Check if DTB area of boot image is in DTBO format */ + if (android_dt_check_header(dtb_img_addr)) { + return android_dt_get_fdt_by_index(dtb_img_addr, index, addr, + size); + } + + /* Find out the address of DTB with specified index in concat blobs */ + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + dtb_img_size = hdr->dtb_size; + unmap_sysmem(hdr); + i = 0; + dtb_addr = dtb_img_addr; + while (dtb_addr < dtb_img_addr + dtb_img_size) { + const struct fdt_header *fdt; + u32 dtb_size; + + fdt = map_sysmem(dtb_addr, sizeof(*fdt)); + if (fdt_check_header(fdt) != 0) { + unmap_sysmem(fdt); + printf("Error: Invalid FDT header for index %u\n", i); + return false; + } + + dtb_size = fdt_totalsize(fdt); + unmap_sysmem(fdt); + + if (i == index) { + if (size) + *size = dtb_size; + if (addr) + *addr = dtb_addr; + return true; + } + + dtb_addr += dtb_size; + ++i; + } + + printf("Error: Index is out of bounds (%u/%u)\n", index, i); + return false; +} + +#if !defined(CONFIG_SPL_BUILD) +/** + * android_print_contents - prints out the contents of the Android format image + * @hdr: pointer to the Android format image header + * + * android_print_contents() formats a multi line Android image contents + * description. + * The routine prints out Android image properties + * + * returns: + * no returned results + */ +void android_print_contents(const struct andr_img_hdr *hdr) +{ + const char * const p = IMAGE_INDENT_STRING; + /* os_version = ver << 11 | lvl */ + u32 os_ver = hdr->os_version >> 11; + u32 os_lvl = hdr->os_version & ((1U << 11) - 1); + + printf("%skernel size: %x\n", p, hdr->kernel_size); + printf("%skernel address: %x\n", p, hdr->kernel_addr); + printf("%sramdisk size: %x\n", p, hdr->ramdisk_size); + printf("%sramdisk address: %x\n", p, hdr->ramdisk_addr); + printf("%ssecond size: %x\n", p, hdr->second_size); + printf("%ssecond address: %x\n", p, hdr->second_addr); + printf("%stags address: %x\n", p, hdr->tags_addr); + printf("%spage size: %x\n", p, hdr->page_size); + /* ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) + * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) */ + printf("%sos_version: %x (ver: %u.%u.%u, level: %u.%u)\n", + p, hdr->os_version, + (os_ver >> 7) & 0x7F, (os_ver >> 14) & 0x7F, os_ver & 0x7F, + (os_lvl >> 4) + 2000, os_lvl & 0x0F); + printf("%sname: %s\n", p, hdr->name); + printf("%scmdline: %s\n", p, hdr->cmdline); + printf("%sheader_version: %d\n", p, hdr->header_version); + + if (hdr->header_version >= 1) { + printf("%srecovery dtbo size: %x\n", p, + hdr->recovery_dtbo_size); + printf("%srecovery dtbo offset: %llx\n", p, + hdr->recovery_dtbo_offset); + printf("%sheader size: %x\n", p, + hdr->header_size); + } + + if (hdr->header_version >= 2) { + printf("%sdtb size: %x\n", p, hdr->dtb_size); + printf("%sdtb addr: %llx\n", p, hdr->dtb_addr); + } +} + +/** + * android_image_print_dtb_info - Print info for one DTB blob in DTB area. + * @fdt: DTB header + * @index: Number of DTB blob in DTB area. + * + * Return: true on success or false on error. + */ +static bool android_image_print_dtb_info(const struct fdt_header *fdt, + u32 index) +{ + int root_node_off; + u32 fdt_size; + const char *model; + const char *compatible; + + root_node_off = fdt_path_offset(fdt, "/"); + if (root_node_off < 0) { + printf("Error: Root node not found\n"); + return false; + } + + fdt_size = fdt_totalsize(fdt); + compatible = fdt_getprop(fdt, root_node_off, "compatible", + NULL); + model = fdt_getprop(fdt, root_node_off, "model", NULL); + + printf(" - DTB #%u:\n", index); + printf(" (DTB)size = %d\n", fdt_size); + printf(" (DTB)model = %s\n", model ? model : "(unknown)"); + printf(" (DTB)compatible = %s\n", + compatible ? compatible : "(unknown)"); + + return true; +} + +/** + * android_image_print_dtb_contents() - Print info for DTB blobs in DTB area. + * @hdr_addr: Boot image header address + * + * DTB payload in Android Boot Image v2+ can be in one of following formats: + * 1. Concatenated DTB blobs + * 2. Android DTBO format (see CONFIG_CMD_ADTIMG for details) + * + * This function does next: + * 1. Prints out the format used in DTB area + * 2. Iterates over all DTB blobs in DTB area and prints out the info for + * each blob. + * + * Return: true on success or false on error. + */ +bool android_image_print_dtb_contents(ulong hdr_addr) +{ + const struct andr_img_hdr *hdr; + bool res; + ulong dtb_img_addr; /* address of DTB part in boot image */ + u32 dtb_img_size; /* size of DTB payload in boot image */ + ulong dtb_addr; /* address of DTB blob with specified index */ + u32 i; /* index iterator */ + + res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); + if (!res) + return false; + + /* Check if DTB area of boot image is in DTBO format */ + if (android_dt_check_header(dtb_img_addr)) { + printf("## DTB area contents (DTBO format):\n"); + android_dt_print_contents(dtb_img_addr); + return true; + } + + printf("## DTB area contents (concat format):\n"); + + /* Iterate over concatenated DTB blobs */ + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + dtb_img_size = hdr->dtb_size; + unmap_sysmem(hdr); + i = 0; + dtb_addr = dtb_img_addr; + while (dtb_addr < dtb_img_addr + dtb_img_size) { + const struct fdt_header *fdt; + u32 dtb_size; + + fdt = map_sysmem(dtb_addr, sizeof(*fdt)); + if (fdt_check_header(fdt) != 0) { + unmap_sysmem(fdt); + printf("Error: Invalid FDT header for index %u\n", i); + return false; + } + + res = android_image_print_dtb_info(fdt, i); + if (!res) { + unmap_sysmem(fdt); + return false; + } + + dtb_size = fdt_totalsize(fdt); + unmap_sysmem(fdt); + dtb_addr += dtb_size; + ++i; + } + + return true; +} +#endif diff --git a/boot/image-board.c b/boot/image-board.c new file mode 100644 index 0000000..ddf30c6 --- /dev/null +++ b/boot/image-board.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Image code used by boards (and not host tools) + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <bootstage.h> +#include <cpu_func.h> +#include <env.h> +#include <fpga.h> +#include <image.h> +#include <mapmem.h> +#include <rtc.h> +#include <watchdog.h> +#include <asm/cache.h> +#include <asm/global_data.h> + +#ifndef CONFIG_SYS_BARGSIZE +#define CONFIG_SYS_BARGSIZE 512 +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/** + * image_get_ramdisk - get and verify ramdisk image + * @rd_addr: ramdisk image start address + * @arch: expected ramdisk architecture + * @verify: checksum verification flag + * + * image_get_ramdisk() returns a pointer to the verified ramdisk image + * header. Routine receives image start address and expected architecture + * flag. Verification done covers data and header integrity and os/type/arch + * fields checking. + * + * returns: + * pointer to a ramdisk image header, if image was found and valid + * otherwise, return NULL + */ +static const image_header_t *image_get_ramdisk(ulong rd_addr, u8 arch, + int verify) +{ + const image_header_t *rd_hdr = (const image_header_t *)rd_addr; + + if (!image_check_magic(rd_hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_RD_MAGIC); + return NULL; + } + + if (!image_check_hcrc(rd_hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_RD_MAGIC); + image_print_contents(rd_hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(rd_hdr)) { + puts("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_RD_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + + bootstage_mark(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + + if (!image_check_os(rd_hdr, IH_OS_LINUX) || + !image_check_arch(rd_hdr, arch) || + !image_check_type(rd_hdr, IH_TYPE_RAMDISK)) { + printf("No Linux %s Ramdisk Image\n", + genimg_get_arch_name(arch)); + bootstage_error(BOOTSTAGE_ID_RAMDISK); + return NULL; + } + + return rd_hdr; +} + +/*****************************************************************************/ +/* Shared dual-format routines */ +/*****************************************************************************/ +ulong image_load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ +ulong image_save_addr; /* Default Save Address */ +ulong image_save_size; /* Default Save Size (in bytes) */ + +static int on_loadaddr(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + image_load_addr = hextoul(value, NULL); + break; + default: + break; + } + + return 0; +} +U_BOOT_ENV_CALLBACK(loadaddr, on_loadaddr); + +ulong env_get_bootm_low(void) +{ + char *s = env_get("bootm_low"); + + if (s) { + ulong tmp = hextoul(s, NULL); + return tmp; + } + +#if defined(CONFIG_SYS_SDRAM_BASE) + return CONFIG_SYS_SDRAM_BASE; +#elif defined(CONFIG_ARM) || defined(CONFIG_MICROBLAZE) || defined(CONFIG_RISCV) + return gd->bd->bi_dram[0].start; +#else + return 0; +#endif +} + +phys_size_t env_get_bootm_size(void) +{ + phys_size_t tmp, size; + phys_addr_t start; + char *s = env_get("bootm_size"); + + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + + start = gd->ram_base; + size = gd->ram_size; + + if (start + size > gd->ram_top) + size = gd->ram_top - start; + + s = env_get("bootm_low"); + if (s) + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + else + tmp = start; + + return size - (tmp - start); +} + +phys_size_t env_get_bootm_mapsize(void) +{ + phys_size_t tmp; + char *s = env_get("bootm_mapsize"); + + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + +#if defined(CONFIG_SYS_BOOTMAPSZ) + return CONFIG_SYS_BOOTMAPSZ; +#else + return env_get_bootm_size(); +#endif +} + +void memmove_wd(void *to, void *from, size_t len, ulong chunksz) +{ + if (to == from) + return; + +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + if (to > from) { + from += len; + to += len; + } + while (len > 0) { + size_t tail = (len > chunksz) ? chunksz : len; + + WATCHDOG_RESET(); + if (to > from) { + to -= tail; + from -= tail; + } + memmove(to, from, tail); + if (to < from) { + to += tail; + from += tail; + } + len -= tail; + } +#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ + memmove(to, from, len); +#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ +} + +/** + * genimg_get_kernel_addr_fit - get the real kernel address and return 2 + * FIT strings + * @img_addr: a string might contain real image address + * @fit_uname_config: double pointer to a char, will hold pointer to a + * configuration unit name + * @fit_uname_kernel: double pointer to a char, will hold pointer to a subimage + * name + * + * genimg_get_kernel_addr_fit get the real kernel start address from a string + * which is normally the first argv of bootm/bootz + * + * returns: + * kernel start address + */ +ulong genimg_get_kernel_addr_fit(char * const img_addr, + const char **fit_uname_config, + const char **fit_uname_kernel) +{ + ulong kernel_addr; + + /* find out kernel image address */ + if (!img_addr) { + kernel_addr = image_load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + image_load_addr); + } else if (CONFIG_IS_ENABLED(FIT) && + fit_parse_conf(img_addr, image_load_addr, &kernel_addr, + fit_uname_config)) { + debug("* kernel: config '%s' from image at 0x%08lx\n", + *fit_uname_config, kernel_addr); + } else if (CONFIG_IS_ENABLED(FIT) && + fit_parse_subimage(img_addr, image_load_addr, &kernel_addr, + fit_uname_kernel)) { + debug("* kernel: subimage '%s' from image at 0x%08lx\n", + *fit_uname_kernel, kernel_addr); + } else { + kernel_addr = hextoul(img_addr, NULL); + debug("* kernel: cmdline image address = 0x%08lx\n", + kernel_addr); + } + + return kernel_addr; +} + +/** + * genimg_get_kernel_addr() is the simple version of + * genimg_get_kernel_addr_fit(). It ignores those return FIT strings + */ +ulong genimg_get_kernel_addr(char * const img_addr) +{ + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; + + return genimg_get_kernel_addr_fit(img_addr, &fit_uname_config, + &fit_uname_kernel); +} + +/** + * genimg_get_format - get image format type + * @img_addr: image start address + * + * genimg_get_format() checks whether provided address points to a valid + * legacy or FIT image. + * + * New uImage format and FDT blob are based on a libfdt. FDT blob + * may be passed directly or embedded in a FIT image. In both situations + * genimg_get_format() must be able to dectect libfdt header. + * + * returns: + * image format type or IMAGE_FORMAT_INVALID if no image is present + */ +int genimg_get_format(const void *img_addr) +{ + if (CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)) { + const image_header_t *hdr; + + hdr = (const image_header_t *)img_addr; + if (image_check_magic(hdr)) + return IMAGE_FORMAT_LEGACY; + } + if (CONFIG_IS_ENABLED(FIT) || CONFIG_IS_ENABLED(OF_LIBFDT)) { + if (!fdt_check_header(img_addr)) + return IMAGE_FORMAT_FIT; + } + if (IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE) && + !android_image_check_header(img_addr)) + return IMAGE_FORMAT_ANDROID; + + return IMAGE_FORMAT_INVALID; +} + +/** + * fit_has_config - check if there is a valid FIT configuration + * @images: pointer to the bootm command headers structure + * + * fit_has_config() checks if there is a FIT configuration in use + * (if FTI support is present). + * + * returns: + * 0, no FIT support or no configuration found + * 1, configuration found + */ +int genimg_has_config(bootm_headers_t *images) +{ + if (CONFIG_IS_ENABLED(FIT) && images->fit_uname_cfg) + return 1; + + return 0; +} + +/** + * select_ramdisk() - Select and locate the ramdisk to use + * + * @images: pointer to the bootm images structure + * @select: name of ramdisk to select, or NULL for any + * @arch: expected ramdisk architecture + * @rd_datap: pointer to a ulong variable, will hold ramdisk pointer + * @rd_lenp: pointer to a ulong variable, will hold ramdisk length + * @return 0 if OK, -ENOPKG if no ramdisk (but an error should not be reported), + * other -ve value on other error + */ +static int select_ramdisk(bootm_headers_t *images, const char *select, u8 arch, + ulong *rd_datap, ulong *rd_lenp) +{ + ulong rd_addr = 0; + char *buf; + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_ramdisk = NULL; + bool processed; + int rd_noffset; + + if (select) { + ulong default_addr; + bool done = true; + + if (CONFIG_IS_ENABLED(FIT)) { + /* + * If the init ramdisk comes from the FIT image and + * the FIT image address is omitted in the command + * line argument, try to use os FIT image address or + * default load address. + */ + if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = image_load_addr; + + if (fit_parse_conf(select, default_addr, &rd_addr, + &fit_uname_config)) { + debug("* ramdisk: config '%s' from image at 0x%08lx\n", + fit_uname_config, rd_addr); + } else if (fit_parse_subimage(select, default_addr, + &rd_addr, + &fit_uname_ramdisk)) { + debug("* ramdisk: subimage '%s' from image at 0x%08lx\n", + fit_uname_ramdisk, rd_addr); + } else { + done = false; + } + } + if (!done) { + rd_addr = hextoul(select, NULL); + debug("* ramdisk: cmdline image address = 0x%08lx\n", + rd_addr); + } + } else if (CONFIG_IS_ENABLED(FIT)) { + /* use FIT configuration provided in first bootm + * command argument. If the property is not defined, + * quit silently (with -ENOPKG ) + */ + rd_addr = map_to_sysmem(images->fit_hdr_os); + rd_noffset = fit_get_node_from_config(images, FIT_RAMDISK_PROP, + rd_addr); + if (rd_noffset == -ENOENT) + return -ENOPKG; + else if (rd_noffset < 0) + return rd_noffset; + } + + /* + * Check if there is an initrd image at the + * address provided in the second bootm argument + * check image type, for FIT images get FIT node. + */ + buf = map_sysmem(rd_addr, 0); + processed = false; + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_LEGACY: + if (CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)) { + const image_header_t *rd_hdr; + + printf("## Loading init Ramdisk from Legacy Image at %08lx ...\n", + rd_addr); + + bootstage_mark(BOOTSTAGE_ID_CHECK_RAMDISK); + rd_hdr = image_get_ramdisk(rd_addr, arch, images->verify); + if (!rd_hdr) + return -ENOENT; + + *rd_datap = image_get_data(rd_hdr); + *rd_lenp = image_get_data_size(rd_hdr); + processed = true; + } + break; + case IMAGE_FORMAT_FIT: + if (CONFIG_IS_ENABLED(FIT)) { + rd_noffset = fit_image_load(images, rd_addr, + &fit_uname_ramdisk, + &fit_uname_config, arch, + IH_TYPE_RAMDISK, + BOOTSTAGE_ID_FIT_RD_START, + FIT_LOAD_OPTIONAL_NON_ZERO, + rd_datap, rd_lenp); + if (rd_noffset < 0) + return rd_noffset; + + images->fit_hdr_rd = map_sysmem(rd_addr, 0); + images->fit_uname_rd = fit_uname_ramdisk; + images->fit_noffset_rd = rd_noffset; + processed = true; + } + break; + case IMAGE_FORMAT_ANDROID: + if (IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE)) { + android_image_get_ramdisk((void *)images->os.start, + rd_datap, rd_lenp); + processed = true; + } + break; + } + + if (!processed) { + if (IS_ENABLED(CONFIG_SUPPORT_RAW_INITRD)) { + char *end = NULL; + + if (select) + end = strchr(select, ':'); + if (end) { + *rd_lenp = hextoul(++end, NULL); + *rd_datap = rd_addr; + processed = true; + } + } + + if (!processed) { + puts("Wrong Ramdisk Image Format\n"); + return -EINVAL; + } + } + + return 0; +} + +/** + * boot_get_ramdisk - main ramdisk handling routine + * @argc: command argument count + * @argv: command argument list + * @images: pointer to the bootm images structure + * @arch: expected ramdisk architecture + * @rd_start: pointer to a ulong variable, will hold ramdisk start address + * @rd_end: pointer to a ulong variable, will hold ramdisk end + * + * boot_get_ramdisk() is responsible for finding a valid ramdisk image. + * Currently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if ramdisk image was found and valid, or skiped + * rd_start and rd_end are set to ramdisk start/end addresses if + * ramdisk image is found and valid + * + * 1, if ramdisk image is found but corrupted, or invalid + * rd_start and rd_end are set to 0 if no ramdisk exists + */ +int boot_get_ramdisk(int argc, char *const argv[], bootm_headers_t *images, + u8 arch, ulong *rd_start, ulong *rd_end) +{ + ulong rd_data, rd_len; + const char *select = NULL; + + *rd_start = 0; + *rd_end = 0; + + if (IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE)) { + char *buf; + + /* Look for an Android boot image */ + buf = map_sysmem(images->os.start, 0); + if (buf && genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) + select = (argc == 0) ? env_get("loadaddr") : argv[0]; + } + + if (argc >= 2) + select = argv[1]; + + /* + * Look for a '-' which indicates to ignore the + * ramdisk argument + */ + if (select && strcmp(select, "-") == 0) { + debug("## Skipping init Ramdisk\n"); + rd_len = 0; + rd_data = 0; + } else if (select || genimg_has_config(images)) { + int ret; + + ret = select_ramdisk(images, select, arch, &rd_data, &rd_len); + if (ret == -ENOPKG) + return 0; + else if (ret) + return ret; + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + /* + * Now check if we have a legacy mult-component image, + * get second entry data start address and len. + */ + bootstage_mark(BOOTSTAGE_ID_RAMDISK); + printf("## Loading init Ramdisk from multi component Legacy Image at %08lx ...\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); + } else { + /* + * no initrd image + */ + bootstage_mark(BOOTSTAGE_ID_NO_RAMDISK); + rd_len = 0; + rd_data = 0; + } + + if (!rd_data) { + debug("## No init Ramdisk\n"); + } else { + *rd_start = rd_data; + *rd_end = rd_data + rd_len; + } + debug(" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n", + *rd_start, *rd_end); + + return 0; +} + +/** + * boot_ramdisk_high - relocate init ramdisk + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @rd_data: ramdisk data start address + * @rd_len: ramdisk data length + * @initrd_start: pointer to a ulong variable, will hold final init ramdisk + * start address (after possible relocation) + * @initrd_end: pointer to a ulong variable, will hold final init ramdisk + * end address (after possible relocation) + * + * boot_ramdisk_high() takes a relocation hint from "initrd_high" environment + * variable and if requested ramdisk data is moved to a specified location. + * + * Initrd_start and initrd_end are set to final (after relocation) ramdisk + * start/end addresses if ramdisk image start and len were provided, + * otherwise set initrd_start and initrd_end set to zeros. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len, + ulong *initrd_start, ulong *initrd_end) +{ + char *s; + ulong initrd_high; + int initrd_copy_to_ram = 1; + + s = env_get("initrd_high"); + if (s) { + /* a value of "no" or a similar string will act like 0, + * turning the "load high" feature off. This is intentional. + */ + initrd_high = hextoul(s, NULL); + if (initrd_high == ~0) + initrd_copy_to_ram = 0; + } else { + initrd_high = env_get_bootm_mapsize() + env_get_bootm_low(); + } + + debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n", + initrd_high, initrd_copy_to_ram); + + if (rd_data) { + if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */ + debug(" in-place initrd\n"); + *initrd_start = rd_data; + *initrd_end = rd_data + rd_len; + lmb_reserve(lmb, rd_data, rd_len); + } else { + if (initrd_high) + *initrd_start = (ulong)lmb_alloc_base(lmb, + rd_len, 0x1000, initrd_high); + else + *initrd_start = (ulong)lmb_alloc(lmb, rd_len, + 0x1000); + + if (*initrd_start == 0) { + puts("ramdisk - allocation error\n"); + goto error; + } + bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK); + + *initrd_end = *initrd_start + rd_len; + printf(" Loading Ramdisk to %08lx, end %08lx ... ", + *initrd_start, *initrd_end); + + memmove_wd((void *)*initrd_start, + (void *)rd_data, rd_len, CHUNKSZ); + + /* + * Ensure the image is flushed to memory to handle + * AMP boot scenarios in which we might not be + * HW cache coherent + */ + if (IS_ENABLED(CONFIG_MP)) { + flush_cache((unsigned long)*initrd_start, + ALIGN(rd_len, ARCH_DMA_MINALIGN)); + } + puts("OK\n"); + } + } else { + *initrd_start = 0; + *initrd_end = 0; + } + debug(" ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n", + *initrd_start, *initrd_end); + + return 0; + +error: + return -1; +} + +int boot_get_setup(bootm_headers_t *images, u8 arch, + ulong *setup_start, ulong *setup_len) +{ + if (!CONFIG_IS_ENABLED(FIT)) + return -ENOENT; + + return boot_get_setup_fit(images, arch, setup_start, setup_len); +} + +int boot_get_fpga(int argc, char *const argv[], bootm_headers_t *images, + u8 arch, const ulong *ld_start, ulong * const ld_len) +{ + ulong tmp_img_addr, img_data, img_len; + void *buf; + int conf_noffset; + int fit_img_result; + const char *uname, *name; + int err; + int devnum = 0; /* TODO support multi fpga platforms */ + + if (!IS_ENABLED(CONFIG_FPGA)) + return -ENOSYS; + + /* Check to see if the images struct has a FIT configuration */ + if (!genimg_has_config(images)) { + debug("## FIT configuration was not specified\n"); + return 0; + } + + /* + * Obtain the os FIT header from the images struct + */ + tmp_img_addr = map_to_sysmem(images->fit_hdr_os); + buf = map_sysmem(tmp_img_addr, 0); + /* + * Check image type. For FIT images get FIT node + * and attempt to locate a generic binary. + */ + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_FIT: + conf_noffset = fit_conf_get_node(buf, images->fit_uname_cfg); + + uname = fdt_stringlist_get(buf, conf_noffset, FIT_FPGA_PROP, 0, + NULL); + if (!uname) { + debug("## FPGA image is not specified\n"); + return 0; + } + fit_img_result = fit_image_load(images, + tmp_img_addr, + (const char **)&uname, + &images->fit_uname_cfg, + arch, + IH_TYPE_FPGA, + BOOTSTAGE_ID_FPGA_INIT, + FIT_LOAD_OPTIONAL_NON_ZERO, + &img_data, &img_len); + + debug("FPGA image (%s) loaded to 0x%lx/size 0x%lx\n", + uname, img_data, img_len); + + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + if (!fpga_is_partial_data(devnum, img_len)) { + name = "full"; + err = fpga_loadbitstream(devnum, (char *)img_data, + img_len, BIT_FULL); + if (err) + err = fpga_load(devnum, (const void *)img_data, + img_len, BIT_FULL); + } else { + name = "partial"; + err = fpga_loadbitstream(devnum, (char *)img_data, + img_len, BIT_PARTIAL); + if (err) + err = fpga_load(devnum, (const void *)img_data, + img_len, BIT_PARTIAL); + } + + if (err) + return err; + + printf(" Programming %s bitstream... OK\n", name); + break; + default: + printf("The given image format is not supported (corrupt?)\n"); + return 1; + } + + return 0; +} + +static void fit_loadable_process(u8 img_type, + ulong img_data, + ulong img_len) +{ + int i; + const unsigned int count = + ll_entry_count(struct fit_loadable_tbl, fit_loadable); + struct fit_loadable_tbl *fit_loadable_handler = + ll_entry_start(struct fit_loadable_tbl, fit_loadable); + /* For each loadable handler */ + for (i = 0; i < count; i++, fit_loadable_handler++) + /* matching this type */ + if (fit_loadable_handler->type == img_type) + /* call that handler with this image data */ + fit_loadable_handler->handler(img_data, img_len); +} + +int boot_get_loadable(int argc, char *const argv[], bootm_headers_t *images, + u8 arch, const ulong *ld_start, ulong * const ld_len) +{ + /* + * These variables are used to hold the current image location + * in system memory. + */ + ulong tmp_img_addr; + /* + * These two variables are requirements for fit_image_load, but + * their values are not used + */ + ulong img_data, img_len; + void *buf; + int loadables_index; + int conf_noffset; + int fit_img_result; + const char *uname; + u8 img_type; + + /* Check to see if the images struct has a FIT configuration */ + if (!genimg_has_config(images)) { + debug("## FIT configuration was not specified\n"); + return 0; + } + + /* + * Obtain the os FIT header from the images struct + */ + tmp_img_addr = map_to_sysmem(images->fit_hdr_os); + buf = map_sysmem(tmp_img_addr, 0); + /* + * Check image type. For FIT images get FIT node + * and attempt to locate a generic binary. + */ + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_FIT: + conf_noffset = fit_conf_get_node(buf, images->fit_uname_cfg); + + for (loadables_index = 0; + uname = fdt_stringlist_get(buf, conf_noffset, + FIT_LOADABLE_PROP, + loadables_index, NULL), uname; + loadables_index++) { + fit_img_result = fit_image_load(images, tmp_img_addr, + &uname, + &images->fit_uname_cfg, + arch, IH_TYPE_LOADABLE, + BOOTSTAGE_ID_FIT_LOADABLE_START, + FIT_LOAD_OPTIONAL_NON_ZERO, + &img_data, &img_len); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + fit_img_result = fit_image_get_node(buf, uname); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + fit_img_result = fit_image_get_type(buf, + fit_img_result, + &img_type); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + fit_loadable_process(img_type, img_data, img_len); + } + break; + default: + printf("The given image format is not supported (corrupt?)\n"); + return 1; + } + + return 0; +} + +/** + * boot_get_cmdline - allocate and initialize kernel cmdline + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @cmd_start: pointer to a ulong variable, will hold cmdline start + * @cmd_end: pointer to a ulong variable, will hold cmdline end + * + * boot_get_cmdline() allocates space for kernel command line below + * BOOTMAPSZ + env_get_bootm_low() address. If "bootargs" U-Boot environment + * variable is present its contents is copied to allocated kernel + * command line. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_cmdline(struct lmb *lmb, ulong *cmd_start, ulong *cmd_end) +{ + char *cmdline; + char *s; + + cmdline = (char *)(ulong)lmb_alloc_base(lmb, CONFIG_SYS_BARGSIZE, 0xf, + env_get_bootm_mapsize() + env_get_bootm_low()); + if (!cmdline) + return -1; + + s = env_get("bootargs"); + if (!s) + s = ""; + + strcpy(cmdline, s); + + *cmd_start = (ulong)cmdline; + *cmd_end = *cmd_start + strlen(cmdline); + + debug("## cmdline at 0x%08lx ... 0x%08lx\n", *cmd_start, *cmd_end); + + return 0; +} + +/** + * boot_get_kbd - allocate and initialize kernel copy of board info + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @kbd: double pointer to board info data + * + * boot_get_kbd() allocates space for kernel copy of board info data below + * BOOTMAPSZ + env_get_bootm_low() address and kernel board info is initialized + * with the current u-boot board info data. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_kbd(struct lmb *lmb, struct bd_info **kbd) +{ + *kbd = (struct bd_info *)(ulong)lmb_alloc_base(lmb, + sizeof(struct bd_info), + 0xf, + env_get_bootm_mapsize() + + env_get_bootm_low()); + if (!*kbd) + return -1; + + **kbd = *gd->bd; + + debug("## kernel board info at 0x%08lx\n", (ulong)*kbd); + +#if defined(DEBUG) + if (IS_ENABLED(CONFIG_CMD_BDI)) + do_bdinfo(NULL, 0, 0, NULL); +#endif + + return 0; +} + +int image_setup_linux(bootm_headers_t *images) +{ + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + int ret; + + if (CONFIG_IS_ENABLED(OF_LIBFDT)) + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + if (IS_ENABLED(CONFIG_SYS_BOOT_GET_CMDLINE)) { + ret = boot_get_cmdline(lmb, &images->cmdline_start, + &images->cmdline_end); + if (ret) { + puts("ERROR with allocation of cmdline\n"); + return ret; + } + } + + if (CONFIG_IS_ENABLED(OF_LIBFDT)) { + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return ret; + } + + if (CONFIG_IS_ENABLED(OF_LIBFDT) && of_size) { + ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb); + if (ret) + return ret; + } + + return 0; +} + +void genimg_print_size(uint32_t size) +{ + printf("%d Bytes = ", size); + print_size(size, "\n"); +} + +void genimg_print_time(time_t timestamp) +{ + struct rtc_time tm; + + rtc_to_tm(timestamp, &tm); + printf("%4d-%02d-%02d %2d:%02d:%02d UTC\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} diff --git a/boot/image-cipher.c b/boot/image-cipher.c new file mode 100644 index 0000000..b906148 --- /dev/null +++ b/boot/image-cipher.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Softathome + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSdTCC*/ +#include <image.h> +#include <uboot_aes.h> +#include <u-boot/aes.h> + +struct cipher_algo cipher_algos[] = { + { + .name = "aes128", + .key_len = AES128_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_128_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + }, + { + .name = "aes192", + .key_len = AES192_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_192_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + }, + { + .name = "aes256", + .key_len = AES256_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_256_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + } +}; + +struct cipher_algo *image_get_cipher_algo(const char *full_name) +{ + int i; + const char *name; + + for (i = 0; i < ARRAY_SIZE(cipher_algos); i++) { + name = cipher_algos[i].name; + if (!strncmp(name, full_name, strlen(name))) + return &cipher_algos[i]; + } + + return NULL; +} + +static int fit_image_setup_decrypt(struct image_cipher_info *info, + const void *fit, int image_noffset, + int cipher_noffset) +{ + const void *fdt = gd_fdt_blob(); + const char *node_name; + char node_path[128]; + int noffset; + char *algo_name; + int ret; + + node_name = fit_get_name(fit, image_noffset, NULL); + if (!node_name) { + printf("Can't get node name\n"); + return -1; + } + + if (fit_image_cipher_get_algo(fit, cipher_noffset, &algo_name)) { + printf("Can't get algo name for cipher '%s' in image '%s'\n", + node_name, node_name); + return -1; + } + + info->keyname = fdt_getprop(fit, cipher_noffset, FIT_KEY_HINT, NULL); + if (!info->keyname) { + printf("Can't get key name\n"); + return -1; + } + + info->iv = fdt_getprop(fit, cipher_noffset, "iv", NULL); + info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL); + + if (!info->iv && !info->ivname) { + printf("Can't get IV or IV name\n"); + return -1; + } + + info->fit = fit; + info->node_noffset = image_noffset; + info->name = algo_name; + info->cipher = image_get_cipher_algo(algo_name); + if (!info->cipher) { + printf("Can't get cipher\n"); + return -1; + } + + ret = fit_image_get_data_size_unciphered(fit, image_noffset, + &info->size_unciphered); + if (ret) { + printf("Can't get size of unciphered data\n"); + return -1; + } + + /* + * Search the cipher node in the u-boot fdt + * the path should be: /cipher/key-<algo>-<key>-<iv> + */ + if (info->ivname) + snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s-%s", + FIT_CIPHER_NODENAME, algo_name, info->keyname, info->ivname); + else + snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s", + FIT_CIPHER_NODENAME, algo_name, info->keyname); + + noffset = fdt_path_offset(fdt, node_path); + if (noffset < 0) { + printf("Can't found cipher node offset\n"); + return -1; + } + + /* read key */ + info->key = fdt_getprop(fdt, noffset, "key", NULL); + if (!info->key) { + printf("Can't get key in cipher node '%s'\n", node_path); + return -1; + } + + /* read iv */ + if (!info->iv) { + info->iv = fdt_getprop(fdt, noffset, "iv", NULL); + if (!info->iv) { + printf("Can't get IV in cipher node '%s'\n", node_path); + return -1; + } + } + + return 0; +} + +int fit_image_decrypt_data(const void *fit, + int image_noffset, int cipher_noffset, + const void *data_ciphered, size_t size_ciphered, + void **data_unciphered, size_t *size_unciphered) +{ + struct image_cipher_info info; + int ret; + + ret = fit_image_setup_decrypt(&info, fit, image_noffset, + cipher_noffset); + if (ret < 0) + goto out; + + ret = info.cipher->decrypt(&info, data_ciphered, size_ciphered, + data_unciphered, size_unciphered); + + out: + return ret; +} diff --git a/boot/image-fdt.c b/boot/image-fdt.c new file mode 100644 index 0000000..7aad6d5 --- /dev/null +++ b/boot/image-fdt.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <fdt_support.h> +#include <fdtdec.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <mapmem.h> +#include <asm/io.h> +#include <tee/optee.h> + +#ifndef CONFIG_SYS_FDT_PAD +#define CONFIG_SYS_FDT_PAD 0x3000 +#endif + +/* adding a ramdisk needs 0x44 bytes in version 2008.10 */ +#define FDT_RAMDISK_OVERHEAD 0x80 + +DECLARE_GLOBAL_DATA_PTR; + +static void fdt_error(const char *msg) +{ + puts("ERROR: "); + puts(msg); + puts(" - must RESET the board to recover.\n"); +} + +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +static const image_header_t *image_get_fdt(ulong fdt_addr) +{ + const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0); + + image_print_contents(fdt_hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_hcrc(fdt_hdr)) { + fdt_error("fdt header checksum invalid"); + return NULL; + } + + if (!image_check_dcrc(fdt_hdr)) { + fdt_error("fdt checksum invalid"); + return NULL; + } + puts("OK\n"); + + if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) { + fdt_error("uImage is not a fdt"); + return NULL; + } + if (image_get_comp(fdt_hdr) != IH_COMP_NONE) { + fdt_error("uImage is compressed"); + return NULL; + } + if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) { + fdt_error("uImage data is not a fdt"); + return NULL; + } + return fdt_hdr; +} +#endif + +static void boot_fdt_reserve_region(struct lmb *lmb, uint64_t addr, + uint64_t size, enum lmb_flags flags) +{ + long ret; + + ret = lmb_reserve_flags(lmb, addr, size, flags); + if (ret >= 0) { + debug(" reserving fdt memory region: addr=%llx size=%llx flags=%x\n", + (unsigned long long)addr, + (unsigned long long)size, flags); + } else { + puts("ERROR: reserving fdt memory region failed "); + printf("(addr=%llx size=%llx flags=%x)\n", + (unsigned long long)addr, + (unsigned long long)size, flags); + } +} + +/** + * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory + * sections as unusable + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @fdt_blob: pointer to fdt blob base address + * + * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block. + * Adding the memreserve regions prevents u-boot from using them to store the + * initrd or the fdt blob. + */ +void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob) +{ + uint64_t addr, size; + int i, total, ret; + int nodeoffset, subnode; + struct fdt_resource res; + enum lmb_flags flags; + + if (fdt_check_header(fdt_blob) != 0) + return; + + /* process memreserve sections */ + total = fdt_num_mem_rsv(fdt_blob); + for (i = 0; i < total; i++) { + if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0) + continue; + boot_fdt_reserve_region(lmb, addr, size, LMB_NONE); + } + + /* process reserved-memory */ + nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory"); + if (nodeoffset >= 0) { + subnode = fdt_first_subnode(fdt_blob, nodeoffset); + while (subnode >= 0) { + /* check if this subnode has a reg property */ + ret = fdt_get_resource(fdt_blob, subnode, "reg", 0, + &res); + if (!ret && fdtdec_get_is_enabled(fdt_blob, subnode)) { + flags = LMB_NONE; + if (fdtdec_get_bool(fdt_blob, subnode, + "no-map")) + flags = LMB_NOMAP; + addr = res.start; + size = res.end - res.start + 1; + boot_fdt_reserve_region(lmb, addr, size, flags); + } + + subnode = fdt_next_subnode(fdt_blob, subnode); + } + } +} + +/** + * boot_relocate_fdt - relocate flat device tree + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_relocate_fdt() allocates a region of memory within the bootmap and + * relocates the of_flat_tree into that region, even if the fdt is already in + * the bootmap. It also expands the size of the fdt by CONFIG_SYS_FDT_PAD + * bytes. + * + * of_flat_tree and of_size are set to final (after relocation) values + * + * returns: + * 0 - success + * 1 - failure + */ +int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) +{ + void *fdt_blob = *of_flat_tree; + void *of_start = NULL; + char *fdt_high; + ulong of_len = 0; + int err; + int disable_relocation = 0; + + /* nothing to do */ + if (*of_size == 0) + return 0; + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + /* position on a 4K boundary before the alloc_current */ + /* Pad the FDT by a specified amount */ + of_len = *of_size + CONFIG_SYS_FDT_PAD; + + /* If fdt_high is set use it to select the relocation address */ + fdt_high = env_get("fdt_high"); + if (fdt_high) { + void *desired_addr = (void *)hextoul(fdt_high, NULL); + + if (((ulong) desired_addr) == ~0UL) { + /* All ones means use fdt in place */ + of_start = fdt_blob; + lmb_reserve(lmb, (ulong)of_start, of_len); + disable_relocation = 1; + } else if (desired_addr) { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + (ulong)desired_addr); + if (of_start == NULL) { + puts("Failed using fdt_high value for Device Tree"); + goto error; + } + } else { + of_start = + (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000); + } + } else { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + env_get_bootm_mapsize() + + env_get_bootm_low()); + } + + if (of_start == NULL) { + puts("device tree - allocation error\n"); + goto error; + } + + if (disable_relocation) { + /* + * We assume there is space after the existing fdt to use + * for padding + */ + fdt_set_totalsize(of_start, of_len); + printf(" Using Device Tree in place at %p, end %p\n", + of_start, of_start + of_len - 1); + } else { + debug("## device tree at %p ... %p (len=%ld [0x%lX])\n", + fdt_blob, fdt_blob + *of_size - 1, of_len, of_len); + + printf(" Loading Device Tree to %p, end %p ... ", + of_start, of_start + of_len - 1); + + err = fdt_open_into(fdt_blob, of_start, of_len); + if (err != 0) { + fdt_error("fdt move failed"); + goto error; + } + puts("OK\n"); + } + + *of_flat_tree = of_start; + *of_size = of_len; + + if (CONFIG_IS_ENABLED(CMD_FDT)) + set_working_fdt_addr(map_to_sysmem(*of_flat_tree)); + return 0; + +error: + return 1; +} + +/** + * select_fdt() - Select and locate the FDT to use + * + * @images: pointer to the bootm images structure + * @select: name of FDT to select, or NULL for any + * @arch: expected FDT architecture + * @fdt_addrp: pointer to a ulong variable, will hold FDT pointer + * @return 0 if OK, -ENOPKG if no FDT (but an error should not be reported), + * other -ve value on other error + */ + +static int select_fdt(bootm_headers_t *images, const char *select, u8 arch, + ulong *fdt_addrp) +{ + const char *buf; + ulong fdt_addr; + +#if CONFIG_IS_ENABLED(FIT) + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_fdt = NULL; + ulong default_addr; + int fdt_noffset; + + if (select) { + /* + * If the FDT blob comes from the FIT image and the + * FIT image address is omitted in the command line + * argument, try to use ramdisk or os FIT image + * address or default load address. + */ + if (images->fit_uname_rd) + default_addr = (ulong)images->fit_hdr_rd; + else if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = image_load_addr; + + if (fit_parse_conf(select, default_addr, &fdt_addr, + &fit_uname_config)) { + debug("* fdt: config '%s' from image at 0x%08lx\n", + fit_uname_config, fdt_addr); + } else if (fit_parse_subimage(select, default_addr, &fdt_addr, + &fit_uname_fdt)) { + debug("* fdt: subimage '%s' from image at 0x%08lx\n", + fit_uname_fdt, fdt_addr); + } else +#endif + { + fdt_addr = hextoul(select, NULL); + debug("* fdt: cmdline image address = 0x%08lx\n", + fdt_addr); + } +#if CONFIG_IS_ENABLED(FIT) + } else { + /* use FIT configuration provided in first bootm + * command argument + */ + fdt_addr = map_to_sysmem(images->fit_hdr_os); + fdt_noffset = fit_get_node_from_config(images, FIT_FDT_PROP, + fdt_addr); + if (fdt_noffset == -ENOENT) + return -ENOPKG; + else if (fdt_noffset < 0) + return fdt_noffset; + } +#endif + debug("## Checking for 'FDT'/'FDT Image' at %08lx\n", + fdt_addr); + + /* + * Check if there is an FDT image at the + * address provided in the second bootm argument + * check image type, for FIT images get a FIT node. + */ + buf = map_sysmem(fdt_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: { + const image_header_t *fdt_hdr; + ulong load, load_end; + ulong image_start, image_data, image_end; + + /* verify fdt_addr points to a valid image header */ + printf("## Flattened Device Tree from Legacy Image at %08lx\n", + fdt_addr); + fdt_hdr = image_get_fdt(fdt_addr); + if (!fdt_hdr) + return -ENOPKG; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = (ulong)fdt_hdr; + image_data = (ulong)image_get_data(fdt_hdr); + image_end = image_get_image_end(fdt_hdr); + + load = image_get_load(fdt_hdr); + load_end = load + image_get_data_size(fdt_hdr); + + if (load == image_start || + load == image_data) { + fdt_addr = load; + break; + } + + if ((load < image_end) && (load_end > image_start)) { + fdt_error("fdt overwritten"); + return -EFAULT; + } + + debug(" Loading FDT from 0x%08lx to 0x%08lx\n", + image_data, load); + + memmove((void *)load, + (void *)image_data, + image_get_data_size(fdt_hdr)); + + fdt_addr = load; + break; + } +#endif + case IMAGE_FORMAT_FIT: + /* + * This case will catch both: new uImage format + * (libfdt based) and raw FDT blob (also libfdt + * based). + */ +#if CONFIG_IS_ENABLED(FIT) + /* check FDT blob vs FIT blob */ + if (!fit_check_format(buf, IMAGE_SIZE_INVAL)) { + ulong load, len; + + fdt_noffset = boot_get_fdt_fit(images, fdt_addr, + &fit_uname_fdt, + &fit_uname_config, + arch, &load, &len); + + if (fdt_noffset < 0) + return -ENOENT; + + images->fit_hdr_fdt = map_sysmem(fdt_addr, 0); + images->fit_uname_fdt = fit_uname_fdt; + images->fit_noffset_fdt = fdt_noffset; + fdt_addr = load; + + break; + } else +#endif + { + /* + * FDT blob + */ + debug("* fdt: raw FDT blob\n"); + printf("## Flattened Device Tree blob at %08lx\n", + (long)fdt_addr); + } + break; + default: + puts("ERROR: Did not find a cmdline Flattened Device Tree\n"); + return -ENOENT; + } + *fdt_addrp = fdt_addr; + + return 0; +} + +/** + * boot_get_fdt - main fdt handling routine + * @argc: command argument count + * @argv: command argument list + * @arch: architecture (IH_ARCH_...) + * @images: pointer to the bootm images structure + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_get_fdt() is responsible for finding a valid flat device tree image. + * Currently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if fdt image was found and valid, or skipped + * of_flat_tree and of_size are set to fdt start address and length if + * fdt image is found and valid + * + * 1, if fdt image is found but corrupted + * of_flat_tree and of_size are set to 0 if no fdt exists + */ +int boot_get_fdt(int flag, int argc, char *const argv[], uint8_t arch, + bootm_headers_t *images, char **of_flat_tree, ulong *of_size) +{ + ulong img_addr; + ulong fdt_addr; + char *fdt_blob = NULL; + void *buf; + const char *select = NULL; + + *of_flat_tree = NULL; + *of_size = 0; + + img_addr = (argc == 0) ? image_load_addr : hextoul(argv[0], NULL); + buf = map_sysmem(img_addr, 0); + + if (argc > 2) + select = argv[2]; + if (select || genimg_has_config(images)) { + int ret; + + ret = select_fdt(images, select, arch, &fdt_addr); + if (ret == -ENOPKG) + goto no_fdt; + else if (ret) + return 1; + printf(" Booting using the fdt blob at %#08lx\n", fdt_addr); + fdt_blob = map_sysmem(fdt_addr, 0); + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + ulong fdt_data, fdt_len; + + /* + * Now check if we have a legacy multi-component image, + * get second entry data start address and len. + */ + printf("## Flattened Device Tree from multi component Image at %08lX\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data, + &fdt_len); + if (fdt_len) { + fdt_blob = (char *)fdt_data; + printf(" Booting using the fdt at 0x%p\n", fdt_blob); + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + if (fdt_totalsize(fdt_blob) != fdt_len) { + fdt_error("fdt size != image size"); + goto error; + } + } else { + debug("## No Flattened Device Tree\n"); + goto no_fdt; + } +#ifdef CONFIG_ANDROID_BOOT_IMAGE + } else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) { + struct andr_img_hdr *hdr = buf; + ulong fdt_data, fdt_len; + u32 fdt_size, dtb_idx; + /* + * Firstly check if this android boot image has dtb field. + */ + dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0); + if (android_image_get_dtb_by_index((ulong)hdr, dtb_idx, &fdt_addr, &fdt_size)) { + fdt_blob = (char *)map_sysmem(fdt_addr, 0); + if (fdt_check_header(fdt_blob)) + goto no_fdt; + + debug("## Using FDT in Android image dtb area with idx %u\n", dtb_idx); + } else if (!android_image_get_second(hdr, &fdt_data, &fdt_len) && + !fdt_check_header((char *)fdt_data)) { + fdt_blob = (char *)fdt_data; + if (fdt_totalsize(fdt_blob) != fdt_len) + goto error; + + debug("## Using FDT in Android image second area\n"); + } else { + fdt_addr = env_get_hex("fdtaddr", 0); + if (!fdt_addr) + goto no_fdt; + + fdt_blob = map_sysmem(fdt_addr, 0); + if (fdt_check_header(fdt_blob)) + goto no_fdt; + + debug("## Using FDT at ${fdtaddr}=Ox%lx\n", fdt_addr); + } +#endif + } else { + debug("## No Flattened Device Tree\n"); + goto no_fdt; + } + + *of_flat_tree = fdt_blob; + *of_size = fdt_totalsize(fdt_blob); + debug(" of_flat_tree at 0x%08lx size 0x%08lx\n", + (ulong)*of_flat_tree, *of_size); + + return 0; + +no_fdt: + debug("Continuing to boot without FDT\n"); + return 0; +error: + return 1; +} + +/* + * Verify the device tree. + * + * This function is called after all device tree fix-ups have been enacted, + * so that the final device tree can be verified. The definition of "verified" + * is up to the specific implementation. However, it generally means that the + * addresses of some of the devices in the device tree are compared with the + * actual addresses at which U-Boot has placed them. + * + * Returns 1 on success, 0 on failure. If 0 is returned, U-Boot will halt the + * boot process. + */ +__weak int ft_verify_fdt(void *fdt) +{ + return 1; +} + +__weak int arch_fixup_fdt(void *blob) +{ + return 0; +} + +int image_setup_libfdt(bootm_headers_t *images, void *blob, + int of_size, struct lmb *lmb) +{ + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + int ret = -EPERM; + int fdt_ret; + + if (fdt_root(blob) < 0) { + printf("ERROR: root node setup failed\n"); + goto err; + } + if (fdt_chosen(blob) < 0) { + printf("ERROR: /chosen node create failed\n"); + goto err; + } + if (arch_fixup_fdt(blob) < 0) { + printf("ERROR: arch-specific fdt fixup failed\n"); + goto err; + } + + fdt_ret = optee_copy_fdt_nodes(blob); + if (fdt_ret) { + printf("ERROR: transfer of optee nodes to new fdt failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + + /* Update ethernet nodes */ + fdt_fixup_ethernet(blob); +#if CONFIG_IS_ENABLED(CMD_PSTORE) + /* Append PStore configuration */ + fdt_fixup_pstore(blob); +#endif + if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) { + const char *skip_board_fixup; + + skip_board_fixup = env_get("skip_board_fixup"); + if (skip_board_fixup && ((int)simple_strtol(skip_board_fixup, NULL, 10) == 1)) { + printf("skip board fdt fixup\n"); + } else { + fdt_ret = ft_board_setup(blob, gd->bd); + if (fdt_ret) { + printf("ERROR: board-specific fdt fixup failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + } + } + if (IS_ENABLED(CONFIG_OF_SYSTEM_SETUP)) { + fdt_ret = ft_system_setup(blob, gd->bd); + if (fdt_ret) { + printf("ERROR: system-specific fdt fixup failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + } + + /* Delete the old LMB reservation */ + if (lmb) + lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, + (phys_size_t)fdt_totalsize(blob)); + + ret = fdt_shrink_to_minimum(blob, 0); + if (ret < 0) + goto err; + of_size = ret; + + if (*initrd_start && *initrd_end) { + of_size += FDT_RAMDISK_OVERHEAD; + fdt_set_totalsize(blob, of_size); + } + /* Create a new LMB reservation */ + if (lmb) + lmb_reserve(lmb, (ulong)blob, of_size); + + fdt_initrd(blob, *initrd_start, *initrd_end); + if (!ft_verify_fdt(blob)) + goto err; + +#if defined(CONFIG_ARCH_KEYSTONE) + if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) + ft_board_setup_ex(blob, gd->bd); +#endif + + return 0; +err: + printf(" - must RESET the board to recover.\n\n"); + + return ret; +} diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c new file mode 100644 index 0000000..63e5423 --- /dev/null +++ b/boot/image-fit-sig.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include <fdt_region.h> +#include <image.h> +#include <u-boot/rsa.h> +#include <u-boot/hash-checksum.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +/** + * fit_region_make_list() - Make a list of image regions + * + * Given a list of fdt_regions, create a list of image_regions. This is a + * simple conversion routine since the FDT and image code use different + * structures. + * + * @fit: FIT image + * @fdt_regions: Pointer to FDT regions + * @count: Number of FDT regions + * @region: Pointer to image regions, which must hold @count records. If + * region is NULL, then (except for an SPL build) the array will be + * allocated. + * @return: Pointer to image regions + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, + int count, + struct image_region *region) +{ + int i; + + debug("Hash regions:\n"); + debug("%10s %10s\n", "Offset", "Size"); + + /* + * Use malloc() except in SPL (to save code size). In SPL the caller + * must allocate the array. + */ + if (!IS_ENABLED(CONFIG_SPL_BUILD) && !region) + region = calloc(sizeof(*region), count); + if (!region) + return NULL; + for (i = 0; i < count; i++) { + debug("%10x %10x\n", fdt_regions[i].offset, + fdt_regions[i].size); + region[i].data = fit + fdt_regions[i].offset; + region[i].size = fdt_regions[i].size; + } + + return region; +} + +static int fit_image_setup_verify(struct image_sign_info *info, + const void *fit, int noffset, + int required_keynode, char **err_msgp) +{ + char *algo_name; + const char *padding_name; + + if (fdt_totalsize(fit) > CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE)) { + *err_msgp = "Total size too large"; + return 1; + } + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + + padding_name = fdt_getprop(fit, noffset, "padding", NULL); + if (!padding_name) + padding_name = RSA_DEFAULT_PADDING_NAME; + + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + info->fit = fit; + info->node_offset = noffset; + info->name = algo_name; + info->checksum = image_get_checksum_algo(algo_name); + info->crypto = image_get_crypto_algo(algo_name); + info->padding = image_get_padding_algo(padding_name); + info->fdt_blob = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->checksum || !info->crypto || !info->padding) { + *err_msgp = "Unknown signature algorithm"; + return -1; + } + + return 0; +} + +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp) +{ + struct image_sign_info info; + struct image_region region; + uint8_t *fit_value; + int fit_value_len; + + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + region.data = data; + region.size = size; + + if (info.crypto->verify(&info, ®ion, 1, fit_value, fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_image_verify_sig(const void *fit, int image_noffset, + const char *data, size_t size, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + if (strchr(name, '@')) { + err_msg = "Node name contains @"; + goto error; + } + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return -1; +} + +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, + const void *sig_blob, int *no_sigsp) +{ + int verify_count = 0; + int noffset; + int sig_node; + + /* Work out what we need to verify */ + *no_sigsp = 1; + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, + sig_blob, noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + verify_count++; + } + + if (verify_count) + *no_sigsp = 0; + + return 0; +} + +/** + * fit_config_check_sig() - Check the signature of a config + * + * @fit: FIT to check + * @noffset: Offset of configuration node (e.g. /configurations/conf-1) + * @required_keynode: Offset in the control FDT of the required key node, + * if any. If this is given, then the configuration wil not + * pass verification unless that key is used. If this is + * -1 then any signature will do. + * @conf_noffset: Offset of the configuration subnode being checked (e.g. + * /configurations/conf-1/kernel) + * @err_msgp: In the event of an error, this will be pointed to a + * help error string to display to the user. + * @return 0 if all verified ok, <0 on error + */ +static int fit_config_check_sig(const void *fit, int noffset, + int required_keynode, int conf_noffset, + char **err_msgp) +{ + static char * const exc_prop[] = { + "data", + "data-size", + "data-position", + "data-offset" + }; + + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; + const char *config_name; + uint8_t *fit_value; + int fit_value_len; + bool found_config; + int max_regions; + int i, prop_len; + char path[200]; + int count; + + config_name = fit_get_name(fit, conf_noffset, NULL); + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + /* Count the number of strings in the property */ + prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); + end = prop ? prop + prop_len : prop; + for (name = prop, count = 0; name < end; name++) + if (!*name) + count++; + if (!count) { + *err_msgp = "Can't get hashed-nodes property"; + return -1; + } + + if (prop && prop_len > 0 && prop[prop_len - 1] != '\0') { + *err_msgp = "hashed-nodes property must be null-terminated"; + return -1; + } + + /* Add a sanity check here since we are using the stack */ + if (count > IMAGE_MAX_HASHED_NODES) { + *err_msgp = "Number of hashed nodes exceeds maximum"; + return -1; + } + + /* Create a list of node names from those strings */ + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); + found_config = false; + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; + if (!strncmp(FIT_CONFS_PATH, name, strlen(FIT_CONFS_PATH)) && + name[sizeof(FIT_CONFS_PATH) - 1] == '/' && + !strcmp(name + sizeof(FIT_CONFS_PATH), config_name)) { + debug(" (found config node %s)", config_name); + found_config = true; + } + } + if (!found_config) { + *err_msgp = "Selected config not in hashed nodes"; + return -1; + } + + /* + * Each node can generate one region for each sub-node. Allow for + * 7 sub-nodes (hash-1, signature-1, etc.) and some extra. + */ + max_regions = 20 + count * 7; + struct fdt_region fdt_regions[max_regions]; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc, count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, max_regions - 1, + path, sizeof(path), 0); + if (count < 0) { + *err_msgp = "Failed to hash configuration"; + return -1; + } + if (count == 0) { + *err_msgp = "No data to hash"; + return -1; + } + if (count >= max_regions - 1) { + *err_msgp = "Too many hash regions"; + return -1; + } + + /* Add the strings */ + strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); + if (strings) { + /* + * The strings region offset must be a static 0x0. + * This is set in tool/image-host.c + */ + fdt_regions[count].offset = fdt_off_dt_strings(fit); + fdt_regions[count].size = fdt32_to_cpu(strings[1]); + count++; + } + + /* Allocate the region list on the stack */ + struct image_region region[count]; + + fit_region_make_list(fit, fdt_regions, count, region); + if (info.crypto->verify(&info, region, count, fit_value, + fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_config_verify_sig(const void *fit, int conf_noffset, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = "No 'signature' subnode found"; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component conf node */ + fdt_for_each_subnode(noffset, fit, conf_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, + conf_noffset, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + if (verified) + return 0; + +error: + printf(" error!\n%s for '%s' hash node in '%s' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); + return -EPERM; +} + +static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const void *sig_blob) +{ + const char *name = fit_get_name(fit, conf_noffset, NULL); + int noffset; + int sig_node; + int verified = 0; + int reqd_sigs = 0; + bool reqd_policy_all = true; + const char *reqd_mode; + + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + if (strchr(name, '@')) { + printf("Configuration node '%s' contains '@'\n", name); + return -EPERM; + } + + /* Work out what we need to verify */ + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + /* Get required-mode policy property from DTB */ + reqd_mode = fdt_getprop(sig_blob, sig_node, "required-mode", NULL); + if (reqd_mode && !strcmp(reqd_mode, "any")) + reqd_policy_all = false; + + debug("%s: required-mode policy set to '%s'\n", __func__, + reqd_policy_all ? "all" : "any"); + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + NULL); + if (!required || strcmp(required, "conf")) + continue; + + reqd_sigs++; + + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + noffset); + if (ret) { + if (reqd_policy_all) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + } else { + verified++; + if (!reqd_policy_all) + break; + } + } + + if (reqd_sigs && !verified) { + printf("Failed to verify 'any' of the required signature(s)\n"); + return -EPERM; + } + + return 0; +} + +int fit_config_verify(const void *fit, int conf_noffset) +{ + return fit_config_verify_required_sigs(fit, conf_noffset, + gd_fdt_blob()); +} diff --git a/boot/image-fit.c b/boot/image-fit.c new file mode 100644 index 0000000..33b4a46 --- /dev/null +++ b/boot/image-fit.c @@ -0,0 +1,2448 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#define LOG_CATEGORY LOGC_BOOT + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#include <linux/libfdt.h> +#include <u-boot/crc.h> +#else +#include <linux/compiler.h> +#include <linux/sizes.h> +#include <common.h> +#include <errno.h> +#include <log.h> +#include <mapmem.h> +#include <asm/io.h> +#include <malloc.h> +#include <asm/global_data.h> +#ifdef CONFIG_DM_HASH +#include <dm.h> +#include <u-boot/hash.h> +#endif +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ + +#include <bootm.h> +#include <image.h> +#include <bootstage.h> +#include <linux/kconfig.h> +#include <u-boot/crc.h> +#include <u-boot/md5.h> +#include <u-boot/sha1.h> +#include <u-boot/sha256.h> +#include <u-boot/sha512.h> + +/*****************************************************************************/ +/* New uImage format routines */ +/*****************************************************************************/ +#ifndef USE_HOSTCC +static int fit_parse_spec(const char *spec, char sepc, ulong addr_curr, + ulong *addr, const char **name) +{ + const char *sep; + + *addr = addr_curr; + *name = NULL; + + sep = strchr(spec, sepc); + if (sep) { + if (sep - spec > 0) + *addr = hextoul(spec, NULL); + + *name = sep + 1; + return 1; + } + + return 0; +} + +/** + * fit_parse_conf - parse FIT configuration spec + * @spec: input string, containing configuration spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * configuration + * @conf_name double pointer to a char, will hold pointer to a configuration + * unit name + * + * fit_parse_conf() expects configuration spec in the form of [<addr>]#<conf>, + * where <addr> is a FIT image address that contains configuration + * with a <conf> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid configuration string, + * addr and conf_name are set accordingly + * 0 otherwise + */ +int fit_parse_conf(const char *spec, ulong addr_curr, + ulong *addr, const char **conf_name) +{ + return fit_parse_spec(spec, '#', addr_curr, addr, conf_name); +} + +/** + * fit_parse_subimage - parse FIT subimage spec + * @spec: input string, containing subimage spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * subimage + * @image_name: double pointer to a char, will hold pointer to a subimage name + * + * fit_parse_subimage() expects subimage spec in the form of + * [<addr>]:<subimage>, where <addr> is a FIT image address that contains + * subimage with a <subimg> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid subimage string, + * addr and image_name are set accordingly + * 0 otherwise + */ +int fit_parse_subimage(const char *spec, ulong addr_curr, + ulong *addr, const char **image_name) +{ + return fit_parse_spec(spec, ':', addr_curr, addr, image_name); +} +#endif /* !USE_HOSTCC */ + +#ifdef USE_HOSTCC +/* Host tools use these implementations for Cipher and Signature support */ +static void *host_blob; + +void image_set_host_blob(void *blob) +{ + host_blob = blob; +} + +void *image_get_host_blob(void) +{ + return host_blob; +} +#endif /* USE_HOSTCC */ + +static void fit_get_debug(const void *fit, int noffset, + char *prop_name, int err) +{ + debug("Can't get '%s' property from FIT 0x%08lx, node: offset %d, name %s (%s)\n", + prop_name, (ulong)fit, noffset, fit_get_name(fit, noffset, NULL), + fdt_strerror(err)); +} + +/** + * fit_get_subimage_count - get component (sub-image) count + * @fit: pointer to the FIT format image header + * @images_noffset: offset of images node + * + * returns: + * number of image components + */ +int fit_get_subimage_count(const void *fit, int images_noffset) +{ + int noffset; + int ndepth; + int count = 0; + + /* Process its subnodes, print out component images details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + count++; + } + } + + return count; +} + +/** + * fit_image_print_data() - prints out the hash node details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash node + * @p: pointer to prefix string + * @type: Type of information to print ("hash" or "sign") + * + * fit_image_print_data() lists properties for the processed hash node + * + * This function avoid using puts() since it prints a newline on the host + * but does not in U-Boot. + * + * returns: + * no returned results + */ +static void fit_image_print_data(const void *fit, int noffset, const char *p, + const char *type) +{ + const char *keyname; + uint8_t *value; + int value_len; + char *algo; + const char *padding; + bool required; + int ret, i; + + debug("%s %s node: '%s'\n", p, type, + fit_get_name(fit, noffset, NULL)); + printf("%s %s algo: ", p, type); + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + printf("invalid/unsupported\n"); + return; + } + printf("%s", algo); + keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + required = fdt_getprop(fit, noffset, FIT_KEY_REQUIRED, NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) + printf(" (required)"); + printf("\n"); + + padding = fdt_getprop(fit, noffset, "padding", NULL); + if (padding) + printf("%s %s padding: %s\n", p, type, padding); + + ret = fit_image_hash_get_value(fit, noffset, &value, + &value_len); + printf("%s %s value: ", p, type); + if (ret) { + printf("unavailable\n"); + } else { + for (i = 0; i < value_len; i++) + printf("%02x", value[i]); + printf("\n"); + } + + debug("%s %s len: %d\n", p, type, value_len); + + /* Signatures have a time stamp */ + if (IMAGE_ENABLE_TIMESTAMP && keyname) { + time_t timestamp; + + printf("%s Timestamp: ", p); + if (fit_get_timestamp(fit, noffset, ×tamp)) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } +} + +/** + * fit_image_print_verification_data() - prints out the hash/signature details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash or signature node + * @p: pointer to prefix string + * + * This lists properties for the processed hash node + * + * returns: + * no returned results + */ +static void fit_image_print_verification_data(const void *fit, int noffset, + const char *p) +{ + const char *name; + + /* + * Check subnode name, must be equal to "hash" or "signature". + * Multiple hash/signature nodes require unique unit node + * names, e.g. hash-1, hash-2, signature-1, signature-2, etc. + */ + name = fit_get_name(fit, noffset, NULL); + if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Hash"); + } else if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Sign"); + } +} + +/** + * fit_conf_print - prints out the FIT configuration details + * @fit: pointer to the FIT format image header + * @noffset: offset of the configuration node + * @p: pointer to prefix string + * + * fit_conf_print() lists all mandatory properties for the processed + * configuration node. + * + * returns: + * no returned results + */ +static void fit_conf_print(const void *fit, int noffset, const char *p) +{ + char *desc; + const char *uname; + int ret; + int fdt_index, loadables_index; + int ndepth; + + /* Mandatory properties */ + ret = fit_get_desc(fit, noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + uname = fdt_getprop(fit, noffset, FIT_KERNEL_PROP, NULL); + printf("%s Kernel: ", p); + if (!uname) + printf("unavailable\n"); + else + printf("%s\n", uname); + + /* Optional properties */ + uname = fdt_getprop(fit, noffset, FIT_RAMDISK_PROP, NULL); + if (uname) + printf("%s Init Ramdisk: %s\n", p, uname); + + uname = fdt_getprop(fit, noffset, FIT_FIRMWARE_PROP, NULL); + if (uname) + printf("%s Firmware: %s\n", p, uname); + + for (fdt_index = 0; + uname = fdt_stringlist_get(fit, noffset, FIT_FDT_PROP, + fdt_index, NULL), uname; + fdt_index++) { + if (fdt_index == 0) + printf("%s FDT: ", p); + else + printf("%s ", p); + printf("%s\n", uname); + } + + uname = fdt_getprop(fit, noffset, FIT_FPGA_PROP, NULL); + if (uname) + printf("%s FPGA: %s\n", p, uname); + + /* Print out all of the specified loadables */ + for (loadables_index = 0; + uname = fdt_stringlist_get(fit, noffset, FIT_LOADABLE_PROP, + loadables_index, NULL), uname; + loadables_index++) { + if (loadables_index == 0) { + printf("%s Loadables: ", p); + } else { + printf("%s ", p); + } + printf("%s\n", uname); + } + + /* Process all hash subnodes of the component configuration node */ + for (ndepth = 0, noffset = fdt_next_node(fit, noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* Direct child node of the component configuration node */ + fit_image_print_verification_data(fit, noffset, p); + } + } +} + +/** + * fit_print_contents - prints out the contents of the FIT format image + * @fit: pointer to the FIT format image header + * @p: pointer to prefix string + * + * fit_print_contents() formats a multi line FIT image contents description. + * The routine prints out FIT image properties (root node level) followed by + * the details of each component image. + * + * returns: + * no returned results + */ +void fit_print_contents(const void *fit) +{ + char *desc; + char *uname; + int images_noffset; + int confs_noffset; + int noffset; + int ndepth; + int count = 0; + int ret; + const char *p; + time_t timestamp; + + if (!CONFIG_IS_ENABLED(FIT_PRINT)) + return; + + /* Indent string is defined in header image.h */ + p = IMAGE_INDENT_STRING; + + /* Root node properties */ + ret = fit_get_desc(fit, 0, &desc); + printf("%sFIT description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%sCreated: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return; + } + + /* Process its subnodes, print out component images details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf("%s Image %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_image_print(fit, noffset, p); + } + } + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't get configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return; + } + + /* get default configuration unit name from default property */ + uname = (char *)fdt_getprop(fit, noffset, FIT_DEFAULT_PROP, NULL); + if (uname) + printf("%s Default Configuration: '%s'\n", p, uname); + + /* Process its subnodes, print out configurations details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the configurations parent node, + * i.e. configuration node. + */ + printf("%s Configuration %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_conf_print(fit, noffset, p); + } + } +} + +/** + * fit_image_print - prints out the FIT component image details + * @fit: pointer to the FIT format image header + * @image_noffset: offset of the component image node + * @p: pointer to prefix string + * + * fit_image_print() lists all mandatory properties for the processed component + * image. If present, hash nodes are printed out as well. Load + * address for images of type firmware is also printed out. Since the load + * address is not mandatory for firmware images, it will be output as + * "unavailable" when not present. + * + * returns: + * no returned results + */ +void fit_image_print(const void *fit, int image_noffset, const char *p) +{ + char *desc; + uint8_t type, arch, os, comp; + size_t size; + ulong load, entry; + const void *data; + int noffset; + int ndepth; + int ret; + + if (!CONFIG_IS_ENABLED(FIT_PRINT)) + return; + + /* Mandatory properties */ + ret = fit_get_desc(fit, image_noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + time_t timestamp; + + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%s Created: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + fit_image_get_type(fit, image_noffset, &type); + printf("%s Type: %s\n", p, genimg_get_type_name(type)); + + fit_image_get_comp(fit, image_noffset, &comp); + printf("%s Compression: %s\n", p, genimg_get_comp_name(comp)); + + ret = fit_image_get_data_and_size(fit, image_noffset, &data, &size); + + if (!tools_build()) { + printf("%s Data Start: ", p); + if (ret) { + printf("unavailable\n"); + } else { + void *vdata = (void *)data; + + printf("0x%08lx\n", (ulong)map_to_sysmem(vdata)); + } + } + + printf("%s Data Size: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_size(size); + + /* Remaining, type dependent properties */ + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK) || (type == IH_TYPE_FIRMWARE) || + (type == IH_TYPE_FLATDT)) { + fit_image_get_arch(fit, image_noffset, &arch); + printf("%s Architecture: %s\n", p, genimg_get_arch_name(arch)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_RAMDISK) || + (type == IH_TYPE_FIRMWARE)) { + fit_image_get_os(fit, image_noffset, &os); + printf("%s OS: %s\n", p, genimg_get_os_name(os)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_FIRMWARE) || (type == IH_TYPE_RAMDISK) || + (type == IH_TYPE_FPGA)) { + ret = fit_image_get_load(fit, image_noffset, &load); + printf("%s Load Address: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", load); + } + + /* optional load address for FDT */ + if (type == IH_TYPE_FLATDT && !fit_image_get_load(fit, image_noffset, &load)) + printf("%s Load Address: 0x%08lx\n", p, load); + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK)) { + ret = fit_image_get_entry(fit, image_noffset, &entry); + printf("%s Entry Point: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", entry); + } + + /* Process all hash subnodes of the component image node */ + for (ndepth = 0, noffset = fdt_next_node(fit, image_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* Direct child node of the component image node */ + fit_image_print_verification_data(fit, noffset, p); + } + } +} + +/** + * fit_get_desc - get node description property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @desc: double pointer to the char, will hold pointer to the description + * + * fit_get_desc() reads description property from a given node, if + * description is found pointer to it is returned in third call argument. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_get_desc(const void *fit, int noffset, char **desc) +{ + int len; + + *desc = (char *)fdt_getprop(fit, noffset, FIT_DESC_PROP, &len); + if (*desc == NULL) { + fit_get_debug(fit, noffset, FIT_DESC_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_get_timestamp - get node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: pointer to the time_t, will hold read timestamp + * + * fit_get_timestamp() reads timestamp property from given node, if timestamp + * is found and has a correct size its value is returned in third call + * argument. + * + * returns: + * 0, on success + * -1, on property read failure + * -2, on wrong timestamp size + */ +int fit_get_timestamp(const void *fit, int noffset, time_t *timestamp) +{ + int len; + const void *data; + + data = fdt_getprop(fit, noffset, FIT_TIMESTAMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TIMESTAMP_PROP, len); + return -1; + } + if (len != sizeof(uint32_t)) { + debug("FIT timestamp with incorrect size of (%u)\n", len); + return -2; + } + + *timestamp = uimage_to_cpu(*((uint32_t *)data)); + return 0; +} + +/** + * fit_image_get_node - get node offset for component image of a given unit name + * @fit: pointer to the FIT format image header + * @image_uname: component image node unit name + * + * fit_image_get_node() finds a component image (within the '/images' + * node) of a provided unit name. If image is found its node offset is + * returned to the caller. + * + * returns: + * image node offset when found (>=0) + * negative number on failure (FDT_ERR_* code) + */ +int fit_image_get_node(const void *fit, const char *image_uname) +{ + int noffset, images_noffset; + + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + debug("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return images_noffset; + } + + noffset = fdt_subnode_offset(fit, images_noffset, image_uname); + if (noffset < 0) { + debug("Can't get node offset for image unit name: '%s' (%s)\n", + image_uname, fdt_strerror(noffset)); + } + + return noffset; +} + +/** + * fit_image_get_os - get os id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: pointer to the uint8_t, will hold os numeric id + * + * fit_image_get_os() finds os property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_os(const void *fit, int noffset, uint8_t *os) +{ + int len; + const void *data; + + /* Get OS name from property data */ + data = fdt_getprop(fit, noffset, FIT_OS_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_OS_PROP, len); + *os = -1; + return -1; + } + + /* Translate OS name to id */ + *os = genimg_get_os_id(data); + return 0; +} + +/** + * fit_image_get_arch - get arch id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: pointer to the uint8_t, will hold arch numeric id + * + * fit_image_get_arch() finds arch property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_arch(const void *fit, int noffset, uint8_t *arch) +{ + int len; + const void *data; + + /* Get architecture name from property data */ + data = fdt_getprop(fit, noffset, FIT_ARCH_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_ARCH_PROP, len); + *arch = -1; + return -1; + } + + /* Translate architecture name to id */ + *arch = genimg_get_arch_id(data); + return 0; +} + +/** + * fit_image_get_type - get type id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: pointer to the uint8_t, will hold type numeric id + * + * fit_image_get_type() finds type property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_type(const void *fit, int noffset, uint8_t *type) +{ + int len; + const void *data; + + /* Get image type name from property data */ + data = fdt_getprop(fit, noffset, FIT_TYPE_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TYPE_PROP, len); + *type = -1; + return -1; + } + + /* Translate image type name to id */ + *type = genimg_get_type_id(data); + return 0; +} + +/** + * fit_image_get_comp - get comp id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: pointer to the uint8_t, will hold comp numeric id + * + * fit_image_get_comp() finds comp property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_comp(const void *fit, int noffset, uint8_t *comp) +{ + int len; + const void *data; + + /* Get compression name from property data */ + data = fdt_getprop(fit, noffset, FIT_COMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_COMP_PROP, len); + *comp = -1; + return -1; + } + + /* Translate compression name to id */ + *comp = genimg_get_comp_id(data); + return 0; +} + +static int fit_image_get_address(const void *fit, int noffset, char *name, + ulong *load) +{ + int len, cell_len; + const fdt32_t *cell; + uint64_t load64 = 0; + + cell = fdt_getprop(fit, noffset, name, &len); + if (cell == NULL) { + fit_get_debug(fit, noffset, name, len); + return -1; + } + + cell_len = len >> 2; + /* Use load64 to avoid compiling warning for 32-bit target */ + while (cell_len--) { + load64 = (load64 << 32) | uimage_to_cpu(*cell); + cell++; + } + + if (len > sizeof(ulong) && (uint32_t)(load64 >> 32)) { + printf("Unsupported %s address size\n", name); + return -1; + } + + *load = (ulong)load64; + + return 0; +} +/** + * fit_image_get_load() - get load addr property for given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @load: pointer to the uint32_t, will hold load address + * + * fit_image_get_load() finds load address property in a given component + * image node. If the property is found, its value is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_load(const void *fit, int noffset, ulong *load) +{ + return fit_image_get_address(fit, noffset, FIT_LOAD_PROP, load); +} + +/** + * fit_image_get_entry() - get entry point address property + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @entry: pointer to the uint32_t, will hold entry point address + * + * This gets the entry point address property for a given component image + * node. + * + * fit_image_get_entry() finds entry point address property in a given + * component image node. If the property is found, its value is returned + * to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_entry(const void *fit, int noffset, ulong *entry) +{ + return fit_image_get_address(fit, noffset, FIT_ENTRY_PROP, entry); +} + +/** + * fit_image_get_data - get data property and its size for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data() finds data property in a given component image node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_data(const void *fit, int noffset, + const void **data, size_t *size) +{ + int len; + + *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len); + if (*data == NULL) { + fit_get_debug(fit, noffset, FIT_DATA_PROP, len); + *size = 0; + return -1; + } + + *size = len; + return 0; +} + +/** + * Get 'data-offset' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_offset: holds the data-offset property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL); + if (!val) + return -ENOENT; + + *data_offset = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-position' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_position: holds the data-position property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_position(const void *fit, int noffset, + int *data_position) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL); + if (!val) + return -ENOENT; + + *data_position = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-size' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_size: holds the data-size property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_size(const void *fit, int noffset, int *data_size) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL); + if (!val) + return -ENOENT; + + *data_size = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-size-unciphered' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_size: holds the data-size property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_size_unciphered(const void *fit, int noffset, + size_t *data_size) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, "data-size-unciphered", NULL); + if (!val) + return -ENOENT; + + *data_size = (size_t)fdt32_to_cpu(*val); + + return 0; +} + +/** + * fit_image_get_data_and_size - get data and its size including + * both embedded and external data + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data_and_size() finds data and its size including + * both embedded and external data. If the property is found + * its data start address and size are returned to the caller. + * + * returns: + * 0, on success + * otherwise, on failure + */ +int fit_image_get_data_and_size(const void *fit, int noffset, + const void **data, size_t *size) +{ + bool external_data = false; + int offset; + int len; + int ret; + + if (!fit_image_get_data_position(fit, noffset, &offset)) { + external_data = true; + } else if (!fit_image_get_data_offset(fit, noffset, &offset)) { + external_data = true; + /* + * For FIT with external data, figure out where + * the external images start. This is the base + * for the data-offset properties in each image. + */ + offset += ((fdt_totalsize(fit) + 3) & ~3); + } + + if (external_data) { + debug("External Data\n"); + ret = fit_image_get_data_size(fit, noffset, &len); + if (!ret) { + *data = fit + offset; + *size = len; + } + } else { + ret = fit_image_get_data(fit, noffset, data, size); + } + + return ret; +} + +/** + * fit_image_hash_get_algo - get hash algorithm name + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @algo: double pointer to char, will hold pointer to the algorithm name + * + * fit_image_hash_get_algo() finds hash algorithm property in a given hash node. + * If the property is found its data start address is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_algo(const void *fit, int noffset, char **algo) +{ + int len; + + *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); + if (*algo == NULL) { + fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_image_hash_get_value - get hash value and length + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: double pointer to uint8_t, will hold address of a hash value data + * @value_len: pointer to an int, will hold hash data length + * + * fit_image_hash_get_value() finds hash value property in a given hash node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, + int *value_len) +{ + int len; + + *value = (uint8_t *)fdt_getprop(fit, noffset, FIT_VALUE_PROP, &len); + if (*value == NULL) { + fit_get_debug(fit, noffset, FIT_VALUE_PROP, len); + *value_len = 0; + return -1; + } + + *value_len = len; + return 0; +} + +/** + * fit_image_hash_get_ignore - get hash ignore flag + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @ignore: pointer to an int, will hold hash ignore flag + * + * fit_image_hash_get_ignore() finds hash ignore property in a given hash node. + * If the property is found and non-zero, the hash algorithm is not verified by + * u-boot automatically. + * + * returns: + * 0, on ignore not found + * value, on ignore found + */ +static int fit_image_hash_get_ignore(const void *fit, int noffset, int *ignore) +{ + int len; + int *value; + + value = (int *)fdt_getprop(fit, noffset, FIT_IGNORE_PROP, &len); + if (value == NULL || len != sizeof(int)) + *ignore = 0; + else + *ignore = *value; + + return 0; +} + +/** + * fit_image_cipher_get_algo - get cipher algorithm name + * @fit: pointer to the FIT format image header + * @noffset: cipher node offset + * @algo: double pointer to char, will hold pointer to the algorithm name + * + * fit_image_cipher_get_algo() finds cipher algorithm property in a given + * cipher node. If the property is found its data start address is returned + * to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo) +{ + int len; + + *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); + if (!*algo) { + fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); + return -1; + } + + return 0; +} + +ulong fit_get_end(const void *fit) +{ + return map_to_sysmem((void *)(fit + fdt_totalsize(fit))); +} + +/** + * fit_set_timestamp - set node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: timestamp value to be set + * + * fit_set_timestamp() attempts to set timestamp property in the requested + * node and returns operation status to the caller. + * + * returns: + * 0, on success + * -ENOSPC if no space in device tree, -1 for other error + */ +int fit_set_timestamp(void *fit, int noffset, time_t timestamp) +{ + uint32_t t; + int ret; + + t = cpu_to_uimage(timestamp); + ret = fdt_setprop(fit, noffset, FIT_TIMESTAMP_PROP, &t, + sizeof(uint32_t)); + if (ret) { + debug("Can't set '%s' property for '%s' node (%s)\n", + FIT_TIMESTAMP_PROP, fit_get_name(fit, noffset, NULL), + fdt_strerror(ret)); + return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -1; + } + + return 0; +} + +/** + * calculate_hash - calculate and return hash for provided input data + * @data: pointer to the input data + * @data_len: data length + * @algo: requested hash algorithm + * @value: pointer to the char, will hold hash value data (caller must + * allocate enough free space) + * value_len: length of the calculated hash + * + * calculate_hash() computes input data hash according to the requested + * algorithm. + * Resulting hash value is placed in caller provided 'value' buffer, length + * of the calculated hash is returned via value_len pointer argument. + * + * returns: + * 0, on success + * -1, when algo is unsupported + */ +int calculate_hash(const void *data, int data_len, const char *name, + uint8_t *value, int *value_len) +{ +#if !defined(USE_HOSTCC) && defined(CONFIG_DM_HASH) + int rc; + enum HASH_ALGO hash_algo; + struct udevice *dev; + + rc = uclass_get_device(UCLASS_HASH, 0, &dev); + if (rc) { + debug("failed to get hash device, rc=%d\n", rc); + return -1; + } + + hash_algo = hash_algo_lookup_by_name(algo); + if (hash_algo == HASH_ALGO_INVALID) { + debug("Unsupported hash algorithm\n"); + return -1; + }; + + rc = hash_digest_wd(dev, hash_algo, data, data_len, value, CHUNKSZ); + if (rc) { + debug("failed to get hash value, rc=%d\n", rc); + return -1; + } + + *value_len = hash_algo_digest_size(hash_algo); +#else + struct hash_algo *algo; + int ret; + + ret = hash_lookup_algo(name, &algo); + if (ret < 0) { + debug("Unsupported hash alogrithm\n"); + return -1; + } + + algo->hash_func_ws(data, data_len, value, algo->chunk_size); + *value_len = algo->digest_size; +#endif + + return 0; +} + +static int fit_image_check_hash(const void *fit, int noffset, const void *data, + size_t size, char **err_msgp) +{ + uint8_t value[FIT_MAX_HASH_LEN]; + int value_len; + char *algo; + uint8_t *fit_value; + int fit_value_len; + int ignore; + + *err_msgp = NULL; + + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + printf("%s", algo); + + if (!tools_build()) { + fit_image_hash_get_ignore(fit, noffset, &ignore); + if (ignore) { + printf("-skipped "); + return 0; + } + } + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + if (calculate_hash(data, size, algo, value, &value_len)) { + *err_msgp = "Unsupported hash algorithm"; + return -1; + } + + if (value_len != fit_value_len) { + *err_msgp = "Bad hash value len"; + return -1; + } else if (memcmp(value, fit_value, value_len) != 0) { + *err_msgp = "Bad hash value"; + return -1; + } + + return 0; +} + +int fit_image_verify_with_data(const void *fit, int image_noffset, + const void *data, size_t size) +{ + int noffset = 0; + char *err_msg = ""; + int verify_all = 1; + int ret; + + /* Verify all required signatures */ + if (FIT_IMAGE_ENABLE_VERIFY && + fit_image_verify_required_sigs(fit, image_noffset, data, size, + gd_fdt_blob(), &verify_all)) { + err_msg = "Unable to verify required signature"; + goto error; + } + + /* Process all hash subnodes of the component image node */ + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + /* + * Check subnode name, must be equal to "hash". + * Multiple hash nodes require unique unit node + * names, e.g. hash-1, hash-2, etc. + */ + if (!strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) { + if (fit_image_check_hash(fit, noffset, data, size, + &err_msg)) + goto error; + puts("+ "); + } else if (FIT_IMAGE_ENABLE_VERIFY && verify_all && + !strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + + /* + * Show an indication on failure, but do not return + * an error. Only keys marked 'required' can cause + * an image validation failure. See the call to + * fit_image_verify_required_sigs() above. + */ + if (ret) + puts("- "); + else + puts("+ "); + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return 1; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return 0; +} + +/** + * fit_image_verify - verify data integrity + * @fit: pointer to the FIT format image header + * @image_noffset: component image node offset + * + * fit_image_verify() goes over component image hash nodes, + * re-calculates each data hash and compares with the value stored in hash + * node. + * + * returns: + * 1, if all hashes are valid + * 0, otherwise (or on error) + */ +int fit_image_verify(const void *fit, int image_noffset) +{ + const char *name = fit_get_name(fit, image_noffset, NULL); + const void *data; + size_t size; + char *err_msg = ""; + + if (IS_ENABLED(CONFIG_FIT_SIGNATURE) && strchr(name, '@')) { + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + err_msg = "Node name contains @"; + goto err; + } + /* Get image data and data length */ + if (fit_image_get_data_and_size(fit, image_noffset, &data, &size)) { + err_msg = "Can't get image data/size"; + goto err; + } + + return fit_image_verify_with_data(fit, image_noffset, data, size); + +err: + printf("error!\n%s in '%s' image node\n", err_msg, + fit_get_name(fit, image_noffset, NULL)); + return 0; +} + +/** + * fit_all_image_verify - verify data integrity for all images + * @fit: pointer to the FIT format image header + * + * fit_all_image_verify() goes over all images in the FIT and + * for every images checks if all it's hashes are valid. + * + * returns: + * 1, if all hashes of all images are valid + * 0, otherwise (or on error) + */ +int fit_all_image_verify(const void *fit) +{ + int images_noffset; + int noffset; + int ndepth; + int count; + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return 0; + } + + /* Process all image subnodes, check hashes for each */ + printf("## Checking hash(es) for FIT Image at %08lx ...\n", + (ulong)fit); + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf(" Hash(es) for Image %u (%s): ", count, + fit_get_name(fit, noffset, NULL)); + count++; + + if (!fit_image_verify(fit, noffset)) + return 0; + printf("\n"); + } + } + return 1; +} + +static int fit_image_uncipher(const void *fit, int image_noffset, + void **data, size_t *size) +{ + int cipher_noffset, ret; + void *dst; + size_t size_dst; + + cipher_noffset = fdt_subnode_offset(fit, image_noffset, + FIT_CIPHER_NODENAME); + if (cipher_noffset < 0) + return 0; + + ret = fit_image_decrypt_data(fit, image_noffset, cipher_noffset, + *data, *size, &dst, &size_dst); + if (ret) + goto out; + + *data = dst; + *size = size_dst; + + out: + return ret; +} + +/** + * fit_image_check_os - check whether image node is of a given os type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: requested image os + * + * fit_image_check_os() reads image os property and compares its numeric + * id with the requested os. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given os type + * 0 otherwise (or on error) + */ +int fit_image_check_os(const void *fit, int noffset, uint8_t os) +{ + uint8_t image_os; + + if (fit_image_get_os(fit, noffset, &image_os)) + return 0; + return (os == image_os); +} + +/** + * fit_image_check_arch - check whether image node is of a given arch + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: requested imagearch + * + * fit_image_check_arch() reads image arch property and compares its numeric + * id with the requested arch. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given arch + * 0 otherwise (or on error) + */ +int fit_image_check_arch(const void *fit, int noffset, uint8_t arch) +{ + uint8_t image_arch; + int aarch32_support = 0; + + /* Let's assume that sandbox can load any architecture */ + if (IS_ENABLED(CONFIG_SANDBOX)) + return true; + + if (IS_ENABLED(CONFIG_ARM64_SUPPORT_AARCH32)) + aarch32_support = 1; + + if (fit_image_get_arch(fit, noffset, &image_arch)) + return 0; + return (arch == image_arch) || + (arch == IH_ARCH_I386 && image_arch == IH_ARCH_X86_64) || + (arch == IH_ARCH_ARM64 && image_arch == IH_ARCH_ARM && + aarch32_support); +} + +/** + * fit_image_check_type - check whether image node is of a given type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: requested image type + * + * fit_image_check_type() reads image type property and compares its numeric + * id with the requested type. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given type + * 0 otherwise (or on error) + */ +int fit_image_check_type(const void *fit, int noffset, uint8_t type) +{ + uint8_t image_type; + + if (fit_image_get_type(fit, noffset, &image_type)) + return 0; + return (type == image_type); +} + +/** + * fit_image_check_comp - check whether image node uses given compression + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: requested image compression type + * + * fit_image_check_comp() reads image compression property and compares its + * numeric id with the requested compression type. Comparison result is + * returned to the caller. + * + * returns: + * 1 if image uses requested compression + * 0 otherwise (or on error) + */ +int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) +{ + uint8_t image_comp; + + if (fit_image_get_comp(fit, noffset, &image_comp)) + return 0; + return (comp == image_comp); +} + +/** + * fdt_check_no_at() - Check for nodes whose names contain '@' + * + * This checks the parent node and all subnodes recursively + * + * @fit: FIT to check + * @parent: Parent node to check + * @return 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@' + */ +static int fdt_check_no_at(const void *fit, int parent) +{ + const char *name; + int node; + int ret; + + name = fdt_get_name(fit, parent, NULL); + if (!name || strchr(name, '@')) + return -EADDRNOTAVAIL; + + fdt_for_each_subnode(node, fit, parent) { + ret = fdt_check_no_at(fit, node); + if (ret) + return ret; + } + + return 0; +} + +int fit_check_format(const void *fit, ulong size) +{ + int ret; + + /* A FIT image must be a valid FDT */ + ret = fdt_check_header(fit); + if (ret) { + log_debug("Wrong FIT format: not a flattened device tree (err=%d)\n", + ret); + return -ENOEXEC; + } + + if (CONFIG_IS_ENABLED(FIT_FULL_CHECK)) { + /* + * If we are not given the size, make do wtih calculating it. + * This is not as secure, so we should consider a flag to + * control this. + */ + if (size == IMAGE_SIZE_INVAL) + size = fdt_totalsize(fit); + ret = fdt_check_full(fit, size); + if (ret) + ret = -EINVAL; + + /* + * U-Boot stopped using unit addressed in 2017. Since libfdt + * can match nodes ignoring any unit address, signature + * verification can see the wrong node if one is inserted with + * the same name as a valid node but with a unit address + * attached. Protect against this by disallowing unit addresses. + */ + if (!ret && CONFIG_IS_ENABLED(FIT_SIGNATURE)) { + ret = fdt_check_no_at(fit, 0); + + if (ret) { + log_debug("FIT check error %d\n", ret); + return ret; + } + } + if (ret) { + log_debug("FIT check error %d\n", ret); + return ret; + } + } + + /* mandatory / node 'description' property */ + if (!fdt_getprop(fit, 0, FIT_DESC_PROP, NULL)) { + log_debug("Wrong FIT format: no description\n"); + return -ENOMSG; + } + + if (IMAGE_ENABLE_TIMESTAMP) { + /* mandatory / node 'timestamp' property */ + if (!fdt_getprop(fit, 0, FIT_TIMESTAMP_PROP, NULL)) { + log_debug("Wrong FIT format: no timestamp\n"); + return -EBADMSG; + } + } + + /* mandatory subimages parent '/images' node */ + if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) { + log_debug("Wrong FIT format: no images parent node\n"); + return -ENOENT; + } + + return 0; +} + +/** + * fit_conf_find_compat + * @fit: pointer to the FIT format image header + * @fdt: pointer to the device tree to compare against + * + * fit_conf_find_compat() attempts to find the configuration whose fdt is the + * most compatible with the passed in device tree. + * + * Example: + * + * / o image-tree + * |-o images + * | |-o fdt-1 + * | |-o fdt-2 + * | + * |-o configurations + * |-o config-1 + * | |-fdt = fdt-1 + * | + * |-o config-2 + * |-fdt = fdt-2 + * + * / o U-Boot fdt + * |-compatible = "foo,bar", "bim,bam" + * + * / o kernel fdt1 + * |-compatible = "foo,bar", + * + * / o kernel fdt2 + * |-compatible = "bim,bam", "baz,biz" + * + * Configuration 1 would be picked because the first string in U-Boot's + * compatible list, "foo,bar", matches a compatible string in the root of fdt1. + * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1. + * + * As an optimization, the compatible property from the FDT's root node can be + * copied into the configuration node in the FIT image. This is required to + * match configurations with compressed FDTs. + * + * returns: + * offset to the configuration to use if one was found + * -1 otherwise + */ +int fit_conf_find_compat(const void *fit, const void *fdt) +{ + int ndepth = 0; + int noffset, confs_noffset, images_noffset; + const void *fdt_compat; + int fdt_compat_len; + int best_match_offset = 0; + int best_match_pos = 0; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (confs_noffset < 0 || images_noffset < 0) { + debug("Can't find configurations or images nodes.\n"); + return -1; + } + + fdt_compat = fdt_getprop(fdt, 0, "compatible", &fdt_compat_len); + if (!fdt_compat) { + debug("Fdt for comparison has no \"compatible\" property.\n"); + return -1; + } + + /* + * Loop over the configurations in the FIT image. + */ + for (noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + const void *fdt; + const char *kfdt_name; + int kfdt_noffset, compat_noffset; + const char *cur_fdt_compat; + int len; + size_t sz; + int i; + + if (ndepth > 1) + continue; + + /* If there's a compat property in the config node, use that. */ + if (fdt_getprop(fit, noffset, "compatible", NULL)) { + fdt = fit; /* search in FIT image */ + compat_noffset = noffset; /* search under config node */ + } else { /* Otherwise extract it from the kernel FDT. */ + kfdt_name = fdt_getprop(fit, noffset, "fdt", &len); + if (!kfdt_name) { + debug("No fdt property found.\n"); + continue; + } + kfdt_noffset = fdt_subnode_offset(fit, images_noffset, + kfdt_name); + if (kfdt_noffset < 0) { + debug("No image node named \"%s\" found.\n", + kfdt_name); + continue; + } + + if (!fit_image_check_comp(fit, kfdt_noffset, + IH_COMP_NONE)) { + debug("Can't extract compat from \"%s\" " + "(compressed)\n", kfdt_name); + continue; + } + + /* search in this config's kernel FDT */ + if (fit_image_get_data_and_size(fit, kfdt_noffset, + &fdt, &sz)) { + debug("Failed to get fdt \"%s\".\n", kfdt_name); + continue; + } + + compat_noffset = 0; /* search kFDT under root node */ + } + + len = fdt_compat_len; + cur_fdt_compat = fdt_compat; + /* + * Look for a match for each U-Boot compatibility string in + * turn in the compat string property. + */ + for (i = 0; len > 0 && + (!best_match_offset || best_match_pos > i); i++) { + int cur_len = strlen(cur_fdt_compat) + 1; + + if (!fdt_node_check_compatible(fdt, compat_noffset, + cur_fdt_compat)) { + best_match_offset = noffset; + best_match_pos = i; + break; + } + len -= cur_len; + cur_fdt_compat += cur_len; + } + } + if (!best_match_offset) { + debug("No match found.\n"); + return -1; + } + + return best_match_offset; +} + +int fit_conf_get_node(const void *fit, const char *conf_uname) +{ + int noffset, confs_noffset; + int len; + const char *s; + char *conf_uname_copy = NULL; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't find configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return confs_noffset; + } + + if (conf_uname == NULL) { + /* get configuration unit name from the default property */ + debug("No configuration specified, trying default...\n"); + if (!tools_build() && IS_ENABLED(CONFIG_MULTI_DTB_FIT)) { + noffset = fit_find_config_node(fit); + if (noffset < 0) + return noffset; + conf_uname = fdt_get_name(fit, noffset, NULL); + } else { + conf_uname = (char *)fdt_getprop(fit, confs_noffset, + FIT_DEFAULT_PROP, &len); + if (conf_uname == NULL) { + fit_get_debug(fit, confs_noffset, FIT_DEFAULT_PROP, + len); + return len; + } + } + debug("Found default configuration: '%s'\n", conf_uname); + } + + s = strchr(conf_uname, '#'); + if (s) { + len = s - conf_uname; + conf_uname_copy = malloc(len + 1); + if (!conf_uname_copy) { + debug("Can't allocate uname copy: '%s'\n", + conf_uname); + return -ENOMEM; + } + memcpy(conf_uname_copy, conf_uname, len); + conf_uname_copy[len] = '\0'; + conf_uname = conf_uname_copy; + } + + noffset = fdt_subnode_offset(fit, confs_noffset, conf_uname); + if (noffset < 0) { + debug("Can't get node offset for configuration unit name: '%s' (%s)\n", + conf_uname, fdt_strerror(noffset)); + } + + if (conf_uname_copy) + free(conf_uname_copy); + + return noffset; +} + +int fit_conf_get_prop_node_count(const void *fit, int noffset, + const char *prop_name) +{ + return fdt_stringlist_count(fit, noffset, prop_name); +} + +int fit_conf_get_prop_node_index(const void *fit, int noffset, + const char *prop_name, int index) +{ + const char *uname; + int len; + + /* get kernel image unit name from configuration kernel property */ + uname = fdt_stringlist_get(fit, noffset, prop_name, index, &len); + if (uname == NULL) + return len; + + return fit_image_get_node(fit, uname); +} + +int fit_conf_get_prop_node(const void *fit, int noffset, + const char *prop_name) +{ + return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0); +} + +static int fit_image_select(const void *fit, int rd_noffset, int verify) +{ + fit_image_print(fit, rd_noffset, " "); + + if (verify) { + puts(" Verifying Hash Integrity ... "); + if (!fit_image_verify(fit, rd_noffset)) { + puts("Bad Data Hash\n"); + return -EACCES; + } + puts("OK\n"); + } + + return 0; +} + +int fit_get_node_from_config(bootm_headers_t *images, const char *prop_name, + ulong addr) +{ + int cfg_noffset; + void *fit_hdr; + int noffset; + + debug("* %s: using config '%s' from image at 0x%08lx\n", + prop_name, images->fit_uname_cfg, addr); + + /* Check whether configuration has this property defined */ + fit_hdr = map_sysmem(addr, 0); + cfg_noffset = fit_conf_get_node(fit_hdr, images->fit_uname_cfg); + if (cfg_noffset < 0) { + debug("* %s: no such config\n", prop_name); + return -EINVAL; + } + + noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name); + if (noffset < 0) { + debug("* %s: no '%s' in config\n", prop_name, prop_name); + return -ENOENT; + } + + return noffset; +} + +/** + * fit_get_image_type_property() - get property name for IH_TYPE_... + * + * @return the properly name where we expect to find the image in the + * config node + */ +static const char *fit_get_image_type_property(int type) +{ + /* + * This is sort-of available in the uimage_type[] table in image.c + * but we don't have access to the short name, and "fdt" is different + * anyway. So let's just keep it here. + */ + switch (type) { + case IH_TYPE_FLATDT: + return FIT_FDT_PROP; + case IH_TYPE_KERNEL: + return FIT_KERNEL_PROP; + case IH_TYPE_FIRMWARE: + return FIT_FIRMWARE_PROP; + case IH_TYPE_RAMDISK: + return FIT_RAMDISK_PROP; + case IH_TYPE_X86_SETUP: + return FIT_SETUP_PROP; + case IH_TYPE_LOADABLE: + return FIT_LOADABLE_PROP; + case IH_TYPE_FPGA: + return FIT_FPGA_PROP; + case IH_TYPE_STANDALONE: + return FIT_STANDALONE_PROP; + } + + return "unknown"; +} + +int fit_image_load(bootm_headers_t *images, ulong addr, + const char **fit_unamep, const char **fit_uname_configp, + int arch, int image_type, int bootstage_id, + enum fit_load_op load_op, ulong *datap, ulong *lenp) +{ + int cfg_noffset, noffset; + const char *fit_uname; + const char *fit_uname_config; + const char *fit_base_uname_config; + const void *fit; + void *buf; + void *loadbuf; + size_t size; + int type_ok, os_ok; + ulong load, load_end, data, len; + uint8_t os, comp; + const char *prop_name; + int ret; + + fit = map_sysmem(addr, 0); + fit_uname = fit_unamep ? *fit_unamep : NULL; + fit_uname_config = fit_uname_configp ? *fit_uname_configp : NULL; + fit_base_uname_config = NULL; + prop_name = fit_get_image_type_property(image_type); + printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr); + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT); + ret = fit_check_format(fit, IMAGE_SIZE_INVAL); + if (ret) { + printf("Bad FIT %s image format! (err=%d)\n", prop_name, ret); + if (CONFIG_IS_ENABLED(FIT_SIGNATURE) && ret == -EADDRNOTAVAIL) + printf("Signature checking prevents use of unit addresses (@) in nodes\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT); + return ret; + } + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK); + if (fit_uname) { + /* get FIT component image node offset */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_UNIT_NAME); + noffset = fit_image_get_node(fit, fit_uname); + } else { + /* + * no image node unit name, try to get config + * node first. If config unit node name is NULL + * fit_conf_get_node() will try to find default config node + */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_NO_UNIT_NAME); + if (IS_ENABLED(CONFIG_FIT_BEST_MATCH) && !fit_uname_config) { + cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob()); + } else { + cfg_noffset = fit_conf_get_node(fit, + fit_uname_config); + } + if (cfg_noffset < 0) { + puts("Could not find configuration node\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_NO_UNIT_NAME); + return -ENOENT; + } + + fit_base_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + printf(" Using '%s' configuration\n", fit_base_uname_config); + /* Remember this config */ + if (image_type == IH_TYPE_KERNEL) + images->fit_uname_cfg = fit_base_uname_config; + + if (FIT_IMAGE_ENABLE_VERIFY && images->verify) { + puts(" Verifying Hash Integrity ... "); + if (fit_config_verify(fit, cfg_noffset)) { + puts("Bad Data Hash\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_HASH); + return -EACCES; + } + puts("OK\n"); + } + + bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG); + + noffset = fit_conf_get_prop_node(fit, cfg_noffset, + prop_name); + fit_uname = fit_get_name(fit, noffset, NULL); + } + if (noffset < 0) { + printf("Could not find subimage node type '%s'\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_SUBNODE); + return -ENOENT; + } + + printf(" Trying '%s' %s subimage\n", fit_uname, prop_name); + + ret = fit_image_select(fit, noffset, images->verify); + if (ret) { + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH); + return ret; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + if (!tools_build() && IS_ENABLED(CONFIG_SANDBOX)) { + if (!fit_image_check_target_arch(fit, noffset)) { + puts("Unsupported Architecture\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + return -ENOEXEC; + } + } + +#ifndef USE_HOSTCC + { + uint8_t os_arch; + + fit_image_get_arch(fit, noffset, &os_arch); + images->os.arch = os_arch; + } +#endif + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + type_ok = fit_image_check_type(fit, noffset, image_type) || + fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE) || + fit_image_check_type(fit, noffset, IH_TYPE_TEE) || + (image_type == IH_TYPE_KERNEL && + fit_image_check_type(fit, noffset, IH_TYPE_KERNEL_NOLOAD)); + + os_ok = image_type == IH_TYPE_FLATDT || + image_type == IH_TYPE_FPGA || + fit_image_check_os(fit, noffset, IH_OS_LINUX) || + fit_image_check_os(fit, noffset, IH_OS_U_BOOT) || + fit_image_check_os(fit, noffset, IH_OS_TEE) || + fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) || + fit_image_check_os(fit, noffset, IH_OS_EFI) || + fit_image_check_os(fit, noffset, IH_OS_VXWORKS); + + /* + * If either of the checks fail, we should report an error, but + * if the image type is coming from the "loadables" field, we + * don't care what it is + */ + if ((!type_ok || !os_ok) && image_type != IH_TYPE_LOADABLE) { + fit_image_get_os(fit, noffset, &os); + printf("No %s %s %s Image\n", + genimg_get_os_name(os), + genimg_get_arch_name(arch), + genimg_get_type_name(image_type)); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + return -EIO; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL_OK); + + /* get image data address and length */ + if (fit_image_get_data_and_size(fit, noffset, + (const void **)&buf, &size)) { + printf("Could not find %s subimage data!\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_GET_DATA); + return -ENOENT; + } + + /* Decrypt data before uncompress/move */ + if (IS_ENABLED(CONFIG_FIT_CIPHER) && IMAGE_ENABLE_DECRYPT) { + puts(" Decrypting Data ... "); + if (fit_image_uncipher(fit, noffset, &buf, &size)) { + puts("Error\n"); + return -EACCES; + } + puts("OK\n"); + } + + /* perform any post-processing on the image data */ + if (!tools_build() && IS_ENABLED(CONFIG_FIT_IMAGE_POST_PROCESS)) + board_fit_image_post_process(fit, noffset, &buf, &size); + + len = (ulong)size; + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_GET_DATA_OK); + + data = map_to_sysmem(buf); + load = data; + if (load_op == FIT_LOAD_IGNORED) { + /* Don't load */ + } else if (fit_image_get_load(fit, noffset, &load)) { + if (load_op == FIT_LOAD_REQUIRED) { + printf("Can't get %s subimage load address!\n", + prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_LOAD); + return -EBADF; + } + } else if (load_op != FIT_LOAD_OPTIONAL_NON_ZERO || load) { + ulong image_start, image_end; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = addr; + image_end = addr + fit_get_size(fit); + + load_end = load + len; + if (image_type != IH_TYPE_KERNEL && + load < image_end && load_end > image_start) { + printf("Error: %s overwritten\n", prop_name); + return -EXDEV; + } + + printf(" Loading %s from 0x%08lx to 0x%08lx\n", + prop_name, data, load); + } else { + load = data; /* No load address specified */ + } + + comp = IH_COMP_NONE; + loadbuf = buf; + /* Kernel images get decompressed later in bootm_load_os(). */ + if (!fit_image_get_comp(fit, noffset, &comp) && + comp != IH_COMP_NONE && + !(image_type == IH_TYPE_KERNEL || + image_type == IH_TYPE_KERNEL_NOLOAD || + image_type == IH_TYPE_RAMDISK)) { + ulong max_decomp_len = len * 20; + if (load == data) { + loadbuf = malloc(max_decomp_len); + load = map_to_sysmem(loadbuf); + } else { + loadbuf = map_sysmem(load, max_decomp_len); + } + if (image_decomp(comp, load, data, image_type, + loadbuf, buf, len, max_decomp_len, &load_end)) { + printf("Error decompressing %s\n", prop_name); + + return -ENOEXEC; + } + len = load_end - load; + } else if (load != data) { + loadbuf = map_sysmem(load, len); + memcpy(loadbuf, buf, len); + } + + if (image_type == IH_TYPE_RAMDISK && comp != IH_COMP_NONE) + puts("WARNING: 'compression' nodes for ramdisks are deprecated," + " please fix your .its file!\n"); + + /* verify that image data is a proper FDT blob */ + if (image_type == IH_TYPE_FLATDT && fdt_check_header(loadbuf)) { + puts("Subimage data is not a FDT"); + return -ENOEXEC; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD); + + *datap = load; + *lenp = len; + if (fit_unamep) + *fit_unamep = (char *)fit_uname; + if (fit_uname_configp) + *fit_uname_configp = (char *)(fit_uname_config ? : + fit_base_uname_config); + + return noffset; +} + +int boot_get_setup_fit(bootm_headers_t *images, uint8_t arch, + ulong *setup_start, ulong *setup_len) +{ + int noffset; + ulong addr; + ulong len; + int ret; + + addr = map_to_sysmem(images->fit_hdr_os); + noffset = fit_get_node_from_config(images, FIT_SETUP_PROP, addr); + if (noffset < 0) + return noffset; + + ret = fit_image_load(images, addr, NULL, NULL, arch, + IH_TYPE_X86_SETUP, BOOTSTAGE_ID_FIT_SETUP_START, + FIT_LOAD_REQUIRED, setup_start, &len); + + return ret; +} + +#ifndef USE_HOSTCC +int boot_get_fdt_fit(bootm_headers_t *images, ulong addr, + const char **fit_unamep, const char **fit_uname_configp, + int arch, ulong *datap, ulong *lenp) +{ + int fdt_noffset, cfg_noffset, count; + const void *fit; + const char *fit_uname = NULL; + const char *fit_uname_config = NULL; + char *fit_uname_config_copy = NULL; + char *next_config = NULL; + ulong load, len; +#ifdef CONFIG_OF_LIBFDT_OVERLAY + ulong image_start, image_end; + ulong ovload, ovlen, ovcopylen; + const char *uconfig; + const char *uname; + void *base, *ov, *ovcopy = NULL; + int i, err, noffset, ov_noffset; +#endif + + fit_uname = fit_unamep ? *fit_unamep : NULL; + + if (fit_uname_configp && *fit_uname_configp) { + fit_uname_config_copy = strdup(*fit_uname_configp); + if (!fit_uname_config_copy) + return -ENOMEM; + + next_config = strchr(fit_uname_config_copy, '#'); + if (next_config) + *next_config++ = '\0'; + if (next_config - 1 > fit_uname_config_copy) + fit_uname_config = fit_uname_config_copy; + } + + fdt_noffset = fit_image_load(images, + addr, &fit_uname, &fit_uname_config, + arch, IH_TYPE_FLATDT, + BOOTSTAGE_ID_FIT_FDT_START, + FIT_LOAD_OPTIONAL, &load, &len); + + if (fdt_noffset < 0) + goto out; + + debug("fit_uname=%s, fit_uname_config=%s\n", + fit_uname ? fit_uname : "<NULL>", + fit_uname_config ? fit_uname_config : "<NULL>"); + + fit = map_sysmem(addr, 0); + + cfg_noffset = fit_conf_get_node(fit, fit_uname_config); + + /* single blob, or error just return as well */ + count = fit_conf_get_prop_node_count(fit, cfg_noffset, FIT_FDT_PROP); + if (count <= 1 && !next_config) + goto out; + + /* we need to apply overlays */ + +#ifdef CONFIG_OF_LIBFDT_OVERLAY + image_start = addr; + image_end = addr + fit_get_size(fit); + /* verify that relocation took place by load address not being in fit */ + if (load >= image_start && load < image_end) { + /* check is simplified; fit load checks for overlaps */ + printf("Overlayed FDT requires relocation\n"); + fdt_noffset = -EBADF; + goto out; + } + + base = map_sysmem(load, len); + + /* apply extra configs in FIT first, followed by args */ + for (i = 1; ; i++) { + if (i < count) { + noffset = fit_conf_get_prop_node_index(fit, cfg_noffset, + FIT_FDT_PROP, i); + uname = fit_get_name(fit, noffset, NULL); + uconfig = NULL; + } else { + if (!next_config) + break; + uconfig = next_config; + next_config = strchr(next_config, '#'); + if (next_config) + *next_config++ = '\0'; + uname = NULL; + + /* + * fit_image_load() would load the first FDT from the + * extra config only when uconfig is specified. + * Check if the extra config contains multiple FDTs and + * if so, load them. + */ + cfg_noffset = fit_conf_get_node(fit, uconfig); + + i = 0; + count = fit_conf_get_prop_node_count(fit, cfg_noffset, + FIT_FDT_PROP); + } + + debug("%d: using uname=%s uconfig=%s\n", i, uname, uconfig); + + ov_noffset = fit_image_load(images, + addr, &uname, &uconfig, + arch, IH_TYPE_FLATDT, + BOOTSTAGE_ID_FIT_FDT_START, + FIT_LOAD_IGNORED, &ovload, &ovlen); + if (ov_noffset < 0) { + printf("load of %s failed\n", uname); + continue; + } + debug("%s loaded at 0x%08lx len=0x%08lx\n", + uname, ovload, ovlen); + ov = map_sysmem(ovload, ovlen); + + ovcopylen = ALIGN(fdt_totalsize(ov), SZ_4K); + ovcopy = malloc(ovcopylen); + if (!ovcopy) { + printf("failed to duplicate DTO before application\n"); + fdt_noffset = -ENOMEM; + goto out; + } + + err = fdt_open_into(ov, ovcopy, ovcopylen); + if (err < 0) { + printf("failed on fdt_open_into for DTO\n"); + fdt_noffset = err; + goto out; + } + + base = map_sysmem(load, len + ovlen); + err = fdt_open_into(base, base, len + ovlen); + if (err < 0) { + printf("failed on fdt_open_into\n"); + fdt_noffset = err; + goto out; + } + + /* the verbose method prints out messages on error */ + err = fdt_overlay_apply_verbose(base, ovcopy); + if (err < 0) { + fdt_noffset = err; + goto out; + } + fdt_pack(base); + len = fdt_totalsize(base); + + free(ovcopy); + ovcopy = NULL; + } +#else + printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not set\n"); + fdt_noffset = -EBADF; +#endif + +out: + if (datap) + *datap = load; + if (lenp) + *lenp = len; + if (fit_unamep) + *fit_unamep = fit_uname; + if (fit_uname_configp) + *fit_uname_configp = fit_uname_config; + +#ifdef CONFIG_OF_LIBFDT_OVERLAY + if (ovcopy) + free(ovcopy); +#endif + if (fit_uname_config_copy) + free(fit_uname_config_copy); + return fdt_noffset; +} +#endif diff --git a/boot/image-host.c b/boot/image-host.c new file mode 100644 index 0000000..20a9521 --- /dev/null +++ b/boot/image-host.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Image code used by host tools (and not boards) + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <time.h> + +void memmove_wd(void *to, void *from, size_t len, ulong chunksz) +{ + memmove(to, from, len); +} + +void genimg_print_size(uint32_t size) +{ + printf("%d Bytes = %.2f KiB = %.2f MiB\n", size, (double)size / 1.024e3, + (double)size / 1.048576e6); +} + +void genimg_print_time(time_t timestamp) +{ + printf("%s", ctime(×tamp)); +} diff --git a/boot/image-sig.c b/boot/image-sig.c new file mode 100644 index 0000000..1aa0b58 --- /dev/null +++ b/boot/image-sig.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#include <image.h> +#include <relocate.h> +#include <u-boot/ecdsa.h> +#include <u-boot/rsa.h> +#include <u-boot/hash-checksum.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +struct checksum_algo checksum_algos[] = { + { + .name = "sha1", + .checksum_len = SHA1_SUM_LEN, + .der_len = SHA1_DER_LEN, + .der_prefix = sha1_der_prefix, + .calculate = hash_calculate, + }, + { + .name = "sha256", + .checksum_len = SHA256_SUM_LEN, + .der_len = SHA256_DER_LEN, + .der_prefix = sha256_der_prefix, + .calculate = hash_calculate, + }, +#ifdef CONFIG_SHA384 + { + .name = "sha384", + .checksum_len = SHA384_SUM_LEN, + .der_len = SHA384_DER_LEN, + .der_prefix = sha384_der_prefix, + .calculate = hash_calculate, + }, +#endif +#ifdef CONFIG_SHA512 + { + .name = "sha512", + .checksum_len = SHA512_SUM_LEN, + .der_len = SHA512_DER_LEN, + .der_prefix = sha512_der_prefix, + .calculate = hash_calculate, + }, +#endif + +}; + +struct checksum_algo *image_get_checksum_algo(const char *full_name) +{ + int i; + const char *name; + + if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) { + static bool done; + + if (!done) { + done = true; + for (i = 0; i < ARRAY_SIZE(checksum_algos); i++) { + struct checksum_algo *algo = &checksum_algos[i]; + + MANUAL_RELOC(algo->name); + MANUAL_RELOC(algo->calculate); + } + } + } + + for (i = 0; i < ARRAY_SIZE(checksum_algos); i++) { + name = checksum_algos[i].name; + /* Make sure names match and next char is a comma */ + if (!strncmp(name, full_name, strlen(name)) && + full_name[strlen(name)] == ',') + return &checksum_algos[i]; + } + + return NULL; +} + +struct crypto_algo *image_get_crypto_algo(const char *full_name) +{ + struct crypto_algo *crypto, *end; + const char *name; + + if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) { + static bool done; + + if (!done) { + done = true; + crypto = ll_entry_start(struct crypto_algo, cryptos); + end = ll_entry_end(struct crypto_algo, cryptos); + for (; crypto < end; crypto++) { + MANUAL_RELOC(crypto->name); + MANUAL_RELOC(crypto->verify); + } + } + } + + /* Move name to after the comma */ + name = strchr(full_name, ','); + if (!name) + return NULL; + name += 1; + + crypto = ll_entry_start(struct crypto_algo, cryptos); + end = ll_entry_end(struct crypto_algo, cryptos); + for (; crypto < end; crypto++) { + if (!strcmp(crypto->name, name)) + return crypto; + } + + /* Not found */ + return NULL; +} + +struct padding_algo *image_get_padding_algo(const char *name) +{ + struct padding_algo *padding, *end; + + if (!name) + return NULL; + + padding = ll_entry_start(struct padding_algo, paddings); + end = ll_entry_end(struct padding_algo, paddings); + for (; padding < end; padding++) { + if (!strcmp(padding->name, name)) + return padding; + } + + return NULL; +} diff --git a/boot/image.c b/boot/image.c new file mode 100644 index 0000000..3fa60b5 --- /dev/null +++ b/boot/image.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <env.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <u-boot/crc.h> + +#ifdef CONFIG_SHOW_BOOT_PROGRESS +#include <status_led.h> +#endif + +#if CONFIG_IS_ENABLED(FIT) || CONFIG_IS_ENABLED(OF_LIBFDT) +#include <linux/libfdt.h> +#include <fdt_support.h> +#endif + +#include <asm/global_data.h> +#include <u-boot/md5.h> +#include <u-boot/sha1.h> +#include <linux/errno.h> +#include <asm/io.h> + +#ifdef CONFIG_CMD_BDI +extern int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* Set this if we have less than 4 MB of malloc() space */ +#if CONFIG_SYS_MALLOC_LEN < (4096 * 1024) +#define CONSERVE_MEMORY true +#else +#define CONSERVE_MEMORY false +#endif + +#else /* USE_HOSTCC */ +#include "mkimage.h" +#include <u-boot/md5.h> +#include <time.h> + +#ifndef __maybe_unused +# define __maybe_unused /* unimplemented */ +#endif + +#define CONSERVE_MEMORY false + +#endif /* !USE_HOSTCC*/ + +#include <abuf.h> +#include <bzlib.h> +#include <display_options.h> +#include <gzip.h> +#include <image.h> +#include <imximage.h> +#include <relocate.h> +#include <linux/lzo.h> +#include <linux/zstd.h> +#include <linux/kconfig.h> +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> +#include <u-boot/crc.h> +#include <u-boot/lz4.h> + +static const table_entry_t uimage_arch[] = { + { IH_ARCH_INVALID, "invalid", "Invalid ARCH", }, + { IH_ARCH_ALPHA, "alpha", "Alpha", }, + { IH_ARCH_ARM, "arm", "ARM", }, + { IH_ARCH_I386, "x86", "Intel x86", }, + { IH_ARCH_IA64, "ia64", "IA64", }, + { IH_ARCH_M68K, "m68k", "M68K", }, + { IH_ARCH_MICROBLAZE, "microblaze", "MicroBlaze", }, + { IH_ARCH_MIPS, "mips", "MIPS", }, + { IH_ARCH_MIPS64, "mips64", "MIPS 64 Bit", }, + { IH_ARCH_NIOS2, "nios2", "NIOS II", }, + { IH_ARCH_PPC, "powerpc", "PowerPC", }, + { IH_ARCH_PPC, "ppc", "PowerPC", }, + { IH_ARCH_S390, "s390", "IBM S390", }, + { IH_ARCH_SH, "sh", "SuperH", }, + { IH_ARCH_SPARC, "sparc", "SPARC", }, + { IH_ARCH_SPARC64, "sparc64", "SPARC 64 Bit", }, + { IH_ARCH_BLACKFIN, "blackfin", "Blackfin", }, + { IH_ARCH_AVR32, "avr32", "AVR32", }, + { IH_ARCH_NDS32, "nds32", "NDS32", }, + { IH_ARCH_OPENRISC, "or1k", "OpenRISC 1000",}, + { IH_ARCH_SANDBOX, "sandbox", "Sandbox", }, + { IH_ARCH_ARM64, "arm64", "AArch64", }, + { IH_ARCH_ARC, "arc", "ARC", }, + { IH_ARCH_X86_64, "x86_64", "AMD x86_64", }, + { IH_ARCH_XTENSA, "xtensa", "Xtensa", }, + { IH_ARCH_RISCV, "riscv", "RISC-V", }, + { -1, "", "", }, +}; + +static const table_entry_t uimage_os[] = { + { IH_OS_INVALID, "invalid", "Invalid OS", }, + { IH_OS_ARM_TRUSTED_FIRMWARE, "arm-trusted-firmware", "ARM Trusted Firmware" }, + { IH_OS_LINUX, "linux", "Linux", }, +#if defined(CONFIG_LYNXKDI) || defined(USE_HOSTCC) + { IH_OS_LYNXOS, "lynxos", "LynxOS", }, +#endif + { IH_OS_NETBSD, "netbsd", "NetBSD", }, + { IH_OS_OSE, "ose", "Enea OSE", }, + { IH_OS_PLAN9, "plan9", "Plan 9", }, + { IH_OS_RTEMS, "rtems", "RTEMS", }, + { IH_OS_TEE, "tee", "Trusted Execution Environment" }, + { IH_OS_U_BOOT, "u-boot", "U-Boot", }, + { IH_OS_VXWORKS, "vxworks", "VxWorks", }, +#if defined(CONFIG_CMD_ELF) || defined(USE_HOSTCC) + { IH_OS_QNX, "qnx", "QNX", }, +#endif +#if defined(CONFIG_INTEGRITY) || defined(USE_HOSTCC) + { IH_OS_INTEGRITY,"integrity", "INTEGRITY", }, +#endif +#ifdef USE_HOSTCC + { IH_OS_4_4BSD, "4_4bsd", "4_4BSD", }, + { IH_OS_DELL, "dell", "Dell", }, + { IH_OS_ESIX, "esix", "Esix", }, + { IH_OS_FREEBSD, "freebsd", "FreeBSD", }, + { IH_OS_IRIX, "irix", "Irix", }, + { IH_OS_NCR, "ncr", "NCR", }, + { IH_OS_OPENBSD, "openbsd", "OpenBSD", }, + { IH_OS_PSOS, "psos", "pSOS", }, + { IH_OS_SCO, "sco", "SCO", }, + { IH_OS_SOLARIS, "solaris", "Solaris", }, + { IH_OS_SVR4, "svr4", "SVR4", }, +#endif +#if defined(CONFIG_BOOTM_OPENRTOS) || defined(USE_HOSTCC) + { IH_OS_OPENRTOS, "openrtos", "OpenRTOS", }, +#endif + { IH_OS_OPENSBI, "opensbi", "RISC-V OpenSBI", }, + { IH_OS_EFI, "efi", "EFI Firmware" }, + + { -1, "", "", }, +}; + +static const table_entry_t uimage_type[] = { + { IH_TYPE_AISIMAGE, "aisimage", "Davinci AIS image",}, + { IH_TYPE_FILESYSTEM, "filesystem", "Filesystem Image", }, + { IH_TYPE_FIRMWARE, "firmware", "Firmware", }, + { IH_TYPE_FLATDT, "flat_dt", "Flat Device Tree", }, + { IH_TYPE_GPIMAGE, "gpimage", "TI Keystone SPL Image",}, + { IH_TYPE_KERNEL, "kernel", "Kernel Image", }, + { IH_TYPE_KERNEL_NOLOAD, "kernel_noload", "Kernel Image (no loading done)", }, + { IH_TYPE_KWBIMAGE, "kwbimage", "Kirkwood Boot Image",}, + { IH_TYPE_IMXIMAGE, "imximage", "Freescale i.MX Boot Image",}, + { IH_TYPE_IMX8IMAGE, "imx8image", "NXP i.MX8 Boot Image",}, + { IH_TYPE_IMX8MIMAGE, "imx8mimage", "NXP i.MX8M Boot Image",}, + { IH_TYPE_INVALID, "invalid", "Invalid Image", }, + { IH_TYPE_MULTI, "multi", "Multi-File Image", }, + { IH_TYPE_OMAPIMAGE, "omapimage", "TI OMAP SPL With GP CH",}, + { IH_TYPE_PBLIMAGE, "pblimage", "Freescale PBL Boot Image",}, + { IH_TYPE_RAMDISK, "ramdisk", "RAMDisk Image", }, + { IH_TYPE_SCRIPT, "script", "Script", }, + { IH_TYPE_SOCFPGAIMAGE, "socfpgaimage", "Altera SoCFPGA CV/AV preloader",}, + { IH_TYPE_SOCFPGAIMAGE_V1, "socfpgaimage_v1", "Altera SoCFPGA A10 preloader",}, + { IH_TYPE_STANDALONE, "standalone", "Standalone Program", }, + { IH_TYPE_UBLIMAGE, "ublimage", "Davinci UBL image",}, + { IH_TYPE_MXSIMAGE, "mxsimage", "Freescale MXS Boot Image",}, + { IH_TYPE_ATMELIMAGE, "atmelimage", "ATMEL ROM-Boot Image",}, + { IH_TYPE_X86_SETUP, "x86_setup", "x86 setup.bin", }, + { IH_TYPE_LPC32XXIMAGE, "lpc32xximage", "LPC32XX Boot Image", }, + { IH_TYPE_RKIMAGE, "rkimage", "Rockchip Boot Image" }, + { IH_TYPE_RKSD, "rksd", "Rockchip SD Boot Image" }, + { IH_TYPE_RKSPI, "rkspi", "Rockchip SPI Boot Image" }, + { IH_TYPE_VYBRIDIMAGE, "vybridimage", "Vybrid Boot Image", }, + { IH_TYPE_ZYNQIMAGE, "zynqimage", "Xilinx Zynq Boot Image" }, + { IH_TYPE_ZYNQMPIMAGE, "zynqmpimage", "Xilinx ZynqMP Boot Image" }, + { IH_TYPE_ZYNQMPBIF, "zynqmpbif", "Xilinx ZynqMP Boot Image (bif)" }, + { IH_TYPE_FPGA, "fpga", "FPGA Image" }, + { IH_TYPE_TEE, "tee", "Trusted Execution Environment Image",}, + { IH_TYPE_FIRMWARE_IVT, "firmware_ivt", "Firmware with HABv4 IVT" }, + { IH_TYPE_PMMC, "pmmc", "TI Power Management Micro-Controller Firmware",}, + { IH_TYPE_STM32IMAGE, "stm32image", "STMicroelectronics STM32 Image" }, + { IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" }, + { IH_TYPE_COPRO, "copro", "Coprocessor Image"}, + { IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" }, + { -1, "", "", }, +}; + +static const table_entry_t uimage_comp[] = { + { IH_COMP_NONE, "none", "uncompressed", }, + { IH_COMP_BZIP2, "bzip2", "bzip2 compressed", }, + { IH_COMP_GZIP, "gzip", "gzip compressed", }, + { IH_COMP_LZMA, "lzma", "lzma compressed", }, + { IH_COMP_LZO, "lzo", "lzo compressed", }, + { IH_COMP_LZ4, "lz4", "lz4 compressed", }, + { IH_COMP_ZSTD, "zstd", "zstd compressed", }, + { -1, "", "", }, +}; + +struct table_info { + const char *desc; + int count; + const table_entry_t *table; +}; + +static const struct comp_magic_map image_comp[] = { + { IH_COMP_BZIP2, "bzip2", {0x42, 0x5a},}, + { IH_COMP_GZIP, "gzip", {0x1f, 0x8b},}, + { IH_COMP_LZMA, "lzma", {0x5d, 0x00},}, + { IH_COMP_LZO, "lzo", {0x89, 0x4c},}, + { IH_COMP_LZ4, "lz4", {0x04, 0x22},}, + { IH_COMP_ZSTD, "zstd", {0x28, 0xb5},}, + { IH_COMP_NONE, "none", {}, }, +}; + +static const struct table_info table_info[IH_COUNT] = { + { "architecture", IH_ARCH_COUNT, uimage_arch }, + { "compression", IH_COMP_COUNT, uimage_comp }, + { "operating system", IH_OS_COUNT, uimage_os }, + { "image type", IH_TYPE_COUNT, uimage_type }, +}; + +/*****************************************************************************/ +/* Legacy format routines */ +/*****************************************************************************/ +int image_check_hcrc(const image_header_t *hdr) +{ + ulong hcrc; + ulong len = image_get_header_size(); + image_header_t header; + + /* Copy header so we can blank CRC field for re-calculation */ + memmove(&header, (char *)hdr, image_get_header_size()); + image_set_hcrc(&header, 0); + + hcrc = crc32(0, (unsigned char *)&header, len); + + return (hcrc == image_get_hcrc(hdr)); +} + +int image_check_dcrc(const image_header_t *hdr) +{ + ulong data = image_get_data(hdr); + ulong len = image_get_data_size(hdr); + ulong dcrc = crc32_wd(0, (unsigned char *)data, len, CHUNKSZ_CRC32); + + return (dcrc == image_get_dcrc(hdr)); +} + +/** + * image_multi_count - get component (sub-image) count + * @hdr: pointer to the header of the multi component image + * + * image_multi_count() returns number of components in a multi + * component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * number of components + */ +ulong image_multi_count(const image_header_t *hdr) +{ + ulong i, count = 0; + uint32_t *size; + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* count non empty slots */ + for (i = 0; size[i]; ++i) + count++; + + return count; +} + +/** + * image_multi_getimg - get component data address and size + * @hdr: pointer to the header of the multi component image + * @idx: index of the requested component + * @data: pointer to a ulong variable, will hold component data address + * @len: pointer to a ulong variable, will hold component size + * + * image_multi_getimg() returns size and data address for the requested + * component in a multi component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * data address and size of the component, if idx is valid + * 0 in data and len, if idx is out of range + */ +void image_multi_getimg(const image_header_t *hdr, ulong idx, + ulong *data, ulong *len) +{ + int i; + uint32_t *size; + ulong offset, count, img_data; + + /* get number of component */ + count = image_multi_count(hdr); + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* get address of the proper component data start, which means + * skipping sizes table (add 1 for last, null entry) */ + img_data = image_get_data(hdr) + (count + 1) * sizeof(uint32_t); + + if (idx < count) { + *len = uimage_to_cpu(size[idx]); + offset = 0; + + /* go over all indices preceding requested component idx */ + for (i = 0; i < idx; i++) { + /* add up i-th component size, rounding up to 4 bytes */ + offset += (uimage_to_cpu(size[i]) + 3) & ~3 ; + } + + /* calculate idx-th component data address */ + *data = img_data + offset; + } else { + *len = 0; + *data = 0; + } +} + +static void image_print_type(const image_header_t *hdr) +{ + const char __maybe_unused *os, *arch, *type, *comp; + + os = genimg_get_os_name(image_get_os(hdr)); + arch = genimg_get_arch_name(image_get_arch(hdr)); + type = genimg_get_type_name(image_get_type(hdr)); + comp = genimg_get_comp_name(image_get_comp(hdr)); + + printf("%s %s %s (%s)\n", arch, os, type, comp); +} + +/** + * image_print_contents - prints out the contents of the legacy format image + * @ptr: pointer to the legacy format image header + * @p: pointer to prefix string + * + * image_print_contents() formats a multi line legacy image contents description. + * The routine prints out all header fields followed by the size/offset data + * for MULTI/SCRIPT images. + * + * returns: + * no returned results + */ +void image_print_contents(const void *ptr) +{ + const image_header_t *hdr = (const image_header_t *)ptr; + const char __maybe_unused *p; + + p = IMAGE_INDENT_STRING; + printf("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name(hdr)); + if (IMAGE_ENABLE_TIMESTAMP) { + printf("%sCreated: ", p); + genimg_print_time((time_t)image_get_time(hdr)); + } + printf("%sImage Type: ", p); + image_print_type(hdr); + printf("%sData Size: ", p); + genimg_print_size(image_get_data_size(hdr)); + printf("%sLoad Address: %08x\n", p, image_get_load(hdr)); + printf("%sEntry Point: %08x\n", p, image_get_ep(hdr)); + + if (image_check_type(hdr, IH_TYPE_MULTI) || + image_check_type(hdr, IH_TYPE_SCRIPT)) { + int i; + ulong data, len; + ulong count = image_multi_count(hdr); + + printf("%sContents:\n", p); + for (i = 0; i < count; i++) { + image_multi_getimg(hdr, i, &data, &len); + + printf("%s Image %d: ", p, i); + genimg_print_size(len); + + if (image_check_type(hdr, IH_TYPE_SCRIPT) && i > 0) { + /* + * the user may need to know offsets + * if planning to do something with + * multiple files + */ + printf("%s Offset = 0x%08lx\n", p, data); + } + } + } else if (image_check_type(hdr, IH_TYPE_FIRMWARE_IVT)) { + printf("HAB Blocks: 0x%08x 0x0000 0x%08x\n", + image_get_load(hdr) - image_get_header_size(), + (int)(image_get_size(hdr) + image_get_header_size() + + sizeof(flash_header_v2_t) - 0x2060)); + } +} + +/** + * print_decomp_msg() - Print a suitable decompression/loading message + * + * @type: OS type (IH_OS_...) + * @comp_type: Compression type being used (IH_COMP_...) + * @is_xip: true if the load address matches the image start + */ +static void print_decomp_msg(int comp_type, int type, bool is_xip) +{ + const char *name = genimg_get_type_name(type); + + if (comp_type == IH_COMP_NONE) + printf(" %s %s\n", is_xip ? "XIP" : "Loading", name); + else + printf(" Uncompressing %s\n", name); +} + +int image_decomp_type(const unsigned char *buf, ulong len) +{ + const struct comp_magic_map *cmagic = image_comp; + + if (len < 2) + return -EINVAL; + + for (; cmagic->comp_id > 0; cmagic++) { + if (!memcmp(buf, cmagic->magic, 2)) + break; + } + + return cmagic->comp_id; +} + +int image_decomp(int comp, ulong load, ulong image_start, int type, + void *load_buf, void *image_buf, ulong image_len, + uint unc_len, ulong *load_end) +{ + int ret = -ENOSYS; + + *load_end = load; + print_decomp_msg(comp, type, load == image_start); + + /* + * Load the image to the right place, decompressing if needed. After + * this, image_len will be set to the number of uncompressed bytes + * loaded, ret will be non-zero on error. + */ + switch (comp) { + case IH_COMP_NONE: + ret = 0; + if (load == image_start) + break; + if (image_len <= unc_len) + memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); + else + ret = -ENOSPC; + break; + case IH_COMP_GZIP: + if (!tools_build() && CONFIG_IS_ENABLED(GZIP)) + ret = gunzip(load_buf, unc_len, image_buf, &image_len); + break; + case IH_COMP_BZIP2: + if (!tools_build() && CONFIG_IS_ENABLED(BZIP2)) { + uint size = unc_len; + + /* + * If we've got less than 4 MB of malloc() space, + * use slower decompression algorithm which requires + * at most 2300 KB of memory. + */ + ret = BZ2_bzBuffToBuffDecompress(load_buf, &size, + image_buf, image_len, CONSERVE_MEMORY, 0); + image_len = size; + } + break; + case IH_COMP_LZMA: + if (!tools_build() && CONFIG_IS_ENABLED(LZMA)) { + SizeT lzma_len = unc_len; + + ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len, + image_buf, image_len); + image_len = lzma_len; + } + break; + case IH_COMP_LZO: + if (!tools_build() && CONFIG_IS_ENABLED(LZO)) { + size_t size = unc_len; + + ret = lzop_decompress(image_buf, image_len, load_buf, &size); + image_len = size; + } + break; + case IH_COMP_LZ4: + if (!tools_build() && CONFIG_IS_ENABLED(LZ4)) { + size_t size = unc_len; + + ret = ulz4fn(image_buf, image_len, load_buf, &size); + image_len = size; + } + break; + case IH_COMP_ZSTD: + if (!tools_build() && CONFIG_IS_ENABLED(ZSTD)) { + struct abuf in, out; + + abuf_init_set(&in, image_buf, image_len); + abuf_init_set(&in, load_buf, unc_len); + ret = zstd_decompress(&in, &out); + if (ret >= 0) { + image_len = ret; + ret = 0; + } + } + break; + } + if (ret == -ENOSYS) { + printf("Unimplemented compression type %d\n", comp); + return ret; + } + if (ret) + return ret; + + *load_end = load + image_len; + + return 0; +} + +const table_entry_t *get_table_entry(const table_entry_t *table, int id) +{ + for (; table->id >= 0; ++table) { + if (table->id == id) + return table; + } + return NULL; +} + +static const char *unknown_msg(enum ih_category category) +{ + static const char unknown_str[] = "Unknown "; + static char msg[30]; + + strcpy(msg, unknown_str); + strncat(msg, table_info[category].desc, + sizeof(msg) - sizeof(unknown_str)); + + return msg; +} + +/** + * genimg_get_cat_name - translate entry id to long name + * @category: category to look up (enum ih_category) + * @id: entry id to be translated + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return long entry name if translation succeeds; error string on failure + */ +const char *genimg_get_cat_name(enum ih_category category, uint id) +{ + const table_entry_t *entry; + + entry = get_table_entry(table_info[category].table, id); + if (!entry) + return unknown_msg(category); + return manual_reloc(entry->lname); +} + +/** + * genimg_get_cat_short_name - translate entry id to short name + * @category: category to look up (enum ih_category) + * @id: entry id to be translated + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return short entry name if translation succeeds; error string on failure + */ +const char *genimg_get_cat_short_name(enum ih_category category, uint id) +{ + const table_entry_t *entry; + + entry = get_table_entry(table_info[category].table, id); + if (!entry) + return unknown_msg(category); + return manual_reloc(entry->sname); +} + +int genimg_get_cat_count(enum ih_category category) +{ + return table_info[category].count; +} + +const char *genimg_get_cat_desc(enum ih_category category) +{ + return table_info[category].desc; +} + +/** + * genimg_cat_has_id - check whether category has entry id + * @category: category to look up (enum ih_category) + * @id: entry id to be checked + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return true if category has entry id; false if not + */ +bool genimg_cat_has_id(enum ih_category category, uint id) +{ + if (get_table_entry(table_info[category].table, id)) + return true; + + return false; +} + +/** + * get_table_entry_name - translate entry id to long name + * @table: pointer to a translation table for entries of a specific type + * @msg: message to be returned when translation fails + * @id: entry id to be translated + * + * get_table_entry_name() will go over translation table trying to find + * entry that matches given id. If matching entry is found, its long + * name is returned to the caller. + * + * returns: + * long entry name if translation succeeds + * msg otherwise + */ +char *get_table_entry_name(const table_entry_t *table, char *msg, int id) +{ + table = get_table_entry(table, id); + if (!table) + return msg; + return manual_reloc(table->lname); +} + +const char *genimg_get_os_name(uint8_t os) +{ + return (get_table_entry_name(uimage_os, "Unknown OS", os)); +} + +const char *genimg_get_arch_name(uint8_t arch) +{ + return (get_table_entry_name(uimage_arch, "Unknown Architecture", + arch)); +} + +const char *genimg_get_type_name(uint8_t type) +{ + return (get_table_entry_name(uimage_type, "Unknown Image", type)); +} + +const char *genimg_get_comp_name(uint8_t comp) +{ + return (get_table_entry_name(uimage_comp, "Unknown Compression", + comp)); +} + +static const char *genimg_get_short_name(const table_entry_t *table, int val) +{ + table = get_table_entry(table, val); + if (!table) + return "unknown"; + return manual_reloc(table->sname); +} + +const char *genimg_get_type_short_name(uint8_t type) +{ + return genimg_get_short_name(uimage_type, type); +} + +const char *genimg_get_comp_short_name(uint8_t comp) +{ + return genimg_get_short_name(uimage_comp, comp); +} + +const char *genimg_get_os_short_name(uint8_t os) +{ + return genimg_get_short_name(uimage_os, os); +} + +const char *genimg_get_arch_short_name(uint8_t arch) +{ + return genimg_get_short_name(uimage_arch, arch); +} + +/** + * get_table_entry_id - translate short entry name to id + * @table: pointer to a translation table for entries of a specific type + * @table_name: to be used in case of error + * @name: entry short name to be translated + * + * get_table_entry_id() will go over translation table trying to find + * entry that matches given short name. If matching entry is found, + * its id returned to the caller. + * + * returns: + * entry id if translation succeeds + * -1 otherwise + */ +int get_table_entry_id(const table_entry_t *table, + const char *table_name, const char *name) +{ + const table_entry_t *t; + + for (t = table; t->id >= 0; ++t) { + if (t->sname && !strcasecmp(manual_reloc(t->sname), name)) + return t->id; + } + debug("Invalid %s Type: %s\n", table_name, name); + + return -1; +} + +int genimg_get_os_id(const char *name) +{ + return (get_table_entry_id(uimage_os, "OS", name)); +} + +int genimg_get_arch_id(const char *name) +{ + return (get_table_entry_id(uimage_arch, "CPU", name)); +} + +int genimg_get_type_id(const char *name) +{ + return (get_table_entry_id(uimage_type, "Image", name)); +} + +int genimg_get_comp_id(const char *name) +{ + return (get_table_entry_id(uimage_comp, "Compression", name)); +} |