diff options
Diffstat (limited to 'common')
107 files changed, 6892 insertions, 3705 deletions
diff --git a/common/.gitignore b/common/.gitignore index 8e0d6c160e..e3e3d504dc 100644 --- a/common/.gitignore +++ b/common/.gitignore @@ -1,2 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + module.lds barebox_default_env* diff --git a/common/Kconfig b/common/Kconfig index f4120b2083..0000dac874 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1,3 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +source "common/boards/Kconfig" + config GREGORIAN_CALENDER bool @@ -23,7 +27,7 @@ config HAS_DMA Drivers that depend on a DMA implementation can depend on this config, so that you don't get a compilation error. -config HAS_ARCH_SJLJ +config ARCH_HAS_SJLJ bool help Architecture has support implemented for setjmp()/longjmp()/initjmp() @@ -37,15 +41,6 @@ config BLOCK config BLOCK_WRITE bool -config USE_COMPRESSED_DTB - bool - depends on ARM || RISCV - select UNCOMPRESS - select LZO_DECOMPRESS - -config ELF - bool "ELF Support" if COMPILE_TEST - config FILETYPE bool @@ -78,20 +73,12 @@ config MENUTREE select GLOB select GLOB_SORT -config EFI_GUID - bool - help - With this option a table of EFI guids is compiled in. - -config EFI_DEVICEPATH - bool - config ARCH_DMA_ADDR_T_64BIT bool config BAREBOX_UPDATE_IMX_NAND_FCB bool - depends on ARCH_IMX6 || ARCH_IMX28 + depends on ARCH_IMX7 || ARCH_IMX6 || ARCH_IMX28 depends on BAREBOX_UPDATE depends on MTD_WRITE depends on NAND_MXS @@ -157,6 +144,9 @@ config MEMINFO bool "display memory info" default y +config MEMTEST + bool + config ENVIRONMENT_VARIABLES bool "environment variables support" @@ -198,14 +188,8 @@ config MMU to enable the data cache which depends on the MMU. See Documentation/mmu.txt for further information. -config MMU_EARLY - bool "Enable MMU early" - depends on ARM - depends on MMU - default y - help - This enables the MMU during early startup. This speeds up things during startup - of barebox, but may lead to harder to debug code. If unsure say yes here. +config MMUINFO + bool config HAVE_CONFIGURABLE_TEXT_BASE bool @@ -214,9 +198,13 @@ config TEXT_BASE depends on HAVE_CONFIGURABLE_TEXT_BASE prompt "TEXT_BASE" hex + range 0 0xffffffff default ARCH_TEXT_BASE help - The Address barebox gets linked at. + The 32-bit address barebox gets linked at. This is forced + to zero for relocatable barebox and fixed up at runtime, + so barebox is executable on arbitrary addresses (given + sufficient alignment). config BAREBOX_MAX_IMAGE_SIZE prompt "Maximum size of barebox" @@ -289,6 +277,11 @@ config MALLOC_SIZE hex default 0x400000 prompt "malloc area size" + +config MALLOC_ALIGNMENT + hex + default 8 + endmenu config BROKEN @@ -360,16 +353,40 @@ config RELOCATABLE allowing it to relocate to the end of the available RAM. This way you have the whole memory in a single piece. -config PANIC_HANG - bool "hang the system in case of a fatal error" - help - This option enables stop of the system in case of a - fatal error, so that you have to reset it manually. - This is probably NOT a good idea for an embedded - system where you want the system to reboot - automatically as fast as possible, but it may be - useful during development since you can try to debug - the conditions that lead to the situation. +choice + prompt "Configure action on fatal error" + default PANIC_RESET + + config PANIC_POWEROFF + bool "power off the system" + help + This option shuts down the system in case of a + fatal error, so that you have to power it on manually. + This is probably NOT a good idea for an embedded + system where you want the system to reboot + automatically as fast as possible, but it may be + useful in emulation, because the system returns + to parent shell immediately. + + config PANIC_HANG + bool "hang the system" + help + This option enables stop of the system in case of a + fatal error, so that you have to reset it manually. + This is probably NOT a good idea for an embedded + system where you want the system to reboot + automatically as fast as possible, but it may be + useful during development since you can try to debug + the conditions that lead to the situation. + + config PANIC_RESET + bool "reset the system" + help + This option enables reset of the system in case of a + fatal error, so you don't have to reset it manually. + This is the recommended configuration in production. + +endchoice config PROMPT string @@ -531,15 +548,6 @@ endchoice endif -config DYNAMIC_CRC_TABLE - bool - depends on CRC32 - prompt "Generate the crc32 table dynamically" - default y - help - Saying yes to this option saves around 800 bytes of binary size. - If unsure say yes. - config ERRNO_MESSAGES bool prompt "print error values as text" @@ -549,12 +557,11 @@ config TIMESTAMP bool default y select GREGORIAN_CALENDER - prompt "print timestamp information from images" + prompt "print timestamp information from uImages" help When CONFIG_TIMESTAMP is selected, the timestamp - (date and time) of an image is printed by image - commands like bootm or iminfo. This option is - automatically enabled when you select CFG_CMD_DATE . + (date and time) of an uImage is printed by image + commands like bootm or uimage. menuconfig BOOTM select UIMAGE @@ -616,6 +623,12 @@ config BOOTM_AIMAGE help Support using Android Images. +config PE + bool "PE/COFF Support" if COMPILE_TEST + +config ELF + bool "ELF Support" if COMPILE_TEST + config BOOTM_ELF bool depends on BOOTM @@ -649,6 +662,22 @@ config BOOTM_FITIMAGE_SIGNATURE Additionally the barebox device tree needs a /signature node with the public key with which the image has been signed. +config BOOTM_FITIMAGE_PUBKEY_ENV + bool "Specify path to public key in environment" + depends on BOOTM_FITIMAGE_SIGNATURE + help + If this option is enabled the path to the device tree snippet + containing the public key for verifying FIT images signature is taken + from make's build-time environment, which can allow for better + integration with some build systems. + + The environment variable has the same name as the corresponding + Kconfig variable: + + CONFIG_BOOTM_FITIMAGE_PUBKEY + +if BOOTM_FITIMAGE_SIGNATURE && !BOOTM_FITIMAGE_PUBKEY_ENV + config BOOTM_FITIMAGE_PUBKEY string "Path to dtsi containing pubkey" default "../fit/pubkey.dtsi" @@ -658,6 +687,12 @@ config BOOTM_FITIMAGE_PUBKEY snippet can then be included in a device tree with "#include CONFIG_BOOTM_FITIMAGE_PUBKEY". + This snippet is usually generated by decompiling a device tree produced + by mkimage. An alternative is CONFIG_CRYPTO_RSA_KEY, which takes a PEM + file or a PKCS#11 URI. + +endif + config BOOTM_FORCE_SIGNED_IMAGES bool prompt "Force booting of signed images" @@ -676,8 +711,8 @@ config BLSPEC bool prompt "Support bootloader spec" help - Enable this to let barebox support the Freedesktop bootloader spec, - see: http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ + Enable this to let barebox support the UAPI bootloader spec, + see: https://uapi-group.org/specifications/specs/boot_loader_specification/ The bootloader spec is a standard interface between the bootloader and the kernel. It allows the bootloader to discover boot options on a device and it allows the Operating System to install / update @@ -707,7 +742,7 @@ config MMCBLKDEV_ROOTARG kernel doesn't contain commit [1]. The first linux kernel release containing that commit is v5.10-rc1. - The appending only happen if barebox 'linux.bootargs.bootm.appendroot' + The appending only happens if barebox' 'linux.bootargs.bootm.appendroot' variable is set or the used blspec entry contains 'linux-appendroot'. Note: It is crucial that the kernel device tree and the barebox device @@ -734,6 +769,12 @@ config IMD select CRC32 bool "barebox metadata support" +config IMD_ENDIANNESS + bool "add endianness record to metadata" + depends on IMD + depends on SYS_SUPPORTS_LITTLE_ENDIAN && SYS_SUPPORTS_BIG_ENDIAN + default y + choice prompt "console support" default CONSOLE_FULL @@ -799,6 +840,16 @@ config CONSOLE_ALLOW_COLOR compile time default for colored console output. After boot it can be controlled using global.allow_color. +config CONSOLE_FLUSH_LINE_BREAK + bool "Flush consoles on new line" if COMPILE_TEST + help + Many serial drivers configure and use hardware FIFOs as not to + delay the boot. When debuging some king of bugs, such as clock + issues that hang the SoC, this can falsify debugging output, + because the UART doesn't output a submitted message fully, before + the SoC hangs. This option will flush serial FIFOs when processing + the new line feed characters. + config CONSOLE_DISABLE_INPUT prompt "Disable input on all consoles by default (non-interactive)" def_bool CONSOLE_NONE @@ -827,6 +878,9 @@ config PARTITION bool prompt "Enable Partitions" +config PARTITION_MANIPULATION + bool + source "common/partitions/Kconfig" config ENV_HANDLING @@ -858,29 +912,30 @@ choice default DEFAULT_COMPRESSION_LZ4 if LZ4_DECOMPRESS default DEFAULT_COMPRESSION_BZIP2 if BZLIB help - Select the default compression for in-barebox binary files. Files - compiled into barebox like for example the default environment will - be compressed with this compression type. + For barebox builds without a prebootloader, select here the default + compression for in-barebox binary files. barebox itself can't be + compressed without a prebootloader, but for example the default + environment will be compressed with this compression type. config DEFAULT_COMPRESSION_GZIP bool "gzip" - depends on ZLIB + depends on !PBL_IMAGE && ZLIB config DEFAULT_COMPRESSION_BZIP2 bool "bzip2" - depends on BZLIB + depends on !PBL_IMAGE && BZLIB config DEFAULT_COMPRESSION_LZO bool "lzo" - depends on LZO_DECOMPRESS + depends on !PBL_IMAGE && LZO_DECOMPRESS config DEFAULT_COMPRESSION_LZ4 bool "lz4" - depends on LZ4_DECOMPRESS + depends on !PBL_IMAGE && LZ4_DECOMPRESS config DEFAULT_COMPRESSION_XZ bool "xz" - depends on XZ_DECOMPRESS + depends on !PBL_IMAGE && XZ_DECOMPRESS config DEFAULT_COMPRESSION_NONE bool "no compression" @@ -990,7 +1045,7 @@ config POLLER config BTHREAD bool "barebox co-operative (green) thread infrastructure" select HAS_SCHED - depends on HAS_ARCH_SJLJ + depends on ARCH_HAS_SJLJ help barebox threads are lightweight cooperative (green) threads that are scheduled within delay loops and the console idle to asynchronously @@ -1002,6 +1057,13 @@ config STATE select ENVIRONMENT_VARIABLES select OFTREE select PARAMETER + imply STATE_DRV + imply CMD_STATE + help + barebox state is a generic framework for atomic power fail-safe + variable storage and retrieval. It can be used to safely maintain + data over reboots and to exchange information with Linux, e.g. + for redundant boot with bootchooser. config STATE_CRYPTO bool "HMAC based authentication support" @@ -1048,7 +1110,7 @@ config RESET_SOURCE config MACHINE_ID bool "compute unique machine-id" depends on FLEXIBLE_BOOTARGS - depends on SHA1 + depends on HAVE_DIGEST_SHA1 help Compute a persistent machine-specific id and store it to $global.machine_id. The id is a hash of device-specific information added via @@ -1100,20 +1162,38 @@ config EXTERNAL_DTS_FRAGMENTS menu "OP-TEE loading" +config HAVE_OPTEE + bool + help + This symbol is selected by configuration where barebox either + starts OP-TEE or runs while OP-TEE is running. Actual + bidirectional communication with OP-TEE is enabled via + CONFIG_OPTEE. + config OPTEE_SIZE hex default 0x02000000 prompt "OP-TEE Memory Size" - depends on BOOTM_OPTEE || PBL_OPTEE + depends on HAVE_OPTEE help Size to reserve in main memory for OP-TEE. Can be smaller than the actual size used by OP-TEE, this is used to prevent barebox from allocating memory in this area. +config OPTEE_SHM_SIZE + hex + default 0x400000 + prompt "OP-TEE Shared Memory Size" + depends on HAVE_OPTEE + help + Size to reserve in main memory for OP-TEE shared memory communication. + Can be used for fixing up the OP-TEE OF node. + config BOOTM_OPTEE bool prompt "support booting OP-TEE" - depends on BOOTM && ARM + depends on BOOTM && ARM && 32BIT + select HAVE_OPTEE help OP-TEE is a trusted execution environment (TEE). With this option enabled barebox supports starting optee_os as part of the bootm command. @@ -1125,6 +1205,7 @@ config PBL_OPTEE bool "Enable OP-TEE early start" depends on ARM depends on !THUMB2_BAREBOX + select HAVE_OPTEE help Allows starting OP-TEE during lowlevel initialization of the PBL. Requires explicit support in the board's lowlevel file. @@ -1213,6 +1294,30 @@ config DEBUG_LL platform *will not work*, so this option should not be enabled for builds that are intended to be portable. +config ARCH_WANT_FRAME_POINTERS + bool + +config FRAME_POINTER + bool "Compile barebox with frame pointers" if COMPILE_TEST + default y if ARCH_WANT_FRAME_POINTERS + help + Selected by platforms that expect frame pointer usage, e.g. + when stack unwinding is enabled. The resulting barebox image + will be slightly larger and slower, but it can give precise + debugging information when print stack traces. + +config DEBUG_IMX_UART + bool + +config DEBUG_ROCKCHIP_UART + bool + +config DEBUG_OMAP_UART + bool + +config DEBUG_BCM283X_UART + bool + choice prompt "Kernel low-level debugging port" depends on DEBUG_LL @@ -1220,6 +1325,7 @@ choice config DEBUG_IMX1_UART bool "i.MX1 Debug UART" depends on ARCH_IMX1 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX1. @@ -1227,6 +1333,7 @@ config DEBUG_IMX1_UART config DEBUG_IMX21_UART bool "i.MX21 Debug UART" depends on ARCH_IMX21 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX21. @@ -1234,6 +1341,7 @@ config DEBUG_IMX21_UART config DEBUG_IMX25_UART bool "i.MX25 Debug UART" depends on ARCH_IMX25 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX25. @@ -1241,6 +1349,7 @@ config DEBUG_IMX25_UART config DEBUG_IMX27_UART bool "i.MX27 Debug UART" depends on ARCH_IMX27 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX27. @@ -1248,6 +1357,7 @@ config DEBUG_IMX27_UART config DEBUG_IMX31_UART bool "i.MX31 Debug UART" depends on ARCH_IMX31 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX31. @@ -1255,6 +1365,7 @@ config DEBUG_IMX31_UART config DEBUG_IMX35_UART bool "i.MX35 Debug UART" depends on ARCH_IMX35 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX35. @@ -1262,6 +1373,7 @@ config DEBUG_IMX35_UART config DEBUG_IMX50_UART bool "i.MX50 Debug UART" depends on ARCH_IMX50 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX50. @@ -1269,6 +1381,7 @@ config DEBUG_IMX50_UART config DEBUG_IMX51_UART bool "i.MX51 Debug UART" depends on ARCH_IMX51 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX51. @@ -1276,6 +1389,7 @@ config DEBUG_IMX51_UART config DEBUG_IMX53_UART bool "i.MX53 Debug UART" depends on ARCH_IMX53 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX53. @@ -1283,6 +1397,7 @@ config DEBUG_IMX53_UART config DEBUG_IMX6Q_UART bool "i.MX6Q Debug UART" depends on ARCH_IMX6 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on i.MX6Q. @@ -1290,6 +1405,7 @@ config DEBUG_IMX6Q_UART config DEBUG_IMX7D_UART bool "i.MX7D Debug UART" depends on ARCH_IMX7 + select DEBUG_IMX_UART help Say Y here if you want barebox low-level debugging support on i.MX7D. @@ -1297,13 +1413,27 @@ config DEBUG_IMX7D_UART config DEBUG_IMX8M_UART bool "i.MX8M Debug UART" depends on ARCH_IMX8M + select DEBUG_IMX_UART help Say Y here if you want barebox low-level debugging support on i.MX8M*. +config DEBUG_IMX9_UART + bool "i.MX9 Debug UART" + depends on ARCH_IMX93 + select DEBUG_IMX_UART + +config DEBUG_VEXPRESS_UART + bool "Vexpress Debug UART" + depends on ARCH_VEXPRESS + help + Say Y here if you want barebox low-level debugging support + on Vexpress. + config DEBUG_VF610_UART bool "VF610 Debug UART" depends on ARCH_VF610 + select DEBUG_IMX_UART help Say Y here if you want kernel low-level debugging support on VF610. @@ -1311,6 +1441,7 @@ config DEBUG_VF610_UART config DEBUG_OMAP3_UART bool "OMAP3 Debug UART" depends on ARCH_OMAP3 + select DEBUG_OMAP_UART help Say Y here if you want kernel low-level debugging support on OMAP3. @@ -1318,6 +1449,7 @@ config DEBUG_OMAP3_UART config DEBUG_OMAP4_UART bool "OMAP4 Debug UART" depends on ARCH_OMAP4 + select DEBUG_OMAP_UART help Say Y here if you want kernel low-level debugging support on OMAP4. @@ -1325,6 +1457,7 @@ config DEBUG_OMAP4_UART config DEBUG_AM33XX_UART bool "AM33XX Debug UART" depends on ARCH_AM33XX + select DEBUG_OMAP_UART help Say Y here if you want kernel low-level debugging support on AM33XX. @@ -1332,6 +1465,7 @@ config DEBUG_AM33XX_UART config DEBUG_ROCKCHIP_RK3188_UART bool "RK3188 Debug UART" depends on ARCH_RK3188 + select DEBUG_ROCKCHIP_UART help Say Y here if you want kernel low-level debugging support on RK3188. @@ -1339,6 +1473,7 @@ config DEBUG_ROCKCHIP_RK3188_UART config DEBUG_ROCKCHIP_RK3288_UART bool "RK3288 Debug UART" depends on ARCH_RK3288 + select DEBUG_ROCKCHIP_UART help Say Y here if you want kernel low-level debugging support on RK3288. @@ -1346,10 +1481,27 @@ config DEBUG_ROCKCHIP_RK3288_UART config DEBUG_ROCKCHIP_RK3568_UART bool "RK3568 Debug UART" depends on ARCH_RK3568 + select DEBUG_ROCKCHIP_UART help Say Y here if you want kernel low-level debugging support on RK3568. +config DEBUG_ROCKCHIP_RK3588_UART + bool "RK3588 Debug UART" + depends on ARCH_RK3588 + select DEBUG_ROCKCHIP_UART + help + Say Y here if you want kernel low-level debugging support + on RK3588. + +config DEBUG_ROCKCHIP_RK3399_UART + bool "RK3399 Debug UART" + depends on ARCH_RK3399 + select DEBUG_ROCKCHIP_UART + help + Say Y here if you want kernel low-level debugging support + on RK3399. + config DEBUG_SOCFPGA_UART0 bool "Use SOCFPGA UART0 for low-level debug" depends on ARCH_SOCFPGA @@ -1364,9 +1516,17 @@ config DEBUG_SOCFPGA_UART1 Say Y here if you want kernel low-level debugging support on SOCFPGA(Arria 10) based platforms. +config DEBUG_STM32MP_UART + bool "Use STM32MP UART4 for low-level debug" + depends on ARCH_STM32 + help + Say Y here if you want kernel low-level debugging support + on STM32MP. + config DEBUG_RPI1_UART bool "RaspberryPi 1 PL011 UART" depends on ARCH_BCM283X + select DEBUG_BCM283X_UART help Say Y here if you want low-level debugging support on RaspberryPi 1 boards. @@ -1381,6 +1541,7 @@ config DEBUG_AT91_UART config DEBUG_RPI2_3_UART bool "RaspberryPi 2/3 PL011 UART" depends on ARCH_BCM283X + select DEBUG_BCM283X_UART help Say Y here if you want low-level debugging support on RaspberryPi 2 and 3 boards. @@ -1388,10 +1549,26 @@ config DEBUG_RPI2_3_UART config DEBUG_RPI3_MINI_UART bool "RaspberryPi 3 mini UART" depends on ARCH_BCM283X + select DEBUG_BCM283X_UART help Say Y here if you want low-level debugging support on RaspberryPi 3 board mini UART. +config DEBUG_RPI4_MINI_UART + bool "RaspberryPi 4 mini UART" + depends on ARCH_BCM283X + select DEBUG_BCM283X_UART + help + Say Y here if you want low-level debugging support on + RaspberryPi 4 board mini UART. + +config DEBUG_ZYNQMP_UART + bool "Zynqmp Debug UART" + depends on ARCH_ZYNQMP + help + Say Y here if you want kernel low-level debugging support + on Zynqmp. + config DEBUG_ERIZO bool "Erizo ns16550 port" depends on SOC_ERIZO @@ -1402,6 +1579,20 @@ config DEBUG_STARFIVE depends on SOC_STARFIVE select DEBUG_LL_NS16550 +config DEBUG_RISCV_VIRT + bool "RISC-V Virt ns16550 port" + depends on SOC_VIRT + select DEBUG_LL_NS16550 + +config DEBUG_RISCVEMU_HTIF + bool "riscvemu HTIF port" + depends on SOC_VIRT + help + When run without graphics support, tinyemu will expose access + to the Virt I/O console as HTIF blocking console device as well. + This is useful for low level debugging before Virt I/O DMA is + initialized. + config DEBUG_SIFIVE bool "SiFive serial0 port" depends on SOC_SIFIVE @@ -1410,6 +1601,19 @@ config DEBUG_LITEX bool "LiteX serial port" depends on SOC_LITEX +config DEBUG_SUN20I + bool "Allwinner Sun20i ns16550 serial0 port" + depends on SOC_ALLWINNER_SUN20I + select DEBUG_LL_NS16550 + +config DEBUG_AM62X_UART + bool "Texas Instruments AM62X debug UART" + depends on ARCH_K3 + +config DEBUG_QEMU_ARM64_VIRT + bool "QEMU ARM64 Virt PL011 console" + depends on ARCH_ARM64_VIRT + endchoice config DEBUG_LL_NS16550 @@ -1429,6 +1633,7 @@ config DEBUG_IMX_UART_PORT DEBUG_IMX6Q_UART || \ DEBUG_IMX7D_UART || \ DEBUG_IMX8M_UART || \ + DEBUG_IMX9_UART || \ DEBUG_VF610_UART default 1 depends on ARCH_IMX @@ -1436,10 +1641,20 @@ config DEBUG_IMX_UART_PORT Choose UART port on which kernel low-level debug messages should be output. +config DEBUG_K3_UART_PORT + int "K3 Debug UART Port Selection" if DEBUG_AM62X_UART + default 0 + depends on ARCH_K3 + help + Choose UART port on which kernel low-level debug messages + should be output. Possible values are: + AM62x: 0 - 6 + config DEBUG_OMAP_UART_PORT int "OMAP Debug UART Port Selection" if DEBUG_OMAP3_UART || \ DEBUG_OMAP4_UART || \ - DEBUG_AM33XX_UART + DEBUG_AM33XX_UART || \ + DEBUG_AM62X_UART default 1 depends on ARCH_OMAP help @@ -1452,7 +1667,9 @@ config DEBUG_OMAP_UART_PORT config DEBUG_ROCKCHIP_UART_PORT int "RK3xxx UART debug port" if DEBUG_ROCKCHIP_RK3188_UART || \ DEBUG_ROCKCHIP_RK3288_UART || \ - DEBUG_ROCKCHIP_RK3568_UART + DEBUG_ROCKCHIP_RK3568_UART || \ + DEBUG_ROCKCHIP_RK3588_UART || \ + DEBUG_ROCKCHIP_RK3399_UART default 2 depends on ARCH_ROCKCHIP help @@ -1498,13 +1715,52 @@ config DEBUG_AT91_UART_BASE config DEBUG_INITCALLS bool "Trace initcalls" + select CONSOLE_FLUSH_LINE_BREAK help If enabled this will print initcall traces. +config DEBUG_PBL + bool "Print PBL debugging information" + depends on PBL_CONSOLE + help + If enabled this will enable all debug prints in the prebootloader. + For this to work, a console needs to be configured in the + board-specific entry point and configured for either DEBUG_LL + or PBL_CONSOLE. + config DEBUG_PROBES - bool "Trace driver probes" + bool "Trace driver probes/removes" + select CONSOLE_FLUSH_LINE_BREAK help - If enabled this will print driver probe traces. + If enabled this will log driver probe and remove traces. If DEBUG_LL is enabled, + probes will be printed even before registering consoles. If it's disabled, they + will be collected in the log and written out once a console is active. + + Removes are written to the log and will be printed as long as consoles exist. + Most consoles do not implement a remove callback to remain operable until + the very end. Consoles using DMA, however, must be removed. + +config DMA_API_DEBUG + bool "Enable debugging of DMA-API usage" + depends on HAS_DMA + help + Enable this option to debug the use of the DMA API by device drivers. + With this option you will be able to detect common bugs in device + drivers like double-freeing of DMA mappings or freeing mappings that + were never allocated. + + This option causes a performance degradation. Use only if you want to + debug device drivers and dma interactions. + + If unsure, say N. + +config DEBUG_LIST + bool "Debug linked list manipulation" + help + Enable this to turn on extended checks in the linked-list + walking routines. + + If unsure, say N. config PBL_BREAK bool "Execute software break on pbl start" @@ -1513,6 +1769,13 @@ config PBL_BREAK If enabled, barebox will be compiled with BKPT instruction on early pbl init. This option should be used only with JTAG debugger! +config PRINTF_FULL + bool "Support all extended printf format specifiers" + help + Adds support for lesser used format specifiers like UUIDs and + hex strings. Code requiring them should select it directly, + so this is mainly for debugging. If unsure, say no. + source "lib/Kconfig.ubsan" source "lib/kasan/Kconfig" @@ -1536,18 +1799,34 @@ config COMPILE_TEST say Y here. If you are a user, say N here to avoid being prompted for inclusion of unrelated drivers. +config WERROR + bool "Compile barebox with warnings as errors" + default COMPILE_TEST + help + A barebox build should not cause any compiler warnings, and this + enables the '-Werror' flags to enforce that rule by default. + + However, if you have a new (or very old) compiler with odd and + unusual warnings, or you have some architecture with problems, + you may need to disable this config option in order to + successfully build barebox. + + If in doubt, say Y. + endmenu config HAS_DEBUG_LL bool -config HAS_ASM_DEBUG_LL - bool - select HAS_DEBUG_LL - config DDR_SPD bool select CRC_ITU_T config HAVE_ARCH_ASAN bool + +config ARCH_USE_SYM_ANNOTATIONS + bool + help + This is selected by architectures that exclusively use the new SYM_ + macros in their assembly code and not the deprecated ENTRY/PROC. diff --git a/common/Makefile b/common/Makefile index 4b45f678c7..96498790b3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,15 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += boards/ obj-y += memory.o obj-y += memory_display.o pbl-$(CONFIG_PBL_CONSOLE) += memory_display.o obj-y += clock.o obj-y += console_common.o -obj-y += deep-probe.o +obj-$(CONFIG_OFDEVICE) += deep-probe.o obj-y += startup.o obj-y += misc.o obj-pbl-y += memsize.o obj-y += resource.o -obj-y += bootsource.o +obj-pbl-y += bootsource.o obj-$(CONFIG_ELF) += elf.o +obj-$(CONFIG_PE) += pe.o obj-y += restart.o obj-y += poweroff.o obj-y += slice.o @@ -24,12 +28,16 @@ obj-$(CONFIG_BLOCK) += block.o obj-$(CONFIG_BLSPEC) += blspec.o obj-$(CONFIG_BOOTM) += bootm.o booti.o obj-$(CONFIG_CMD_LOADS) += s_record.o -obj-$(CONFIG_CMD_MEMTEST) += memtest.o +obj-$(CONFIG_MEMTEST) += memtest.o obj-$(CONFIG_COMMAND_SUPPORT) += command.o obj-$(CONFIG_CONSOLE_FULL) += console.o obj-$(CONFIG_CONSOLE_SIMPLE) += console_simple.o obj-y += console_countdown.o obj-pbl-$(CONFIG_DDR_SPD) += ddr_spd.o +obj-pbl-$(CONFIG_DDR_SPD) += ddr1_dimm_params.o +obj-pbl-$(CONFIG_DDR_SPD) += ddr2_dimm_params.o +obj-pbl-$(CONFIG_DDR_SPD) += ddr3_dimm_params.o +obj-pbl-$(CONFIG_DDR_SPD) += ddr4_dimm_params.o obj-$(CONFIG_ENV_HANDLING) += environment.o envfs-core.o obj-$(CONFIG_DEFAULT_ENVIRONMENT) += envfs-core.o obj-$(CONFIG_ENVIRONMENT_VARIABLES) += env.o @@ -63,9 +71,6 @@ obj-$(CONFIG_BOOTCHOOSER) += bootchooser.o obj-$(CONFIG_UIMAGE) += image.o uimage.o obj-$(CONFIG_FITIMAGE) += image-fit.o obj-$(CONFIG_MENUTREE) += menutree.o -obj-$(CONFIG_EFI_BOOTUP) += efi/ -obj-$(CONFIG_EFI_GUID) += efi-guid.o -obj-$(CONFIG_EFI_DEVICEPATH) += efi-devicepath.o lwl-$(CONFIG_IMD) += imd-barebox.o obj-$(CONFIG_IMD) += imd.o obj-y += file-list.o @@ -75,8 +80,7 @@ obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o obj-$(CONFIG_BOOT) += boot.o obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o obj-$(CONFIG_USBGADGET_START) += usbgadget.o -pbl-$(CONFIG_PBL_OPTEE) += optee.o -obj-$(CONFIG_BOOTM_OPTEE) += optee.o +obj-pbl-$(CONFIG_HAVE_OPTEE) += optee.o obj-$(CONFIG_FASTBOOT_BASE) += fastboot.o ifdef CONFIG_PASSWORD diff --git a/common/bbu.c b/common/bbu.c index cd7bdc40b7..ba2566acdf 100644 --- a/common/bbu.c +++ b/common/bbu.c @@ -19,47 +19,47 @@ #include <malloc.h> #include <linux/stat.h> #include <image-metadata.h> +#include <environment.h> #include <file-list.h> static LIST_HEAD(bbu_image_handlers); -static void append_bbu_entry(struct bbu_handler *handler, struct file_list *files) +static void append_bbu_entry(const char *_name, + const char *devicefile, + struct file_list *files) { char *name; - name = basprintf("bbu-%s", handler->name); + name = basprintf("bbu-%s", _name); - if (file_list_add_entry(files, name, handler->devicefile, 0)) + if (file_list_add_entry(files, name, devicefile, 0)) pr_warn("duplicate partition name %s\n", name); free(name); } -static bool bbu_handler_is_available(struct bbu_handler *handler) -{ - struct stat s; - int ret; - - device_detect_by_name(devpath_to_name(handler->devicefile)); - - ret = stat(handler->devicefile, &s); - if (ret) - return false; - - return true; -} - void bbu_append_handlers_to_file_list(struct file_list *files) { struct bbu_handler *handler; list_for_each_entry(handler, &bbu_image_handlers, list) { - if (bbu_handler_is_available(handler)) { - append_bbu_entry(handler, files); + const char *cdevname; + struct stat s; + char *devpath; + + cdevname = devpath_to_name(handler->devicefile); + device_detect_by_name(cdevname); + + devpath = basprintf("/dev/%s", cdevname); + + if (stat(devpath, &s) == 0) { + append_bbu_entry(handler->name, devpath, files); } else { pr_info("Skipping unavailable handler bbu-%s\n", handler->name); } + + free(devpath); } } @@ -95,17 +95,23 @@ out: int bbu_confirm(struct bbu_data *data) { int key; + const char *prompt; if (data->flags & BBU_FLAG_YES) - return 0; + prompt = "."; + else + prompt = " (y/n)?"; if (data->imagefile) - printf("update barebox from %s using handler %s to %s (y/n)?\n", - data->imagefile, data->handler_name, - data->devicefile); + printf("update barebox on %s from %s using handler %s%s\n", + data->devicefile, data->imagefile, + data->handler_name, prompt); else - printf("Refresh barebox on %s using handler %s (y/n)?\n", - data->devicefile, data->handler_name); + printf("Refresh barebox on %s using handler %s%s\n", + data->devicefile, data->handler_name, prompt); + + if (data->flags & BBU_FLAG_YES) + return 0; key = read_key(); @@ -154,36 +160,47 @@ struct bbu_handler *bbu_find_handler_by_device(const char *devicepath) return NULL; } -static int bbu_check_of_compat(struct bbu_data *data) +static int bbu_check_of_compat(struct bbu_data *data, unsigned short of_compat_nr) { + const struct imd_header *imd = data->imd_data; + const struct imd_header *of_compat; struct device_node *root_node; const char *machine, *str; int ret; - const struct imd_header *of_compat; if (!IS_ENABLED(CONFIG_OFDEVICE) || !IS_ENABLED(CONFIG_IMD)) return 0; - of_compat = imd_find_type(data->imd_data, IMD_TYPE_OF_COMPATIBLE); - if (!of_compat) - return 0; - root_node = of_get_root_node(); if (!root_node) return 0; - str = imd_string_data(of_compat, 0); - - if (of_machine_is_compatible(str)) { - pr_info("Devicetree compatible \"%s\" matches current machine\n", str); + if (!of_compat_nr) return 0; - } ret = of_property_read_string(root_node, "compatible", &machine); if (ret) return 0; - if (!bbu_force(data, "machine is incompatible with \"%s\", have \"%s\"\n", str, machine)) + for (; of_compat_nr; of_compat_nr--) { + of_compat = imd_find_type(imd, IMD_TYPE_OF_COMPATIBLE); + if (!of_compat) + return 0; + + str = imd_string_data(of_compat, 0); + + if (of_machine_is_compatible(str)) { + pr_info("Devicetree compatible \"%s\" matches current machine\n", str); + return 0; + } + + pr_debug("machine is incompatible with \"%s\", have \"%s\"\n", + str, machine); + + imd = of_compat; + } + + if (!bbu_force(data, "incompatible machine \"%s\"\n", machine)) return -EINVAL; return 0; @@ -191,6 +208,7 @@ static int bbu_check_of_compat(struct bbu_data *data) static int bbu_check_metadata(struct bbu_data *data) { + unsigned short imd_of_compat_nr = 0; const struct imd_header *imd; int ret; char *str; @@ -211,6 +229,9 @@ static int bbu_check_metadata(struct bbu_data *data) imd_for_each(data->imd_data, imd) { uint32_t type = imd_read_type(imd); + if (imd_read_type(imd) == IMD_TYPE_OF_COMPATIBLE) + imd_of_compat_nr++; + if (!imd_is_string(type)) continue; @@ -220,7 +241,7 @@ static int bbu_check_metadata(struct bbu_data *data) free(str); } - ret = bbu_check_of_compat(data); + ret = bbu_check_of_compat(data, imd_of_compat_nr); if (ret) return ret; @@ -304,23 +325,107 @@ struct bbu_std { enum filetype filetype; }; -static int bbu_std_file_handler(struct bbu_handler *handler, - struct bbu_data *data) +int bbu_mmcboot_handler(struct bbu_handler *handler, struct bbu_data *data, + int (*chained_handler)(struct bbu_handler *, struct bbu_data *)) +{ + struct bbu_data _data = *data; + int ret; + char *devicefile = NULL, *bootpartvar = NULL, *bootackvar = NULL; + const char *bootpart; + const char *devname = devpath_to_name(data->devicefile); + + ret = device_detect_by_name(devname); + if (ret) { + pr_err("Couldn't detect device '%s'\n", devname); + return ret; + } + + ret = asprintf(&bootpartvar, "%s.boot", devname); + if (ret < 0) + return ret; + + bootpart = getenv(bootpartvar); + if (!bootpart) { + ret = -ENOENT; + goto out; + } + + if (!strcmp(bootpart, "boot0")) { + bootpart = "boot1"; + } else { + bootpart = "boot0"; + } + + ret = asprintf(&devicefile, "/dev/%s.%s", devname, bootpart); + if (ret < 0) + goto out; + + _data.devicefile = devicefile; + + ret = chained_handler(handler, &_data); + if (ret < 0) + goto out; + + if (handler->flags & BBU_HANDLER_FLAG_MMC_BOOT_ACK) { + ret = asprintf(&bootackvar, "%s.boot_ack", devname); + if (ret < 0) + goto out; + + ret = setenv(bootackvar, "1"); + if (ret) + goto out; + } + + /* on success switch boot source */ + ret = setenv(bootpartvar, bootpart); + +out: + free(bootackvar); + free(devicefile); + free(bootpartvar); + + return ret; +} + +static int bbu_internal_mmcboot_update(struct bbu_handler *handler, + struct bbu_data *data) +{ + int ret; + + ret = bbu_mmcboot_handler(handler, data, bbu_std_file_handler); + if (ret == -ENOENT) + pr_err("Couldn't read the value of .boot parameter\n"); + + return ret; +} + +int bbu_mmcboot_register_handler(const char *name, + const char *devicefile, + unsigned long flags) +{ + struct bbu_handler *handler; + int ret; + + handler = xzalloc(sizeof(*handler)); + handler->devicefile = devicefile; + handler->name = name; + handler->handler = bbu_internal_mmcboot_update; + handler->flags = flags; + + ret = bbu_register_handler(handler); + if (ret) + free(handler); + + return ret; +} + +int bbu_std_file_handler(struct bbu_handler *handler, + struct bbu_data *data) { - struct bbu_std *std = container_of(handler, struct bbu_std, handler); int fd, ret; - enum filetype filetype; struct stat s; unsigned oflags = O_WRONLY; - filetype = file_detect_type(data->image, data->len); - if (filetype != std->filetype) { - if (!bbu_force(data, "incorrect image type. Expected: %s, got %s", - file_type_to_string(std->filetype), - file_type_to_string(filetype))) - return -EINVAL; - } - device_detect_by_name(devpath_to_name(data->devicefile)); ret = stat(data->devicefile, &s); @@ -369,6 +474,23 @@ err_close: return ret; } +static int bbu_std_file_handler_checked(struct bbu_handler *handler, + struct bbu_data *data) +{ + struct bbu_std *std = container_of(handler, struct bbu_std, handler); + enum filetype filetype; + + filetype = file_detect_type(data->image, data->len); + if (filetype != std->filetype) { + if (!bbu_force(data, "incorrect image type. Expected: %s, got %s", + file_type_to_string(std->filetype), + file_type_to_string(filetype))) + return -EINVAL; + } + + return bbu_std_file_handler(handler, data); +} + /** * bbu_register_std_file_update() - register a barebox update handler for a * standard file-to-device-copy operation @@ -399,7 +521,7 @@ int bbu_register_std_file_update(const char *name, unsigned long flags, handler->flags = flags; handler->devicefile = devicefile; handler->name = name; - handler->handler = bbu_std_file_handler; + handler->handler = bbu_std_file_handler_checked; ret = bbu_register_handler(handler); if (ret) diff --git a/common/binfmt.c b/common/binfmt.c index 1846477206..6a1e9fc83e 100644 --- a/common/binfmt.c +++ b/common/binfmt.c @@ -15,9 +15,13 @@ static LIST_HEAD(binfmt_hooks); static int binfmt_run(char *file, int argc, char **argv) { struct binfmt_hook *b; - enum filetype type = file_name_detect_type(file); + enum filetype type; int ret; + ret = file_name_detect_type(file, &type); + if (ret) + return ret; + list_for_each_entry(b, &binfmt_hooks, list) { if (b->type != type) continue; diff --git a/common/block.c b/common/block.c index 1d386edcfd..c7ca4fb403 100644 --- a/common/block.c +++ b/common/block.c @@ -6,12 +6,12 @@ */ #include <common.h> #include <block.h> +#include <disks.h> #include <malloc.h> #include <linux/err.h> #include <linux/list.h> #include <dma.h> - -#define BLOCKSIZE(blk) (1 << blk->blockbits) +#include <file-list.h> LIST_HEAD(block_device_list); @@ -282,6 +282,17 @@ static ssize_t block_op_write(struct cdev *cdev, const void *buf, size_t count, blkcnt_t blocks; int ret; + /* + * When the offset that is written to is within the first two + * LBAs then the partition table has changed, reparse the partition + * table at close time in this case. A GPT covers more space than + * only the first two LBAs, but a CRC of the remaining pieces is + * written to LBA1, so LBA1 must change as well when the partioning + * is changed. + */ + if (offset < 2 * SECTOR_SIZE) + blk->need_reparse = true; + if (offset & mask) { size_t now = BLOCKSIZE(blk) - (offset & mask); void *iobuf = block_get(blk, block); @@ -339,7 +350,19 @@ static int block_op_flush(struct cdev *cdev) return writebuffer_flush(blk); } -static int block_op_close(struct cdev *cdev) __alias(block_op_flush); +static int block_op_close(struct cdev *cdev) +{ + struct block_device *blk = cdev->priv; + + block_op_flush(cdev); + + if (blk->need_reparse) { + reparse_partition_table(blk); + blk->need_reparse = false; + } + + return 0; +} static int block_op_discard_range(struct cdev *cdev, loff_t count, loff_t offset) { @@ -361,6 +384,14 @@ static struct cdev_operations block_ops = { .discard_range = block_op_discard_range, }; +struct block_device *cdev_get_block_device(const struct cdev *cdev) +{ + if (!cdev || cdev->ops != &block_ops) + return NULL; + + return cdev->priv; +} + int blockdevice_register(struct block_device *blk) { loff_t size = (loff_t)blk->num_blocks * BLOCKSIZE(blk); @@ -380,6 +411,11 @@ int blockdevice_register(struct block_device *blk) dev_dbg(blk->dev, "rdbufsize: %d blockbits: %d blkmask: 0x%08x\n", blk->rdbufsize, blk->blockbits, blk->blkmask); + if (!blk->rdbufsize) { + pr_warn("block size of %u not supported\n", BLOCKSIZE(blk)); + return -ENOSYS; + } + for (i = 0; i < 8; i++) { struct chunk *chunk = xzalloc(sizeof(*chunk)); chunk->data = dma_alloc(BUFSIZE); @@ -395,6 +431,9 @@ int blockdevice_register(struct block_device *blk) cdev_create_default_automount(&blk->cdev); + /* Lack of partition table is unusual, but not a failure */ + (void)parse_partition_table(blk); + return 0; } @@ -441,3 +480,42 @@ int block_write(struct block_device *blk, void *buf, sector_t block, blkcnt_t nu return ret < 0 ? ret : 0; } + +unsigned file_list_add_blockdevs(struct file_list *files) +{ + struct block_device *blk; + unsigned count = 0; + int err; + + list_for_each_entry(blk, &block_device_list, list) { + err = file_list_add_cdev_entry(files, &blk->cdev, 0); + if (!err) + count++; + } + + return count; +} + +const char *blk_type_str(enum blk_type type) +{ + switch (type) { + case BLK_TYPE_UNSPEC: + return "unspecified"; + case BLK_TYPE_SD: + return "SD"; + case BLK_TYPE_MMC: + return "MMC"; + case BLK_TYPE_VIRTUAL: + return "virtual"; + case BLK_TYPE_IDE: + return "IDE"; + case BLK_TYPE_AHCI: + return "AHCI"; + case BLK_TYPE_USB: + return "USB"; + case BLK_TYPE_NVME: + return "NVMe"; + default: + return "unknown"; + } +} diff --git a/common/blspec.c b/common/blspec.c index 158fd1e9a2..23a24c63db 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -15,6 +15,7 @@ #include <libbb.h> #include <init.h> #include <bootm.h> +#include <glob.h> #include <net.h> #include <fs.h> #include <of.h> @@ -87,7 +88,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun) bootm_data_init_defaults(&data); - data.verbose = verbose || data.verbose; + data.verbose = max(verbose, data.verbose); devicetree = blspec_entry_var_get(entry, "devicetree"); initrd = blspec_entry_var_get(entry, "initrd"); @@ -315,7 +316,7 @@ static int blspec_have_entry(struct bootentries *bootentries, const char *path) */ static const char *nfs_find_mountpath(const char *nfshostpath) { - struct fs_device_d *fsdev; + struct fs_device *fsdev; for_each_fs_device(fsdev) { if (fsdev->backingstore && !strcmp(fsdev->backingstore, nfshostpath)) @@ -340,10 +341,10 @@ static char *parse_nfs_url(const char *url) int ret; if (!IS_ENABLED(CONFIG_FS_NFS)) - return ERR_PTR(-ENOSYS); + return NULL; if (strncmp(url, "nfs://", 6)) - return ERR_PTR(-EINVAL); + return NULL; url += 6; @@ -412,7 +413,7 @@ out: if (ret) free(mountpath); - return ret ? ERR_PTR(ret) : mountpath; + return ret ? NULL : mountpath; } /* @@ -425,10 +426,10 @@ static bool entry_is_of_compatible(struct blspec_entry *entry) { const char *devicetree; const char *abspath; - size_t size; - void *fdt = NULL; int ret; - struct device_node *root = NULL, *barebox_root; + struct device_node *barebox_root; + size_t size; + void *fdt; const char *compat; char *filename; @@ -458,33 +459,22 @@ static bool entry_is_of_compatible(struct blspec_entry *entry) fdt = read_file(filename, &size); if (!fdt) { - pr_err("Cannot read: %s\n", filename); - ret = false; - goto out; - } - - root = of_unflatten_dtb(fdt, size); - if (IS_ERR(root)) { ret = false; - root = NULL; goto out; } - if (of_device_is_compatible(root, compat)) { + if (fdt_machine_is_compatible(fdt, size, compat)) { ret = true; goto out; } - pr_info("ignoring entry with incompatible devicetree \"%s\"\n", - (char *)of_get_property(root, "compatible", NULL)); + pr_info("ignoring entry with incompatible devicetree: %s\n", devicetree); ret = false; out: - if (root) - of_delete_node(root); - free(filename); free(fdt); + free(filename); return ret; } @@ -513,6 +503,55 @@ static bool entry_is_match_machine_id(struct blspec_entry *entry) return ret; } +int blspec_scan_file(struct bootentries *bootentries, const char *root, + const char *configname) +{ + char *devname = NULL, *hwdevname = NULL; + struct blspec_entry *entry; + + if (blspec_have_entry(bootentries, configname)) + return -EEXIST; + + entry = blspec_entry_open(bootentries, configname); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + root = root ?: get_mounted_path(configname); + entry->rootpath = xstrdup(root); + entry->configpath = xstrdup(configname); + entry->cdev = get_cdev_by_mountpath(root); + + if (!entry_is_of_compatible(entry)) { + blspec_entry_free(&entry->entry); + return -ENODEV; + } + + if (!entry_is_match_machine_id(entry)) { + blspec_entry_free(&entry->entry); + return -ENODEV; + } + + if (entry->cdev && entry->cdev->dev) { + devname = xstrdup(dev_name(entry->cdev->dev)); + if (entry->cdev->dev->parent) + hwdevname = xstrdup(dev_name(entry->cdev->dev->parent)); + } + + entry->entry.title = xasprintf("%s (%s)", blspec_entry_var_get(entry, "title"), + configname); + entry->entry.description = basprintf("blspec entry, device: %s hwdevice: %s", + devname ? devname : "none", + hwdevname ? hwdevname : "none"); + free(devname); + free(hwdevname); + + entry->entry.me.type = MENU_ENTRY_NORMAL; + entry->entry.release = blspec_entry_free; + + bootentries_add_entry(bootentries, &entry->entry); + return 1; +} + /* * blspec_scan_directory - scan over a directory * @@ -522,115 +561,40 @@ static bool entry_is_match_machine_id(struct blspec_entry *entry) */ int blspec_scan_directory(struct bootentries *bootentries, const char *root) { - struct blspec_entry *entry; - DIR *dir; - struct dirent *d; + glob_t globb; char *abspath; int ret, found = 0; const char *dirname = "loader/entries"; - char *nfspath = NULL; - - nfspath = parse_nfs_url(root); - if (!IS_ERR(nfspath)) - root = nfspath; + int i; pr_debug("%s: %s %s\n", __func__, root, dirname); - abspath = basprintf("%s/%s", root, dirname); + abspath = basprintf("%s/%s/*.conf", root, dirname); - dir = opendir(abspath); - if (!dir) { + ret = glob(abspath, 0, NULL, &globb); + if (ret) { pr_debug("%s: %s: %s\n", __func__, abspath, strerror(errno)); ret = -errno; goto err_out; } - while ((d = readdir(dir))) { - char *configname; + for (i = 0; i < globb.gl_pathc; i++) { + const char *configname = globb.gl_pathv[i]; struct stat s; - char *dot; - char *devname = NULL, *hwdevname = NULL; - - if (*d->d_name == '.') - continue; - - configname = basprintf("%s/%s", abspath, d->d_name); - - dot = strrchr(configname, '.'); - if (!dot) { - free(configname); - continue; - } - - if (strcmp(dot, ".conf")) { - free(configname); - continue; - } ret = stat(configname, &s); - if (ret) { - free(configname); - continue; - } - - if (!S_ISREG(s.st_mode)) { - free(configname); + if (ret || !S_ISREG(s.st_mode)) continue; - } - - if (blspec_have_entry(bootentries, configname)) { - free(configname); - continue; - } - entry = blspec_entry_open(bootentries, configname); - if (IS_ERR(entry)) { - free(configname); - continue; - } - - entry->rootpath = xstrdup(root); - entry->configpath = configname; - entry->cdev = get_cdev_by_mountpath(root); - - if (!entry_is_of_compatible(entry)) { - blspec_entry_free(&entry->entry); - continue; - } - - if (!entry_is_match_machine_id(entry)) { - blspec_entry_free(&entry->entry); - continue; - } - - found++; - - if (entry->cdev && entry->cdev->dev) { - devname = xstrdup(dev_name(entry->cdev->dev)); - if (entry->cdev->dev->parent) - hwdevname = xstrdup(dev_name(entry->cdev->dev->parent)); - } - - entry->entry.title = xasprintf("%s (%s)", blspec_entry_var_get(entry, "title"), - configname); - entry->entry.description = basprintf("blspec entry, device: %s hwdevice: %s", - devname ? devname : "none", - hwdevname ? hwdevname : "none"); - free(devname); - free(hwdevname); - - entry->entry.me.type = MENU_ENTRY_NORMAL; - entry->entry.release = blspec_entry_free; - - bootentries_add_entry(bootentries, &entry->entry); + ret = blspec_scan_file(bootentries, root, configname); + if (ret > 0) + found += ret; } ret = found; - closedir(dir); + globfree(&globb); err_out: - if (!IS_ERR(nfspath)) - free(nfspath); free(abspath); return ret; @@ -647,7 +611,7 @@ err_out: */ static int blspec_scan_ubi(struct bootentries *bootentries, struct cdev *cdev) { - struct device_d *child; + struct device *child; int ret, found = 0; pr_debug("%s: %s\n", __func__, cdev->name); @@ -702,9 +666,7 @@ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev) found += ret; } - rootpath = cdev_get_mount_path(cdev); - if (!rootpath) - rootpath = cdev_mount_default(cdev, NULL); + rootpath = cdev_mount(cdev); if (!IS_ERR(rootpath)) { ret = blspec_scan_directory(bootentries, rootpath); if (ret > 0) @@ -723,7 +685,7 @@ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev) */ int blspec_scan_devices(struct bootentries *bootentries) { - struct device_d *dev; + struct device *dev; struct block_device *bdev; int ret, found = 0; @@ -751,9 +713,9 @@ int blspec_scan_devices(struct bootentries *bootentries) * Returns the number of entries found or a negative error code if some unexpected * error occurred. */ -int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) +int blspec_scan_device(struct bootentries *bootentries, struct device *dev) { - struct device_d *child; + struct device *child; struct cdev *cdev; int ret, found = 0; @@ -767,7 +729,7 @@ int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) * partition with the MBR type id of 0xEA already exists it * should be used as $BOOT */ - if (cdev->dos_partition_type == 0xea) { + if (cdev_is_mbr_partitioned(cdev->master) && cdev->dos_partition_type == 0xea) { ret = blspec_scan_cdev(bootentries, cdev); if (ret == 0) ret = -ENOENT; @@ -815,11 +777,14 @@ int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) */ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname) { - struct device_d *dev; + struct device *dev; struct cdev *cdev; pr_debug("%s: %s\n", __func__, devname); + /* Support both boot /dev/disk0.rootfs and boot disk0.rootfs */ + devname += str_has_prefix(devname, "/dev/"); + device_detect_by_name(devname); cdev = cdev_by_name(devname); @@ -839,6 +804,7 @@ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname) static int blspec_bootentry_provider(struct bootentries *bootentries, const char *name) { + struct stat s; int ret, found = 0; ret = blspec_scan_devicename(bootentries, name); @@ -846,9 +812,24 @@ static int blspec_bootentry_provider(struct bootentries *bootentries, found += ret; if (*name == '/' || !strncmp(name, "nfs://", 6)) { - ret = blspec_scan_directory(bootentries, name); + char *nfspath = parse_nfs_url(name); + + if (nfspath) + name = nfspath; + + ret = stat(name, &s); + if (ret) + goto out; + + if (S_ISDIR(s.st_mode)) + ret = blspec_scan_directory(bootentries, name); + else if (S_ISREG(s.st_mode) && strends(name, ".conf")) + ret = blspec_scan_file(bootentries, NULL, name); if (ret > 0) found += ret; + +out: + free(nfspath); } return found; diff --git a/common/boards/Kconfig b/common/boards/Kconfig new file mode 100644 index 0000000000..f6d4a56f88 --- /dev/null +++ b/common/boards/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config BOARD_QEMU_VIRT + bool + select OF_OVERLAY + +config BOARD_PHYTEC_SOM_DETECTION + bool + +config BOARD_PHYTEC_SOM_IMX8M_DETECTION + bool + select BOARD_PHYTEC_SOM_DETECTION + +config BOARD_TQ + select CRC_ITU_T + bool diff --git a/common/boards/Makefile b/common/boards/Makefile new file mode 100644 index 0000000000..147c36643d --- /dev/null +++ b/common/boards/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_BOARD_QEMU_VIRT) += qemu-virt/ +obj-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec/ +obj-$(CONFIG_BOARD_TQ) += tq/ diff --git a/common/boards/phytec/Makefile b/common/boards/phytec/Makefile new file mode 100644 index 0000000000..fef6134a16 --- /dev/null +++ b/common/boards/phytec/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +lwl- += dummy.o +lwl-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec-som-detection.o +lwl-$(CONFIG_BOARD_PHYTEC_SOM_IMX8M_DETECTION) += phytec-som-imx8m-detection.o diff --git a/common/boards/phytec/phytec-som-detection.c b/common/boards/phytec/phytec-som-detection.c new file mode 100644 index 0000000000..e338639d03 --- /dev/null +++ b/common/boards/phytec/phytec-som-detection.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 PHYTEC Messtechnik GmbH + * Author: Teresa Remmet <t.remmet@phytec.de> + */ + +#include <boards/phytec/phytec-som-imx8m-detection.h> +#include <common.h> +#include <pbl/eeprom.h> + +struct phytec_eeprom_data eeprom_data; + +#define POLY (0x1070U << 3) + +static u8 _crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + + return data >> 8; +} + +static unsigned int crc8(unsigned int crc, const u8 *vptr, int len) +{ + int i; + + for (i = 0; i < len; i++) + crc = _crc8((crc ^ vptr[i]) << 8); + + return crc; +} + +const char *phytec_get_opt(const struct phytec_eeprom_data *data) +{ + const char *opt; + + if (!data) + data = &eeprom_data; + + switch (data->api_rev) { + case PHYTEC_API_REV0: + case PHYTEC_API_REV1: + opt = data->data.data_api0.opt; + break; + case PHYTEC_API_REV2: + opt = data->data.data_api2.opt; + break; + default: + opt = NULL; + break; + }; + + return opt; +} + +static int phytec_eeprom_data_init(struct pbl_i2c *i2c, + struct phytec_eeprom_data *data, + int addr, u8 phytec_som_type) +{ + unsigned int crc; + const char *opt; + int *ptr; + int ret = -1, i; + u8 som; + + if (!data) + data = &eeprom_data; + + eeprom_read(i2c, addr, I2C_ADDR_16_BIT, data, sizeof(struct phytec_eeprom_data)); + + if (data->api_rev == 0xff) { + pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__); + return -EINVAL; + } + + for (i = 0, ptr = (int *)data; + i < sizeof(struct phytec_eeprom_data); + i += sizeof(ptr), ptr++) + if (*ptr != 0x0) + break; + + if (i == sizeof(struct phytec_eeprom_data)) { + pr_err("%s: EEPROM data is all zero. Erased?\n", __func__); + return -EINVAL; + } + + if (data->api_rev > PHYTEC_API_REV2) { + pr_err("%s: EEPROM API revision %u not supported\n", + __func__, data->api_rev); + return -EINVAL; + } + + /* We are done here for early revisions */ + if (data->api_rev <= PHYTEC_API_REV1) + return 0; + + crc = crc8(0, (const unsigned char *)data, + sizeof(struct phytec_eeprom_data)); + pr_debug("%s: crc: %x\n", __func__, crc); + + if (crc) { + pr_err("%s: CRC mismatch. EEPROM data is not usable\n", __func__); + return -EINVAL; + } + + som = data->data.data_api2.som_no; + pr_debug("%s: som id: %u\n", __func__, som); + opt = phytec_get_opt(data); + if (!opt) + return -EINVAL; + + if (IS_ENABLED(CONFIG_BOARD_PHYTEC_SOM_IMX8M_DETECTION)) + ret = phytec_imx8m_detect(som, opt, phytec_som_type); + + if (ret) { + pr_err("%s: SoM ID does not match. Wrong EEPROM data?\n", __func__); + return -EINVAL; + } + + return 0; +} + +void phytec_print_som_info(const struct phytec_eeprom_data *data) +{ + const struct phytec_api2_data *api2; + char pcb_sub_rev; + unsigned int ksp_no, sub_som_type1 = -1, sub_som_type2 = -1; + + if (!data) + data = &eeprom_data; + + if (data->api_rev < PHYTEC_API_REV2) + return; + + api2 = &data->data.data_api2; + + /* Calculate PCB subrevision */ + pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f; + pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' '; + + /* print standard product string */ + if (api2->som_type <= 1) { + pr_info("SoM: %s-%03u-%s.%s PCB rev: %u%c\n", + phytec_som_type_str[api2->som_type], api2->som_no, + api2->opt, api2->bom_rev, api2->pcb_rev, pcb_sub_rev); + return; + } + /* print KSP/KSM string */ + if (api2->som_type <= 3) { + ksp_no = (api2->ksp_no << 8) | api2->som_no; + pr_info("SoM: %s-%u ", + phytec_som_type_str[api2->som_type], ksp_no); + /* print standard product based KSP/KSM strings */ + } else { + switch (api2->som_type) { + case 4: + sub_som_type1 = 0; + sub_som_type2 = 3; + break; + case 5: + sub_som_type1 = 0; + sub_som_type2 = 2; + break; + case 6: + sub_som_type1 = 1; + sub_som_type2 = 3; + break; + case 7: + sub_som_type1 = 1; + sub_som_type2 = 2; + break; + default: + break; + }; + + pr_info("SoM: %s-%03u-%s-%03u ", + phytec_som_type_str[sub_som_type1], + api2->som_no, phytec_som_type_str[sub_som_type2], + api2->ksp_no); + } + + printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt, + api2->bom_rev, api2->pcb_rev, pcb_sub_rev); +} + +int phytec_eeprom_data_setup(struct pbl_i2c *i2c, struct phytec_eeprom_data *data, + int addr, int addr_fallback, u8 cpu_type) +{ + int ret; + + ret = phytec_eeprom_data_init(i2c, data, addr, cpu_type); + if (ret) { + pr_err("%s: init failed. Trying fall back address 0x%x\n", + __func__, addr_fallback); + ret = phytec_eeprom_data_init(i2c, data, addr_fallback, cpu_type); + } + + if (ret) + pr_err("%s: EEPROM data init failed\n", __func__); + else + pr_debug("%s: init successful\n", __func__); + + return ret; +} diff --git a/common/boards/phytec/phytec-som-imx8m-detection.c b/common/boards/phytec/phytec-som-imx8m-detection.c new file mode 100644 index 0000000000..495896f5b2 --- /dev/null +++ b/common/boards/phytec/phytec-som-imx8m-detection.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 PHYTEC Messtechnik GmbH + * Author: Teresa Remmet <t.remmet@phytec.de> + */ + +#include <boards/phytec/phytec-som-imx8m-detection.h> +#include <common.h> +#include <mach/imx/generic.h> + +extern struct phytec_eeprom_data eeprom_data; + +/* Check if the SoM is actually one of the following products: + * - i.MX8MM + * - i.MX8MN + * - i.MX8MP + * - i.MX8MQ + * + * Returns 0 in case it's a known SoM. Otherwise, returns -errno. + */ +int phytec_imx8m_detect(u8 som, const char *opt, u8 cpu_type) +{ + if (som == PHYTEC_IMX8MP_SOM && cpu_type == IMX_CPU_IMX8MP) + return 0; + + if (som == PHYTEC_IMX8MM_SOM) { + if (((opt[0] - '0') != 0) && + ((opt[1] - '0') == 0) && cpu_type == IMX_CPU_IMX8MM) + return 0; + else if (((opt[0] - '0') == 0) && + ((opt[1] - '0') != 0) && cpu_type == IMX_CPU_IMX8MN) + return 0; + return -EINVAL; + } + + if (som == PHYTEC_IMX8MQ_SOM && cpu_type == IMX_CPU_IMX8MQ) + return 0; + + return -EINVAL; +} + +/* + * So far all PHYTEC i.MX8M boards have RAM size definition at the + * same location. + */ +enum phytec_imx8m_ddr_size phytec_get_imx8m_ddr_size(const struct phytec_eeprom_data *data) +{ + const char *opt; + u8 ddr_id; + + if (!data) + data = &eeprom_data; + + opt = phytec_get_opt(data); + if (opt) + ddr_id = opt[2] - '0'; + else + ddr_id = PHYTEC_IMX8M_DDR_AUTODETECT; + + pr_debug("%s: ddr id: %u\n", __func__, ddr_id); + + return ddr_id; +} + +/* + * Filter SPI-NOR flash information. All i.MX8M boards have this at + * the same location. + * returns: 0x0 if no SPI is populated. Otherwise a board depended + * code for the size. PHYTEC_EEPROM_INVAL when the data is invalid. + */ +u8 phytec_get_imx8m_spi(const struct phytec_eeprom_data *data) +{ + const char *opt; + u8 spi; + + if (!data) + data = &eeprom_data; + + if (data->api_rev < PHYTEC_API_REV2) + return PHYTEC_EEPROM_INVAL; + + opt = phytec_get_opt(data); + if (opt) + spi = opt[4] - '0'; + else + spi = PHYTEC_EEPROM_INVAL; + + pr_debug("%s: spi: %u\n", __func__, spi); + + return spi; +} + +/* + * Filter ethernet phy information. All i.MX8M boards have this at + * the same location. + * returns: 0x0 if no ethernet phy is poulated. 0x1 if it is populated. + * PHYTEC_EEPROM_INVAL when the data is invalid. + */ +u8 phytec_get_imx8m_eth(const struct phytec_eeprom_data *data) +{ + const char *opt; + u8 eth; + + if (!data) + data = &eeprom_data; + + if (data->api_rev < PHYTEC_API_REV2) + return PHYTEC_EEPROM_INVAL; + + opt = phytec_get_opt(data); + if (opt) { + eth = opt[5] - '0'; + eth &= 0x1; + } else { + eth = PHYTEC_EEPROM_INVAL; + } + + pr_debug("%s: eth: %u\n", __func__, eth); + + return eth; +} + +/* + * Filter RTC information. + * returns: 0 if no RTC is poulated. 1 if it is populated. + * PHYTEC_EEPROM_INVAL when the data is invalid. + */ +u8 phytec_get_imx8mp_rtc(const struct phytec_eeprom_data *data) +{ + const char *opt; + u8 rtc; + + if (!data) + data = &eeprom_data; + + if (data->api_rev < PHYTEC_API_REV2) + return PHYTEC_EEPROM_INVAL; + + opt = phytec_get_opt(data); + if (opt) { + rtc = opt[5] - '0'; + rtc &= 0x4; + rtc = !(rtc >> 2); + } else { + rtc = PHYTEC_EEPROM_INVAL; + } + + pr_debug("%s: rtc: %u\n", __func__, rtc); + + return rtc; +} diff --git a/common/boards/qemu-virt/Makefile b/common/boards/qemu-virt/Makefile new file mode 100644 index 0000000000..30bf4f1955 --- /dev/null +++ b/common/boards/qemu-virt/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += board.o +obj-y += qemu-virt-flash.dtbo.o fitimage-pubkey.dtb.o +ifeq ($(CONFIG_RISCV),y) +DTC_CPP_FLAGS_qemu-virt-flash.dtbo := -DCONFIG_RISCV +endif +ifeq ($(CONFIG_ARM),y) +DTC_CPP_FLAGS_qemu-virt-flash.dtbo := -DCONFIG_ARM +endif + +clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z +clean-files += *.dtbo *.dtbo.S .*.dtso diff --git a/common/boards/qemu-virt/board.c b/common/boards/qemu-virt/board.c new file mode 100644 index 0000000000..4f2f7374c5 --- /dev/null +++ b/common/boards/qemu-virt/board.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Pengutronix e.K. + * + */ +#include <common.h> +#include <init.h> +#include <of.h> +#include <deep-probe.h> +#include "qemu-virt-flash.h" + +#ifdef CONFIG_64BIT +#define MACHINE "virt64" +#else +#define MACHINE "virt" +#endif + +#ifdef CONFIG_ARM +#include <asm/system_info.h> + +static inline void arm_virt_init(void) +{ + const char *hostname = MACHINE; + + if (cpu_is_cortex_a7()) + hostname = "virt-a7"; + else if (cpu_is_cortex_a15()) + hostname = "virt-a15"; + + barebox_set_model("ARM QEMU " MACHINE); + barebox_set_hostname(hostname); +} + +#else +static inline void arm_virt_init(void) {} +#endif + +extern char __dtbo_qemu_virt_flash_start[]; +extern char __dtb_fitimage_pubkey_start[]; + +static const struct of_device_id virt_of_match[] = { + { .compatible = "linux,dummy-virt", .data = arm_virt_init }, + { .compatible = "riscv-virtio" }, + { /* Sentinel */}, +}; +BAREBOX_DEEP_PROBE_ENABLE(virt_of_match); + +/* + * We don't have a dedicated qemu-virt device tree and instead rely + * on what Qemu passes us. To be able to get fundamental changes + * in very early, we forego having a board driver here and do this + * directly in the initcall. + */ +static int virt_board_driver_init(void) +{ + struct device_node *root = of_get_root_node(); + struct device_node *flash, *overlay, *pubkey; + const struct of_device_id *id; + void (*init)(void); + + id = of_match_node(virt_of_match, root); + if (!id) + return 0; + + if (id->data) { + init = id->data; + init(); + } + + /* + * Catch both old Qemu versions that place /flash in /soc and + * configurations, where the first flash bank is secure-world only + */ + flash = of_find_node_by_path(PARTS_TARGET_PATH_STR); + if (flash && of_device_is_available(flash)) { + overlay = of_unflatten_dtb(__dtbo_qemu_virt_flash_start, INT_MAX); + of_overlay_apply_tree(root, overlay); + } + + pubkey = of_unflatten_dtb(__dtb_fitimage_pubkey_start, INT_MAX); + of_merge_nodes(root, pubkey); + + /* fragment may have added aliases to the DT */ + of_alias_scan(); + + /* of_probe() will happen later at of_populate_initcall */ + + return 0; +} +postcore_initcall(virt_board_driver_init); diff --git a/common/boards/qemu-virt/fitimage-pubkey.dts b/common/boards/qemu-virt/fitimage-pubkey.dts new file mode 100644 index 0000000000..497799fa4b --- /dev/null +++ b/common/boards/qemu-virt/fitimage-pubkey.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +#ifdef CONFIG_BOOTM_FITIMAGE_PUBKEY +#include CONFIG_BOOTM_FITIMAGE_PUBKEY +#endif + +/{ }; diff --git a/common/boards/qemu-virt/qemu-virt-flash.dtso b/common/boards/qemu-virt/qemu-virt-flash.dtso new file mode 100644 index 0000000000..087568a26d --- /dev/null +++ b/common/boards/qemu-virt/qemu-virt-flash.dtso @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/dts-v1/; +/plugin/; + +#include "qemu-virt-flash.h" + +&{PARTS_TARGET_PATH} { +#ifdef CONFIG_ARM + virtual-reg = <0x1000>; +#endif + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "initramfs"; + reg = <0x0 0x3c00000>; + }; + + environment_flash: partition@3c00000 { + label = "barebox-environment"; + reg = <0x3c00000 0x200000>; + }; + + backend_state_flash: partition@3e00000 { + label = "barebox-state"; + reg = <0x3e00000 0x200000>; + }; + }; +}; + +&{/chosen} { + environment { + compatible = "barebox,environment"; + device-path = ENV_DEVICE_PATH_STR; + }; +}; + +&{/} { + aliases { + state = "/state"; + }; + + state { + #address-cells = <1>; + #size-cells = <1>; + compatible = "barebox,state"; + magic = <0x290cf8c6>; + backend-type = "raw"; + backend = < &backend_state_flash >; + backend-stridesize = <0x200>; + + bootstate { + #address-cells = <1>; + #size-cells = <1>; + + system0 { + #address-cells = <1>; + #size-cells = <1>; + + remaining_attempts@0 { + reg = <0x0 0x4>; + type = "uint32"; + default = <3>; + }; + + priority@4 { + reg = <0x4 0x4>; + type = "uint32"; + default = <20>; + }; + }; + + system1 { + #address-cells = <1>; + #size-cells = <1>; + + remaining_attempts@8 { + reg = <0x8 0x4>; + type = "uint32"; + default = <3>; + }; + + priority@c { + reg = <0xc 0x4>; + type = "uint32"; + default = <21>; + }; + }; + + last_chosen@10 { + reg = <0x10 0x4>; + type = "uint32"; + }; + }; + }; +}; diff --git a/common/boards/qemu-virt/qemu-virt-flash.h b/common/boards/qemu-virt/qemu-virt-flash.h new file mode 100644 index 0000000000..85f67ff030 --- /dev/null +++ b/common/boards/qemu-virt/qemu-virt-flash.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __QEMU_VIRT_FLASH_H__ +#define __QEMU_VIRT_FLASH_H__ + +#include <linux/stringify.h> + +#ifdef CONFIG_RISCV +#define PARTS_TARGET_PATH /flash@20000000 +#define ENV_DEVICE_PATH /flash@20000000/partitions/partition@3c00000 +#elif defined CONFIG_ARM +#define PARTS_TARGET_PATH /flash@0 +#define ENV_DEVICE_PATH /flash@0/partitions/partition@3c00000 +#else +#define PARTS_TARGET_PATH +#define ENV_DEVICE_PATH +#endif + +#define PARTS_TARGET_PATH_STR __stringify(PARTS_TARGET_PATH) +#define ENV_DEVICE_PATH_STR __stringify(ENV_DEVICE_PATH) + +#endif diff --git a/common/boards/tq/Makefile b/common/boards/tq/Makefile new file mode 100644 index 0000000000..9950cbdb00 --- /dev/null +++ b/common/boards/tq/Makefile @@ -0,0 +1 @@ +obj-pbl-y += tq_eeprom.o diff --git a/common/boards/tq/tq_eeprom.c b/common/boards/tq/tq_eeprom.c new file mode 100644 index 0000000000..fe776d6bab --- /dev/null +++ b/common/boards/tq/tq_eeprom.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2014-2023 TQ-Systems GmbH <u-boot@ew.tq-group.com>, + * D-82229 Seefeld, Germany. + * Author: Markus Niebel + */ + +#include <common.h> +#include <net.h> +#include <linux/ctype.h> +#include <crc.h> +#include <pbl/i2c.h> +#include <pbl/eeprom.h> +#include <boards/tq/tq_eeprom.h> + +/* + * static EEPROM layout + */ +#define TQ_EE_HRCW_BYTES 0x20 +#define TQ_EE_RSV1_BYTES 10 +#define TQ_EE_RSV2_BYTES 8 + +struct __packed tq_eeprom_data { + union { + struct tq_vard vard; + _Static_assert(sizeof(struct tq_vard) == TQ_EE_HRCW_BYTES, \ + "struct tq_vard has incorrect size"); + u8 hrcw_primary[TQ_EE_HRCW_BYTES]; + } tq_hw_data; + u8 mac[TQ_EE_MAC_BYTES]; /* 0x20 ... 0x25 */ + u8 rsv1[TQ_EE_RSV1_BYTES]; + u8 serial[TQ_EE_SERIAL_BYTES]; /* 0x30 ... 0x37 */ + u8 rsv2[TQ_EE_RSV2_BYTES]; + u8 id[TQ_EE_BDID_BYTES]; /* 0x40 ... 0x7f */ +}; + +static bool tq_vard_valid(const struct tq_vard *vard) +{ + const unsigned char *start = (const unsigned char *)(vard) + + sizeof(vard->crc); + u16 crc; + + crc = crc_itu_t(0, start, sizeof(*vard) - sizeof(vard->crc)); + + return vard->crc == crc; +} + +phys_size_t tq_vard_memsize(u8 val, unsigned int multiply, unsigned int tmask) +{ + phys_size_t result = 0; + + if (val != VARD_MEMSIZE_DEFAULT) { + result = 1 << (size_t)(val & VARD_MEMSIZE_MASK_EXP); + if (val & tmask) + result *= 3; + result *= multiply; + } + + return result; +} + +void tq_vard_show(const struct tq_vard *vard) +{ + /* display data anyway to support developer */ + printf("HW\tREV.%02uxx\n", (unsigned int)vard->hwrev); + printf("RAM\ttype %u, %lu MiB, %s\n", + (unsigned int)(vard->memtype & VARD_MEMTYPE_MASK_TYPE), + (unsigned long)(tq_vard_ramsize(vard) / (SZ_1M)), + (tq_vard_has_ramecc(vard) ? "ECC" : "no ECC")); + printf("RTC\t%c\nSPINOR\t%c\ne-MMC\t%c\nSE\t%c\nEEPROM\t%c\n", + (tq_vard_has_rtc(vard) ? 'y' : 'n'), + (tq_vard_has_spinor(vard) ? 'y' : 'n'), + (tq_vard_has_emmc(vard) ? 'y' : 'n'), + (tq_vard_has_secelem(vard) ? 'y' : 'n'), + (tq_vard_has_eeprom(vard) ? 'y' : 'n')); + + if (tq_vard_has_eeprom(vard)) + printf("EEPROM\ttype %u, %lu KiB, page %zu\n", + (unsigned int)(vard->eepromtype & VARD_EETYPE_MASK_MFR) >> 4, + (unsigned long)(tq_vard_eepromsize(vard) / (SZ_1K)), + tq_vard_eeprom_pgsize(vard)); + + printf("FORMFACTOR: "); + + switch (tq_vard_get_formfactor(vard)) { + case VARD_FORMFACTOR_TYPE_LGA: + printf("LGA\n"); + break; + case VARD_FORMFACTOR_TYPE_CONNECTOR: + printf("CONNECTOR\n"); + break; + case VARD_FORMFACTOR_TYPE_SMARC2: + printf("SMARC-2\n"); + break; + case VARD_FORMFACTOR_TYPE_NONE: + /* + * applies to boards with no variants or older boards + * where this field is not written + */ + printf("UNSPECIFIED\n"); + break; + default: + /* + * generic fall trough + * unhandled form factor or invalid data + */ + printf("UNKNOWN\n"); + break; + } +} + +static void tq_read_string(const char *src, char *dst, int len) +{ + int i; + + for (i = 0; i < len && isprint(src[i]) && isascii(src[i]); ++i) + dst[i] = src[i]; + dst[i] = '\0'; +} + +struct tq_eeprom *pbl_tq_read_eeprom(struct pbl_i2c *i2c, u8 addr, u32 eeprom_addr) +{ + struct tq_eeprom_data raw; + static struct tq_eeprom eeprom; + int ret; + + ret = eeprom_read(i2c, addr, eeprom_addr, &raw, sizeof(raw)); + if (ret) + return NULL; + + if (tq_vard_valid(&raw.tq_hw_data.vard)) + eeprom.vard = raw.tq_hw_data.vard; + + memcpy(eeprom.mac, raw.mac, TQ_EE_MAC_BYTES); + tq_read_string(raw.serial, eeprom.serial, TQ_EE_SERIAL_BYTES); + tq_read_string(raw.id, eeprom.id, TQ_EE_BDID_BYTES); + + return &eeprom; +} diff --git a/common/boot.c b/common/boot.c index 8220b8d3fb..cbfe6649b3 100644 --- a/common/boot.c +++ b/common/boot.c @@ -75,7 +75,7 @@ static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun) struct bootm_data data = {}; - if (dryrun) { + if (dryrun == 1) { printf("Would run %s\n", bs->scriptpath); return 0; } @@ -94,8 +94,8 @@ static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun) if (verbose) data.verbose = verbose; - if (dryrun) - data.dryrun = dryrun; + if (dryrun >= 2) + data.dryrun = dryrun - 1; return bootm_boot(&data); } @@ -115,11 +115,18 @@ struct watchdog *boot_get_enabled_watchdog(void) } static char *global_boot_default; + +void boot_set_default(const char *boot_default) +{ + free(global_boot_default); + global_boot_default = xstrdup(boot_default); +} + static char *global_user; static int init_boot(void) { - global_boot_default = xstrdup("net"); + global_boot_default = global_boot_default ? : xstrdup("net"); globalvar_add_simple_string("boot.default", &global_boot_default); globalvar_add_simple_int("boot.watchdog_timeout", &boot_watchdog_timeout, "%u"); @@ -151,8 +158,8 @@ int boot_entry(struct bootentry *be, int verbose, int dryrun) } ret = be->boot(be, verbose, dryrun); - if (ret) - pr_err("Booting entry '%s' failed\n", be->title); + if (ret && ret != -ENOMEDIUM) + pr_err("Booting entry '%s' failed: %pe\n", be->title, ERR_PTR(ret)); return ret; } @@ -183,8 +190,12 @@ static int bootscript_create_entry(struct bootentries *bootentries, const char * { struct bootentry_script *bs; enum filetype type; + int ret; + + ret = file_name_detect_type(name, &type); + if (ret) + return ret; - type = file_name_detect_type(name); if (type != filetype_sh) return -EINVAL; @@ -272,6 +283,7 @@ int bootentry_register_provider(int (*fn)(struct bootentries *bootentries, const * name can be: * - a name of a boot script under /env/boot * - a full path of a boot script + * - a full path of a bootloader spec entry * - a device name * - a cdev name * - a full path of a directory containing bootloader spec entries @@ -313,10 +325,12 @@ int bootentry_create_from_name(struct bootentries *bootentries, /* * bootsources_menu - show a menu from an array of names */ -void bootsources_menu(struct bootentries *bootentries, int timeout) +void bootsources_menu(struct bootentries *bootentries, + unsigned default_entry, int timeout) { struct bootentry *entry; struct menu_entry *back_entry; + int i = 1; if (!IS_ENABLED(CONFIG_MENU)) { pr_warn("no menu support available\n"); @@ -328,6 +342,9 @@ void bootsources_menu(struct bootentries *bootentries, int timeout) entry->me.display = xstrdup(entry->title); entry->me.action = bootsource_action; menu_add_entry(bootentries->menu, &entry->me); + + if (i++ == default_entry) + bootentries->menu->selected = &entry->me; } back_entry = xzalloc(sizeof(*back_entry)); @@ -336,6 +353,9 @@ void bootsources_menu(struct bootentries *bootentries, int timeout) back_entry->non_re_ent = 1; menu_add_entry(bootentries->menu, back_entry); + if (i == default_entry) + bootentries->menu->selected = back_entry; + if (timeout >= 0) bootentries->menu->auto_select = timeout; diff --git a/common/bootchooser.c b/common/bootchooser.c index 2f22e03c47..022e225165 100644 --- a/common/bootchooser.c +++ b/common/bootchooser.c @@ -13,7 +13,7 @@ #include <libfile.h> #include <common.h> #include <malloc.h> -#include <printk.h> +#include <linux/printk.h> #include <xfuncs.h> #include <envfs.h> #include <errno.h> @@ -77,6 +77,7 @@ struct bootchooser_target { enum reset_attempts { RESET_ATTEMPTS_POWER_ON, RESET_ATTEMPTS_ALL_ZERO, + RESET_ATTEMPTS_SRC_RST, }; static unsigned long reset_attempts; @@ -128,7 +129,7 @@ static void pr_target(struct bootchooser_target *target) printf(" disabled due to %s\n", reason); } -static int pr_setenv(struct bootchooser *bc, const char *fmt, ...) +static int bc_setenv(struct bootchooser *bc, const char *fmt, ...) { va_list ap; int ret = 0; @@ -162,7 +163,7 @@ err: return ret; } -static const char *pr_getenv(const char *fmt, ...) +static const char *bc_getenv(const char *fmt, ...) { va_list ap; char *str; @@ -258,7 +259,7 @@ static struct bootchooser_target *bootchooser_target_new(struct bootchooser *bc, target->remaining_attempts = 0; } - val = pr_getenv("%s.boot", target->prefix); + val = bc_getenv("%s.boot", target->prefix); if (!val) val = target->name; target->boot = xstrdup(val); @@ -439,6 +440,13 @@ struct bootchooser *bootchooser_get(void) attempts_resetted = 1; } + if (test_bit(RESET_ATTEMPTS_SRC_RST, &reset_attempts) && + reset_source_get() == RESET_RST && !attempts_resetted) { + pr_info("RST Reset, resetting remaining attempts\n"); + bootchooser_reset_attempts(bc); + attempts_resetted = 1; + } + if (test_bit(RESET_ATTEMPTS_ALL_ZERO, &reset_attempts)) { int attempts = 0; @@ -497,17 +505,17 @@ int bootchooser_save(struct bootchooser *bc) int ret; if (bc->last_chosen) - pr_setenv(bc, "%s.last_chosen=%d", bc->state_prefix, + bc_setenv(bc, "%s.last_chosen=%d", bc->state_prefix, bc->last_chosen->id); list_for_each_entry(target, &bc->targets, list) { - ret = pr_setenv(bc, "%s.remaining_attempts=%d", + ret = bc_setenv(bc, "%s.remaining_attempts=%d", target->state_prefix, target->remaining_attempts); if (ret) return ret; - ret = pr_setenv(bc, "%s.priority=%d", + ret = bc_setenv(bc, "%s.priority=%d", target->state_prefix, target->priority); if (ret) return ret; @@ -915,6 +923,7 @@ static int bootchooser_add_entry(struct bootentries *entries, const char *name) static const char * const reset_attempts_names[] = { [RESET_ATTEMPTS_POWER_ON] = "power-on", [RESET_ATTEMPTS_ALL_ZERO] = "all-zero", + [RESET_ATTEMPTS_SRC_RST] = "reset", }; static const char * const reset_priorities_names[] = { @@ -951,6 +960,8 @@ BAREBOX_MAGICVAR(global.bootchooser.targets, "bootchooser: Space separated list of target names"); BAREBOX_MAGICVAR(global.bootchooser.default_attempts, "bootchooser: Default number of attempts for a target"); +BAREBOX_MAGICVAR(global.bootchooser.reset_attempts, + "bootchooser: Choose condition to reset number of attempts for all enabled targets ('power-on', 'all-zero', 'reset')"); BAREBOX_MAGICVAR(global.bootchooser.default_priority, "bootchooser: Default priority for a target"); BAREBOX_MAGICVAR(global.bootchooser.state_prefix, diff --git a/common/booti.c b/common/booti.c index a2d63d8c31..e745ff6963 100644 --- a/common/booti.c +++ b/common/booti.c @@ -1,17 +1,39 @@ // SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: 2018 Sascha Hauer <s.hauer@pengutronix.de> +#define pr_fmt(fmt) "booti: " fmt + #include <common.h> #include <memory.h> #include <bootm.h> #include <linux/sizes.h> +static unsigned long get_kernel_address(unsigned long os_address, + unsigned long text_offset) +{ + resource_size_t start, end; + int ret; + + if (os_address == UIMAGE_SOME_ADDRESS || + os_address == UIMAGE_INVALID_ADDRESS) { + ret = memory_bank_first_find_space(&start, &end); + if (ret) + return UIMAGE_INVALID_ADDRESS; + + return ALIGN(start, SZ_2M) + text_offset; + } + + if (os_address >= text_offset && IS_ALIGNED(os_address - text_offset, SZ_2M)) + return os_address; + + return ALIGN(os_address, SZ_2M) + text_offset; +} + void *booti_load_image(struct image_data *data, phys_addr_t *oftree) { const void *kernel_header = data->os_fit ? data->fit_kernel : data->os_header; - resource_size_t start, end; - unsigned long text_offset, image_size, devicetree, kernel; + unsigned long text_offset, image_size, kernel; unsigned long image_end; int ret; void *fdt; @@ -19,11 +41,14 @@ void *booti_load_image(struct image_data *data, phys_addr_t *oftree) text_offset = le64_to_cpup(kernel_header + 8); image_size = le64_to_cpup(kernel_header + 16); - ret = memory_bank_first_find_space(&start, &end); - if (ret) - return ERR_PTR(ret); + kernel = get_kernel_address(data->os_address, text_offset); - kernel = ALIGN(start, SZ_2M) + text_offset; + print_hex_dump_bytes("header ", DUMP_PREFIX_OFFSET, + kernel_header, 80); + pr_debug("Kernel to be loaded to %lx+%lx\n", kernel, image_size); + + if (kernel == UIMAGE_INVALID_ADDRESS) + return ERR_PTR(-ENOENT); ret = bootm_load_os(data, kernel); if (ret) @@ -32,6 +57,8 @@ void *booti_load_image(struct image_data *data, phys_addr_t *oftree) image_end = PAGE_ALIGN(kernel + image_size); if (oftree) { + unsigned long devicetree; + if (bootm_has_initrd(data)) { ret = bootm_load_initrd(data, image_end); if (ret) @@ -59,7 +86,7 @@ void *booti_load_image(struct image_data *data, phys_addr_t *oftree) printf("Loaded kernel to 0x%08lx", kernel); if (oftree) - printf(", devicetree at 0x%08lx", devicetree); + printf(", devicetree at %pa", oftree); printf("\n"); return (void *)kernel; diff --git a/common/bootm.c b/common/bootm.c index 89e3e93f2c..4cc88eed76 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -13,6 +13,7 @@ #include <linux/stat.h> #include <magicvar.h> #include <uncompress.h> +#include <zero_page.h> static LIST_HEAD(handler_list); @@ -41,6 +42,7 @@ static struct image_handler *bootm_find_handler(enum filetype filetype, } static int bootm_appendroot; +static int bootm_earlycon; static int bootm_provide_machine_id; static int bootm_verbosity; @@ -69,6 +71,11 @@ enum bootm_verify bootm_get_verify_mode(void) return bootm_verify_mode; } +void bootm_set_verify_mode(enum bootm_verify mode) +{ + bootm_verify_mode = mode; +} + static const char * const bootm_verify_names[] = { #ifndef CONFIG_BOOTM_FORCE_SIGNED_IMAGES [BOOTM_VERIFY_NONE] = "none", @@ -113,12 +120,12 @@ int bootm_load_os(struct image_data *data, unsigned long load_address) load_address, kernel_size); if (!data->os_res) { pr_err("unable to request SDRAM region for kernel at" - "0x%08llx-0x%08llx\n", + " 0x%08llx-0x%08llx\n", (unsigned long long)load_address, (unsigned long long)load_address + kernel_size - 1); return -ENOMEM; } - memcpy((void *)load_address, kernel, kernel_size); + zero_page_memcpy((void *)load_address, kernel, kernel_size); return 0; } @@ -243,12 +250,10 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address) goto done1; } - type = file_name_detect_type(data->initrd_file); - - if ((int)type < 0) { - pr_err("could not open %s: %s\n", data->initrd_file, - strerror(-type)); - return (int)type; + ret = file_name_detect_type(data->initrd_file, &type); + if (ret) { + pr_err("could not open initrd \"%s\": %s\n", data->initrd_file, strerror(-ret)); + return ret; } if (type == filetype_uimage) { @@ -365,12 +370,11 @@ void *bootm_get_devicetree(struct image_data *data) } else if (data->oftree_file) { size_t size; - type = file_name_detect_type(data->oftree_file); - - if ((int)type < 0) { - pr_err("could not open %s: %s\n", data->oftree_file, - strerror(-type)); - return ERR_PTR((int)type); + ret = file_name_detect_type(data->oftree_file, &type); + if (ret) { + pr_err("could not open device tree \"%s\": %s\n", data->oftree_file, + strerror(-ret)); + return ERR_PTR(ret); } switch (type) { @@ -400,10 +404,13 @@ void *bootm_get_devicetree(struct image_data *data) } } else { - data->of_root_node = of_get_root_node(); - if (!data->of_root_node) + struct device_node *root = of_get_root_node(); + + if (!root) return NULL; + data->of_root_node = of_dup(root); + if (bootm_verbose(data) > 1 && data->of_root_node) printf("using internal devicetree\n"); } @@ -414,7 +421,9 @@ void *bootm_get_devicetree(struct image_data *data) of_add_reserve_entry(data->initrd_res->start, data->initrd_res->end); } - oftree = of_get_fixed_tree(data->of_root_node); + of_fix_tree(data->of_root_node); + + oftree = of_flatten_dtb(data->of_root_node); if (!oftree) return ERR_PTR(-EINVAL); @@ -459,8 +468,10 @@ int bootm_load_devicetree(struct image_data *data, void *fdt, memcpy((void *)data->oftree_res->start, fdt, fdt_size); of_print_cmdline(data->of_root_node); - if (bootm_verbose(data) > 1) - of_print_nodes(data->of_root_node, 0); + if (bootm_verbose(data) > 1) { + of_print_nodes(data->of_root_node, 0, ~0); + fdt_print_reserve_map(fdt); + } return 0; } @@ -518,18 +529,84 @@ static int bootm_open_os_uimage(struct image_data *data) return 0; } +static int bootm_open_fit(struct image_data *data) +{ + struct fit_handle *fit; + struct fdt_header *header; + static const char *kernel_img = "kernel"; + size_t flen, hlen; + int ret; + + if (!IS_ENABLED(CONFIG_FITIMAGE)) + return 0; + + header = (struct fdt_header *)data->os_header; + flen = bootm_get_os_size(data); + hlen = fdt32_to_cpu(header->totalsize); + + fit = fit_open(data->os_file, data->verbose, data->verify, + min(flen, hlen)); + if (IS_ERR(fit)) { + pr_err("Loading FIT image %s failed with: %pe\n", data->os_file, fit); + return PTR_ERR(fit); + } + + data->os_fit = fit; + + data->fit_config = fit_open_configuration(data->os_fit, + data->os_part); + if (IS_ERR(data->fit_config)) { + pr_err("Cannot open FIT image configuration '%s'\n", + data->os_part ? data->os_part : "default"); + return PTR_ERR(data->fit_config); + } + + ret = fit_open_image(data->os_fit, data->fit_config, kernel_img, + &data->fit_kernel, &data->fit_kernel_size); + if (ret) + return ret; + if (data->os_address == UIMAGE_SOME_ADDRESS) { + ret = fit_get_image_address(data->os_fit, + data->fit_config, + kernel_img, + "load", &data->os_address); + if (!ret) + pr_info("Load address from FIT '%s': 0x%lx\n", + kernel_img, data->os_address); + /* Note: Error case uses default value. */ + } + if (data->os_entry == UIMAGE_SOME_ADDRESS) { + unsigned long entry; + ret = fit_get_image_address(data->os_fit, + data->fit_config, + kernel_img, + "entry", &entry); + if (!ret) { + data->os_entry = entry - data->os_address; + pr_info("Entry address from FIT '%s': 0x%lx\n", + kernel_img, entry); + } + /* Note: Error case uses default value. */ + } + + return 0; +} + static int bootm_open_elf(struct image_data *data) { + struct elf_image *elf; + if (!IS_ENABLED(CONFIG_ELF)) return -ENOSYS; - data->elf = elf_open(data->os_file); - if (IS_ERR(data->elf)) - return PTR_ERR(data->elf); + elf = elf_open(data->os_file); + if (IS_ERR(elf)) + return PTR_ERR(elf); - pr_info("Entry Point: %08llx\n", data->elf->entry); + pr_info("Entry Point: %08llx\n", elf->entry); - data->os_address = data->elf->entry; + data->os_address = elf->entry; + data->elf = elf; return 0; } @@ -632,74 +709,25 @@ int bootm_boot(struct bootm_data *bootm_data) } } - if (IS_ENABLED(CONFIG_FITIMAGE) && os_type == filetype_oftree) { - struct fit_handle *fit; - static const char *kernel_img = "kernel"; - - fit = fit_open(data->os_file, data->verbose, data->verify); - if (IS_ERR(fit)) { - pr_err("Loading FIT image %s failed with: %pe\n", data->os_file, fit); - ret = PTR_ERR(fit); - goto err_out; - } - - data->os_fit = fit; - - data->fit_config = fit_open_configuration(data->os_fit, - data->os_part); - if (IS_ERR(data->fit_config)) { - pr_err("Cannot open FIT image configuration '%s'\n", - data->os_part ? data->os_part : "default"); - ret = PTR_ERR(data->fit_config); - goto err_out; - } - - ret = fit_open_image(data->os_fit, data->fit_config, kernel_img, - &data->fit_kernel, &data->fit_kernel_size); - if (ret) - goto err_out; - if (data->os_address == UIMAGE_SOME_ADDRESS) { - ret = fit_get_image_address(data->os_fit, - data->fit_config, - kernel_img, - "load", &data->os_address); - if (!ret) - pr_info("Load address from FIT '%s': 0x%lx\n", - kernel_img, data->os_address); - /* Note: Error case uses default value. */ - } - if (data->os_entry == UIMAGE_SOME_ADDRESS) { - unsigned long entry; - ret = fit_get_image_address(data->os_fit, - data->fit_config, - kernel_img, - "entry", &entry); - if (!ret) { - data->os_entry = entry - data->os_address; - pr_info("Entry address from FIT '%s': 0x%lx\n", - kernel_img, entry); - } - /* Note: Error case uses default value. */ - } - } - - if (os_type == filetype_uimage) { + switch (os_type) { + case filetype_oftree: + ret = bootm_open_fit(data); + break; + case filetype_uimage: ret = bootm_open_os_uimage(data); - if (ret) { - pr_err("Loading OS image failed with: %s\n", - strerror(-ret)); - goto err_out; - } + break; + case filetype_elf: + ret = bootm_open_elf(data); + break; + default: + ret = 0; + break; } - if (os_type == filetype_elf) { - ret = bootm_open_elf(data); - if (ret) { - pr_err("Loading ELF image failed with: %s\n", - strerror(-ret)); - data->elf = NULL; - goto err_out; - } + if (ret) { + pr_err("Loading %s image failed with: %pe\n", + file_type_to_short_string(os_type), ERR_PTR(ret)); + goto err_out; } if (bootm_data->appendroot) { @@ -709,9 +737,8 @@ int bootm_boot(struct bootm_data *bootm_data) const char *root_dev_name = devpath_to_name(bootm_data->root_dev); const struct cdev *root_cdev = cdev_by_name(root_dev_name); - if (root_cdev && root_cdev->partuuid[0] != 0) { - rootarg = basprintf("root=PARTUUID=%s", root_cdev->partuuid); - } else { + rootarg = cdev_get_linux_rootarg(root_cdev); + if (!rootarg) { rootarg = ERR_PTR(-EINVAL); if (!root_cdev) @@ -724,7 +751,10 @@ int bootm_boot(struct bootm_data *bootm_data) } else { rootarg = path_get_linux_rootarg(data->os_file); } - if (!IS_ERR(rootarg)) { + + if (IS_ERR(rootarg)) { + pr_err("Failed to append kernel cmdline parameter 'root='\n"); + } else { pr_info("Adding \"%s\" to Kernel commandline\n", rootarg); globalvar_add_simple("linux.bootargs.bootm.appendroot", rootarg); @@ -732,6 +762,26 @@ int bootm_boot(struct bootm_data *bootm_data) } } + if (bootm_earlycon) { + struct console_device *console; + const char *earlycon = NULL; + + for_each_console(console) { + if (!(console->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR))) + continue; + + earlycon = dev_get_param(&console->class_dev, "linux.bootargs.earlycon"); + if (earlycon) + break; + } + + if (!earlycon) + earlycon = "earlycon"; + + pr_info("Adding \"%s\" to Kernel commandline\n", earlycon); + globalvar_add_simple("linux.bootargs.bootm.earlycon", earlycon); + } + if (bootm_data->provide_machine_id) { const char *machine_id = getenv_nonempty("global.machine_id"); char *machine_id_bootarg; @@ -795,9 +845,10 @@ err_out: elf_close(data->elf); if (IS_ENABLED(CONFIG_FITIMAGE) && data->os_fit) fit_close(data->os_fit); - if (data->of_root_node && data->of_root_node != of_get_root_node()) + if (data->of_root_node) of_delete_node(data->of_root_node); + globalvar_remove("linux.bootargs.bootm.earlycon"); globalvar_remove("linux.bootargs.bootm.appendroot"); free(data->os_header); free(data->os_file); @@ -889,6 +940,12 @@ static struct image_handler xz_bootm_handler = { .filetype = filetype_xz_compressed, }; +static struct image_handler zstd_bootm_handler = { + .name = "ZSTD compressed file", + .bootm = do_bootm_compressed, + .filetype = filetype_zstd_compressed, +}; + static int bootm_init(void) { globalvar_add_simple("bootm.image", NULL); @@ -897,6 +954,7 @@ static int bootm_init(void) globalvar_add_simple("bootm.root_dev", NULL); globalvar_add_simple("bootm.tee", NULL); globalvar_add_simple_bool("bootm.appendroot", &bootm_appendroot); + globalvar_add_simple_bool("bootm.earlycon", &bootm_earlycon); globalvar_add_simple_bool("bootm.provide_machine_id", &bootm_provide_machine_id); if (IS_ENABLED(CONFIG_BOOTM_INITRD)) { globalvar_add_simple("bootm.initrd", NULL); @@ -922,6 +980,8 @@ static int bootm_init(void) register_image_handler(&lz4_bootm_handler); if (IS_ENABLED(CONFIG_XZ_DECOMPRESS)) register_image_handler(&xz_bootm_handler); + if (IS_ENABLED(CONFIG_ZSTD_DECOMPRESS)) + register_image_handler(&zstd_bootm_handler); return 0; } @@ -936,6 +996,7 @@ BAREBOX_MAGICVAR(global.bootm.oftree, "bootm default oftree"); BAREBOX_MAGICVAR(global.bootm.tee, "bootm default tee image"); BAREBOX_MAGICVAR(global.bootm.verify, "bootm default verify level"); BAREBOX_MAGICVAR(global.bootm.verbose, "bootm default verbosity level (0=quiet)"); +BAREBOX_MAGICVAR(global.bootm.earlycon, "Add earlycon option to Kernel for early log output"); BAREBOX_MAGICVAR(global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from (default, device can be overridden via global.bootm.root_dev)"); BAREBOX_MAGICVAR(global.bootm.root_dev, "bootm default root device (overrides default device in global.bootm.appendroot)"); BAREBOX_MAGICVAR(global.bootm.provide_machine_id, "If true, append systemd.machine_id=$global.machine_id to Kernel command line"); diff --git a/common/bootsource.c b/common/bootsource.c index 1f8d053a81..6808c9c51d 100644 --- a/common/bootsource.c +++ b/common/bootsource.c @@ -10,7 +10,7 @@ #include <magicvar.h> #include <init.h> -static const char *bootsource_str[] = { +static const char *bootsource_str[BOOTSOURCE_MAX] = { [BOOTSOURCE_UNKNOWN] = "unknown", [BOOTSOURCE_NAND] = "nand", [BOOTSOURCE_NOR] = "nor", @@ -33,6 +33,42 @@ static enum bootsource bootsource = BOOTSOURCE_UNKNOWN; static int bootsource_instance = BOOTSOURCE_INSTANCE_UNKNOWN; const char *bootsource_alias_name = NULL; +const char *bootsource_to_string(enum bootsource src) +{ + if (src >= ARRAY_SIZE(bootsource_str)) + return NULL; + + return bootsource_str[src]; +} + +const char *bootsource_get_alias_stem(enum bootsource src) +{ + switch (src) { + /* + * For I2C and SPI EEPROMs we set the stem to be 'i2c' + * and 'spi' correspondingly. The resulting alias will + * be pointing at the controller said EEPROM is + * attached to. + * + * NOTE: This code assumes single bootable EEPROM per + * controller + */ + case BOOTSOURCE_I2C_EEPROM: + return bootsource_str[BOOTSOURCE_I2C]; + case BOOTSOURCE_SPI_EEPROM: + case BOOTSOURCE_SPI_NOR: + return bootsource_str[BOOTSOURCE_SPI]; + case BOOTSOURCE_SERIAL: /* FALLTHROUGH */ + case BOOTSOURCE_I2C: /* FALLTHROUGH */ + case BOOTSOURCE_MMC: /* FALLTHROUGH */ + case BOOTSOURCE_SPI: /* FALLTHROUGH */ + case BOOTSOURCE_CAN: + return bootsource_str[src]; + default: + return NULL; + } +} + /** * bootsource_get_alias_name() - Get the name of the bootsource alias * @@ -58,33 +94,9 @@ char *bootsource_get_alias_name(void) if (bootsource_alias_name) return strdup(bootsource_alias_name); - switch (bootsource) { - /* - * For I2C and SPI EEPROMs we set the stem to be 'i2c' - * and 'spi' correspondingly. The resulting alias will - * be pointing at the controller said EEPROM is - * attached to. - * - * NOTE: This code assumes single bootable EEPROM per - * controller - */ - case BOOTSOURCE_I2C_EEPROM: - stem = bootsource_str[BOOTSOURCE_I2C]; - break; - case BOOTSOURCE_SPI_EEPROM: - case BOOTSOURCE_SPI_NOR: - stem = bootsource_str[BOOTSOURCE_SPI]; - break; - case BOOTSOURCE_SERIAL: /* FALLTHROUGH */ - case BOOTSOURCE_I2C: /* FALLTHROUGH */ - case BOOTSOURCE_MMC: /* FALLTHROUGH */ - case BOOTSOURCE_SPI: /* FALLTHROUGH */ - case BOOTSOURCE_CAN: - stem = bootsource_str[bootsource]; - break; - default: + stem = bootsource_get_alias_stem(bootsource); + if (!stem) return NULL; - } /* * We expect SoC specific bootsource detection code to properly @@ -96,33 +108,92 @@ char *bootsource_get_alias_name(void) return basprintf("%s%d", stem, bootsource_instance); } +struct device_node *bootsource_of_node_get(struct device_node *root) +{ + struct device_node *np; + char *alias_name; + + alias_name = bootsource_get_alias_name(); + if (!alias_name) + return NULL; + + np = of_find_node_by_alias(root, alias_name); + + free(alias_name); + + return np; +} + void bootsource_set_alias_name(const char *name) { bootsource_alias_name = name; } -void bootsource_set(enum bootsource src) +void bootsource_set_raw(enum bootsource src, int instance) { - if (src >= ARRAY_SIZE(bootsource_str)) + if (src >= BOOTSOURCE_MAX) src = BOOTSOURCE_UNKNOWN; bootsource = src; - setenv("bootsource", bootsource_str[src]); + setenv("bootsource", bootsource_to_string(src)); + + bootsource_set_raw_instance(instance); } -void bootsource_set_instance(int instance) +void bootsource_set_raw_instance(int instance) { - char buf[32]; - bootsource_instance = instance; if (instance < 0) - sprintf(buf, "unknown"); + setenv("bootsource_instance","unknown"); else - snprintf(buf, sizeof(buf), "%d", instance); + pr_setenv("bootsource_instance", "%d", instance); +} + +int bootsource_of_alias_xlate(enum bootsource src, int instance) +{ + char chosen[sizeof("barebox,bootsource-harddisk4294967295")]; + const char *bootsource_stem; + struct device_node *np; + int alias_id; + + if (!IS_ENABLED(CONFIG_OFDEVICE)) + return BOOTSOURCE_INSTANCE_UNKNOWN; + + if (src == BOOTSOURCE_UNKNOWN || + instance == BOOTSOURCE_INSTANCE_UNKNOWN) + return BOOTSOURCE_INSTANCE_UNKNOWN; + + bootsource_stem = bootsource_get_alias_stem(src); + if (!bootsource_stem) + return BOOTSOURCE_INSTANCE_UNKNOWN; + + scnprintf(chosen, sizeof(chosen), "barebox,bootsource-%s%u", + bootsource_stem, instance); + + np = of_find_node_by_chosen(chosen, NULL); + if (!np) + return BOOTSOURCE_INSTANCE_UNKNOWN; + + alias_id = of_alias_get_id(np, bootsource_stem); + if (alias_id < 0) + return BOOTSOURCE_INSTANCE_UNKNOWN; + + return alias_id; +} + +int bootsource_set(enum bootsource src, int instance) +{ + int alias_id; + + alias_id = bootsource_of_alias_xlate(src, instance); + if (alias_id == BOOTSOURCE_INSTANCE_UNKNOWN) + alias_id = instance; + + bootsource_set_raw(src, alias_id); - setenv("bootsource_instance", buf); + return alias_id; } enum bootsource bootsource_get(void) @@ -141,8 +212,7 @@ BAREBOX_MAGICVAR(bootsource_instance, "The instance of the source barebox has be static int bootsource_init(void) { - bootsource_set(bootsource); - bootsource_set_instance(bootsource_instance); + bootsource_set_raw(bootsource, bootsource_instance); export("bootsource"); export("bootsource_instance"); diff --git a/common/bthread.c b/common/bthread.c index 46e6987149..d40c0b0f9e 100644 --- a/common/bthread.c +++ b/common/bthread.c @@ -70,6 +70,8 @@ bool bthread_is_main(struct bthread *bthread) static void bthread_free(struct bthread *bthread) { + if (!bthread) + return; free(bthread->stack); free(bthread->name); free(bthread); diff --git a/common/calloc.c b/common/calloc.c index 2b933ec272..65e843350d 100644 --- a/common/calloc.c +++ b/common/calloc.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <malloc.h> diff --git a/common/complete.c b/common/complete.c index e504b75606..3911535621 100644 --- a/common/complete.c +++ b/common/complete.c @@ -14,17 +14,39 @@ #include <command.h> #include <environment.h> -static int file_complete(struct string_list *sl, char *instr, int exec) +static bool is_valid_escape(const char *str) +{ + return str[0] == '\\' && (str[1] == ' ' || str[1] == '\\'); +} + +static bool strstarts_escaped(const char *whole, const char *prefix_escaped) +{ + if (!prefix_escaped) + return true; + + while (*prefix_escaped) { + if (is_valid_escape(prefix_escaped)) + prefix_escaped++; + + if (*whole++ != *prefix_escaped++) + return false; + } + + return true; +} + +static int file_complete(struct string_list *sl, char *instr, + const char *dirn, int exec) { char *path = strdup(instr); struct stat s; DIR *dir; struct dirent *d; char tmp[PATH_MAX]; - char *base, *dirn; + char *base; base = basename(instr); - dirn = dirname(path); + dirn = dirn ?: dirname(path); dir = opendir(dirn); if (!dir) @@ -34,7 +56,7 @@ static int file_complete(struct string_list *sl, char *instr, int exec) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; - if (strncmp(base, d->d_name, strlen(base))) + if (!strstarts_escaped(d->d_name, base)) continue; strcpy(tmp, instr); @@ -93,7 +115,7 @@ static int path_command_complete(struct string_list *sl, char *instr) !strcmp(d->d_name, "..")) continue; - if (!strncmp(instr, d->d_name, strlen(instr))) { + if (strstarts_escaped(d->d_name, instr)) { strcpy(tmp, d->d_name); if (!stat(tmp, &s) && S_ISDIR(s.st_mode)) @@ -134,16 +156,10 @@ EXPORT_SYMBOL(command_complete); int device_complete(struct string_list *sl, char *instr) { - struct device_d *dev; - int len; - - if (!instr) - instr = ""; - - len = strlen(instr); + struct device *dev; for_each_device(dev) { - if (strncmp(instr, dev_name(dev), len)) + if (!strstarts_escaped(dev_name(dev), instr)) continue; string_list_add_asprintf(sl, "%s ", dev_name(dev)); @@ -153,26 +169,38 @@ int device_complete(struct string_list *sl, char *instr) } EXPORT_SYMBOL(device_complete); -static int device_param_complete(struct device_d *dev, struct string_list *sl, - char *instr, int eval) +static int device_param_complete(struct device *dev, const char *devname, + struct string_list *sl, char *instr, int eval) { struct param_d *param; - int len; - - len = strlen(instr); list_for_each_entry(param, &dev->parameters, list) { - if (strncmp(instr, param->name, len)) + if (!strstarts_escaped(param->name, instr)) continue; string_list_add_asprintf(sl, "%s%s.%s%c", - eval ? "$" : "", dev_name(dev), param->name, + eval ? "$" : "", devname, param->name, eval ? ' ' : '='); } return 0; } +int driver_complete(struct string_list *sl, char *instr) +{ + struct driver_d *drv; + + for_each_driver(drv) { + if (!strstarts_escaped(drv->name, instr)) + continue; + + string_list_add_asprintf(sl, "%s ", drv->name); + } + + return COMPLETE_CONTINUE; +} +EXPORT_SYMBOL(driver_complete); + int empty_complete(struct string_list *sl, char *instr) { return COMPLETE_END; @@ -229,7 +257,7 @@ int devicetree_nodepath_complete(struct string_list *sl, char *instr) for_each_child_of_node(node, child) { if (strncmp(base, child->name, strlen(base))) continue; - string_list_add_asprintf(sl, "%s/", child->full_name); + string_list_add_asprintf(sl, "%pOF/", child); } out: free(path); @@ -250,15 +278,23 @@ EXPORT_SYMBOL(devicetree_complete); int devicetree_file_complete(struct string_list *sl, char *instr) { devicetree_complete(sl, instr); - file_complete(sl, instr, 0); + file_complete(sl, instr, NULL, 0); return 0; } EXPORT_SYMBOL(devicetree_file_complete); +int tutorial_complete(struct string_list *sl, char *instr) +{ + file_complete(sl, instr, "/env/data/tutorial", 0); + + return 0; +} +EXPORT_SYMBOL(tutorial_complete); + static int env_param_complete(struct string_list *sl, char *instr, int eval) { - struct device_d *dev; + struct device *dev; struct variable_d *var; struct env_context *c; int len; @@ -299,14 +335,12 @@ static int env_param_complete(struct string_list *sl, char *instr, int eval) char *devname; devname = xstrndup(instr, dot - instr); - - dev = get_device_by_name(devname); - free(devname); if (dev) - device_param_complete(dev, sl, dot + 1, eval); + device_param_complete(dev, devname, sl, dot + 1, eval); + free(devname); pos = dot + 1; } @@ -314,7 +348,7 @@ static int env_param_complete(struct string_list *sl, char *instr, int eval) for_each_device(dev) { if (!strncmp(instr, dev_name(dev), len)) - device_param_complete(dev, sl, "", eval); + device_param_complete(dev, dev_name(dev), sl, "", eval); } return 0; @@ -333,21 +367,43 @@ void complete_reset(void) tab_pressed = 0; } +static char *skip_to_last_unescaped_space(char *instr) +{ + char *t; + + t = strrchr(instr, ' '); + if (t && (instr == t || t[-1] != '\\')) + return t + 1; + + return instr; +} + +static size_t strlen_escaped(char *instr) +{ + size_t count = 0; + + for (; *instr; instr++) { + if (is_valid_escape(instr)) + instr++; + + count++; + } + + return count; +} + static char* cmd_complete_lookup(struct string_list *sl, char *instr) { struct command *cmdtp; int len; int ret = COMPLETE_END; char *res = NULL; - char *t; for_each_command(cmdtp) { len = strlen(cmdtp->name); if (!strncmp(instr, cmdtp->name, len) && instr[len] == ' ') { instr += len + 1; - t = strrchr(instr, ' '); - if (t) - instr = t + 1; + instr = skip_to_last_unescaped_space(instr); if (cmdtp->complete) { ret = cmdtp->complete(sl, instr); @@ -392,11 +448,11 @@ int complete(char *instr, char **outstr) if (!instr) { instr = t; if (t && (t[0] == '/' || !strncmp(t, "./", 2))) { - file_complete(&sl, t, 1); + file_complete(&sl, t, NULL, 1); instr = t; } else if ((t = strrchr(t, ' '))) { t++; - file_complete(&sl, t, 0); + file_complete(&sl, t, NULL, 0); instr = t; } else { command_complete(&sl, instr); @@ -407,7 +463,7 @@ int complete(char *instr, char **outstr) env_param_complete(&sl, instr + 1, 1); } - pos = strlen(instr); + pos = strlen_escaped(instr); *outstr = ""; if (list_empty(&sl.list)) diff --git a/common/console.c b/common/console.c index 8727b187cf..5a0fd66ab3 100644 --- a/common/console.c +++ b/common/console.c @@ -220,6 +220,21 @@ static void console_init_early(void) initialized = CONSOLE_INITIALIZED_BUFFER; } +static void console_add_earlycon_param(struct console_device *cdev, unsigned baudrate) +{ + char *str; + + if (!cdev->linux_earlycon_name) + return; + + str = basprintf("earlycon=%s,0x%lx", cdev->linux_earlycon_name, + (ulong)cdev->phys_base); + + dev_add_param_fixed(&cdev->class_dev, "linux.bootargs.earlycon", str); + + free(str); +} + static void console_set_stdoutpath(struct console_device *cdev, unsigned baudrate) { int id; @@ -228,7 +243,7 @@ static void console_set_stdoutpath(struct console_device *cdev, unsigned baudrat if (!cdev->linux_console_name) return; - id = of_alias_get_id(cdev->dev->device_node, "serial"); + id = of_alias_get_id(cdev->dev->of_node, "serial"); if (id < 0) return; @@ -239,14 +254,34 @@ static void console_set_stdoutpath(struct console_device *cdev, unsigned baudrat free(str); } +struct console_device *of_console_by_stdout_path(void) +{ + struct console_device *console; + struct device_node *stdout_np; + + stdout_np = of_get_stdoutpath(NULL); + if (!stdout_np) + return NULL; + + for_each_console(console) { + if (dev_of_node(console->dev) == stdout_np) + return console; + } + + return NULL; +} + static int __console_puts(struct console_device *cdev, const char *s, size_t nbytes) { size_t i; for (i = 0; i < nbytes; i++) { - if (*s == '\n') + if (*s == '\n') { cdev->putc(cdev, '\r'); + if (IS_ENABLED(CONFIG_CONSOLE_FLUSH_LINE_BREAK) && cdev->flush) + cdev->flush(cdev); + } cdev->putc(cdev, *s); s++; @@ -294,7 +329,7 @@ static ssize_t fops_write(struct cdev* dev, const void* buf, size_t count, int console_register(struct console_device *newcdev) { struct device_node *serdev_node = console_is_serdev_node(newcdev); - struct device_d *dev = &newcdev->class_dev; + struct device *dev = &newcdev->class_dev; int activate = 0, ret; unsigned baudrate = CONFIG_BAUDRATE; @@ -332,6 +367,8 @@ int console_register(struct console_device *newcdev) console_set_stdoutpath(newcdev, baudrate); } + console_add_earlycon_param(newcdev, baudrate); + if (newcdev->setbrg) { ret = newcdev->setbrg(newcdev, baudrate); if (ret) @@ -389,7 +426,7 @@ EXPORT_SYMBOL(console_register); int console_unregister(struct console_device *cdev) { - struct device_d *dev = &cdev->class_dev; + struct device *dev = &cdev->class_dev; int status; /* @@ -591,7 +628,7 @@ int ctrlc(void) if (ctrlc_abort) return 1; -#ifdef ARCH_HAS_CTRLC +#ifdef CONFIG_ARCH_HAS_CTRLC ret = arch_ctrlc(); #else if (tstc() && getchar() == 3) diff --git a/common/console_common.c b/common/console_common.c index 91a81e50fa..0113a64138 100644 --- a/common/console_common.c +++ b/common/console_common.c @@ -10,36 +10,39 @@ #include <errno.h> #include <console.h> #include <init.h> +#include <string.h> #include <environment.h> #include <globalvar.h> #include <magicvar.h> +#include <memory.h> #include <of.h> #include <password.h> #include <clock.h> #include <malloc.h> #include <linux/pstore.h> #include <linux/math64.h> +#include <linux/sizes.h> +#include <linux/overflow.h> #ifndef CONFIG_CONSOLE_NONE static const char *colored_log_level[] = { - [MSG_EMERG] = "\033[31mEMERG:\033[0m ", /* red */ - [MSG_ALERT] = "\033[31mALERT:\033[0m ", /* red */ - [MSG_CRIT] = "\033[31mCRITICAL:\033[0m ", /* red */ - [MSG_ERR] = "\033[31mERROR:\033[0m ", /* red */ - [MSG_WARNING] = "\033[33mWARNING:\033[0m ", /* yellow */ - [MSG_NOTICE] = "\033[34mNOTICE:\033[0m ", /* blue */ + [MSG_EMERG] = "\033[1;31mEMERG:\033[0m ", /* red */ + [MSG_ALERT] = "\033[1;31mALERT:\033[0m ", /* red */ + [MSG_CRIT] = "\033[1;31mCRITICAL:\033[0m ", /* red */ + [MSG_ERR] = "\033[1;31mERROR:\033[0m ", /* red */ + [MSG_WARNING] = "\033[1;33mWARNING:\033[0m ", /* yellow */ + [MSG_NOTICE] = "\033[1;34mNOTICE:\033[0m ", /* blue */ }; int barebox_loglevel = CONFIG_DEFAULT_LOGLEVEL; LIST_HEAD(barebox_logbuf); static int barebox_logbuf_num_messages; -static int barebox_log_max_messages = 1000; +static int barebox_log_max_messages; static void log_del(struct log_entry *log) { - free(log->msg); list_del(&log->list); free(log); barebox_logbuf_num_messages--; @@ -68,7 +71,7 @@ void log_clean(unsigned int limit) } } -static void print_colored_log_level(const int level) +static void print_colored_log_level(unsigned int ch, const int level) { if (!console_allow_color()) return; @@ -77,7 +80,7 @@ static void print_colored_log_level(const int level) if (!colored_log_level[level]) return; - puts(colored_log_level[level]); + console_puts(ch, colored_log_level[level]); } static void pr_puts(int level, const char *str) @@ -89,15 +92,14 @@ static void pr_puts(int level, const char *str) log_clean(barebox_log_max_messages - 1); if (barebox_log_max_messages >= 0) { - log = malloc(sizeof(*log)); + int msglen; + + msglen = strlen(str); + log = malloc(struct_size(log, msg, msglen + 1)); if (!log) goto nolog; - log->msg = strdup(str); - if (!log->msg) { - free(log); - goto nolog; - } + memcpy(log->msg, str, msglen + 1); log->timestamp = get_time_ns(); log->level = level; @@ -112,8 +114,8 @@ nolog: if (level > barebox_loglevel) return; - print_colored_log_level(level); - puts(str); + print_colored_log_level(CONSOLE_STDERR, level); + console_puts(CONSOLE_STDERR, str); } int pr_print(int level, const char *fmt, ...) @@ -134,7 +136,7 @@ int pr_print(int level, const char *fmt, ...) return i; } -int dev_printf(int level, const struct device_d *dev, const char *format, ...) +int dev_printf(int level, const struct device *dev, const char *format, ...) { va_list args; int ret = 0; @@ -144,7 +146,7 @@ int dev_printf(int level, const struct device_d *dev, const char *format, ...) if (!IS_ENABLED(CONFIG_LOGBUF) && level > barebox_loglevel) return 0; - if (dev->driver && dev->driver->name) + if (dev && dev->driver && dev->driver->name) ret += snprintf(printbuffer, size, "%s ", dev->driver->name); ret += snprintf(printbuffer + ret, size - ret, "%s: ", dev_name(dev)); @@ -173,15 +175,18 @@ bool console_allow_color(void) static int console_common_init(void) { - if (IS_ENABLED(CONFIG_LOGBUF)) + if (IS_ENABLED(CONFIG_LOGBUF)) { + barebox_log_max_messages + = clamp(mem_malloc_size() / SZ_32K, 1000UL, 100000UL); globalvar_add_simple_int("log_max_messages", &barebox_log_max_messages, "%d"); + } globalvar_add_simple_bool("allow_color", &__console_allow_color); return globalvar_add_simple_int("loglevel", &barebox_loglevel, "%d"); } -device_initcall(console_common_init); +core_initcall(console_common_init); int log_writefile(const char *filepath) { @@ -203,7 +208,7 @@ int log_writefile(const char *filepath) return ret < 0 ? ret : nbytes; } -void log_print(unsigned flags, unsigned levels) +int log_print(unsigned flags, unsigned levels) { struct log_entry *log; unsigned long last = 0; @@ -214,10 +219,12 @@ void log_print(unsigned flags, unsigned levels) if (levels && !(levels & (1 << log->level))) continue; + if (ctrlc()) + return -EINTR; if (!(flags & (BAREBOX_LOG_PRINT_RAW | BAREBOX_LOG_PRINT_TIME | BAREBOX_LOG_DIFF_TIME))) - print_colored_log_level(log->level); + print_colored_log_level(CONSOLE_STDOUT, log->level); if (flags & BAREBOX_LOG_PRINT_RAW) printf("<%i>", log->level); @@ -242,6 +249,8 @@ void log_print(unsigned flags, unsigned levels) printf("%s", log->msg); } + + return 0; } int printf(const char *fmt, ...) @@ -284,7 +293,7 @@ int vprintf(const char *fmt, va_list args) } EXPORT_SYMBOL(vprintf); -struct console_device *console_get_by_dev(struct device_d *dev) +struct console_device *console_get_by_dev(struct device *dev) { struct console_device *cdev; @@ -336,7 +345,7 @@ EXPORT_SYMBOL(console_get_first_active); struct console_device *of_console_get_by_alias(const char *alias) { struct device_node *node; - struct device_d *dev; + struct device *dev; node = of_find_node_by_alias(NULL, alias); if (!node) diff --git a/common/console_simple.c b/common/console_simple.c index 8c404ad264..702087bd23 100644 --- a/common/console_simple.c +++ b/common/console_simple.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <config.h> #include <common.h> #include <fs.h> @@ -68,7 +70,7 @@ EXPORT_SYMBOL(console_flush); int ctrlc (void) { int ret = 0; -#ifdef ARCH_HAS_CTRLC +#ifdef CONFIG_ARCH_HAS_CTRLC ret = arch_ctrlc(); #else if (tstc() && getchar() == 3) diff --git a/common/ddr1_dimm_params.c b/common/ddr1_dimm_params.c new file mode 100644 index 0000000000..4a7db30e2e --- /dev/null +++ b/common/ddr1_dimm_params.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2008 Freescale Semiconductor, Inc. + */ +#include <common.h> +#include <linux/log2.h> +#include <ddr_dimms.h> + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + */ + +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)); + bsize <<= 24ULL; + debug("DDR: DDR I rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + return ((trctrfc_ext & 0x1) * 256 + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; +} + +/* + * tCKmax from DDR I SPD Byte 43 + * + * Bits 7:2 == whole ns + * Bits 1:0 == quarter ns + * 00 == 0.00 ns + * 01 == 0.25 ns + * 10 == 0.50 ns + * 11 == 0.75 ns + * + * Returns picoseconds. + */ +static unsigned int +compute_tckmax_from_spd_ps(unsigned int byte43) +{ + return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can be. + * If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-E + * Table 11. + * + * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2 + */ + /* CL2.0 CL2.5 CL3.0 */ +unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 }; + +static unsigned int +compute_derated_DDR1_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr1_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && lowest_tCKmin_found <= x && x <= mclk_ps) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 1; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr1_compute_dimm_parameters for DDR1 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the struct dimm_params structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int ddr1_compute_dimm_parameters(unsigned int mclk_ps, + const struct ddr1_spd_eeprom *spd, + struct dimm_params *pdimm) +{ + int ret; + + if (spd->mem_type != SPD_MEMTYPE_DDR) { + printf("DIMM: SPD data is not DDR1\n"); + return 3; + } + + ret = ddr1_spd_check(spd); + if (ret) { + printf("DIMM: failed checksum\n"); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = spd->nrows; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw_lsb; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* + * FIXME: Need to determine registered_dimm status. + * 1 == register buffered + * 0 == unbuffered + */ + pdimm->registered_dimm = 0; /* unbuffered */ + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tckmin_x_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tckmin_x_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tckmin_x_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tckmax_ps = compute_tckmax_from_spd_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_x should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_x = ilog2(spd->cas_lat); + pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x)); + pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x) + & ~(1 << pdimm->caslat_x_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated = compute_derated_DDR1_CAS_latency( + mclk_ps); + + /* Compute timing parameters */ + pdimm->trcd_ps = spd->trcd * 250; + pdimm->trp_ps = spd->trp * 250; + pdimm->tras_ps = spd->tras * 1000; + + pdimm->twr_ps = mclk_ps * 3; + pdimm->twtr_ps = mclk_ps * 1; + pdimm->trfc_ps = compute_trfc_ps_from_spd(0, spd->trfc); + + pdimm->trrd_ps = spd->trrd * 250; + pdimm->trc_ps = compute_trc_ps_from_spd(0, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tds_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tdh_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->trtp_ps = mclk_ps * 2; /* By the book. */ + pdimm->tdqsq_max_ps = spd->tdqsq * 10; + pdimm->tqhs_ps = spd->tqhs * 10; + + return 0; +} diff --git a/common/ddr2_dimm_params.c b/common/ddr2_dimm_params.c new file mode 100644 index 0000000000..b9c0922385 --- /dev/null +++ b/common/ddr2_dimm_params.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2008 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <linux/log2.h> +#include <ddr_dimms.h> + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + */ +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + debug("DDR: DDR II rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + return (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can. + * be. If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-2C + * Table 40, "DDR2 SDRAM stanadard speed bins and tCK, tRCD, tRP, tRAS, + * and tRC for corresponding bin" + * + * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3 + * Not certain if any good value exists for CL=2 + */ + /* CL2 CL3 CL4 CL5 CL6 CL7*/ +static unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 }; + +static unsigned int +compute_derated_DDR2_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr2_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr2_compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the struct dimm_params structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int ddr2_compute_dimm_parameters(unsigned int mclk_ps, + const struct ddr2_spd_eeprom *spd, + struct dimm_params *pdimm) +{ + int ret; + + if (spd->mem_type != SPD_MEMTYPE_DDR2 && + spd->mem_type != SPD_MEMTYPE_DDR2_FBDIMM && + spd->mem_type != SPD_MEMTYPE_DDR2_FBDIMM_PROBE) { + printf("DIMM: SPD data is not DDR2\n"); + return 3; + } + + ret = ddr2_spd_check(spd); + if (ret) { + printf("DIMM: failed checksum\n"); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */ + switch (spd->dimm_type) { + case DDR2_SPD_DIMMTYPE_RDIMM: + case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM: + case DDR2_SPD_DIMMTYPE_MINI_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + break; + + case DDR2_SPD_DIMMTYPE_UDIMM: + case DDR2_SPD_DIMMTYPE_SO_DIMM: + case DDR2_SPD_DIMMTYPE_MICRO_DIMM: + case DDR2_SPD_DIMMTYPE_MINI_UDIMM: + /* Unbuffered DIMMs */ + pdimm->registered_dimm = 0; + break; + + case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM: + default: + printf("unknown dimm_type 0x%02X\n", spd->dimm_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tckmin_x_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tckmin_x_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tckmin_x_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tckmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_x should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_x = ilog2(spd->cas_lat); + pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x)); + pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_x) + & ~(1 << pdimm->caslat_x_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated = compute_derated_DDR2_CAS_latency( + mclk_ps); + + /* Compute timing parameters */ + pdimm->trcd_ps = spd->trcd * 250; + pdimm->trp_ps = spd->trp * 250; + pdimm->tras_ps = spd->tras * 1000; + + pdimm->twr_ps = spd->twr * 250; + pdimm->twtr_ps = spd->twtr * 250; + pdimm->trfc_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc); + + pdimm->trrd_ps = spd->trrd * 250; + pdimm->trc_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tds_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tdh_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->trtp_ps = spd->trtp * 250; + pdimm->tdqsq_max_ps = spd->tdqsq * 10; + pdimm->tqhs_ps = spd->tqhs * 10; + + return 0; +} diff --git a/common/ddr3_dimm_params.c b/common/ddr3_dimm_params.c new file mode 100644 index 0000000000..1b7512a275 --- /dev/null +++ b/common/ddr3_dimm_params.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_11R18.pdf + */ + +#include <common.h> +#include <ddr_dimms.h> + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * each rank size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte8[2:0] + * sdram width = spd byte7[2:0] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * + * SPD byte8 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte7 - module organiztion + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + * + */ +static unsigned long long +compute_ranksize(const struct ddr3_spd_eeprom *spd) +{ + unsigned long long bsize; + + int nbit_sdram_cap_bsize = 0; + int nbit_primary_bus_width = 0; + int nbit_sdram_width = 0; + + if ((spd->density_banks & 0xf) < 7) + nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + nbit_primary_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + nbit_sdram_width = (spd->organization & 0x7) + 2; + + bsize = 1ULL << (nbit_sdram_cap_bsize - 3 + + nbit_primary_bus_width - nbit_sdram_width); + + debug("DDR: DDR III rank density = 0x%16llx\n", bsize); + + return bsize; +} + +/* + * ddr3_compute_dimm_parameters for DDR3 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the struct dimm_params structure pointed by pdimm. + * + */ +unsigned int ddr3_compute_dimm_parameters(const struct ddr3_spd_eeprom *spd, + struct dimm_params *pdimm) +{ + int ret; + unsigned int mtb_ps; + int ftb_10th_ps; + int i; + + if (spd->mem_type != SPD_MEMTYPE_DDR3) { + printf("DIMM: SPD data is not DDR3\n"); + return 3; + } + + ret = ddr3_spd_check(spd); + if (ret) { + printf("DIMM: failed checksum\n"); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + if ((spd->info_size_crc & 0xF) > 1) + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->data_width = pdimm->primary_sdram_width + + pdimm->ec_sdram_width; + pdimm->device_width = 1 << ((spd->organization & 0x7) + 2); + + /* These are the types defined by the JEDEC DDR3 SPD spec */ + pdimm->mirrored_dimm = 0; + pdimm->registered_dimm = 0; + switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) { + case DDR3_SPD_MODULETYPE_RDIMM: + case DDR3_SPD_MODULETYPE_MINI_RDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + for (i = 0; i < 16; i += 2) { + u8 rcw = spd->mod_section.registered.rcw[i/2]; + pdimm->rcw[i] = (rcw >> 0) & 0x0F; + pdimm->rcw[i+1] = (rcw >> 4) & 0x0F; + } + break; + + case DDR3_SPD_MODULETYPE_UDIMM: + case DDR3_SPD_MODULETYPE_SO_DIMM: + case DDR3_SPD_MODULETYPE_MICRO_DIMM: + case DDR3_SPD_MODULETYPE_MINI_UDIMM: + case DDR3_SPD_MODULETYPE_MINI_CDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_UDIMM: + case DDR3_SPD_MODULETYPE_72B_SO_CDIMM: + case DDR3_SPD_MODULETYPE_LRDIMM: + case DDR3_SPD_MODULETYPE_16B_SO_DIMM: + case DDR3_SPD_MODULETYPE_32B_SO_DIMM: + /* Unbuffered DIMMs */ + if (spd->mod_section.unbuffered.addr_mapping & 0x1) + pdimm->mirrored_dimm = 1; + break; + + default: + printf("unknown module_type 0x%02X\n", spd->module_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7); + + /* + * The SPD spec has not the ECC bit, + * We consider the DIMM as ECC capability + * when the extension bus exist + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec has not the burst length byte + * but DDR3 spec has nature BL8 and BC4, + * BL8 -bit3, BC4 -bit2 + */ + pdimm->burst_lengths_bitmask = 0x0c; + + /* MTB - medium timebase + * The unit in the SPD spec is ns, + * We convert it to ps. + * eg: MTB = 0.125ns (125ps) + */ + mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor; + pdimm->mtb_ps = mtb_ps; + + /* + * FTB - fine timebase + * use 1/10th of ps as our unit to avoid floating point + * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps + */ + ftb_10th_ps = + ((spd->ftb_div & 0xf0) >> 4) * 10 / (spd->ftb_div & 0x0f); + pdimm->ftb_10th_ps = ftb_10th_ps; + /* + * sdram minimum cycle time + * we assume the MTB is 0.125ns + * eg: + * tck_min=15 MTB (1.875ns) ->DDR3-1066 + * =12 MTB (1.5ns) ->DDR3-1333 + * =10 MTB (1.25ns) ->DDR3-1600 + */ + pdimm->tckmin_x_ps = spd->tck_min * mtb_ps + + (spd->fine_tck_min * ftb_10th_ps) / 10; + + /* + * CAS latency supported + * bit4 - CL4 + * bit5 - CL5 + * bit18 - CL18 + */ + pdimm->caslat_x = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4; + + /* + * min CAS latency time + * eg: taa_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->taa_ps = spd->taa_min * mtb_ps + + (spd->fine_taa_min * ftb_10th_ps) / 10; + + /* + * min write recovery time + * eg: + * twr_min = 120 MTB (15ns) -> all speed grades. + */ + pdimm->twr_ps = spd->twr_min * mtb_ps; + + /* + * min RAS to CAS delay time + * eg: trcd_min = + * DDR3-800 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25) + */ + pdimm->trcd_ps = spd->trcd_min * mtb_ps + + (spd->fine_trcd_min * ftb_10th_ps) / 10; + + /* + * min row active to row active delay time + * eg: trrd_min = + * DDR3-800(1KB page) 80 MTB (10ns) + * DDR3-1333(1KB page) 48 MTB (6ns) + */ + pdimm->trrd_ps = spd->trrd_min * mtb_ps; + + /* + * min row precharge delay time + * eg: trp_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->trp_ps = spd->trp_min * mtb_ps + + (spd->fine_trp_min * ftb_10th_ps) / 10; + + /* min active to precharge delay time + * eg: tRAS_min = + * DDR3-800D 300 MTB (37.5ns) + * DDR3-1066F 300 MTB (37.5ns) + * DDR3-1333H 288 MTB (36ns) + * DDR3-1600H 280 MTB (35ns) + */ + pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) | spd->tras_min_lsb) + * mtb_ps; + /* + * min active to actice/refresh delay time + * eg: tRC_min = + * DDR3-800D 400 MTB (50ns) + * DDR3-1066F 405 MTB (50.625ns) + * DDR3-1333H 396 MTB (49.5ns) + * DDR3-1600H 370 MTB (46.25ns) + */ + pdimm->trc_ps = (((spd->tras_trc_ext & 0xf0) << 4) | spd->trc_min_lsb) + * mtb_ps + (spd->fine_trc_min * ftb_10th_ps) / 10; + /* + * min refresh recovery delay time + * eg: tRFC_min = + * 512Mb 720 MTB (90ns) + * 1Gb 880 MTB (110ns) + * 2Gb 1280 MTB (160ns) + */ + pdimm->trfc_ps = ((spd->trfc_min_msb << 8) | spd->trfc_min_lsb) + * mtb_ps; + /* + * min internal write to read command delay time + * eg: twtr_min = 40 MTB (7.5ns) - all speed bins. + * tWRT is at least 4 mclk independent of operating freq. + */ + pdimm->twtr_ps = spd->twtr_min * mtb_ps; + + /* + * min internal read to precharge command delay time + * eg: trtp_min = 40 MTB (7.5ns) - all speed bins. + * tRTP is at least 4 mclk independent of operating freq. + */ + pdimm->trtp_ps = spd->trtp_min * mtb_ps; + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + * = 3.9 us at ext temperature range + */ + pdimm->refresh_rate_ps = 7800000; + if ((spd->therm_ref_opt & 0x1) && !(spd->therm_ref_opt & 0x2)) { + pdimm->refresh_rate_ps = 3900000; + pdimm->extended_op_srt = 1; + } + + /* + * min four active window delay time + * eg: tfaw_min = + * DDR3-800(1KB page) 320 MTB (40ns) + * DDR3-1066(1KB page) 300 MTB (37.5ns) + * DDR3-1333(1KB page) 240 MTB (30ns) + * DDR3-1600(1KB page) 240 MTB (30ns) + */ + pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) + * mtb_ps; + + return 0; +} diff --git a/common/ddr4_dimm_params.c b/common/ddr4_dimm_params.c new file mode 100644 index 0000000000..045cbd457c --- /dev/null +++ b/common/ddr4_dimm_params.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2014-2016 Freescale Semiconductor, Inc. + * Copyright 2017-2018 NXP Semiconductor + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_12R23A.pdf + * + * + */ + +#include <common.h> +#include <ddr_dimms.h> + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Total DIMM size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * * Logical Ranks per DIMM + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte13[2:0] + * sdram width = spd byte12[2:0] + * Logical Ranks per DIMM = spd byte12[5:3] for SDP, DDP, QDP + * spd byte12{5:3] * spd byte6[6:4] for 3DS + * + * To simplify each rank size = total DIMM size / Number of Package Ranks + * where Number of Package Ranks = spd byte12[5:3] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * 0111 32Gb 4GB + * + * SPD byte13 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte12 - module organization + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + * + * SPD byte12 - module organization + * bit[5:3] number of package ranks per DIMM + * 000 1 + * 001 2 + * 010 3 + * 011 4 + * + * SPD byte6 - SDRAM package type + * bit[6:4] Die count + * 000 1 + * 001 2 + * 010 3 + * 011 4 + * 100 5 + * 101 6 + * 110 7 + * 111 8 + * + * SPD byte6 - SRAM package type + * bit[1:0] Signal loading + * 00 Not specified + * 01 Multi load stack + * 10 Sigle load stack (3DS) + * 11 Reserved + */ +static unsigned long long +compute_ranksize(const struct ddr4_spd_eeprom *spd) +{ + unsigned long long bsize; + + int nbit_sdram_cap_bsize = 0; + int nbit_primary_bus_width = 0; + int nbit_sdram_width = 0; + int die_count = 0; + bool package_3ds; + + if ((spd->density_banks & 0xf) <= 7) + nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + nbit_primary_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + nbit_sdram_width = (spd->organization & 0x7) + 2; + package_3ds = (spd->package_type & 0x3) == 0x2; + if ((spd->package_type & 0x80) && !package_3ds) { /* other than 3DS */ + printf("Warning: not supported SDRAM package type\n"); + return 0; + } + if (package_3ds) + die_count = (spd->package_type >> 4) & 0x7; + + bsize = 1ULL << (nbit_sdram_cap_bsize - 3 + + nbit_primary_bus_width - nbit_sdram_width + + die_count); + + debug("DDR: DDR rank density = 0x%16llx\n", bsize); + + return bsize; +} + +#define spd_to_ps(mtb, ftb) \ + (mtb * pdimm->mtb_ps + (ftb * pdimm->ftb_10th_ps) / 10) +/* + * ddr4_compute_dimm_parameters for DDR4 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the struct dimm_params structure pointed by pdimm. + * + */ +unsigned int ddr4_compute_dimm_parameters(const struct ddr4_spd_eeprom *spd, + struct dimm_params *pdimm) +{ + int ret; + int i; + const u8 udimm_rc_e_dq[18] = { + 0x0c, 0x2c, 0x15, 0x35, 0x15, 0x35, 0x0b, 0x2c, 0x15, + 0x35, 0x0b, 0x35, 0x0b, 0x2c, 0x0b, 0x35, 0x15, 0x36 + }; + int spd_error = 0; + u8 *ptr; + u8 val; + + if (spd->mem_type != SPD_MEMTYPE_DDR4) { + printf("DIMM: SPD data is not DDR4\n"); + return 3; + } + + ret = ddr4_spd_check(spd); + if (ret) { + printf("DIMM: failed checksum\n"); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + if ((spd->info_size_crc & 0xF) > 2) + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->die_density = spd->density_banks & 0xf; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->data_width = pdimm->primary_sdram_width + + pdimm->ec_sdram_width; + pdimm->device_width = 1 << ((spd->organization & 0x7) + 2); + pdimm->package_3ds = (spd->package_type & 0x3) == 0x2 ? + (spd->package_type >> 4) & 0x7 : 0; + + /* These are the types defined by the JEDEC SPD spec */ + pdimm->mirrored_dimm = 0; + pdimm->registered_dimm = 0; + switch (spd->module_type & DDR4_SPD_MODULETYPE_MASK) { + case DDR4_SPD_MODULETYPE_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + if (spd->mod_section.registered.reg_map & 0x1) + pdimm->mirrored_dimm = 1; + val = spd->mod_section.registered.ca_stren; + pdimm->rcw[3] = val >> 4; + pdimm->rcw[4] = ((val & 0x3) << 2) | ((val & 0xc) >> 2); + val = spd->mod_section.registered.clk_stren; + pdimm->rcw[5] = ((val & 0x3) << 2) | ((val & 0xc) >> 2); + /* Not all in SPD. For convience only. Boards may overwrite. */ + pdimm->rcw[6] = 0xf; + /* + * A17 only used for 16Gb and above devices. + * C[2:0] only used for 3DS. + */ + pdimm->rcw[8] = pdimm->die_density >= 0x6 ? 0x0 : 0x8 | + (pdimm->package_3ds > 0x3 ? 0x0 : + (pdimm->package_3ds > 0x1 ? 0x1 : + (pdimm->package_3ds > 0 ? 0x2 : 0x3))); + if (pdimm->package_3ds || pdimm->n_ranks != 4) + pdimm->rcw[13] = 0xc; + else + pdimm->rcw[13] = 0xd; /* Fix encoded by board */ + + break; + + case DDR4_SPD_MODULETYPE_UDIMM: + case DDR4_SPD_MODULETYPE_SO_DIMM: + /* Unbuffered DIMMs */ + if (spd->mod_section.unbuffered.addr_mapping & 0x1) + pdimm->mirrored_dimm = 1; + if ((spd->mod_section.unbuffered.mod_height & 0xe0) == 0 && + (spd->mod_section.unbuffered.ref_raw_card == 0x04)) { + /* Fix SPD error found on DIMMs with raw card E0 */ + for (i = 0; i < 18; i++) { + if (spd->mapping[i] == udimm_rc_e_dq[i]) + continue; + spd_error = 1; + debug("SPD byte %d: 0x%x, should be 0x%x\n", + 60 + i, spd->mapping[i], + udimm_rc_e_dq[i]); + ptr = (u8 *)&spd->mapping[i]; + *ptr = udimm_rc_e_dq[i]; + } + if (spd_error) + printf("SPD DQ mapping error fixed\n"); + } + break; + + default: + printf("unknown module_type 0x%02X\n", spd->module_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->bank_addr_bits = (spd->density_banks >> 4) & 0x3; + pdimm->bank_group_bits = (spd->density_banks >> 6) & 0x3; + + /* + * The SPD spec has not the ECC bit, + * We consider the DIMM as ECC capability + * when the extension bus exist + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec has not the burst length byte + * but DDR4 spec has nature BL8 and BC4, + * BL8 -bit3, BC4 -bit2 + */ + pdimm->burst_lengths_bitmask = 0x0c; + + /* MTB - medium timebase + * The MTB in the SPD spec is 125ps, + * + * FTB - fine timebase + * use 1/10th of ps as our unit to avoid floating point + * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps + */ + if ((spd->timebases & 0xf) == 0x0) { + pdimm->mtb_ps = 125; + pdimm->ftb_10th_ps = 10; + + } else { + printf("Unknown Timebases\n"); + } + + /* sdram minimum cycle time */ + pdimm->tckmin_x_ps = spd_to_ps(spd->tck_min, spd->fine_tck_min); + + /* sdram max cycle time */ + pdimm->tckmax_ps = spd_to_ps(spd->tck_max, spd->fine_tck_max); + + /* + * CAS latency supported + * bit0 - CL7 + * bit4 - CL11 + * bit8 - CL15 + * bit12- CL19 + * bit16- CL23 + */ + pdimm->caslat_x = (spd->caslat_b1 << 7) | + (spd->caslat_b2 << 15) | + (spd->caslat_b3 << 23); + + BUG_ON(spd->caslat_b4 != 0); + + /* + * min CAS latency time + */ + pdimm->taa_ps = spd_to_ps(spd->taa_min, spd->fine_taa_min); + + /* + * min RAS to CAS delay time + */ + pdimm->trcd_ps = spd_to_ps(spd->trcd_min, spd->fine_trcd_min); + + /* + * Min Row Precharge Delay Time + */ + pdimm->trp_ps = spd_to_ps(spd->trp_min, spd->fine_trp_min); + + /* min active to precharge delay time */ + pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) + + spd->tras_min_lsb) * pdimm->mtb_ps; + + /* min active to actice/refresh delay time */ + pdimm->trc_ps = spd_to_ps((((spd->tras_trc_ext & 0xf0) << 4) + + spd->trc_min_lsb), spd->fine_trc_min); + /* Min Refresh Recovery Delay Time */ + pdimm->trfc1_ps = ((spd->trfc1_min_msb << 8) | (spd->trfc1_min_lsb)) * + pdimm->mtb_ps; + pdimm->trfc2_ps = ((spd->trfc2_min_msb << 8) | (spd->trfc2_min_lsb)) * + pdimm->mtb_ps; + pdimm->trfc4_ps = ((spd->trfc4_min_msb << 8) | (spd->trfc4_min_lsb)) * + pdimm->mtb_ps; + /* min four active window delay time */ + pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) * + pdimm->mtb_ps; + + /* min row active to row active delay time, different bank group */ + pdimm->trrds_ps = spd_to_ps(spd->trrds_min, spd->fine_trrds_min); + /* min row active to row active delay time, same bank group */ + pdimm->trrdl_ps = spd_to_ps(spd->trrdl_min, spd->fine_trrdl_min); + /* min CAS to CAS Delay Time (tCCD_Lmin), same bank group */ + pdimm->tccdl_ps = spd_to_ps(spd->tccdl_min, spd->fine_tccdl_min); + + if (pdimm->package_3ds) { + if (pdimm->die_density <= 0x4) { + pdimm->trfc_slr_ps = 260000; + } else if (pdimm->die_density <= 0x5) { + pdimm->trfc_slr_ps = 350000; + } else { + printf("WARN: Unsupported logical rank density 0x%x\n", + pdimm->die_density); + } + } + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + */ + pdimm->refresh_rate_ps = 7800000; + + for (i = 0; i < 18; i++) + pdimm->dq_mapping[i] = spd->mapping[i]; + + pdimm->dq_mapping_ors = ((spd->mapping[0] >> 6) & 0x3) == 0 ? 1 : 0; + + return 0; +} diff --git a/common/ddr_spd.c b/common/ddr_spd.c index 7089923afb..b7693f3fd2 100644 --- a/common/ddr_spd.c +++ b/common/ddr_spd.c @@ -6,6 +6,7 @@ #include <common.h> #include <crc.h> #include <ddr_spd.h> +#include <pbl/i2c.h> /* used for ddr1 and ddr2 spd */ static int spd_check(const u8 *buf, u8 spd_rev, u8 spd_cksum) @@ -64,8 +65,8 @@ int ddr3_spd_check(const struct ddr3_spd_eeprom *spd) char *p = (char *)spd; int csum16; int len; - char crc_lsb; /* byte 126 */ - char crc_msb; /* byte 127 */ + unsigned char crc_lsb; /* byte 126 */ + unsigned char crc_msb; /* byte 127 */ /* * SPD byte0[7] - CRC coverage @@ -76,8 +77,8 @@ int ddr3_spd_check(const struct ddr3_spd_eeprom *spd) len = !(spd->info_size_crc & 0x80) ? 126 : 117; csum16 = crc_itu_t(0, p, len); - crc_lsb = (char) (csum16 & 0xff); - crc_msb = (char) (csum16 >> 8); + crc_lsb = csum16 & 0xff; + crc_msb = csum16 >> 8; if (spd->crc[0] == crc_lsb && spd->crc[1] == crc_msb) { return 0; @@ -95,14 +96,14 @@ int ddr4_spd_check(const struct ddr4_spd_eeprom *spd) char *p = (char *)spd; int csum16; int len; - char crc_lsb; /* byte 126 */ - char crc_msb; /* byte 127 */ + unsigned char crc_lsb; /* byte 126 */ + unsigned char crc_msb; /* byte 127 */ len = 126; csum16 = crc_itu_t(0, p, len); - crc_lsb = (char) (csum16 & 0xff); - crc_msb = (char) (csum16 >> 8); + crc_lsb = csum16 & 0xff; + crc_msb = csum16 >> 8; if (spd->crc[0] != crc_lsb || spd->crc[1] != crc_msb) { printf("SPD checksum unexpected.\n" @@ -116,8 +117,8 @@ int ddr4_spd_check(const struct ddr4_spd_eeprom *spd) len = 126; csum16 = crc_itu_t(0, p, len); - crc_lsb = (char) (csum16 & 0xff); - crc_msb = (char) (csum16 >> 8); + crc_lsb = csum16 & 0xff; + crc_msb = csum16 >> 8; if (spd->mod_section.uc[126] != crc_lsb || spd->mod_section.uc[127] != crc_msb) { @@ -230,11 +231,31 @@ static int ddr2_sdram_ctime(uint8_t byte) return ctime; } +static void spd_print_manufacturing_date(uint8_t year, uint8_t week) +{ + /* + * According to JEDEC Standard the year/week bytes must be in BCD + * format. However, that is not always true for actual DIMMs out + * there, so fall back to binary format if it makes more sense. + */ + + printf("%-48s ", "Manufacturing Date"); + if ((year & 0xf0) <= 0x90 && (year & 0xf) <= 0x9 + && (week & 0xf0) <= 0x90 && (week & 0xf) <= 0x9) { + printf("20%02X-W%02X", year, week); + } else if (year <= 99 && week >= 1 && week <= 53) { + printf("20%02d-W%02d", year, week); + } else { + printf("0x%02X%02X", year, week); + } + printf("\n"); +} + /* * Based on * https://github.com/groeck/i2c-tools/blob/master/eeprom/decode-dimms */ -void ddr_spd_print(uint8_t *record) +static void ddr2_spd_print(uint8_t *record) { int highestCAS = 0; int i, i_i, k, x, y; @@ -245,11 +266,6 @@ void ddr_spd_print(uint8_t *record) char *ref, *sum; struct ddr2_spd_eeprom *s = (struct ddr2_spd_eeprom *)record; - if (s->mem_type != SPD_MEMTYPE_DDR2) { - printf("Can't dump information for non-DDR2 memory\n"); - return; - } - ctime = ddr2_sdram_ctime(s->clk_cycle); ddrclk = 2 * (100000 / ctime); tbits = (s->res_7 << 8) + (s->dataw); @@ -419,20 +435,543 @@ void ddr_spd_print(uint8_t *record) printf("%d", record[i]); } printf("\n"); - printf("%-48s 20%d-W%d\n", "Manufacturing Date", record[93], - record[94]); + spd_print_manufacturing_date(record[93], record[94]); printf("%-48s 0x", "Assembly Serial Number"); for (i = 95; i < 99; i++) printf("%02X", record[i]); printf("\n"); } +static const char * const ddr3_spd_moduletypes[] = { + "Undefined", + "RDIMM", + "UDIMM", + "SO-DIMM", + "Micro-DIMM", + "Mini-RDIMM", + "Mini-UDIMM", + "Mini-CDIMM", + "72b-SO-UDIMM", + "72b-SO-RDIMM", + "72b-SO-CDIMM", + "LRDIMM", + "16b-SO-DIMM", + "32b-SO-DIMM" +}; + +static const char * const ddr3_spd_moduletypes_width[] = { + "Unknown", + "133.35 mm", + "133.35 mm", + "67.6 mm", + "TBD", + "82.0 mm", + "82.0 mm", + "67.6 mm", + "67.6 mm", + "67.6 mm", + "67.6 mm", + "133.35 mm", + "67.6 mm", + "67.6 mm" +}; + +#define DDR3_UNBUFFERED 1 +#define DDR3_REGISTERED 2 +#define DDR3_CLOCKED 3 +#define DDR3_LOAD_REDUCED 4 + +static const uint8_t ddr3_spd_moduletypes_family[] = { + 0, + DDR3_REGISTERED, + DDR3_UNBUFFERED, + DDR3_UNBUFFERED, + DDR3_UNBUFFERED, + DDR3_REGISTERED, + DDR3_UNBUFFERED, + DDR3_CLOCKED, + DDR3_UNBUFFERED, + DDR3_REGISTERED, + DDR3_CLOCKED, + DDR3_LOAD_REDUCED, + DDR3_UNBUFFERED, + DDR3_UNBUFFERED +}; + +static const int ddr3_std_speeds[] = { + 1000 * 7.5 / 8, + 1000 * 7.5 / 7, + 1000 * 1.25, + 1000 * 1.5, + 1000 * 1.875, + 1000 * 2.5 +}; + +static const char * const ddr3_maximum_activated_counts[] = { + "Untested", + "700 K", + "600 K", + "500 K", + "400 K", + "300 K", + "200 K", + "Reserved", + "Unlimited", +}; + +static int ddr3_timing_from_mtb_ftb(uint16_t txx, int8_t fine_txx, + uint8_t mtb_dividend, uint8_t mtb_divisor, + uint8_t ftb_div) +{ + /* + * Given mtb in ns and ftb in ps, return the result in ps, + * carefully rounding to the nearest picosecond. + */ + int result = txx * 10000 * mtb_dividend / mtb_divisor + + fine_txx * (ftb_div >> 4) / (ftb_div & 0xf); + return DIV_ROUND_CLOSEST(result, 10); +} + +static int ddr3_adjust_ctime(int ctime, uint8_t ftb_div) +{ + uint8_t ftb_divisor = ftb_div >> 4; + uint8_t ftb_dividend = ftb_div & 0xf; + int i; + + /* + * Starting with DDR3-1866, vendors may start approximating the + * minimum cycle time. Try to guess what they really meant so + * that the reported speed matches the standard. + */ + for (i = 7; i < 15; i++) { + int test = ctime * ftb_divisor - (int)(1000 * 7.5) * ftb_divisor / i; + + if (test > -(int)ftb_dividend && test < ftb_dividend) + return (int)(1000 * 7.5) / i; + } + + return ctime; +} + +static void ddr3_print_reference_card(uint8_t ref_raw_card, uint8_t mod_height) +{ + const char alphabet[] = "ABCDEFGHJKLMNPRTUVWY"; + uint8_t ref = ref_raw_card & 0x1f; + uint8_t revision = mod_height >> 5; + char ref_card[3] = { 0 }; + + if (ref == 0x1f) { + printf("%-48s %s\n", "Module Reference Card", "ZZ"); + return; + } + + if (ref_raw_card & 0x80) + ref += 0x1f; + if (!revision) + revision = ((ref_raw_card >> 5) & 0x3); + + if (ref < ARRAY_SIZE(alphabet) - 1) { + /* One letter reference card */ + ref_card[0] = alphabet[ref]; + } else { + /* Two letter reference card */ + uint8_t ref1 = ref / (ARRAY_SIZE(alphabet) - 1); + + ref -= (ARRAY_SIZE(alphabet) - 1) * ref1; + ref_card[0] = alphabet[ref1]; + ref_card[1] = alphabet[ref]; + } + + printf("%-48s %s revision %u\n", "Module Reference Card", ref_card, revision); +} + +static void ddr3_print_revision_number(const char *name, uint8_t rev) +{ + uint8_t h = rev >> 4; + uint8_t l = rev & 0xf; + + /* Decode as suggested by JEDEC Standard 21-C */ + if (!h) + printf("%-48s %d\n", name, l); + if (h < 0xa) + printf("%-48s %d.%d\n", name, h, l); + else + printf("%-48s %c%d\n", name, 'A' + h - 0xa, l); +} + +static void ddr3_spd_print(uint8_t *record) +{ + struct ddr3_spd_eeprom *s = (struct ddr3_spd_eeprom *)record; + const char *sum; + uint8_t spd_len; + int size, bytes_written; + int ctime, ddrclk; + uint8_t tbits; + int pcclk; + uint8_t cap, k; + int taa, trcd, trp, tras; + uint16_t cas_sup; + int twr, trrd, trc, trfc, twtr, trtp, tfaw; + uint8_t die_count, loading; + uint8_t mac; + uint8_t family; + int i; + + sum = ddr3_spd_check(s) ? "ERR" : "OK"; + + printf("\n---=== SPD EEPROM Information ===---\n"); + + printf("EEPROM CRC of bytes 0-%3d %22s %s (0x%02X%02X)\n", + (s->info_size_crc & 0x80) ? 116 : 125, "", sum, s->crc[1], s->crc[0]); + + spd_len = (s->info_size_crc >> 4) & 0x7; + size = 64 << (s->info_size_crc & 0xf); + if (spd_len == 0) { + bytes_written = 128; + } else if (spd_len == 1) { + bytes_written = 176; + } else if (spd_len == 2) { + bytes_written = 256; + } else { + size = 64; + bytes_written = 64; + } + printf("%-48s %d\n", "# of bytes written to SDRAM EEPROM", bytes_written); + printf("%-48s %d\n", "Total number of bytes in EEPROM", size); + printf("%-48s %s\n", "Fundamental Memory type", type_list[s->mem_type]); + + if (s->spd_rev != 0xff) + printf("%-48s %x.%x\n", "SPD Revision", s->spd_rev >> 4, + s->spd_rev & 0xf); + + printf("%-48s ", "Module Type"); + if (s->module_type <= ARRAY_SIZE(ddr3_spd_moduletypes)) + printf("%s\n", ddr3_spd_moduletypes[s->module_type]); + else + printf("Reserved (0x%02x)\n", s->module_type); + + if ((s->ftb_div & 0x0f) == 0 || s->mtb_divisor == 0) { + printf("Invalid time base divisor, can't decode\n"); + return; + } + + printf("\n---=== Memory Characteristics ===---\n"); + + ctime = ddr3_timing_from_mtb_ftb(s->tck_min, s->fine_tck_min, + s->mtb_dividend, s->mtb_divisor, s->ftb_div); + ctime = ddr3_adjust_ctime(ctime, s->ftb_div); + + ddrclk = 2 * 1000 * 1000 / ctime; + tbits = 1 << ((s->bus_width & 0x7) + 3); + pcclk = ddrclk * tbits / 8; + pcclk = pcclk - (pcclk % 100); /* Round down to comply with JEDEC */ + printf("%-48s %d MT/s (PC3-%d)\n", "Maximum module speed", ddrclk, pcclk); + + cap = (s->density_banks & 0xf) + 28 + (s->bus_width & 0x7) + 3 + - ((s->organization & 0x7) + 2) - (20 + 3); + k = (s->organization >> 3) + 1; + printf("%-48s %d MB\n", "Size", (1 << cap) * k); + + printf("%-48s %d x %d x %d x %d\n", "Banks x Rows x Columns x Bits", + 1 << (((s->density_banks >> 4) & 0x7) + 3), + (((s->addressing >> 3) & 0x1f) + 12), + ((s->addressing & 7) + 9), + (1 << ((s->bus_width & 0x7) + 3))); + + printf("%-48s %d\n", "Ranks", k); + + printf("%-48s %d bits\n", "SDRAM Device Width", + (1 << ((s->organization & 0x7) + 2))); + + printf("%-48s %d bits\n", "Primary Bus Width", + (8 << (s->bus_width & 0x7))); + if (s->bus_width & 0x18) + printf("%-48s %d bits\n", "Bus Width Extension", s->bus_width & 0x18); + + taa = ddr3_timing_from_mtb_ftb(s->taa_min, s->fine_taa_min, + s->mtb_dividend, s->mtb_divisor, s->ftb_div); + trcd = ddr3_timing_from_mtb_ftb(s->trcd_min, s->fine_trcd_min, + s->mtb_dividend, s->mtb_divisor, s->ftb_div); + trp = ddr3_timing_from_mtb_ftb(s->trp_min, s->fine_trp_min, + s->mtb_dividend, s->mtb_divisor, s->ftb_div); + tras = (((s->tras_trc_ext & 0xf) << 8) + s->tras_min_lsb) + * 1000 * s->mtb_dividend / s->mtb_divisor; + + printf("%-48s %d-%d-%d-%d\n", "tCL-tRCD-tRP-tRAS", + DIV_ROUND_UP(taa, ctime), DIV_ROUND_UP(trcd, ctime), + DIV_ROUND_UP(trp, ctime), DIV_ROUND_UP(tras, ctime)); + + printf("%-48s", "Supported CAS Latencies (tCL)"); + cas_sup = (s->caslat_msb << 8) + s->caslat_lsb; + for (i = 14; i >= 0; i--) { + if (cas_sup & (1 << i)) + printf(" %dT", i + 4); + } + printf("\n"); + + printf("\n---=== Timings at Standard Speeds ===---\n"); + + for (i = 0; i < ARRAY_SIZE(ddr3_std_speeds); i++) { + int ctime_at_speed = ddr3_std_speeds[i]; + int best_cas = 0; + int j; + + /* Find min CAS latency at this speed */ + for (j = 14; j >= 0; j--) { + if (!(cas_sup & (1 << j))) + continue; + if (DIV_ROUND_UP(taa, ctime_at_speed) <= j + 4) { + best_cas = j + 4; + } + } + + if (!best_cas || ctime_at_speed < ctime) + continue; + + printf("tCL-tRCD-tRP-tRAS as DDR3-%-4d %17s %d-%d-%d-%d\n", + 2000 * 1000 / ctime_at_speed, "", best_cas, + DIV_ROUND_UP(trcd, ctime_at_speed), + DIV_ROUND_UP(trp, ctime_at_speed), + DIV_ROUND_UP(tras, ctime_at_speed)); + } + + printf("\n---=== Timing Parameters ===---\n"); + + printf("%-48s %d.%03d ns\n", "Minimum Cycle Time (tCK)", + ctime / 1000, ctime % 1000); + printf("%-48s %d.%03d ns\n", "Minimum CAS Latency Time (tAA)", + taa / 1000, taa % 1000); + twr = s->twr_min * 1000 * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Write Recovery time (tWR)", + twr / 1000, twr % 1000); + printf("%-48s %d.%03d ns\n", "Minimum RAS# to CAS# Delay (tRCD)", + trcd / 1000, trcd % 1000); + trrd = s->trrd_min * 1000 * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Row Active to Row Active Delay (tRRD)", + trrd / 1000, trrd % 1000); + printf("%-48s %d.%03d ns\n", "Minimum Row Precharge Delay (tRP)", + trp / 1000, trp % 1000); + printf("%-48s %d.%03d ns\n", "Minimum Active to Precharge Delay (tRAS)", + tras / 1000, tras % 1000); + trc = ddr3_timing_from_mtb_ftb(((s->tras_trc_ext & 0xf0) << 4) + s->trc_min_lsb, + s->fine_trc_min, s->mtb_dividend, s->mtb_divisor, + s->ftb_div); + printf("%-48s %d.%03d ns\n", "Minimum Active to Auto-Refresh Delay (tRC)", + trc / 1000, trc % 1000); + trfc = ((s->trfc_min_msb << 8) + s->trfc_min_lsb) * 1000 + * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Recovery Delay (tRFC)", + trfc / 1000, trfc % 1000); + twtr = s->twtr_min * 1000 * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Write to Read CMD Delay (tWTR)", + twtr / 1000, twtr % 1000); + trtp = s->trtp_min * 1000 * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Read to Pre-charge CMD Delay (tRTP)", + trtp / 1000, trtp % 1000); + tfaw = (((s->tfaw_msb & 0xf) << 8) + s->tfaw_min) * 1000 + * s->mtb_dividend / s->mtb_divisor; + printf("%-48s %d.%03d ns\n", "Minimum Four Activate Window Delay (tFAW)", + tfaw / 1000, tfaw % 1000); + + printf("\n---=== Optional Features ===---\n"); + + printf("%-48s 1.5V%s%s%s\n", "Operable voltages", + (s->module_vdd & 0x1) ? " tolerant" : "", + (s->module_vdd & 0x2) ? ", 1.35V" : "", + (s->module_vdd & 0x4) ? ", 1.2X V" : ""); + printf("%-48s %s\n", "RZQ/6 supported?", + (s->opt_features & 1) ? "Yes" : "No"); + printf("%-48s %s\n", "RZQ/7 supported?", + (s->opt_features & 2) ? "Yes" : "No"); + printf("%-48s %s\n", "DLL-Off Mode supported?", + (s->opt_features & 0x80) ? "Yes" : "No"); + printf("%-48s 0-%d degrees C\n", "Operating temperature range", + (s->therm_ref_opt & 0x1) ? 95 : 85); + if (s->therm_ref_opt & 0x1) + printf("%-48s %s\n", "Refresh Rate in extended temp range", + (s->therm_ref_opt & 0x2) ? "1X" : "2X"); + printf("%-48s %s\n", "Auto Self-Refresh?", + (s->therm_ref_opt & 0x4) ? "Yes" : "No"); + printf("%-48s %s\n", "On-Die Thermal Sensor readout?", + (s->therm_ref_opt & 0x8) ? "Yes" : "No"); + printf("%-48s %s\n", "Partial Array Self-Refresh?", + (s->therm_ref_opt & 0x80) ? "Yes" : "No"); + printf("%-48s %s\n", "Module Thermal Sensor", + (s->therm_sensor & 0x80) ? "Yes" : "No"); + + printf("%-48s %s\n", "SDRAM Device Type", + (s->device_type & 0x80) ? "Non-Standard" : "Standard Monolithic"); + + die_count = (s->device_type >> 4) & 0x7; + if (die_count == 1) + printf("%-48s Single die\n", ""); + else if (die_count == 2) + printf("%-48s 2 die\n", ""); + else if (die_count == 3) + printf("%-48s 4 die\n", ""); + else if (die_count == 4) + printf("%-48s 8 die\n", ""); + + loading = (s->device_type >> 2) & 0x3; + if (loading == 1) + printf("%-48s Multi load stack\n", ""); + else if (loading == 2) + printf("%-48s Single load stack\n", ""); + + mac = s->res_39_59[2] & 0xf; + if (mac < ARRAY_SIZE(ddr3_maximum_activated_counts)) + printf("%-48s %s\n", "Maximum Activate Count (MAC)", + ddr3_maximum_activated_counts[mac]); + + /* + * The following bytes are type-specific, so don't continue if the type + * is unknown. + */ + if (!s->module_type || s->module_type > ARRAY_SIZE(ddr3_spd_moduletypes)) + return; + + family = ddr3_spd_moduletypes_family[s->module_type]; + if (family == DDR3_UNBUFFERED || family == DDR3_REGISTERED + || family == DDR3_CLOCKED || family == DDR3_LOAD_REDUCED) { + printf("\n---=== Physical Characteristics ===---\n"); + printf("%-48s %d mm\n", "Module Height", + ((s->mod_section.unbuffered.mod_height & 0x1f) + 15)); + printf("%-48s %d mm front, %d mm back\n", "Module Thickness", + (s->mod_section.unbuffered.mod_thickness & 0xf) + 1, + ((s->mod_section.unbuffered.mod_thickness >> 4) & 0xf) + 1); + printf("%-48s %s\n", "Module Width", + ddr3_spd_moduletypes_width[s->module_type]); + ddr3_print_reference_card(s->mod_section.unbuffered.ref_raw_card, + s->mod_section.unbuffered.mod_height); + } + + if (family == DDR3_UNBUFFERED) + printf("%-48s %s\n", "Rank 1 Mapping", + (s->mod_section.unbuffered.addr_mapping) ? "Mirrored" : "Standard"); + + if (family == DDR3_REGISTERED) { + uint8_t rows = (s->mod_section.registered.modu_attr >> 2) & 0x3; + uint8_t registers = s->mod_section.registered.modu_attr & 0x3; + + printf("\n---=== Registered DIMM ===---\n"); + + if (!rows) + printf("%-48s Undefined\n", "# DRAM Rows"); + else + printf("%-48s %u\n", "# DRAM Rows", 1 << (rows - 1)); + + if (!registers) + printf("%-48s Undefined\n", "# Registers"); + else + printf("%-48s %u\n", "# Registers", 1 << (registers - 1)); + printf("%-48s 0x%02x%02x\n", "Register manufacturer ID", + s->mod_section.registered.reg_id_hi, + s->mod_section.registered.reg_id_lo); + printf("%-48s %s\n", "Register device type", + (!s->mod_section.registered.reg_type) ? "SSTE32882" : "Undefined"); + if (s->mod_section.registered.reg_rev != 0xff) + ddr3_print_revision_number("Register revision", + s->mod_section.registered.reg_rev); + printf("%-48s %s\n", "Heat spreader", + (s->mod_section.registered.thermal & 0x80) ? "Yes" : "No"); + } + + if (family == DDR3_LOAD_REDUCED) { + uint8_t modu_attr = s->mod_section.loadreduced.modu_attr; + uint8_t rows = (modu_attr >> 2) & 0x3; + static const char *mirroring[] = { + "None", "Odd ranks", "Reserved", "Reserved" + }; + + printf("\n---=== Load Reduced DIMM ===---\n"); + + if (!rows) + printf("%-48s Undefined\n", "# DRAM Rows"); + else if (rows == 0x3) + printf("%-48s Reserved\n", "# DRAM Rows"); + else + printf("%-48s %u\n", "# DRAM Rows", 1 << (rows - 1)); + + printf("%-48s %s\n", "Mirroring", + mirroring[modu_attr & 0x3]); + + printf("%-48s %s\n", "Rank Numbering", + (modu_attr & 0x20) ? "Even only" : "Contiguous"); + printf("%-48s %s\n", "Buffer Orientation", + (modu_attr & 0x10) ? "Horizontal" : "Vertical"); + printf("%-48s 0x%02x%02x\n", "Register Manufacturer ID", + s->mod_section.loadreduced.buf_id_hi, + s->mod_section.loadreduced.buf_id_lo); + if (s->mod_section.loadreduced.buf_rev_id != 0xff) + ddr3_print_revision_number("Buffer Revision", + s->mod_section.loadreduced.buf_rev_id); + printf("%-48s %s\n", "Heat spreader", + (s->mod_section.loadreduced.modu_attr & 0x80) ? "Yes" : "No"); + } + + printf("\n---=== Manufacturer Data ===---\n"); + + printf("%-48s 0x%02x%02x\n", "Module Manufacturer ID", + s->mmid_msb, s->mmid_lsb); + + if (!((s->dmid_lsb == 0xff) && (s->dmid_msb == 0xff)) + && !((s->dmid_lsb == 0x0) && (s->dmid_msb == 0x0))) + printf("%-48s 0x%02x%02x\n", "DRAM Manufacturer ID", + s->dmid_msb, s->dmid_lsb); + + if (!(s->mloc == 0xff) && !(s->mloc == 0x0)) + printf("%-48s 0x%02x\n", "Manufacturing Location Code", + s->mloc); + + if (!((s->mdate[0] == 0xff) && (s->mdate[1] == 0xff)) + && !((s->mdate[0] == 0x0) && (s->mdate[1] == 0x0))) + spd_print_manufacturing_date(s->mdate[0], s->mdate[1]); + + if ((s->sernum[0] != s->sernum[1]) + && (s->sernum[0] != s->sernum[2]) + && (s->sernum[1] != s->sernum[3]) + && ((s->sernum[0] != 0xff) || (s->sernum[0] != 0x0))) + printf("%-48s 0x%02X%02X%02X%02X\n", "Assembly Serial Number", + s->sernum[0], s->sernum[1], s->sernum[2], s->sernum[3]); + + printf("%-48s ", "Part Number"); + if (!(s->mpart[0] >= 32 && s->mpart[0] < 127)) { + printf("Undefined\n"); + } else { + for (i = 0; i < ARRAY_SIZE(s->mpart); i++) { + if (s->mpart[i] >= 32 && s->mpart[i] < 127) + printf("%c", s->mpart[i]); + else + break; + } + printf("\n"); + } + + if (!((s->mrev[0] == 0xff) && (s->mrev[1] == 0xff)) + && !((s->mrev[0] == 0x0) && (s->mrev[1] == 0x0))) + printf("%-48s 0x%02X%02X\n", "Revision Code", s->mrev[0], s->mrev[1]); +} + +void ddr_spd_print(uint8_t *record) +{ + uint8_t mem_type = record[2]; + + switch (mem_type) { + case SPD_MEMTYPE_DDR2: + ddr2_spd_print(record); + break; + case SPD_MEMTYPE_DDR3: + ddr3_spd_print(record); + break; + default: + printf("Can only dump SPD information for DDR2 and DDR3 memory types\n"); + } +} + #define SPD_SPA0_ADDRESS 0x36 #define SPD_SPA1_ADDRESS 0x37 -static int select_page(void *ctx, - int (*xfer)(void *ctx, struct i2c_msg *msgs, int num), - uint8_t addr) +static int select_page(struct pbl_i2c *i2c, uint8_t addr) { struct i2c_msg msg = { .addr = addr, @@ -440,15 +979,14 @@ static int select_page(void *ctx, }; int ret; - ret = xfer(ctx, &msg, 1); + ret = pbl_i2c_xfer(i2c, &msg, 1); if (ret < 0) return ret; return 0; } -static int read_buf(void *ctx, - int (*xfer)(void *ctx, struct i2c_msg *msgs, int num), +static int read_buf(struct pbl_i2c *i2c, uint8_t addr, int page, void *buf) { uint8_t pos = 0; @@ -466,11 +1004,11 @@ static int read_buf(void *ctx, } }; - ret = select_page(ctx, xfer, page); + ret = select_page(i2c, page); if (ret < 0) return ret; - ret = xfer(ctx, msg, 2); + ret = pbl_i2c_xfer(i2c, msg, 2); if (ret < 0) return ret; @@ -479,29 +1017,32 @@ static int read_buf(void *ctx, /** * spd_read_eeprom - Read contents of a SPD EEPROM - * @ctx: Context pointer for the xfer function - * @xfer: I2C message transfer function + * @i2c: I2C controller handle * @addr: I2C bus address for the EEPROM * @buf: buffer to read the SPD data to + * @memtype: Memory type, such as SPD_MEMTYPE_DDR4 * * This function takes a I2C message transfer function and reads the contents * from a SPD EEPROM to the buffer provided at @buf. The buffer should at least * have a size of 512 bytes. Returns 0 for success or a negative error code * otherwise. */ -int spd_read_eeprom(void *ctx, - int (*xfer)(void *ctx, struct i2c_msg *msgs, int num), - uint8_t addr, void *buf) +int spd_read_eeprom(struct pbl_i2c *i2c, + uint8_t addr, void *buf, + int memtype) { - unsigned char *buf8 = buf; int ret; - ret = read_buf(ctx, xfer, addr, SPD_SPA0_ADDRESS, buf); - if (ret < 0) - return ret; + if (memtype == SPD_MEMTYPE_DDR4) { + ret = read_buf(i2c, addr, SPD_SPA0_ADDRESS, buf); + if (ret < 0) + return ret; - if (buf8[2] == SPD_MEMTYPE_DDR4) { - ret = read_buf(ctx, xfer, addr, SPD_SPA1_ADDRESS, buf + 256); + ret = read_buf(i2c, addr, SPD_SPA1_ADDRESS, buf + 256); + if (ret < 0) + return ret; + } else { + ret = read_buf(i2c, addr, 0, buf); if (ret < 0) return ret; } diff --git a/common/deep-probe.c b/common/deep-probe.c index 1020ad93b7..931e5a1770 100644 --- a/common/deep-probe.c +++ b/common/deep-probe.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) "deep-probe: " fmt + #include <common.h> #include <deep-probe.h> #include <of.h> @@ -27,7 +29,7 @@ bool deep_probe_is_supported(void) for (; matches->compatible; matches++) { if (of_machine_is_compatible(matches->compatible)) { boardstate = DEEP_PROBE_SUPPORTED; - printk("Deep probe supported due to %s\n", matches->compatible); + pr_info("supported due to %s\n", matches->compatible); return true; } } diff --git a/common/dlmalloc.c b/common/dlmalloc.c index ae10c9ae30..c41487d54b 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -1,8 +1,10 @@ #include <config.h> +#include <errno.h> #include <malloc.h> #include <string.h> #include <memory.h> +#include <linux/build_bug.h> #include <stdio.h> #include <module.h> @@ -627,7 +629,13 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /* conversion from malloc headers to user pointers, and back */ #define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ)) -#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +static inline mchunkptr mem2chunk(void *mem) +{ + OPTIMIZER_HIDE_VAR(mem); + + return mem - 2 * SIZE_SZ; +} /* pad request bytes into a usable size */ @@ -884,6 +892,8 @@ static mbinptr av_[NAV * 2 + 2] = { /* Other static bookkeeping data */ +static_assert(MALLOC_ALIGNMENT >= CONFIG_MALLOC_ALIGNMENT); + /* variables holding tunable values */ #ifndef __BAREBOX__ static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; @@ -1158,8 +1168,10 @@ void *malloc(size_t bytes) INTERNAL_SIZE_T nb; - if ((long) bytes < 0) + if ((long) bytes < 0) { + errno = ENOMEM; return NULL; + } nb = request2size(bytes); /* padded request size; */ @@ -1313,8 +1325,10 @@ void *malloc(size_t bytes) if ((remainder_size = chunksize (top) - nb) < (long) MINSIZE) { /* Try to extend */ malloc_extend_top(nb); - if ((remainder_size = chunksize(top) - nb) < (long) MINSIZE) + if ((remainder_size = chunksize(top) - nb) < (long) MINSIZE) { + errno = ENOMEM; return NULL; /* propagate failure */ + } } victim = top; @@ -1478,8 +1492,10 @@ void *realloc(void *oldmem, size_t bytes) } #endif - if ((long)bytes < 0) + if ((long)bytes < 0) { + errno = ENOMEM; return NULL; + } /* realloc of null is supposed to be same as malloc */ if (!oldmem) @@ -1645,8 +1661,10 @@ void *memalign(size_t alignment, size_t bytes) mchunkptr remainder; /* spare room at end to split off */ long remainder_size; /* its size */ - if ((long) bytes < 0) + if ((long) bytes < 0) { + errno = ENOMEM; return NULL; + } /* If need less alignment than we give anyway, just relay to malloc */ @@ -1680,8 +1698,8 @@ void *memalign(size_t alignment, size_t bytes) this is always possible. */ - brk = (char*) mem2chunk(((unsigned long) (m + alignment - 1)) & - -((signed) alignment)); + brk = (char*) mem2chunk((void *)(((unsigned long) (m + alignment - 1)) & + -((signed) alignment))); if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; @@ -1728,8 +1746,10 @@ void *calloc(size_t n, size_t elem_size) mchunkptr oldtop = top; INTERNAL_SIZE_T oldtopsize = chunksize(top); - if ((long)n < 0) + if ((long)n < 0) { + errno = ENOMEM; return NULL; + } mem = malloc(sz); diff --git a/common/dummy_malloc.c b/common/dummy_malloc.c index d99b5059cf..7a96afec76 100644 --- a/common/dummy_malloc.c +++ b/common/dummy_malloc.c @@ -23,7 +23,7 @@ void *memalign(size_t alignment, size_t bytes) void *malloc(size_t size) { - return memalign(8, size); + return memalign(CONFIG_MALLOC_ALIGNMENT, size); } void free(void *ptr) diff --git a/common/efi-devicepath.c b/common/efi-devicepath.c deleted file mode 100644 index f17b9294cc..0000000000 --- a/common/efi-devicepath.c +++ /dev/null @@ -1,1195 +0,0 @@ -#include <common.h> -#include <efi.h> -#include <malloc.h> -#include <string.h> -#include <wchar.h> - -struct string { - char *str; - int len; -}; - -char *cprintf(struct string *str, const char *fmt, ...) - __attribute__ ((format(__printf__, 2, 3))); - -char *cprintf(struct string *str, const char *fmt, ...) -{ - va_list args; - int len; - - va_start(args, fmt); - if (str->str) - len = vsprintf(str->str + str->len, fmt, args); - else - len = vsnprintf(NULL, 0, fmt, args); - va_end(args); - - str->len += len; - - return NULL; -} - -#define MIN_ALIGNMENT_SIZE 8 /* FIXME: X86_64 specific */ -#define ALIGN_SIZE(a) ((a % MIN_ALIGNMENT_SIZE) ? MIN_ALIGNMENT_SIZE - (a % MIN_ALIGNMENT_SIZE) : 0) - -#define EFI_DP_TYPE_MASK 0x7f -#define EFI_DP_TYPE_UNPACKED 0x80 - -#define END_DEVICE_PATH_TYPE 0x7f - -#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff -#define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01 -#define END_DEVICE_PATH_LENGTH (sizeof(struct efi_device_path)) - -#define DP_IS_END_TYPE(a) -#define DP_IS_END_SUBTYPE(a) ( ((a)->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE ) - -#define device_path_type(a) ( ((a)->type) & EFI_DP_TYPE_MASK ) -#define next_device_path_node(a) ( (struct efi_device_path *) ( ((u8 *) (a)) + (a)->length)) -#define is_device_path_end_type(a) ( device_path_type(a) == END_DEVICE_PATH_TYPE ) -#define is_device_path_end_sub_type(a) ( (a)->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE ) -#define is_device_path_end(a) ( is_device_path_end_type(a) && is_device_path_end_sub_type(a) ) -#define is_device_path_unpacked(a) ( (a)->type & EFI_DP_TYPE_UNPACKED ) - -#define set_device_path_end_node(a) { \ - (a)->type = END_DEVICE_PATH_TYPE; \ - (a)->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; \ - (a)->length = sizeof(struct efi_device_path); \ - } - -/* - * Hardware Device Path (UEFI 2.4 specification, version 2.4 § 9.3.2.) - */ - -#define HARDWARE_DEVICE_PATH 0x01 - -#define HW_PCI_DP 0x01 -struct pci_device_path { - struct efi_device_path header; - u8 Function; - u8 Device; -}; - -#define HW_PCCARD_DP 0x02 -struct pccard_device_path { - struct efi_device_path header; - u8 function_number; -}; - -#define HW_MEMMAP_DP 0x03 -struct memmap_device_path { - struct efi_device_path header; - u32 memory_type; - efi_physical_addr_t starting_address; - efi_physical_addr_t ending_address; -}; - -#define HW_VENDOR_DP 0x04 -struct vendor_device_path { - struct efi_device_path header; - efi_guid_t Guid; -}; - -struct unknown_device_vendor_device_path { - struct vendor_device_path device_path; - u8 legacy_drive_letter; -}; - -#define HW_CONTROLLER_DP 0x05 -struct controller_device_path { - struct efi_device_path header; - u32 Controller; -}; - -/* - * ACPI Device Path (UEFI 2.4 specification, version 2.4 § 9.3.3 and 9.3.4.) - */ -#define ACPI_DEVICE_PATH 0x02 - -#define ACPI_DP 0x01 -struct acpi_hid_device_path { - struct efi_device_path header; - u32 HID; - u32 UID; -}; - -#define EXPANDED_ACPI_DP 0x02 -struct expanded_acpi_hid_device_path { - struct efi_device_path header; - u32 HID; - u32 UID; - u32 CID; - u8 hid_str[1]; -}; - -#define ACPI_ADR_DP 3 -struct acpi_adr_device_path { - struct efi_device_path header; - u32 ADR; -}; - -/* - * EISA ID Macro - * EISA ID Definition 32-bits - * bits[15:0] - three character compressed ASCII EISA ID. - * bits[31:16] - binary number - * Compressed ASCII is 5 bits per character 0b00001 = 'A' 0b11010 = 'Z' - */ -#define PNP_EISA_ID_CONST 0x41d0 -#define EISA_ID(_Name, _Num) ((u32) ((_Name) | (_Num) << 16)) -#define EISA_PNP_ID(_PNPId) (EISA_ID(PNP_EISA_ID_CONST, (_PNPId))) - -#define PNP_EISA_ID_MASK 0xffff -#define EISA_ID_TO_NUM(_Id) ((_Id) >> 16) - -/* - * Messaging Device Path (UEFI 2.4 specification, version 2.4 § 9.3.5.) - */ -#define MESSAGING_DEVICE_PATH 0x03 - -#define MSG_ATAPI_DP 0x01 -struct atapi_device_path { - struct efi_device_path header; - u8 primary_secondary; - u8 slave_master; - u16 Lun; -}; - -#define MSG_SCSI_DP 0x02 -struct scsi_device_path { - struct efi_device_path header; - u16 Pun; - u16 Lun; -}; - -#define MSG_FIBRECHANNEL_DP 0x03 -struct fibrechannel_device_path { - struct efi_device_path header; - u32 Reserved; - u64 WWN; - u64 Lun; -}; - -/** - * Fibre Channel Ex sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.5.6. - */ -#define MSG_FIBRECHANNELEX_DP 21 -struct fibrechannelex_device_path { - struct efi_device_path header; - u32 Reserved; - u8 WWN[8]; /* World Wide Name */ - u8 Lun[8]; /* Logical unit, T-10 SCSI Architecture Model 4 specification */ -}; - -#define MSG_1394_DP 0x04 -struct f1394_device_path { - struct efi_device_path header; - u32 Reserved; - u64 Guid; -}; - -#define MSG_USB_DP 0x05 -struct usb_device_path { - struct efi_device_path header; - u8 Port; - u8 Endpoint; -}; - -/** - * SATA Device Path sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.5.6. - */ -#define MSG_SATA_DP 18 -struct sata_device_path { - struct efi_device_path header; - u16 HBAPort_number; - u16 port_multiplier_port_number; - u16 Lun; /* Logical Unit Number */ -}; - -/** - * USB WWID Device Path sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.5.7. - */ -#define MSG_USB_WWID_DP 16 -struct usb_wwid_device_path { - struct efi_device_path header; - u16 interface_number; - u16 vendor_id; - u16 product_id; - s16 serial_number[1]; /* UTF-16 characters of the USB serial number */ -}; - -/** - * Device Logical Unit sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.5.8. - */ -#define MSG_DEVICE_LOGICAL_UNIT_DP 17 -struct device_logical_unit_device_path { - struct efi_device_path header; - u8 Lun; /* Logical Unit Number */ -}; - -#define MSG_USB_CLASS_DP 0x0_f -struct usb_class_device_path { - struct efi_device_path header; - u16 vendor_id; - u16 product_id; - u8 device_class; - u8 device_subclass; - u8 device_protocol; -}; - -#define MSG_I2_o_DP 0x06 -struct i2_o_device_path { - struct efi_device_path header; - u32 Tid; -}; - -#define MSG_MAC_ADDR_DP 0x0b -struct mac_addr_device_path { - struct efi_device_path header; - efi_mac_address mac_address; - u8 if_type; -}; - -#define MSG_IPv4_DP 0x0c -struct ipv4_device_path { - struct efi_device_path header; - efi_ipv4_address local_ip_address; - efi_ipv4_address remote_ip_address; - u16 local_port; - u16 remote_port; - u16 Protocol; - bool static_ip_address; - /* new from UEFI version 2, code must check length field in header */ - efi_ipv4_address gateway_ip_address; - efi_ipv4_address subnet_mask; -}; - -#define MSG_IPv6_DP 0x0d -struct ipv6_device_path { - struct efi_device_path header; - efi_ipv6_address local_ip_address; - efi_ipv6_address remote_ip_address; - u16 local_port; - u16 remote_port; - u16 Protocol; - bool IPAddress_origin; - /* new from UEFI version 2, code must check length field in header */ - u8 prefix_length; - efi_ipv6_address gateway_ip_address; -}; - -/** - * Device Logical Unit sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.5.8. - */ -#define MSG_VLAN_DP 20 -struct vlan_device_path { - struct efi_device_path header; - u16 vlan_id; -}; - -#define MSG_INFINIBAND_DP 0x09 -struct infiniband_device_path { - struct efi_device_path header; - u32 resource_flags; - efi_guid_t port_gid; - u64 service_id; - u64 target_port_id; - u64 device_id; -}; - -#define MSG_UART_DP 0x0e -struct uart_device_path { - struct efi_device_path header; - u32 Reserved; - u64 baud_rate; - u8 data_bits; - u8 Parity; - u8 stop_bits; -}; - -#define MSG_VENDOR_DP 0x0a -/* Use VENDOR_DEVICE_PATH struct */ - -#define DEVICE_PATH_MESSAGING_PC_ANSI \ - { 0xe0c14753, 0xf9be, 0x11d2, {0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} } - -#define DEVICE_PATH_MESSAGING_VT_100 \ - { 0xdfa66065, 0xb419, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} } - -#define DEVICE_PATH_MESSAGING_VT_100_PLUS \ - { 0x7baec70b , 0x57e0 , 0x4c76 , { 0x8e , 0x87 , 0x2f , 0x9e , 0x28 , 0x08 , 0x83 , 0x43 } } - -#define DEVICE_PATH_MESSAGING_VT_UTF8 \ - { 0xad15a0d6 , 0x8bec , 0x4acf , { 0xa0 , 0x73 , 0xd0 , 0x1d , 0xe7 , 0x7e , 0x2d , 0x88 } } - -#define EFI_PC_ANSI_GUID \ - { 0xe0c14753 , 0xf9be , 0x11d2 , 0x9a , 0x0c , 0x00 , 0x90 , 0x27 , 0x3f , 0xc1 , 0x4d } - -#define EFI_VT_100_GUID \ - { 0xdfa66065 , 0xb419 , 0x11d3 , 0x9a , 0x2d , 0x00 , 0x90 , 0x27 , 0x3f , 0xc1 , 0x4d } - -#define EFI_VT_100_PLUS_GUID \ - { 0x7baec70b , 0x57e0 , 0x4c76 , 0x8e , 0x87 , 0x2f , 0x9e , 0x28 , 0x08 , 0x83 , 0x43 } - -#define EFI_VT_UTF8_GUID \ - { 0xad15a0d6 , 0x8bec , 0x4acf , 0xa0 , 0x73 , 0xd0 , 0x1d , 0xe7 , 0x7e , 0x2d , 0x88 } - -/* - * Media Device Path (UEFI 2.4 specification, version 2.4 § 9.3.6.) - */ -#define MEDIA_DEVICE_PATH 0x04 - -#define MEDIA_HARDDRIVE_DP 0x01 -struct harddrive_device_path { - struct efi_device_path header; - u32 partition_number; - u64 partition_start; - u64 partition_size; - u8 signature[16]; - u8 mbr_type; - u8 signature_type; -}; - -#define MBR_TYPE_PCAT 0x01 -#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 - -#define SIGNATURE_TYPE_MBR 0x01 -#define SIGNATURE_TYPE_GUID 0x02 - -#define MEDIA_CDROM_DP 0x02 -struct cdrom_device_path { - struct efi_device_path header; - u32 boot_entry; - u64 partition_start; - u64 partition_size; -}; - -#define MEDIA_VENDOR_DP 0x03 -/* Use VENDOR_DEVICE_PATH struct */ - -#define MEDIA_FILEPATH_DP 0x04 -struct filepath_device_path { - struct efi_device_path header; - s16 path_name[1]; -}; - -#define SIZE_OF_FILEPATH_DEVICE_PATH offsetof(FILEPATH_DEVICE_PATH,path_name) - -#define MEDIA_PROTOCOL_DP 0x05 -struct media_protocol_device_path { - struct efi_device_path header; - efi_guid_t Protocol; -}; - -/** - * PIWG Firmware File sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.6.6. - */ -#define MEDIA_PIWG_FW_FILE_DP 6 -struct media_fw_vol_filepath_device_path { - struct efi_device_path header; - efi_guid_t fv_file_name; -}; - -/** - * PIWG Firmware Volume Device Path sub_type. - * UEFI 2.0 specification version 2.4 § 9.3.6.7. - */ -#define MEDIA_PIWG_FW_VOL_DP 7 -struct media_fw_vol_device_path { - struct efi_device_path header; - efi_guid_t fv_name; -}; - -/** - * Media relative offset range device path. - * UEFI 2.0 specification version 2.4 § 9.3.6.8. - */ -#define MEDIA_RELATIVE_OFFSET_RANGE_DP 8 -struct media_relative_offset_range_device_path { - struct efi_device_path header; - u32 Reserved; - u64 starting_offset; - u64 ending_offset; -}; - -/* - * BIOS Boot Specification Device Path (UEFI 2.4 specification, version 2.4 § 9.3.7.) - */ -#define BBS_DEVICE_PATH 0x05 - -#define BBS_BBS_DP 0x01 -struct bbs_bbs_device_path { - struct efi_device_path header; - u16 device_type; - u16 status_flag; - s8 String[1]; -}; - -/* device_type definitions - from BBS specification */ -#define BBS_TYPE_FLOPPY 0x01 -#define BBS_TYPE_HARDDRIVE 0x02 -#define BBS_TYPE_CDROM 0x03 -#define BBS_TYPE_PCMCIA 0x04 -#define BBS_TYPE_USB 0x05 -#define BBS_TYPE_EMBEDDED_NETWORK 0x06 -#define BBS_TYPE_DEV 0x80 -#define BBS_TYPE_UNKNOWN 0x_fF - -struct efi_device_path end_device_path = { - .type = END_DEVICE_PATH_TYPE, - .sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE, - .length = END_DEVICE_PATH_LENGTH, -}; - -struct efi_device_path end_instance_device_path = { - .type = END_DEVICE_PATH_TYPE, - .sub_type = END_INSTANCE_DEVICE_PATH_SUBTYPE, - .length = END_DEVICE_PATH_LENGTH, -}; - -struct efi_device_path * -device_path_from_handle(efi_handle_t Handle) -{ - efi_status_t Status; - struct efi_device_path *device_path; - - Status = BS->handle_protocol(Handle, &efi_device_path_protocol_guid, - (void *) &device_path); - if (EFI_ERROR(Status)) - device_path = NULL; - - return device_path; -} - -static struct efi_device_path * -unpack_device_path(struct efi_device_path *dev_path) -{ - struct efi_device_path *Src, *Dest, *new_path; - unsigned long Size; - - /* Walk device path and round sizes to valid boundaries */ - - Src = dev_path; - Size = 0; - for (;;) { - Size += Src->length; - Size += ALIGN_SIZE(Size); - - if (is_device_path_end(Src)) { - break; - } - - Src = next_device_path_node(Src); - } - - new_path = xzalloc(Size); - - Src = dev_path; - Dest = new_path; - for (;;) { - Size = Src->length; - memcpy(Dest, Src, Size); - Size += ALIGN_SIZE(Size); - Dest->length = Size; - Dest->type |= EFI_DP_TYPE_UNPACKED; - Dest = - (struct efi_device_path *) (((u8 *) Dest) + Size); - - if (is_device_path_end(Src)) - break; - - Src = next_device_path_node(Src); - } - - return new_path; -} - -static void -dev_path_pci(struct string *str, void *dev_path) -{ - struct pci_device_path *Pci; - - Pci = dev_path; - cprintf(str, "Pci(0x%x,0x%x)", Pci->Device, Pci->Function); -} - -static void -dev_path_pccard(struct string *str, void *dev_path) -{ - struct pccard_device_path *Pccard; - - Pccard = dev_path; - cprintf(str, "Pccard(0x%x)", Pccard->function_number); -} - -static void -dev_path_mem_map(struct string *str, void *dev_path) -{ - struct memmap_device_path *mem_map; - - mem_map = dev_path; - cprintf(str, "mem_map(%d,0x%llx,0x%llx)", - mem_map->memory_type, - mem_map->starting_address, mem_map->ending_address); -} - -static void -dev_path_controller(struct string *str, void *dev_path) -{ - struct controller_device_path *Controller; - - Controller = dev_path; - cprintf(str, "Ctrl(%d)", Controller->Controller); -} - -static void -dev_path_vendor(struct string *str, void *dev_path) -{ - struct vendor_device_path *Vendor; - char *type; - struct unknown_device_vendor_device_path *unknown_dev_path; - - Vendor = dev_path; - switch (device_path_type(&Vendor->header)) { - case HARDWARE_DEVICE_PATH: - type = "Hw"; - break; - case MESSAGING_DEVICE_PATH: - type = "Msg"; - break; - case MEDIA_DEVICE_PATH: - type = "Media"; - break; - default: - type = "?"; - break; - } - - cprintf(str, "Ven%s(%pU", type, &Vendor->Guid); - if (efi_guidcmp(Vendor->Guid, efi_unknown_device_guid) == 0) { - /* GUID used by EFI to enumerate an EDD 1.1 device */ - unknown_dev_path = - (struct unknown_device_vendor_device_path *) Vendor; - cprintf(str, ":%02x)", unknown_dev_path->legacy_drive_letter); - } else { - cprintf(str, ")"); - } -} - -/* - type: 2 (ACPI Device Path) sub_type: 1 (ACPI Device Path) - */ -static void -dev_path_acpi(struct string *str, void *dev_path) -{ - struct acpi_hid_device_path *Acpi; - - Acpi = dev_path; - if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { - switch (EISA_ID_TO_NUM(Acpi->HID)) { - case 0x301: - cprintf(str, "Keyboard(%d)", Acpi->UID); - break; - - case 0x401: - cprintf(str, "parallel_port(%d)", Acpi->UID); - break; - case 0x501: - cprintf(str, "Serial(%d)", Acpi->UID); - break; - case 0x604: - cprintf(str, "Floppy(%d)", Acpi->UID); - break; - case 0xa03: - cprintf(str, "pci_root(%d)", Acpi->UID); - break; - case 0xa08: - cprintf(str, "pcie_root(%d)", Acpi->UID); - break; - default: - cprintf(str, "Acpi(PNP%04x", - EISA_ID_TO_NUM(Acpi->HID)); - if (Acpi->UID) - cprintf(str, ",%d", Acpi->UID); - cprintf(str, ")"); - break; - } - } else { - cprintf(str, "Acpi(0x%X", Acpi->HID); - if (Acpi->UID) - cprintf(str, ",%d", Acpi->UID); - cprintf(str, ")"); - } -} - -static void -dev_path_atapi(struct string *str, void *dev_path) -{ - struct atapi_device_path *Atapi; - - Atapi = dev_path; - cprintf(str, "Ata(%s,%s)", - Atapi->primary_secondary ? "Secondary" : "Primary", - Atapi->slave_master ? "Slave" : "Master"); -} - -static void -dev_path_scsi(struct string *str, void *dev_path) -{ - struct scsi_device_path *Scsi; - - Scsi = dev_path; - cprintf(str, "Scsi(%d,%d)", Scsi->Pun, Scsi->Lun); -} - -static void -dev_path_fibre(struct string *str, void *dev_path) -{ - struct fibrechannel_device_path *Fibre; - - Fibre = dev_path; - cprintf(str, "Fibre%s(0x%016llx,0x%016llx)", - device_path_type(&Fibre->header) == - MSG_FIBRECHANNEL_DP ? "" : "Ex", Fibre->WWN, Fibre->Lun); -} - -static void -dev_path1394(struct string *str, void *dev_path) -{ - struct f1394_device_path *F1394; - - F1394 = dev_path; - cprintf(str, "1394(%pU)", &F1394->Guid); -} - -static void -dev_path_usb(struct string *str, void *dev_path) -{ - struct usb_device_path *Usb; - - Usb = dev_path; - cprintf(str, "Usb(0x%x,0x%x)", Usb->Port, Usb->Endpoint); -} - -static void -dev_path_i2_o(struct string *str, void *dev_path) -{ - struct i2_o_device_path *i2_o; - - i2_o = dev_path; - cprintf(str, "i2_o(0x%X)", i2_o->Tid); -} - -static void -dev_path_mac_addr(struct string *str, void *dev_path) -{ - struct mac_addr_device_path *MAC; - unsigned long hw_address_size; - unsigned long Index; - - MAC = dev_path; - - /* hw_address_size = sizeof(EFI_MAC_ADDRESS); */ - hw_address_size = MAC->header.length; - hw_address_size -= sizeof (MAC->header); - hw_address_size -= sizeof (MAC->if_type); - if (MAC->if_type == 0x01 || MAC->if_type == 0x00) - hw_address_size = 6; - - cprintf(str, "Mac("); - - for (Index = 0; Index < hw_address_size; Index++) - cprintf(str, "%02x", MAC->mac_address.Addr[Index]); - - if (MAC->if_type != 0) - cprintf(str, ",%d", MAC->if_type); - - cprintf(str, ")"); -} - -static void -cat_print_iPv4(struct string *str, efi_ipv4_address * address) -{ - cprintf(str, "%d.%d.%d.%d", address->Addr[0], address->Addr[1], - address->Addr[2], address->Addr[3]); -} - -static bool -is_not_null_iPv4(efi_ipv4_address * address) -{ - u8 val; - - val = address->Addr[0] | address->Addr[1]; - val |= address->Addr[2] | address->Addr[3]; - - return val != 0; -} - -static void -cat_print_network_protocol(struct string *str, u16 Proto) -{ - if (Proto == 6) - cprintf(str, "TCP"); - else if (Proto == 17) - cprintf(str, "UDP"); - else - cprintf(str, "%d", Proto); -} - -static void -dev_path_iPv4(struct string *str, void *dev_path) -{ - struct ipv4_device_path *ip; - bool show; - - ip = dev_path; - cprintf(str, "IPv4("); - cat_print_iPv4(str, &ip->remote_ip_address); - cprintf(str, ","); - cat_print_network_protocol(str, ip->Protocol); - cprintf(str, ",%s", ip->static_ip_address ? "Static" : "DHCP"); - show = is_not_null_iPv4(&ip->local_ip_address); - if (!show - && ip->header.length == - sizeof (struct ipv4_device_path)) { - /* only version 2 includes gateway and netmask */ - show |= is_not_null_iPv4(&ip->gateway_ip_address); - show |= is_not_null_iPv4(&ip->subnet_mask); - } - if (show) { - cprintf(str, ","); - cat_print_iPv4(str, &ip->local_ip_address); - if (ip->header.length == - sizeof (struct ipv4_device_path)) { - /* only version 2 includes gateway and netmask */ - show = is_not_null_iPv4(&ip->gateway_ip_address); - show |= is_not_null_iPv4(&ip->subnet_mask); - if (show) { - cprintf(str, ","); - cat_print_iPv4(str, &ip->gateway_ip_address); - if (is_not_null_iPv4(&ip->subnet_mask)) { - cprintf(str, ","); - cat_print_iPv4(str, &ip->subnet_mask); - } - } - } - } - cprintf(str, ")"); -} - -#define cat_print_iPv6_ADD( x , y ) ( ( (u16) ( x ) ) << 8 | ( y ) ) -static void -cat_print_ipv6(struct string *str, efi_ipv6_address * address) -{ - cprintf(str, "%x:%x:%x:%x:%x:%x:%x:%x", - cat_print_iPv6_ADD(address->Addr[0], address->Addr[1]), - cat_print_iPv6_ADD(address->Addr[2], address->Addr[3]), - cat_print_iPv6_ADD(address->Addr[4], address->Addr[5]), - cat_print_iPv6_ADD(address->Addr[6], address->Addr[7]), - cat_print_iPv6_ADD(address->Addr[8], address->Addr[9]), - cat_print_iPv6_ADD(address->Addr[10], address->Addr[11]), - cat_print_iPv6_ADD(address->Addr[12], address->Addr[13]), - cat_print_iPv6_ADD(address->Addr[14], address->Addr[15])); -} - -static void -dev_path_iPv6(struct string *str, void *dev_path) -{ - struct ipv6_device_path *ip; - - ip = dev_path; - cprintf(str, "IPv6("); - cat_print_ipv6(str, &ip->remote_ip_address); - cprintf(str, ","); - cat_print_network_protocol(str, ip->Protocol); - cprintf(str, ",%s,", ip->IPAddress_origin ? - (ip->IPAddress_origin == 1 ? "stateless_auto_configure" : - "stateful_auto_configure") : "Static"); - cat_print_ipv6(str, &ip->local_ip_address); - if (ip->header.length == - sizeof (struct ipv6_device_path)) { - cprintf(str, ","); - cat_print_ipv6(str, &ip->gateway_ip_address); - cprintf(str, ","); - cprintf(str, "%d", ip->prefix_length); - } - cprintf(str, ")"); -} - -static void -dev_path_infini_band(struct string *str, void *dev_path) -{ - struct infiniband_device_path *infini_band; - - infini_band = dev_path; - cprintf(str, "Infiniband(0x%x,%pU,0x%llx,0x%llx,0x%llx)", - infini_band->resource_flags, &infini_band->port_gid, - infini_band->service_id, infini_band->target_port_id, - infini_band->device_id); -} - -static void -dev_path_uart(struct string *str, void *dev_path) -{ - struct uart_device_path *Uart; - s8 Parity; - - Uart = dev_path; - switch (Uart->Parity) { - case 0: - Parity = 'D'; - break; - case 1: - Parity = 'N'; - break; - case 2: - Parity = 'E'; - break; - case 3: - Parity = 'O'; - break; - case 4: - Parity = 'M'; - break; - case 5: - Parity = 'S'; - break; - default: - Parity = 'x'; - break; - } - - if (Uart->baud_rate == 0) - cprintf(str, "Uart(DEFAULT %c", Parity); - else - cprintf(str, "Uart(%lld %c", Uart->baud_rate, Parity); - - if (Uart->data_bits == 0) - cprintf(str, "D"); - else - cprintf(str, "%d", Uart->data_bits); - - switch (Uart->stop_bits) { - case 0: - cprintf(str, "D)"); - break; - case 1: - cprintf(str, "1)"); - break; - case 2: - cprintf(str, "1.5)"); - break; - case 3: - cprintf(str, "2)"); - break; - default: - cprintf(str, "x)"); - break; - } -} - -static void -dev_path_sata(struct string *str, void *dev_path) -{ - struct sata_device_path *sata; - - sata = dev_path; - cprintf(str, "Sata(0x%x,0x%x,0x%x)", sata->HBAPort_number, - sata->port_multiplier_port_number, sata->Lun); -} - -static void -dev_path_hard_drive(struct string *str, void *dev_path) -{ - struct harddrive_device_path *hd; - - hd = dev_path; - switch (hd->signature_type) { - case SIGNATURE_TYPE_MBR: - cprintf(str, "HD(Part%d,Sig%08x)", - hd->partition_number, *((u32 *) (&(hd->signature[0]))) - ); - break; - case SIGNATURE_TYPE_GUID: - cprintf(str, "HD(Part%d,Sig%pU)", - hd->partition_number, - (efi_guid_t *) & (hd->signature[0]) - ); - break; - default: - cprintf(str, "HD(Part%d,mbr_type=%02x,sig_type=%02x)", - hd->partition_number, hd->mbr_type, hd->signature_type); - break; - } -} - -static void -dev_path_cdrom(struct string *str, void *dev_path) -{ - struct cdrom_device_path *cd; - - cd = dev_path; - cprintf(str, "CDROM(0x%x)", cd->boot_entry); -} - -static void -dev_path_file_path(struct string *str, void *dev_path) -{ - struct filepath_device_path *Fp; - char *dst; - - Fp = dev_path; - - dst = strdup_wchar_to_char(Fp->path_name); - - cprintf(str, "%s", dst); - - free(dst); -} - -static void -dev_path_media_protocol(struct string *str, void *dev_path) -{ - struct media_protocol_device_path *media_prot; - - media_prot = dev_path; - cprintf(str, "%pU", &media_prot->Protocol); -} - -static void -dev_path_bss_bss(struct string *str, void *dev_path) -{ - struct bbs_bbs_device_path *Bss; - char *type; - - Bss = dev_path; - switch (Bss->device_type) { - case BBS_TYPE_FLOPPY: - type = "Floppy"; - break; - case BBS_TYPE_HARDDRIVE: - type = "Harddrive"; - break; - case BBS_TYPE_CDROM: - type = "CDROM"; - break; - case BBS_TYPE_PCMCIA: - type = "PCMCIA"; - break; - case BBS_TYPE_USB: - type = "Usb"; - break; - case BBS_TYPE_EMBEDDED_NETWORK: - type = "Net"; - break; - default: - type = "?"; - break; - } - - cprintf(str, "Bss-%s(%s)", type, Bss->String); -} - -static void -dev_path_end_instance(struct string *str, void *dev_path) -{ - cprintf(str, ","); -} - -/** - * Print unknown device node. - * UEFI 2.4 § 9.6.1.6 table 89. - */ - -static void -dev_path_node_unknown(struct string *str, void *dev_path) -{ - struct efi_device_path *Path; - u8 *value; - int length, index; - Path = dev_path; - value = dev_path; - value += 4; - switch (Path->type) { - case HARDWARE_DEVICE_PATH:{ - /* Unknown Hardware Device Path */ - cprintf(str, "hardware_path(%d", Path->sub_type); - break; - } - case ACPI_DEVICE_PATH:{/* Unknown ACPI Device Path */ - cprintf(str, "acpi_path(%d", Path->sub_type); - break; - } - case MESSAGING_DEVICE_PATH:{ - /* Unknown Messaging Device Path */ - cprintf(str, "Msg(%d", Path->sub_type); - break; - } - case MEDIA_DEVICE_PATH:{ - /* Unknown Media Device Path */ - cprintf(str, "media_path(%d", Path->sub_type); - break; - } - case BBS_DEVICE_PATH:{ /* Unknown BIOS Boot Specification Device Path */ - cprintf(str, "bbs_path(%d", Path->sub_type); - break; - } - default:{ /* Unknown Device Path */ - cprintf(str, "Path(%d,%d", Path->type, Path->sub_type); - break; - } - } - length = Path->length; - for (index = 0; index < length; index++) { - if (index == 0) - cprintf(str, ",0x"); - cprintf(str, "%02x", *value); - value++; - } - cprintf(str, ")"); -} - -/* - * Table to convert "type" and "sub_type" to a "convert to text" function/ - * Entries hold "type" and "sub_type" for know values. - * Special "sub_type" 0 is used as default for known type with unknown subtype. - */ -struct { - u8 type; - u8 sub_type; - void (*Function) (struct string *, void *); -} dev_path_table[] = { - { - HARDWARE_DEVICE_PATH, HW_PCI_DP, dev_path_pci}, { - HARDWARE_DEVICE_PATH, HW_PCCARD_DP, dev_path_pccard}, { - HARDWARE_DEVICE_PATH, HW_MEMMAP_DP, dev_path_mem_map}, { - HARDWARE_DEVICE_PATH, HW_VENDOR_DP, dev_path_vendor}, { - HARDWARE_DEVICE_PATH, HW_CONTROLLER_DP, dev_path_controller}, { - ACPI_DEVICE_PATH, ACPI_DP, dev_path_acpi}, { - MESSAGING_DEVICE_PATH, MSG_ATAPI_DP, dev_path_atapi}, { - MESSAGING_DEVICE_PATH, MSG_SCSI_DP, dev_path_scsi}, { - MESSAGING_DEVICE_PATH, MSG_FIBRECHANNEL_DP, dev_path_fibre}, { - MESSAGING_DEVICE_PATH, MSG_1394_DP, dev_path1394}, { - MESSAGING_DEVICE_PATH, MSG_USB_DP, dev_path_usb}, { - MESSAGING_DEVICE_PATH, MSG_I2_o_DP, dev_path_i2_o}, { - MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP, dev_path_mac_addr}, { - MESSAGING_DEVICE_PATH, MSG_IPv4_DP, dev_path_iPv4}, { - MESSAGING_DEVICE_PATH, MSG_IPv6_DP, dev_path_iPv6}, { - MESSAGING_DEVICE_PATH, MSG_INFINIBAND_DP, dev_path_infini_band}, { - MESSAGING_DEVICE_PATH, MSG_UART_DP, dev_path_uart}, { - MESSAGING_DEVICE_PATH, MSG_SATA_DP, dev_path_sata}, { - MESSAGING_DEVICE_PATH, MSG_VENDOR_DP, dev_path_vendor}, { - MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP, dev_path_hard_drive}, { - MEDIA_DEVICE_PATH, MEDIA_CDROM_DP, dev_path_cdrom}, { - MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, dev_path_vendor}, { - MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP, dev_path_file_path}, { - MEDIA_DEVICE_PATH, MEDIA_PROTOCOL_DP, dev_path_media_protocol}, { - BBS_DEVICE_PATH, BBS_BBS_DP, dev_path_bss_bss}, { - END_DEVICE_PATH_TYPE, END_INSTANCE_DEVICE_PATH_SUBTYPE, - dev_path_end_instance}, { - 0, 0, NULL} -}; - -static int __device_path_to_str(struct string *str, struct efi_device_path *dev_path) -{ - struct efi_device_path *dev_path_node; - void (*dump_node) (struct string *, void *); - int i; - - dev_path = unpack_device_path(dev_path); - - dev_path_node = dev_path; - while (!is_device_path_end(dev_path_node)) { - dump_node = NULL; - for (i = 0; dev_path_table[i].Function; i += 1) { - - if (device_path_type(dev_path_node) == - dev_path_table[i].type - && dev_path_node->sub_type == - dev_path_table[i].sub_type) { - dump_node = dev_path_table[i].Function; - break; - } - } - - if (!dump_node) - dump_node = dev_path_node_unknown; - - if (str->len && dump_node != dev_path_end_instance) - cprintf(str, "/"); - - dump_node(str, dev_path_node); - - dev_path_node = next_device_path_node(dev_path_node); - } - - return 0; -} - -char *device_path_to_str(struct efi_device_path *dev_path) -{ - struct string str = {}; - - __device_path_to_str(&str, dev_path); - - str.str = malloc(str.len + 1); - if (!str.str) - return NULL; - - str.len = 0; - - __device_path_to_str(&str, dev_path); - - return str.str; -} - -u8 device_path_to_type(struct efi_device_path *dev_path) -{ - struct efi_device_path *dev_path_next; - - dev_path = unpack_device_path(dev_path); - dev_path_next = next_device_path_node(dev_path); - - while (!is_device_path_end(dev_path_next)) { - dev_path = dev_path_next; - dev_path_next = next_device_path_node(dev_path); - } - - return device_path_type(dev_path); -} - -char *device_path_to_partuuid(struct efi_device_path *dev_path) -{ - struct efi_device_path *dev_path_node; - struct harddrive_device_path *hd; - char *str = NULL;; - - dev_path = unpack_device_path(dev_path); - - for (dev_path_node = dev_path; !is_device_path_end(dev_path_node); - dev_path_node = next_device_path_node(dev_path_node)) { - - if (device_path_type(dev_path_node) != MEDIA_DEVICE_PATH) - continue; - - if (dev_path_node->sub_type != MEDIA_HARDDRIVE_DP) - continue; - - hd = (struct harddrive_device_path *)dev_path_node; - - if (hd->signature_type != SIGNATURE_TYPE_GUID) - continue; - - str = xasprintf("%pUl", (efi_guid_t *)&(hd->signature[0])); - break; - } - - return str; -} - diff --git a/common/efi-guid.c b/common/efi-guid.c deleted file mode 100644 index 2bf2395e85..0000000000 --- a/common/efi-guid.c +++ /dev/null @@ -1,93 +0,0 @@ -#include <common.h> -#include <efi.h> - -efi_guid_t efi_file_info_id = EFI_FILE_INFO_GUID; -efi_guid_t efi_simple_file_system_protocol_guid = EFI_SIMPLE_FILE_SYSTEM_GUID; -efi_guid_t efi_device_path_protocol_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; -efi_guid_t efi_loaded_image_protocol_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; -efi_guid_t efi_unknown_device_guid = EFI_UNKNOWN_DEVICE_GUID; -efi_guid_t efi_null_guid = EFI_NULL_GUID; -efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; -efi_guid_t efi_block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID; -efi_guid_t efi_barebox_vendor_guid = EFI_BAREBOX_VENDOR_GUID; -efi_guid_t efi_systemd_vendor_guid = EFI_SYSTEMD_VENDOR_GUID; - -#define EFI_GUID_STRING(guid, short, long) do { \ - if (!efi_guidcmp(guid, *g)) \ - return long; \ - } while(0) - -const char *efi_guid_string(efi_guid_t *g) -{ - EFI_GUID_STRING(EFI_NULL_GUID, "NULL", "NULL GUID"); - EFI_GUID_STRING(EFI_MPS_TABLE_GUID, "MPS Table", "MPS Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_ACPI_TABLE_GUID, "ACPI Table", "ACPI 1.0 Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_ACPI_20_TABLE_GUID, "ACPI 2.0 Table", "ACPI 2.0 Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_SMBIOS_TABLE_GUID, "SMBIOS Table", "SMBIOS Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_SAL_SYSTEM_TABLE_GUID, "SAL System Table", "SAL System Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_HCDP_TABLE_GUID, "HDCP Table", "HDCP Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_UGA_IO_PROTOCOL_GUID, "UGA Protocol", "EFI 1.1 UGA Protocol"); - EFI_GUID_STRING(EFI_GLOBAL_VARIABLE_GUID, "Efi", "Efi Variable GUID"); - EFI_GUID_STRING(EFI_UV_SYSTEM_TABLE_GUID, "SAL System Table", "SAL System Table GUID in EFI System Table"); - EFI_GUID_STRING(EFI_LINUX_EFI_CRASH_GUID, "Linux EFI Crash", "Linux EFI Crash GUID"); - EFI_GUID_STRING(EFI_LOADED_IMAGE_PROTOCOL_GUID, "LoadedImage Protocol", "EFI 1.0 Loaded Image Protocol"); - EFI_GUID_STRING(EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, "EFI Graphics Output Protocol", "UEFI Graphics Output Protocol"); - EFI_GUID_STRING(EFI_UGA_PROTOCOL_GUID, "UGA Draw Protocol", "EFI 1.1 UGA Draw Protocol"); - EFI_GUID_STRING(EFI_UGA_IO_PROTOCOL_GUID, "UGA Protocol", "EFI 1.1 UGA Protocol"); - EFI_GUID_STRING(EFI_PCI_IO_PROTOCOL_GUID, "PCI IO Protocol", "EFI 1.1 PCI IO Protocol"); - EFI_GUID_STRING(EFI_USB_IO_PROTOCOL_GUID, "USB IO Protocol", "EFI 1.0 USB IO Protocol"); - EFI_GUID_STRING(EFI_FILE_INFO_GUID, "File Info", "EFI File Infom"); - EFI_GUID_STRING(EFI_SIMPLE_FILE_SYSTEM_GUID, "Filesystem", "EFI 1.0 Simple FileSystem"); - EFI_GUID_STRING(EFI_DEVICE_TREE_GUID, "Device Tree", "EFI Device Tree GUID"); - EFI_GUID_STRING(EFI_DEVICE_PATH_PROTOCOL_GUID, "Device Path Protocol", "EFI 1.0 Device Path protocol"); - EFI_GUID_STRING(EFI_SIMPLE_NETWORK_PROTOCOL_GUID, "Simple Network Protocol", "EFI 1.0 Simple Network Protocol"); - EFI_GUID_STRING(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, "Filesystem Protocol", "EFI 1.0 Simple FileSystem Protocol"); - EFI_GUID_STRING(EFI_UNKNOWN_DEVICE_GUID, "Efi Unknown Device", "Efi Unknown Device GUID"); - EFI_GUID_STRING(EFI_BLOCK_IO_PROTOCOL_GUID, "BlockIo Protocol", "EFI 1.0 Block IO protocol"); - - EFI_GUID_STRING(EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID, "FirmwareVolume2Protocol", "Efi FirmwareVolume2Protocol"); - EFI_GUID_STRING(EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID, "FirmwareVolumeBlock Protocol", "Firmware Volume Block protocol"); - EFI_GUID_STRING(EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID, "PciRootBridgeIo Protocol", "EFI 1.1 Pci Root Bridge IO Protocol"); - EFI_GUID_STRING(EFI_ISA_ACPI_PROTOCOL_GUID, "ISA Acpi Protocol", "ISA Acpi Protocol"); - EFI_GUID_STRING(EFI_ISA_IO_PROTOCOL_GUID, "ISA IO Protocol", "ISA IO Protocol"); - EFI_GUID_STRING(EFI_STANDARD_ERROR_DEVICE_GUID, "Standard Error Device Guid", "EFI Standard Error Device Guid"); - EFI_GUID_STRING(EFI_CONSOLE_OUT_DEVICE_GUID, "Console Out Device Guid", "EFI Console Out Device Guid"); - EFI_GUID_STRING(EFI_CONSOLE_IN_DEVICE_GUID, "Console In Device Guid", "EFI Console In Device Guid"); - EFI_GUID_STRING(EFI_SIMPLE_TEXT_OUT_PROTOCOL_GUID, "Simple Text Out Protocol", "EFI 1.0 Simple Text Out Protocol"); - EFI_GUID_STRING(EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID, "Simple Text Input Ex Protocol", "UEFI 2.1 Simple Text Input Ex Protocol"); - EFI_GUID_STRING(EFI_SIMPLE_TEXT_IN_PROTOCOL_GUID, "Simple Text In Protocol", "EFI 1.0 Simple Text In Protocol"); - EFI_GUID_STRING(EFI_DISK_IO_PROTOCOL_GUID, "DiskIo Protocol", "EFI 1.0 Disk IO Protocol"); - EFI_GUID_STRING(EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID, "IDE Controller Init Protocol", "Platform IDE Init Protocol"); - EFI_GUID_STRING(EFI_DISK_INFO_PROTOCOL_GUID, "Disk Info Protocol", "Disk Info Protocol"); - EFI_GUID_STRING(EFI_SERIAL_IO_PROTOCOL_GUID, "SerialIo Protocol", "EFI 1.0 Serial IO Protocol"); - EFI_GUID_STRING(EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID, "Bus Specific Driver Override Protocol", "EFI 1.1 Bus Specific Driver Override Protocol"); - EFI_GUID_STRING(EFI_LOAD_FILE2_PROTOCOL_GUID, "LoadFile2 Protocol", "EFI Load File 2 Protocol"); - EFI_GUID_STRING(EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID, "MTFTP4 Service Binding Protocol", "MTFTP4 Service Binding Protocol"); - EFI_GUID_STRING(EFI_DHCP4_PROTOCOL_GUID, "DHCP4 Protocol", "DHCP4 Protocol"); - EFI_GUID_STRING(EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID, "UDP4 Service Binding Protocol", "UDP4 Service Binding Protocol"); - EFI_GUID_STRING(EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID, "TCP4 Service Binding Protocol", "TCP4 Service Binding Protocol"); - EFI_GUID_STRING(EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID, "IP4 Service Binding Protocol", "IP4 Service Binding Protocol"); - EFI_GUID_STRING(EFI_IP4_CONFIG_PROTOCOL_GUID, "Ip4Config Protocol", "Ip4Config Protocol"); - EFI_GUID_STRING(EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID, "ARP Service Binding Protocol", "ARP Service Binding Protocol"); - EFI_GUID_STRING(EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID, "Managed Network Service Binding Protocol", "Managed Network Service Binding Protocol"); - EFI_GUID_STRING(EFI_VLAN_CONFIG_PROTOCOL_GUID, "VlanConfig Protocol", "VlanConfig Protocol"); - EFI_GUID_STRING(EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID, "HII Config Access Protocol", "HII Config Access 2.1 protocol"); - EFI_GUID_STRING(LOAD_FILE_PROTOCOL_GUID, "LoadFile Protocol", "EFI 1.0 Load File Protocol"); - EFI_GUID_STRING(EFI_COMPONENT_NAME2_PROTOCOL_GUID, "Component Name2 Protocol", "UEFI 2.0 Component Name2 Protocol"); - EFI_GUID_STRING(EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID_31, "Network Interface Identifier Protocol_31", "EFI1.1 Network Interface Identifier Protocol"); - EFI_GUID_STRING(EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID, "Network Interface Identifier Protocol", "EFI Network Interface Identifier Protocol"); - EFI_GUID_STRING(EFI_TIMESTAMP_PROTOCOL_GUID, "Timestamp", "Timestamp"); - - /* TPM 1.2 */ - EFI_GUID_STRING( EFI_TCG_PROTOCOL_GUID, "TcgService", "TCGServices Protocol"); - /* TPM 2.0 */ - EFI_GUID_STRING( EFI_TCG2_PROTOCOL_GUID, "Tcg2Service", "TCG2Services Protocol"); - - /* File */ - EFI_GUID_STRING(EFI_IDEBUSDXE_INF_GUID, "IdeBusDxe.inf", "EFI IdeBusDxe.inf File GUID"); - EFI_GUID_STRING(EFI_TERMINALDXE_INF_GUID, "TerminalDxe.inf", "EFI TerminalDxe.inf File GUID"); - EFI_GUID_STRING(EFI_ISCSIDXE_INF_GUID, "IScsiDxe.inf", "EFI IScsiDxe.inf File GUID"); - EFI_GUID_STRING(EFI_VLANCONFIGDXE_INF_GUID, "VlanConfigDxe.inf", "EFI VlanConfigDxe.inf File GUID"); - - return "unknown"; -} diff --git a/common/efi/Makefile b/common/efi/Makefile deleted file mode 100644 index d746fabe21..0000000000 --- a/common/efi/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-y += efi.o -obj-y += efi-image.o -bbenv-y += env-efi -obj-$(CONFIG_CMD_IOMEM) += efi-iomem.o diff --git a/common/efi/efi-image.c b/common/efi/efi-image.c deleted file mode 100644 index bd1c58438e..0000000000 --- a/common/efi/efi-image.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * efi-image.c - barebox EFI payload support - * - * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <clock.h> -#include <common.h> -#include <linux/sizes.h> -#include <memory.h> -#include <command.h> -#include <magicvar.h> -#include <init.h> -#include <driver.h> -#include <io.h> -#include <efi.h> -#include <malloc.h> -#include <string.h> -#include <linux/err.h> -#include <boot.h> -#include <bootm.h> -#include <fs.h> -#include <libfile.h> -#include <binfmt.h> -#include <wchar.h> -#include <efi/efi.h> -#include <efi/efi-device.h> - -struct linux_kernel_header { - /* first sector of the image */ - uint8_t code1[0x0020]; - uint16_t cl_magic; /**< Magic number 0xA33F */ - uint16_t cl_offset; /**< The offset of command line */ - uint8_t code2[0x01F1 - 0x0020 - 2 - 2]; - uint8_t setup_sects; /**< The size of the setup in sectors */ - uint16_t root_flags; /**< If the root is mounted readonly */ - uint16_t syssize; /**< obsolete */ - uint16_t swap_dev; /**< obsolete */ - uint16_t ram_size; /**< obsolete */ - uint16_t vid_mode; /**< Video mode control */ - uint16_t root_dev; /**< Default root device number */ - uint16_t boot_flag; /**< 0xAA55 magic number */ - - /* second sector of the image */ - uint16_t jump; /**< Jump instruction (this is code!) */ - uint32_t header; /**< Magic signature "HdrS" */ - uint16_t version; /**< Boot protocol version supported */ - uint32_t realmode_swtch; /**< Boot loader hook */ - uint16_t start_sys; /**< The load-low segment (obsolete) */ - uint16_t kernel_version; /**< Points to kernel version string */ - uint8_t type_of_loader; /**< Boot loader identifier */ - uint8_t loadflags; /**< Boot protocol option flags */ - uint16_t setup_move_size; /**< Move to high memory size */ - uint32_t code32_start; /**< Boot loader hook */ - uint32_t ramdisk_image; /**< initrd load address */ - uint32_t ramdisk_size; /**< initrd size */ - uint32_t bootsect_kludge; /**< obsolete */ - uint16_t heap_end_ptr; /**< Free memory after setup end */ - uint8_t ext_loader_ver; /**< boot loader's extension of the version number */ - uint8_t ext_loader_type; /**< boot loader's extension of its type */ - uint32_t cmd_line_ptr; /**< Points to the kernel command line */ - uint32_t initrd_addr_max; /**< Highest address for initrd */ - uint32_t kernel_alignment; /**< Alignment unit required by the kernel */ - uint8_t relocatable_kernel; /** */ - uint8_t min_alignment; /** */ - uint16_t xloadflags; /** */ - uint32_t cmdline_size; /** */ - uint32_t hardware_subarch; /** */ - uint64_t hardware_subarch_data; /** */ - uint32_t payload_offset; /** */ - uint32_t payload_length; /** */ - uint64_t setup_data; /** */ - uint64_t pref_address; /** */ - uint32_t init_size; /** */ - uint32_t handover_offset; /** */ -} __attribute__ ((packed)); - -static int efi_load_image(const char *file, efi_loaded_image_t **loaded_image, - efi_handle_t *h) -{ - void *exe; - size_t size; - efi_handle_t handle; - efi_status_t efiret = EFI_SUCCESS; - - exe = read_file(file, &size); - if (!exe) - return -EINVAL; - - efiret = BS->load_image(false, efi_parent_image, efi_device_path, exe, size, - &handle); - if (EFI_ERROR(efiret)) { - pr_err("failed to LoadImage: %s\n", efi_strerror(efiret)); - goto out; - }; - - efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid, - (void **)loaded_image, - efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(efiret)) { - pr_err("failed to OpenProtocol: %s\n", efi_strerror(efiret)); - BS->unload_image(handle); - goto out; - } - - *h = handle; -out: - memset(exe, 0, size); - free(exe); - return -efi_errno(efiret); -} - -static int efi_execute_image(const char *file) -{ - efi_handle_t handle; - efi_loaded_image_t *loaded_image; - efi_status_t efiret; - struct linux_kernel_header *image_header; - const char *options; - bool is_driver; - int ret; - - ret = efi_load_image(file, &loaded_image, &handle); - if (ret) - return ret; - - is_driver = (loaded_image->image_code_type == EFI_BOOT_SERVICES_CODE) || - (loaded_image->image_code_type == EFI_RUNTIME_SERVICES_CODE); - - image_header = (struct linux_kernel_header *)loaded_image->image_base; - if (image_header->boot_flag == 0xAA55 && - image_header->header == 0x53726448) { - pr_debug("Linux kernel detected. Adding bootargs."); - options = linux_bootargs_get(); - pr_err("add linux options '%s'\n", options); - loaded_image->load_options = xstrdup_char_to_wchar(options); - loaded_image->load_options_size = - (strlen(options) + 1) * sizeof(wchar_t); - shutdown_barebox(); - } - - efiret = BS->start_image(handle, NULL, NULL); - if (EFI_ERROR(efiret)) - pr_err("failed to StartImage: %s\n", efi_strerror(efiret)); - - if (!is_driver) - BS->unload_image(handle); - - efi_connect_all(); - efi_register_devices(); - - return -efi_errno(efiret); -} - -#ifdef __x86_64__ -typedef void(*handover_fn)(void *image, efi_system_table_t *table, - struct linux_kernel_header *header); - -static inline void linux_efi_handover(efi_handle_t handle, - struct linux_kernel_header *header) -{ - handover_fn handover; - - handover = (handover_fn)((long)header->code32_start + 512 + - header->handover_offset); - handover(handle, efi_sys_table, header); -} -#else -typedef void(*handover_fn)(VOID *image, EFI_SYSTEM_TABLE *table, - struct SetupHeader *setup) __attribute__((regparm(0))); - -static inline void linux_efi_handover(efi_handle_t handle, - struct linux_kernel_header *header) -{ - handover_fn handover; - - handover = (handover_fn)((long)header->code32_start + - header->handover_offset); - handover(handle, efi_sys_table, header); -} -#endif - -static int do_bootm_efi(struct image_data *data) -{ - void *tmp; - void *initrd = NULL; - size_t size; - efi_handle_t handle; - int ret; - const char *options; - efi_loaded_image_t *loaded_image; - struct linux_kernel_header *image_header, *boot_header; - - ret = efi_load_image(data->os_file, &loaded_image, &handle); - if (ret) - return ret; - - image_header = (struct linux_kernel_header *)loaded_image->image_base; - - if (image_header->boot_flag != 0xAA55 || - image_header->header != 0x53726448 || - image_header->version < 0x20b || - !image_header->relocatable_kernel) { - pr_err("Not a valid kernel image!\n"); - BS->unload_image(handle); - return -EINVAL; - } - - boot_header = xmalloc(0x4000); - memset(boot_header, 0, 0x4000); - memcpy(boot_header, image_header, sizeof(*image_header)); - - boot_header->type_of_loader = 0xff; - - if (data->initrd_file) { - tmp = read_file(data->initrd_file, &size); - initrd = xmemalign(PAGE_SIZE, PAGE_ALIGN(size)); - memcpy(initrd, tmp, size); - memset(initrd + size, 0, PAGE_ALIGN(size) - size); - free(tmp); - boot_header->ramdisk_image = (uint64_t)initrd; - boot_header->ramdisk_size = PAGE_ALIGN(size); - } - - options = linux_bootargs_get(); - boot_header->cmd_line_ptr = (uint64_t)options; - boot_header->cmdline_size = strlen(options); - - boot_header->code32_start = (uint64_t)loaded_image->image_base + - (image_header->setup_sects+1) * 512; - - if (bootm_verbose(data)) { - printf("\nStarting kernel at 0x%p", loaded_image->image_base); - if (data->initrd_file) - printf(", initrd at 0x%08x", - boot_header->ramdisk_image); - printf("...\n"); - } - - if (data->dryrun) { - BS->unload_image(handle); - free(boot_header); - free(initrd); - return 0; - } - - efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid, - get_time_ns()/1000); - - shutdown_barebox(); - linux_efi_handover(handle, boot_header); - - return 0; -} - -static struct image_handler efi_handle_tr = { - .name = "EFI Application", - .bootm = do_bootm_efi, - .filetype = filetype_exe, -}; - -static int efi_execute(struct binfmt_hook *b, char *file, int argc, char **argv) -{ - return efi_execute_image(file); -} - -static struct binfmt_hook binfmt_efi_hook = { - .type = filetype_exe, - .hook = efi_execute, -}; - -static int efi_register_image_handler(void) -{ - register_image_handler(&efi_handle_tr); - binfmt_register(&binfmt_efi_hook); - - return 0; -} -late_initcall(efi_register_image_handler); diff --git a/common/efi/efi-iomem.c b/common/efi/efi-iomem.c deleted file mode 100644 index e223c595c4..0000000000 --- a/common/efi/efi-iomem.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2019 Ahmad Fatoum, Pengutronix - -#define pr_fmt(fmt) "efi-iomem: " fmt - -#include <common.h> -#include <init.h> -#include <efi.h> -#include <efi/efi.h> -#include <memory.h> -#include <linux/sizes.h> - -static int efi_parse_mmap(struct efi_memory_desc *desc) -{ - struct resource *res; - u32 flags; - const char *name; - char *fullname; - resource_size_t va_base, va_size; - int ret = 0; - - va_size = desc->npages * SZ_4K; - if (!va_size) - return 0; - - /* XXX At least OVMF doesn't populate ->virt_start and leaves it at zero - * for all mapping. Thus assume a 1:1 mapping and ignore virt_start - */ - va_base = desc->phys_start; - - switch (desc->type) { - case EFI_RESERVED_TYPE: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "reserved"; - flags = IORESOURCE_MEM | IORESOURCE_DISABLED; - break; - case EFI_LOADER_CODE: - return barebox_add_memory_bank("loader code", va_base, va_size); - case EFI_LOADER_DATA: - return barebox_add_memory_bank("loader data", va_base, va_size); - case EFI_BOOT_SERVICES_CODE: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "boot services code"; - flags = IORESOURCE_MEM | IORESOURCE_READONLY; - break; - case EFI_BOOT_SERVICES_DATA: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "boot services data"; - flags = IORESOURCE_MEM; - break; - case EFI_RUNTIME_SERVICES_CODE: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "runtime services code"; - flags = IORESOURCE_MEM | IORESOURCE_READONLY; - break; - case EFI_RUNTIME_SERVICES_DATA: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "runtime services data"; - flags = IORESOURCE_MEM; - break; - case EFI_CONVENTIONAL_MEMORY: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "conventional memory"; - flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_CACHEABLE; - break; - case EFI_UNUSABLE_MEMORY: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "unusable"; - flags = IORESOURCE_MEM | IORESOURCE_DISABLED; - break; - case EFI_ACPI_RECLAIM_MEMORY: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "ACPI reclaim memory"; - flags = IORESOURCE_MEM | IORESOURCE_READONLY; - break; - case EFI_ACPI_MEMORY_NVS: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "ACPI NVS memory"; - flags = IORESOURCE_MEM | IORESOURCE_READONLY; - break; - case EFI_MEMORY_MAPPED_IO: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "MMIO"; - flags = IORESOURCE_MEM; - break; - case EFI_MEMORY_MAPPED_IO_PORT_SPACE: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "MMIOPORT"; - flags = IORESOURCE_IO; - break; - case EFI_PAL_CODE: - if (!IS_ENABLED(DEBUG)) - return 0; - name = "PAL code"; - flags = IORESOURCE_MEM | IORESOURCE_ROM_BIOS_COPY; - break; - default: - if (!(desc->type & (1U << 31))) { - pr_warn("illegal memory type = %u >= %u\n", - desc->type, EFI_MAX_MEMORY_TYPE); - return -EINVAL; - } - - if (!IS_ENABLED(DEBUG)) - return 0; - - name = "vendor reserved"; - flags = IORESOURCE_MEM | IORESOURCE_ROM_BIOS_COPY; - } - - fullname = xasprintf("%s@%llx", name, desc->phys_start); - - pr_debug("%s: (0x%llx+0x%llx)\n", fullname, va_base, va_size); - - res = request_iomem_region(fullname, va_base, va_base + va_size - 1); - if (IS_ERR(res)) { - ret = PTR_ERR(res); - goto out; - } - - res->flags |= flags; - -out: - free(fullname); - return ret; -} - -static int efi_barebox_populate_mmap(void) -{ - void *desc; - u8 *mmap_buf = NULL; - efi_status_t efiret; - size_t mmap_size; - size_t mapkey; - size_t descsz; - u32 descver; - int ret = 0; - - mmap_size = sizeof(struct efi_memory_desc); - - do { - mmap_buf = xrealloc(mmap_buf, mmap_size); - efiret = BS->get_memory_map(&mmap_size, mmap_buf, - &mapkey, &descsz, &descver); - } while (efiret == EFI_BUFFER_TOO_SMALL); - - if (EFI_ERROR(efiret)) { - ret = -efi_errno(efiret); - goto out; - } - - if (descver != 1) { - ret = -ENOSYS; - goto out; - } - - for (desc = mmap_buf; (u8 *)desc < &mmap_buf[mmap_size]; desc += descsz) - efi_parse_mmap(desc); - -out: - free(mmap_buf); - return ret; -} -mem_initcall(efi_barebox_populate_mmap); diff --git a/common/efi/efi.c b/common/efi/efi.c deleted file mode 100644 index 7f12342cf9..0000000000 --- a/common/efi/efi.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * efi.c - barebox EFI payload support - * - * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/linkage.h> -#include <common.h> -#include <linux/sizes.h> -#include <memory.h> -#include <clock.h> -#include <command.h> -#include <magicvar.h> -#include <init.h> -#include <restart.h> -#include <poweroff.h> -#include <driver.h> -#include <platform_data/serial-ns16550.h> -#include <io.h> -#include <efi.h> -#include <malloc.h> -#include <string.h> -#include <linux/err.h> -#include <boot.h> -#include <fs.h> -#include <binfmt.h> -#include <wchar.h> -#include <envfs.h> -#include <efi.h> -#include <efi/efi.h> -#include <efi/efi-device.h> -#include <libfile.h> -#include <state.h> -#include <bbu.h> - -efi_runtime_services_t *RT; -efi_boot_services_t *BS; -efi_system_table_t *efi_sys_table; -efi_handle_t efi_parent_image; -struct efi_device_path *efi_device_path; -efi_loaded_image_t *efi_loaded_image; - -void *efi_get_variable(char *name, efi_guid_t *vendor, int *var_size) -{ - efi_status_t efiret; - void *buf; - unsigned long size = 0; - s16 *name16 = xstrdup_char_to_wchar(name); - - efiret = RT->get_variable(name16, vendor, NULL, &size, NULL); - - if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL) { - buf = ERR_PTR(-efi_errno(efiret)); - goto out; - } - - buf = malloc(size); - if (!buf) { - buf = ERR_PTR(-ENOMEM); - goto out; - } - - efiret = RT->get_variable(name16, vendor, NULL, &size, buf); - if (EFI_ERROR(efiret)) { - free(buf); - buf = ERR_PTR(-efi_errno(efiret)); - goto out; - } - - if (var_size) - *var_size = size; - -out: - free(name16); - - return buf; -} - -int efi_set_variable(char *name, efi_guid_t *vendor, uint32_t attributes, - void *buf, unsigned long size) -{ - efi_status_t efiret = EFI_SUCCESS; - s16 *name16 = xstrdup_char_to_wchar(name); - - efiret = RT->set_variable(name16, vendor, attributes, size, buf); - - free(name16); - - return -efi_errno(efiret); -} - -int efi_set_variable_usec(char *name, efi_guid_t *vendor, uint64_t usec) -{ - char buf[20]; - wchar_t buf16[40]; - - snprintf(buf, sizeof(buf), "%lld", usec); - strcpy_char_to_wchar(buf16, buf); - - return efi_set_variable(name, vendor, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, buf16, - (strlen(buf)+1) * sizeof(wchar_t)); -} - -struct efi_boot { - u32 attributes; - u16 file_path_len; - char *description; - struct efi_device_path *path; - void *binary; -}; - -static struct efi_boot *efi_get_boot(int num) -{ - struct efi_boot *boot = xzalloc(sizeof(*boot)); - void *buf, *ptr; - int size; - char *name; - - name = xasprintf("Boot%04X", num); - - buf = efi_get_global_var(name, &size); - - free(name); - - if (IS_ERR(buf)) { - free(boot); - return NULL; - } - - ptr = buf; - - boot->attributes = *(u32 *)ptr; - - ptr += sizeof(u32); - - boot->file_path_len = *(u16 *)ptr; - - ptr += sizeof(u16); - - boot->description = xstrdup_wchar_to_char(ptr); - - ptr += (strlen(boot->description) + 1) * 2; - - printf("description: %s\n", boot->description); - - boot->path = memdup(ptr, boot->file_path_len); - - printf("path: %s\n", device_path_to_str(boot->path)); - - return boot; -} - -static int misc_init(void) -{ - efi_get_boot(1); - efi_get_boot(2); - efi_get_boot(3); - - return 0; -} -late_initcall(misc_init); - -const char *efi_strerror(efi_status_t err) -{ - const char *str; - - switch (err) { - case EFI_SUCCESS: str = "Success"; break; - case EFI_LOAD_ERROR: str = "Load Error"; break; - case EFI_INVALID_PARAMETER: str = "Invalid Parameter"; break; - case EFI_UNSUPPORTED: str = "Unsupported"; break; - case EFI_BAD_BUFFER_SIZE: str = "Bad Buffer Size"; break; - case EFI_BUFFER_TOO_SMALL: str = "Buffer Too Small"; break; - case EFI_NOT_READY: str = "Not Ready"; break; - case EFI_DEVICE_ERROR: str = "Device Error"; break; - case EFI_WRITE_PROTECTED: str = "Write Protected"; break; - case EFI_OUT_OF_RESOURCES: str = "Out of Resources"; break; - case EFI_VOLUME_CORRUPTED: str = "Volume Corrupt"; break; - case EFI_VOLUME_FULL: str = "Volume Full"; break; - case EFI_NO_MEDIA: str = "No Media"; break; - case EFI_MEDIA_CHANGED: str = "Media changed"; break; - case EFI_NOT_FOUND: str = "Not Found"; break; - case EFI_ACCESS_DENIED: str = "Access Denied"; break; - case EFI_NO_RESPONSE: str = "No Response"; break; - case EFI_NO_MAPPING: str = "No mapping"; break; - case EFI_TIMEOUT: str = "Time out"; break; - case EFI_NOT_STARTED: str = "Not started"; break; - case EFI_ALREADY_STARTED: str = "Already started"; break; - case EFI_ABORTED: str = "Aborted"; break; - case EFI_ICMP_ERROR: str = "ICMP Error"; break; - case EFI_TFTP_ERROR: str = "TFTP Error"; break; - case EFI_PROTOCOL_ERROR: str = "Protocol Error"; break; - case EFI_INCOMPATIBLE_VERSION: str = "Incompatible Version"; break; - case EFI_SECURITY_VIOLATION: str = "Security Violation"; break; - case EFI_CRC_ERROR: str = "CRC Error"; break; - case EFI_END_OF_MEDIA: str = "End of Media"; break; - case EFI_END_OF_FILE: str = "End of File"; break; - case EFI_INVALID_LANGUAGE: str = "Invalid Language"; break; - case EFI_COMPROMISED_DATA: str = "Compromised Data"; break; - default: str = "unknown error"; - } - - return str; -} - -int efi_errno(efi_status_t err) -{ - int ret; - - switch (err) { - case EFI_SUCCESS: ret = 0; break; - case EFI_LOAD_ERROR: ret = EIO; break; - case EFI_INVALID_PARAMETER: ret = EINVAL; break; - case EFI_UNSUPPORTED: ret = ENOTSUPP; break; - case EFI_BAD_BUFFER_SIZE: ret = EINVAL; break; - case EFI_BUFFER_TOO_SMALL: ret = EINVAL; break; - case EFI_NOT_READY: ret = EAGAIN; break; - case EFI_DEVICE_ERROR: ret = EIO; break; - case EFI_WRITE_PROTECTED: ret = EROFS; break; - case EFI_OUT_OF_RESOURCES: ret = ENOMEM; break; - case EFI_VOLUME_CORRUPTED: ret = EIO; break; - case EFI_VOLUME_FULL: ret = ENOSPC; break; - case EFI_NO_MEDIA: ret = ENOMEDIUM; break; - case EFI_MEDIA_CHANGED: ret = ENOMEDIUM; break; - case EFI_NOT_FOUND: ret = ENODEV; break; - case EFI_ACCESS_DENIED: ret = EACCES; break; - case EFI_NO_RESPONSE: ret = ETIMEDOUT; break; - case EFI_NO_MAPPING: ret = EINVAL; break; - case EFI_TIMEOUT: ret = ETIMEDOUT; break; - case EFI_NOT_STARTED: ret = EINVAL; break; - case EFI_ALREADY_STARTED: ret = EINVAL; break; - case EFI_ABORTED: ret = EINTR; break; - case EFI_ICMP_ERROR: ret = EINVAL; break; - case EFI_TFTP_ERROR: ret = EINVAL; break; - case EFI_PROTOCOL_ERROR: ret = EPROTO; break; - case EFI_INCOMPATIBLE_VERSION: ret = EINVAL; break; - case EFI_SECURITY_VIOLATION: ret = EINVAL; break; - case EFI_CRC_ERROR: ret = EINVAL; break; - case EFI_END_OF_MEDIA: ret = EINVAL; break; - case EFI_END_OF_FILE: ret = EINVAL; break; - case EFI_INVALID_LANGUAGE: ret = EINVAL; break; - case EFI_COMPROMISED_DATA: ret = EINVAL; break; - default: ret = EINVAL; - } - - return ret; -} - -static struct NS16550_plat ns16550_plat = { - .clock = 115200 * 16, -}; - -static int efi_console_init(void) -{ - barebox_set_model("barebox EFI payload"); - - add_generic_device("efi-stdio", DEVICE_ID_SINGLE, NULL, 0 , 0, 0, NULL); - - add_ns16550_device(0, 0x3f8, 0x10, IORESOURCE_IO | IORESOURCE_MEM_8BIT, - &ns16550_plat); - - return 0; -} -console_initcall(efi_console_init); - -static void __noreturn efi_restart_system(struct restart_handler *rst) -{ - RT->reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL); - - hang(); -} - -static void __noreturn efi_poweroff_system(struct poweroff_handler *handler) -{ - shutdown_barebox(); - RT->reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); - - hang(); -} - -static int restart_register_feature(void) -{ - restart_handler_register_fn("efi", efi_restart_system); - poweroff_handler_register_fn(efi_poweroff_system); - - return 0; -} -coredevice_initcall(restart_register_feature); - -extern char image_base[]; -extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[], - __barebox_initcalls_end[]; - -static int efi_init(void) -{ - char *env; - - defaultenv_append_directory(env_efi); - - env = xasprintf("/efivars/barebox-env-%pUl", &efi_barebox_vendor_guid); - default_environment_path_set(env); - - return 0; -} -device_initcall(efi_init); - -asmlinkage efi_status_t efi_main(efi_handle_t, efi_system_table_t *); - -/** - * efi-main - Entry point for EFI images - */ -efi_status_t efi_main(efi_handle_t image, efi_system_table_t *sys_table) -{ - efi_physical_addr_t mem; - size_t memsize; - efi_status_t efiret; - -#ifdef DEBUG - sys_table->con_out->output_string(sys_table->con_out, L"barebox\n"); -#endif - - BS = sys_table->boottime; - - efi_parent_image = image; - efi_sys_table = sys_table; - RT = sys_table->runtime; - - efiret = BS->open_protocol(efi_parent_image, &efi_loaded_image_protocol_guid, - (void **)&efi_loaded_image, - efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (!EFI_ERROR(efiret)) - BS->handle_protocol(efi_loaded_image->device_handle, - &efi_device_path_protocol_guid, (void **)&efi_device_path); - - mem = 0x3fffffff; - for (memsize = SZ_256M; memsize >= SZ_8M; memsize /= 2) { - efiret = BS->allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_LOADER_DATA, - memsize/PAGE_SIZE, &mem); - if (!EFI_ERROR(efiret)) - break; - if (efiret != EFI_OUT_OF_RESOURCES) - panic("failed to allocate malloc pool: %s\n", - efi_strerror(efiret)); - } - if (EFI_ERROR(efiret)) - panic("failed to allocate malloc pool: %s\n", - efi_strerror(efiret)); - mem_malloc_init((void *)mem, (void *)mem + memsize - 1); - - start_barebox(); - - return EFI_SUCCESS; -} - -static int efi_core_init(void) -{ - struct device_d *dev; - int ret; - - dev = device_alloc("efi-cs", DEVICE_ID_SINGLE); - ret = platform_device_register(dev); - if (ret) - return ret; - - dev = device_alloc("efi-wdt", DEVICE_ID_SINGLE); - return platform_device_register(dev); -} -core_initcall(efi_core_init); - -static int efi_postcore_init(void) -{ - char *uuid; - - efi_set_variable_usec("LoaderTimeInitUSec", &efi_systemd_vendor_guid, - get_time_ns()/1000); - - uuid = device_path_to_partuuid(device_path_from_handle( - efi_loaded_image->device_handle)); - if (uuid) { - wchar_t *uuid16 = xstrdup_char_to_wchar(uuid); - efi_set_variable("LoaderDevicePartUUID", - &efi_systemd_vendor_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - uuid16, (strlen(uuid)+1) * sizeof(wchar_t)); - free(uuid); - free(uuid16); - } - - bbu_register_std_file_update("fat", 0, "/boot/EFI/BOOT/BOOTx64.EFI", - filetype_exe); - - return 0; -} -postcore_initcall(efi_postcore_init); - -static int efi_late_init(void) -{ - char *state_desc; - int ret; - - if (!IS_ENABLED(CONFIG_STATE)) - return 0; - - state_desc = xasprintf("/boot/EFI/barebox/state.dtb"); - - if (state_desc) { - void *fdt; - size_t size; - struct device_node *root = NULL; - struct device_node *np = NULL; - struct state *state; - - fdt = read_file(state_desc, &size); - if (!fdt) { - pr_err("unable to read %s: %s\n", state_desc, - strerror(errno)); - return -errno; - } - - if (file_detect_type(fdt, size) != filetype_oftree) { - pr_err("%s is not an oftree file.\n", state_desc); - free(fdt); - return -EINVAL; - } - - root = of_unflatten_dtb(fdt, size); - - free(fdt); - - if (IS_ERR(root)) - return PTR_ERR(root); - - of_set_root_node(root); - - np = of_find_node_by_alias(root, "state"); - - state = state_new_from_node(np, false); - if (IS_ERR(state)) - return PTR_ERR(state); - - ret = state_load(state); - if (ret) - pr_warn("Failed to load persistent state, continuing with defaults, %d\n", - ret); - - return 0; - } - - return 0; -} -late_initcall(efi_late_init); - -static int do_efiexit(int argc, char *argv[]) -{ - return BS->exit(efi_parent_image, EFI_SUCCESS, 0, NULL); -} - -BAREBOX_CMD_HELP_START(efiexit) -BAREBOX_CMD_HELP_TEXT("Leave barebox and return to the calling EFI process\n") -BAREBOX_CMD_HELP_END - -BAREBOX_CMD_START(efiexit) - .cmd = do_efiexit, - BAREBOX_CMD_DESC("Usage: efiexit") - BAREBOX_CMD_GROUP(CMD_GRP_MISC) - BAREBOX_CMD_HELP(cmd_efiexit_help) -BAREBOX_CMD_END diff --git a/common/efi/env-efi/network/eth0-discover b/common/efi/env-efi/network/eth0-discover deleted file mode 100644 index 62c31a553c..0000000000 --- a/common/efi/env-efi/network/eth0-discover +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -for i in /boot/network-drivers/*; do - $i; -done diff --git a/common/elf.c b/common/elf.c index af22be37e6..62f793010f 100644 --- a/common/elf.c +++ b/common/elf.c @@ -10,6 +10,7 @@ #include <libfile.h> #include <memory.h> #include <unistd.h> +#include <zero_page.h> #include <linux/fs.h> #include <linux/list_sort.h> @@ -59,14 +60,13 @@ static int request_elf_segment(struct elf_image *elf, void *phdr) { void *dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, phdr); int ret; - u64 p_filesz = elf_phdr_p_filesz(elf, phdr); u64 p_memsz = elf_phdr_p_memsz(elf, phdr); /* we care only about PT_LOAD segments */ if (elf_phdr_p_type(elf, phdr) != PT_LOAD) return 0; - if (!p_filesz) + if (!p_memsz) return 0; if (dst < elf->low_addr) @@ -74,9 +74,9 @@ static int request_elf_segment(struct elf_image *elf, void *phdr) if (dst + p_memsz > elf->high_addr) elf->high_addr = dst + p_memsz; - pr_debug("Requesting segment 0x%p (%llu bytes)\n", dst, p_filesz); + pr_debug("Requesting segment 0x%p (%llu bytes)\n", dst, p_memsz); - ret = elf_request_region(elf, (resource_size_t)dst, p_filesz, phdr); + ret = elf_request_region(elf, (resource_size_t)dst, p_memsz, phdr); if (ret) return ret; @@ -101,47 +101,57 @@ static int elf_section_cmp(void *priv, struct list_head *a, struct list_head *b) static int load_elf_to_memory(struct elf_image *elf) { void *dst; - int ret, fd; + int ret = 0, fd = -1; u64 p_filesz, p_memsz, p_offset; struct elf_section *r; struct list_head *list = &elf->list; - fd = open(elf->filename, O_RDONLY); - if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); - return -errno; + if (elf->filename) { + fd = open(elf->filename, O_RDONLY); + if (fd < 0) { + pr_err("could not open: %m\n"); + return -errno; + } } + zero_page_access(); + list_for_each_entry(r, list, list) { p_offset = elf_phdr_p_offset(elf, r->phdr); p_filesz = elf_phdr_p_filesz(elf, r->phdr); p_memsz = elf_phdr_p_memsz(elf, r->phdr); dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, r->phdr); - ret = lseek(fd, p_offset, SEEK_SET); - if (ret == -1) { - pr_err("lseek at offset 0x%llx failed\n", p_offset); - close(fd); - return ret; - } - pr_debug("Loading phdr offset 0x%llx to 0x%p (%llu bytes)\n", p_offset, dst, p_filesz); - if (read_full(fd, dst, p_filesz) < 0) { - pr_err("could not read elf segment: %s\n", - errno_str()); - close(fd); - return -errno; + if (fd >= 0) { + ret = lseek(fd, p_offset, SEEK_SET); + if (ret == -1) { + pr_err("lseek at offset 0x%llx failed\n", + p_offset); + goto out; + } + + if (read_full(fd, dst, p_filesz) < 0) { + pr_err("could not read elf segment: %m\n"); + ret = -errno; + goto out; + } + } else { + memcpy(dst, elf->hdr_buf + p_offset, p_filesz); } if (p_filesz < p_memsz) memset(dst + p_filesz, 0x00, p_memsz - p_filesz); } +out: + zero_page_faulting(); + close(fd); - return 0; + return ret >= 0 ? 0 : ret; } static int load_elf_image_segments(struct elf_image *elf) @@ -202,6 +212,37 @@ static int elf_check_image(struct elf_image *elf, void *buf) return 0; } +static void elf_init_struct(struct elf_image *elf) +{ + INIT_LIST_HEAD(&elf->list); + elf->low_addr = (void *) (unsigned long) -1; + elf->high_addr = 0; + elf->filename = NULL; +} + +struct elf_image *elf_open_binary(void *buf) +{ + int ret; + struct elf_image *elf; + + elf = calloc(1, sizeof(*elf)); + if (!elf) + return ERR_PTR(-ENOMEM); + + elf_init_struct(elf); + + elf->hdr_buf = buf; + ret = elf_check_image(elf, buf); + if (ret) { + free(elf); + return ERR_PTR(-EINVAL); + } + + elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf); + + return elf; +} + static struct elf_image *elf_check_init(const char *filename) { int ret, fd; @@ -213,20 +254,18 @@ static struct elf_image *elf_check_init(const char *filename) if (!elf) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&elf->list); - elf->low_addr = (void *) (unsigned long) -1; - elf->high_addr = 0; + elf_init_struct(elf); /* First pass is to read elf header only */ fd = open(filename, O_RDONLY); if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); + pr_err("could not open: %m\n"); ret = -errno; goto err_free_elf; } if (read_full(fd, &hdr, sizeof(hdr)) < 0) { - pr_err("could not read elf header: %s\n", errno_str()); + pr_err("could not read elf header: %m\n"); close(fd); ret = -errno; goto err_free_elf; @@ -254,13 +293,13 @@ static struct elf_image *elf_check_init(const char *filename) */ fd = open(filename, O_RDONLY); if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); + pr_err("could not open: %m\n"); ret = -errno; goto err_free_hdr_buf; } if (read_full(fd, elf->hdr_buf, hdr_size) < 0) { - pr_err("could not read elf program headers: %s\n", errno_str()); + pr_err("could not read elf program headers: %m\n"); ret = -errno; close(fd); goto err_free_hdr_buf; @@ -299,7 +338,10 @@ void elf_close(struct elf_image *elf) { elf_release_regions(elf); - free(elf->hdr_buf); - free(elf->filename); + if (elf->filename) { + free(elf->hdr_buf); + free(elf->filename); + } + free(elf); } diff --git a/common/env.c b/common/env.c index 05add63f62..7a213cadb2 100644 --- a/common/env.c +++ b/common/env.c @@ -80,7 +80,7 @@ int env_push_context(void) */ int env_pop_context(void) { - struct env_context *c = context; + struct env_context *c; if (context->parent) { c = context->parent; @@ -127,7 +127,7 @@ static const char *dev_getenv(const char *name) { const char *pos, *val, *dot, *varname; char *devname; - struct device_d *dev; + struct device *dev; pos = name; @@ -215,7 +215,7 @@ static int dev_setenv(const char *name, const char *val) { const char *pos, *dot, *varname; char *devname; - struct device_d *dev; + struct device *dev; pos = name; @@ -244,13 +244,12 @@ static int dev_setenv(const char *name, const char *val) /** * setenv - set environment variables - * @_name - Variable name + * @name - Variable name * @value - the value to set, empty string not handled specially * * Returns 0 for success and a negative error code otherwise * Use unsetenv() to unset. */ - int setenv(const char *_name, const char *value) { char *name = strdup(_name); @@ -277,6 +276,35 @@ out: } EXPORT_SYMBOL(setenv); +/** + * pr_setenv - set environment variables + * @name - Variable name + * @fmt - the format string to use + * + * Returns 0 for success and a negative error code otherwise + * Use unsetenv() to unset. + */ +int pr_setenv(const char *name, const char *fmt, ...) +{ + va_list ap; + int ret = 0; + char *value; + int len; + + va_start(ap, fmt); + len = vasprintf(&value, fmt, ap); + va_end(ap); + + if (len < 0) + return -ENOMEM; + + ret = setenv(name, value); + free(value); + + return ret; +} +EXPORT_SYMBOL(pr_setenv); + int export(const char *varname) { const char *val = getenv_raw(&context->local, varname); @@ -315,19 +343,30 @@ const char *getenv_nonempty(const char *var) } EXPORT_SYMBOL(getenv_nonempty); -int getenv_ull(const char *var , unsigned long long *val) +static int getenv_ull_base(const char *var, int base, unsigned long long *val) { const char *valstr = getenv(var); if (!valstr || !*valstr) return -EINVAL; - *val = simple_strtoull(valstr, NULL, 0); + *val = simple_strtoull(valstr, NULL, base); return 0; } + +int getenv_ull(const char *var , unsigned long long *val) +{ + return getenv_ull_base(var, 0, val); +} EXPORT_SYMBOL(getenv_ull); +int getenv_ullx(const char *var , unsigned long long *val) +{ + return getenv_ull_base(var, 16, val); +} +EXPORT_SYMBOL(getenv_ullx); + int getenv_ul(const char *var , unsigned long *val) { const char *valstr = getenv(var); diff --git a/common/envfs-core.c b/common/envfs-core.c index 1898c1c8cb..20b3e647d3 100644 --- a/common/envfs-core.c +++ b/common/envfs-core.c @@ -12,6 +12,8 @@ * the default environment when building the barebox binary. So * do not add any new barebox related functions here! */ +#define pr_fmt(fmt) "envfs: " fmt + #ifdef __BAREBOX__ #include <common.h> #include <fs.h> @@ -22,7 +24,8 @@ #include <environment.h> #include <libfile.h> #else -# define errno_str(x) ("void") +#define pr_info(fmt, ...) printf(pr_fmt(fmt), ##__VA_ARGS__) +#define pr_warn(fmt, ...) printf(pr_fmt(fmt), ##__VA_ARGS__) #endif static int dir_remove_action(const char *filename, struct stat *statbuf, @@ -39,17 +42,17 @@ static int dir_remove_action(const char *filename, struct stat *statbuf, int envfs_check_super(struct envfs_super *super, size_t *size) { if (ENVFS_32(super->magic) != ENVFS_MAGIC) { - printf("envfs: no envfs (magic mismatch) - envfs never written?\n"); + pr_info("no envfs (magic mismatch) - envfs never written?\n"); return -EIO; } if (crc32(0, super, sizeof(*super) - 4) != ENVFS_32(super->sb_crc)) { - printf("wrong crc on env superblock\n"); + pr_warn("wrong crc on env superblock\n"); return -EIO; } if (super->major < ENVFS_MAJOR) - printf("envfs version %d.%d loaded into %d.%d\n", + pr_info("version %d.%d loaded into %d.%d\n", super->major, super->minor, ENVFS_MAJOR, ENVFS_MINOR); @@ -64,7 +67,7 @@ int envfs_check_data(struct envfs_super *super, const void *buf, size_t size) crc = crc32(0, buf, size); if (crc != ENVFS_32(super->crc)) { - printf("wrong crc on env\n"); + pr_warn("wrong crc on env\n"); return -EIO; } @@ -93,7 +96,7 @@ int envfs_load_data(struct envfs_super *super, void *buf, size_t size, buf += sizeof(struct envfs_inode); if (ENVFS_32(inode->magic) != ENVFS_INODE_MAGIC) { - printf("envfs: wrong magic\n"); + pr_warn("wrong magic\n"); ret = -EIO; goto out; } @@ -147,7 +150,7 @@ int envfs_load_data(struct envfs_super *super, void *buf, size_t size, fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644); free(str); if (fd < 0) { - printf("Open %s\n", errno_str()); + printf("Open %m\n"); ret = fd; goto out; } diff --git a/common/environment.c b/common/environment.c index 0d31f5b4f7..39cad0c16a 100644 --- a/common/environment.c +++ b/common/environment.c @@ -26,8 +26,10 @@ #include <environment.h> #include <globalvar.h> #include <libfile.h> +#include <block.h> +#include <efi/partition.h> +#include <bootsource.h> #else -# define errno_str(x) ("void") #define EXPORT_SYMBOL(x) #endif @@ -49,15 +51,83 @@ struct action_data { #define TMPDIR "/.defaultenv" -static char *default_environment_path = "/dev/env0"; +static char *default_environment_path; -void default_environment_path_set(char *path) +void default_environment_path_set(const char *path) { - default_environment_path = path; + free(default_environment_path); + + default_environment_path = xstrdup(path); } -char *default_environment_path_get(void) +static guid_t partition_barebox_env_guid = PARTITION_BAREBOX_ENVIRONMENT_GUID; + +/* + * default_environment_path_search - look for environment partition + * + * This searches for a barebox environment partition on block devices. barebox + * environment partitions are recognized by the guid + * 6c3737f2-07f8-45d1-ad45-15d260aab24d. The device barebox itself has booted + * from is preferred over other devices. + * + * @return: The cdev providing the environment of found, NULL otherwise + */ +static struct cdev *default_environment_path_search(void) { + struct cdev *part; + struct device_node *boot_node; + int max_score = 0; + struct cdev *env_cdev = NULL; + struct block_device *blk; + + if (!IS_ENABLED(CONFIG_BLOCK)) + return NULL; + + boot_node = bootsource_of_node_get(NULL); + + if (boot_node) { + struct device *dev; + + dev = of_find_device_by_node(boot_node); + if (dev) + device_detect(dev); + } + + for_each_block_device(blk) { + int score = 0; + + part = cdev_find_child_by_gpt_typeuuid(&blk->cdev, + &partition_barebox_env_guid); + if (IS_ERR(part)) + continue; + + score++; + + if (boot_node && boot_node == blk->cdev.device_node) + score++; + + if (score > max_score) { + max_score = score; + env_cdev = part; + } + } + + return env_cdev; +} + +const char *default_environment_path_get(void) +{ + struct cdev *cdev; + + if (default_environment_path) + return default_environment_path; + + cdev = default_environment_path_search(); + if (cdev) + default_environment_path = basprintf("/dev/%s", cdev->name); + else + default_environment_path = xstrdup("/dev/env0"); + return default_environment_path; } @@ -297,7 +367,7 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) envfd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (envfd < 0) { - printf("could not open %s: %s\n", filename, errno_str()); + printf("could not open %s: %m\n", filename); ret = -errno; goto out1; } @@ -306,7 +376,7 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) /* ENOSYS and EOPNOTSUPP aren't errors here, many devices don't need it */ if (ret && errno != ENOSYS && errno != EOPNOTSUPP) { - printf("could not unprotect %s: %s\n", filename, errno_str()); + printf("could not unprotect %s: %m\n", filename); goto out; } @@ -314,7 +384,7 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) /* ENOSYS and EOPNOTSUPP aren't errors here, many devices don't need it */ if (ret && errno != ENOSYS && errno != EOPNOTSUPP) { - printf("could not erase %s: %s\n", filename, errno_str()); + printf("could not erase %s: %m\n", filename); goto out; } @@ -337,7 +407,7 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) /* ENOSYS and EOPNOTSUPP aren't errors here, many devices don't need it */ if (ret && errno != ENOSYS && errno != EOPNOTSUPP) { - printf("could not protect %s: %s\n", filename, errno_str()); + printf("could not protect %s: %m\n", filename); goto out; } @@ -385,7 +455,7 @@ int envfs_load(const char *filename, const char *dir, unsigned flags) envfd = open(filename, O_RDONLY); if (envfd < 0) { - printf("environment load %s: %s\n", filename, errno_str()); + printf("environment load %s: %m\n", filename); if (errno == ENOENT) printf("Maybe you have to create the partition.\n"); return -1; diff --git a/common/fastboot.c b/common/fastboot.c index 04a8573b4a..d8dabd89ab 100644 --- a/common/fastboot.c +++ b/common/fastboot.c @@ -30,7 +30,9 @@ #include <ubiformat.h> #include <unistd.h> #include <magicvar.h> +#include <linux/log2.h> #include <linux/sizes.h> +#include <memory.h> #include <progress.h> #include <environment.h> #include <globalvar.h> @@ -45,7 +47,7 @@ #define FASTBOOT_VERSION "0.4" -static unsigned int fastboot_max_download_size = SZ_8M; +static unsigned int fastboot_max_download_size; static int fastboot_bbu; static char *fastboot_partitions; @@ -64,7 +66,7 @@ static void fb_setvar(struct fb_variable *var, const char *fmt, ...) va_end(ap); } -static struct fb_variable *fb_addvar(struct fastboot *fb, const char *fmt, ...) +static struct fb_variable *fb_addvar(struct fastboot *fb, struct list_head *list, const char *fmt, ...) { struct fb_variable *var = xzalloc(sizeof(*var)); va_list ap; @@ -73,12 +75,12 @@ static struct fb_variable *fb_addvar(struct fastboot *fb, const char *fmt, ...) var->name = bvasprintf(fmt, ap); va_end(ap); - list_add_tail(&var->list, &fb->variables); + list_add_tail(&var->list, list); return var; } -static int fastboot_add_partition_variables(struct fastboot *fb, +static int fastboot_add_partition_variables(struct fastboot *fb, struct list_head *list, struct file_list_entry *fentry) { struct stat s; @@ -95,6 +97,14 @@ static int fastboot_add_partition_variables(struct fastboot *fb, } if (ret) { + if (fentry->flags & FILE_LIST_FLAG_OPTIONAL) { + pr_info("skipping unavailable optional partition %s for fastboot gadget\n", + fentry->filename); + ret = 0; + type = "unavailable"; + goto out; + } + if (fentry->flags & FILE_LIST_FLAG_CREATE) { ret = 0; type = "file"; @@ -142,9 +152,9 @@ out: if (ret) return ret; - var = fb_addvar(fb, "partition-size:%s", fentry->name); + var = fb_addvar(fb, list, "partition-size:%s", fentry->name); fb_setvar(var, "%08zx", size); - var = fb_addvar(fb, "partition-type:%s", fentry->name); + var = fb_addvar(fb, list, "partition-type:%s", fentry->name); fb_setvar(var, "%s", type); return ret; @@ -152,18 +162,16 @@ out: int fastboot_generic_init(struct fastboot *fb, bool export_bbu) { - int ret; - struct file_list_entry *fentry; struct fb_variable *var; INIT_LIST_HEAD(&fb->variables); - var = fb_addvar(fb, "version"); + var = fb_addvar(fb, &fb->variables, "version"); fb_setvar(var, "0.4"); - var = fb_addvar(fb, "bootloader-version"); + var = fb_addvar(fb, &fb->variables, "bootloader-version"); fb_setvar(var, release_string); if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) { - var = fb_addvar(fb, "max-download-size"); + var = fb_addvar(fb, &fb->variables, "max-download-size"); fb_setvar(var, "%u", fastboot_max_download_size); } @@ -171,28 +179,29 @@ int fastboot_generic_init(struct fastboot *fb, bool export_bbu) if (!fb->tempname) return -ENOMEM; + if (!fb->files) + fb->files = file_list_new(); if (export_bbu) bbu_append_handlers_to_file_list(fb->files); - file_list_for_each_entry(fb->files, fentry) { - ret = fastboot_add_partition_variables(fb, fentry); - if (ret) - return ret; - } - return 0; } -void fastboot_generic_free(struct fastboot *fb) +static void fastboot_free_variables(struct list_head *list) { struct fb_variable *var, *tmp; - list_for_each_entry_safe(var, tmp, &fb->variables, list) { + list_for_each_entry_safe(var, tmp, list, list) { free(var->name); free(var->value); list_del(&var->list); free(var); } +} + +void fastboot_generic_free(struct fastboot *fb) +{ + fastboot_free_variables(&fb->variables); free(fb->tempname); @@ -288,26 +297,51 @@ static int strcmp_l1(const char *s1, const char *s2) static void cb_getvar(struct fastboot *fb, const char *cmd) { struct fb_variable *var; + LIST_HEAD(partition_list); + struct file_list_entry *fentry; + + file_list_for_each_entry(fb->files, fentry) { + int ret; + + ret = fastboot_add_partition_variables(fb, &partition_list, fentry); + if (ret) { + pr_warn("Failed to add partition variables: %pe", ERR_PTR(ret)); + return; + } + } pr_debug("getvar: \"%s\"\n", cmd); if (!strcmp_l1(cmd, "all")) { - list_for_each_entry(var, &fb->variables, list) { + list_for_each_entry(var, &fb->variables, list) fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "%s: %s", var->name, var->value); - } + + list_for_each_entry(var, &partition_list, list) + fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "%s: %s", + var->name, var->value); + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); - return; + goto out; } list_for_each_entry(var, &fb->variables, list) { if (!strcmp(cmd, var->name)) { fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, var->value); - return; + goto out; + } + } + + list_for_each_entry(var, &partition_list, list) { + if (!strcmp(cmd, var->name)) { + fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, var->value); + goto out; } } fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, ""); +out: + fastboot_free_variables(&partition_list); } int fastboot_handle_download_data(struct fastboot *fb, const void *buffer, @@ -586,7 +620,7 @@ static int fastboot_handle_sparse(struct fastboot *fb, pos = lseek(fd, pos, SEEK_SET); if (pos == -1) { - ret = -errno; + ret = errno == EINVAL ? -ENOSPC : -errno; goto out; } @@ -614,7 +648,11 @@ static void cb_flash(struct fastboot *fb, const char *cmd) const char *filename = NULL; enum filetype filetype; - filetype = file_name_detect_type(fb->tempname); + ret = file_name_detect_type(fb->tempname, &filetype); + if (ret) { + fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "internal error"); + goto out; + } fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Copying file to %s...", cmd); @@ -673,7 +711,8 @@ static void cb_flash(struct fastboot *fb, const char *cmd) goto out; } - if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) { + if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && + (filetype_is_barebox_image(filetype) || strstarts(fentry->name, "bbu-"))) { void *buf; struct bbu_handler *handler; struct bbu_data data = { @@ -916,20 +955,28 @@ bool get_fastboot_bbu(void) return fastboot_bbu; } +void set_fastboot_bbu(unsigned int enable) +{ + fastboot_bbu = enable; +} + struct file_list *get_fastboot_partitions(void) { if (fastboot_partitions && *fastboot_partitions) return file_list_parse_null(fastboot_partitions); - if (!system_partitions_empty()) - return system_partitions_get(); - return NULL; + return system_partitions_get_null(); } static int fastboot_globalvars_init(void) { - if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) + if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) { + fastboot_max_download_size + = roundup_pow_of_two(clamp_t(ulong, mem_malloc_size() / 8, + SZ_8M, SZ_128M)); globalvar_add_simple_int("fastboot.max_download_size", &fastboot_max_download_size, "%u"); + } + globalvar_add_simple_bool("fastboot.bbu", &fastboot_bbu); globalvar_add_simple_string("fastboot.partitions", &fastboot_partitions); diff --git a/common/file-list.c b/common/file-list.c index 407b312833..7ecc8d00bb 100644 --- a/common/file-list.c +++ b/common/file-list.c @@ -9,6 +9,7 @@ #include <stringlist.h> #include <linux/err.h> #include <driver.h> +#include <block.h> #define PARSE_DEVICE 0 #define PARSE_NAME 1 @@ -26,8 +27,8 @@ struct file_list_entry *file_list_entry_by_name(struct file_list *files, const c return NULL; } -int file_list_add_entry(struct file_list *files, const char *name, const char *filename, - unsigned long flags) +static int __file_list_add_entry(struct file_list *files, char *name, char *filename, + unsigned long flags) { struct file_list_entry *entry; @@ -37,8 +38,8 @@ int file_list_add_entry(struct file_list *files, const char *name, const char *f entry = xzalloc(sizeof(*entry)); - entry->name = xstrdup(name); - entry->filename = xstrdup(filename); + entry->name = name; + entry->filename = filename; entry->flags = flags; list_add_tail(&entry->list, &files->list); @@ -46,12 +47,41 @@ int file_list_add_entry(struct file_list *files, const char *name, const char *f return 0; } +int file_list_add_entry(struct file_list *files, const char *name, const char *filename, + unsigned long flags) +{ + return __file_list_add_entry(files, xstrdup(name), xstrdup(filename), flags); +} + +int file_list_add_cdev_entry(struct file_list *files, struct cdev *cdev, + unsigned long flags) +{ + return __file_list_add_entry(files, xstrdup(cdev->name), + xasprintf("/dev/%s", cdev->name), flags); +} + +static bool file_list_handle_spec(struct file_list *files, const char *spec) +{ + unsigned count = 0; + bool autoadd; + + autoadd = !strcmp(spec, "auto"); + if (autoadd || !strcmp(spec, "block")) + count += file_list_add_blockdevs(files); + else + return false; + + pr_debug("'%s' spcifier resulted in %u entries\n", spec, count); + return true; +} + static int file_list_parse_one(struct file_list *files, const char *partstr, const char **endstr) { int i = 0, state = PARSE_DEVICE; char filename[PATH_MAX]; char name[PATH_MAX]; unsigned long flags = 0; + bool special = false; memset(filename, 0, sizeof(filename)); memset(name, 0, sizeof(name)); @@ -88,6 +118,9 @@ static int file_list_parse_one(struct file_list *files, const char *partstr, con case 'u': flags |= FILE_LIST_FLAG_UBI; break; + case 'o': + flags |= FILE_LIST_FLAG_OPTIONAL; + break; default: pr_err("Unknown flag '%c'\n", *partstr); return -EINVAL; @@ -99,7 +132,10 @@ static int file_list_parse_one(struct file_list *files, const char *partstr, con partstr++; } - if (state != PARSE_FLAGS) { + if (state == PARSE_DEVICE) + special = file_list_handle_spec(files, filename); + + if (!special && state != PARSE_FLAGS) { pr_err("Missing ')'\n"); return -EINVAL; } @@ -108,12 +144,12 @@ static int file_list_parse_one(struct file_list *files, const char *partstr, con partstr++; *endstr = partstr; - return file_list_add_entry(files, name, filename, flags); + return special ? 0 : file_list_add_entry(files, name, filename, flags); } static const char *flags_to_str(int flags) { - static char str[sizeof "srcu"]; + static char str[sizeof "srcuo"]; char *s = str;; if (flags & FILE_LIST_FLAG_SAFE) @@ -124,22 +160,33 @@ static const char *flags_to_str(int flags) *s++ = 'c'; if (flags & FILE_LIST_FLAG_UBI) *s++ = 'u'; + if (flags & FILE_LIST_FLAG_OPTIONAL) + *s++ = 'o'; *s = '\0'; return str; } -struct file_list *file_list_parse(const char *str) +struct file_list *file_list_new(void) { struct file_list *files; - int ret; - const char *endptr; files = xzalloc(sizeof(*files)); INIT_LIST_HEAD(&files->list); + return files; +} + +struct file_list *file_list_parse(const char *str) +{ + struct file_list *files; + int ret; + const char *endptr; + + files = file_list_new(); + while (*str) { ret = file_list_parse_one(files, str, &endptr); if (ret) { @@ -195,9 +242,7 @@ struct file_list *file_list_dup(struct file_list *old) struct file_list_entry *old_entry; struct file_list *new; - new = xzalloc(sizeof(*new)); - - INIT_LIST_HEAD(&new->list); + new = file_list_new(); list_for_each_entry(old_entry, &old->list, list) { (void)file_list_add_entry(new, old_entry->name, old_entry->filename, diff --git a/common/filetype.c b/common/filetype.c index 8ffcd6adcd..f922494500 100644 --- a/common/filetype.c +++ b/common/filetype.c @@ -17,8 +17,9 @@ #include <disks.h> #include <image-sparse.h> #include <elf.h> +#include <linux/zstd.h> -#include <arm/mach-imx/include/mach/imx-header.h> +#include <mach/imx/imx-header.h> struct filetype_str { const char *name; /* human readable filetype */ @@ -45,6 +46,7 @@ static const struct filetype_str filetype_str[] = { [filetype_mbr] = { "MBR sector", "mbr" }, [filetype_bmp] = { "BMP image", "bmp" }, [filetype_png] = { "PNG image", "png" }, + [filetype_qoi] = { "QOI image", "qoi" }, [filetype_ext] = { "EXT filesystem", "ext" }, [filetype_gpt] = { "GUID Partition Table", "gpt" }, [filetype_ubifs] = { "UBIFS image", "ubifs" }, @@ -61,19 +63,25 @@ static const struct filetype_str filetype_str[] = { [filetype_kwbimage_v1] = { "MVEBU kwbimage (v1)", "kwb1" }, [filetype_android_sparse] = { "Android sparse image", "sparse" }, [filetype_arm64_linux_image] = { "ARM aarch64 Linux image", "aarch64-linux" }, + [filetype_arm64_efi_linux_image] = { "ARM aarch64 Linux/EFI image", "aarch64-efi-linux" }, [filetype_riscv_linux_image] = { "RISC-V Linux image", "riscv-linux" }, + [filetype_riscv_efi_linux_image] = { "RISC-V Linux/EFI image", "riscv-efi-linux" }, [filetype_riscv_barebox_image] = { "RISC-V barebox image", "riscv-barebox" }, [filetype_elf] = { "ELF", "elf" }, [filetype_imx_image_v1] = { "i.MX image (v1)", "imx-image-v1" }, [filetype_imx_image_v2] = { "i.MX image (v2)", "imx-image-v2" }, [filetype_layerscape_image] = { "Layerscape image", "layerscape-PBL" }, [filetype_layerscape_qspi_image] = { "Layerscape QSPI image", "layerscape-qspi-PBL" }, + [filetype_nxp_fspi_image] = { "NXP FlexSPI image", "nxp-fspi-image" }, [filetype_ubootvar] = { "U-Boot environmemnt variable data", "ubootvar" }, - [filetype_stm32_image_v1] = { "STM32 image (v1)", "stm32-image-v1" }, + [filetype_stm32_image_fsbl_v1] = { "STM32MP FSBL image (v1)", "stm32-fsbl-v1" }, + [filetype_stm32_image_ssbl_v1] = { "STM32MP SSBL image (v1)", "stm32-ssbl-v1" }, [filetype_zynq_image] = { "Zynq image", "zynq-image" }, [filetype_mxs_sd_image] = { "i.MX23/28 SD card image", "mxs-sd-image" }, [filetype_rockchip_rkns_image] = { "Rockchip boot image", "rk-image" }, + [filetype_fip] = { "TF-A Firmware Image Package", "fip" }, + [filetype_zstd_compressed] = { "ZSTD compressed", "zstd" }, }; const char *file_type_to_string(enum filetype f) @@ -245,6 +253,11 @@ enum filetype file_detect_partition_table(const void *_buf, size_t bufsize) return filetype_unknown; } +static bool is_dos_exe(const u8 *buf8) +{ + return buf8[0] == 'M' && buf8[1] == 'Z'; +} + #define CH_TOC_section_name 0x14 enum filetype file_detect_type(const void *_buf, size_t bufsize) @@ -300,18 +313,25 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize) return filetype_aimage; if (buf64[0] == le64_to_cpu(0x0a1a0a0d474e5089ull)) return filetype_png; + if (strncmp(buf8, "qoif", 4) == 0) + return filetype_qoi; if (is_barebox_mips_head(_buf)) return filetype_mips_barebox; if (buf[0] == be32_to_cpu(0x534F4659)) return filetype_bpk; if (le32_to_cpu(buf[14]) == 0x644d5241) - return filetype_arm64_linux_image; + return is_dos_exe(buf8) ? filetype_arm64_efi_linux_image : filetype_arm64_linux_image; if (le32_to_cpu(buf[14]) == 0x05435352) - return filetype_riscv_linux_image; + return is_dos_exe(buf8) ? filetype_riscv_efi_linux_image : filetype_riscv_linux_image; if (le32_to_cpu(buf[14]) == 0x56435352 && !memcmp(&buf[12], "barebox", 8)) return filetype_riscv_barebox_image; if (strncmp(buf8, "RKNS", 4) == 0) return filetype_rockchip_rkns_image; + if (le32_to_cpu(buf[0]) == le32_to_cpu(0xaa640001)) + return filetype_fip; + if (le32_to_cpu(buf[0]) == le32_to_cpu(ZSTD_MAGICNUMBER)) + return filetype_zstd_compressed; + if ((buf8[0] == 0x5a || buf8[0] == 0x69 || buf8[0] == 0x78 || buf8[0] == 0x8b || buf8[0] == 0x9c) && buf8[0x1] == 0 && buf8[0x2] == 0 && buf8[0x3] == 0 && @@ -340,6 +360,8 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize) return filetype_layerscape_image; if (buf[0] == 0x01ee0100 && buf[1] == 0xaa55aa55) return filetype_layerscape_qspi_image; + if (buf[0] == 0xaa55aa55 && buf[1] == 0x80100000) + return filetype_layerscape_image; if (le32_to_cpu(buf[0]) == 0x00112233 && le32_to_cpu(buf[1]) == 0x1) return filetype_mxs_sd_image; @@ -358,15 +380,21 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize) if (buf[9] == 0x016f2818 || buf[9] == 0x18286f01) return filetype_arm_zimage; - if (buf8[0] == 'M' && buf8[1] == 'Z') + if (is_dos_exe(buf8)) return filetype_exe; if (bufsize < 256) return filetype_unknown; if (strncmp(buf8, "STM\x32", 4) == 0) { - if (buf8[74] == 0x01) - return filetype_stm32_image_v1; + if (buf8[74] == 0x01) { + switch(le32_to_cpu(buf[63])) { + case 0x00000000: + return filetype_stm32_image_ssbl_v1; + case 0x10000000: + return filetype_stm32_image_fsbl_v1; + } + } } if (bufsize < 512) @@ -395,21 +423,24 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize) if (is_imx_flash_header_v2(_buf)) return filetype_imx_image_v2; + if (buf[0] == cpu_to_be32(FCFB_HEAD_TAG) && + buf[1] == cpu_to_le32(FCFB_VERSION)) + return filetype_nxp_fspi_image; + if (buf[8] == 0xAA995566 && buf[9] == 0x584C4E58) return filetype_zynq_image; return filetype_unknown; } -enum filetype file_name_detect_type_offset(const char *filename, loff_t pos) +int file_name_detect_type_offset(const char *filename, loff_t pos, enum filetype *type) { int fd, ret; void *buf; - enum filetype type = filetype_unknown; fd = open_and_lseek(filename, O_RDONLY, pos); if (fd < 0) - goto out; + return fd; buf = xzalloc(FILE_TYPE_SAFE_BUFSIZE); @@ -417,34 +448,29 @@ enum filetype file_name_detect_type_offset(const char *filename, loff_t pos) if (ret < 0) goto err_out; - type = file_detect_type(buf, ret); + *type = file_detect_type(buf, ret); + ret = 0; err_out: close(fd); free(buf); -out: - return type; + + return ret; } -enum filetype file_name_detect_type(const char *filename) +int file_name_detect_type(const char *filename, enum filetype *type) { - return file_name_detect_type_offset(filename, 0); + return file_name_detect_type_offset(filename, 0, type); } -enum filetype cdev_detect_type(const char *name) +int cdev_detect_type(struct cdev *cdev, enum filetype *type) { - enum filetype type = filetype_unknown; int ret; - struct cdev *cdev; void *buf; - cdev = cdev_open(name, O_RDONLY); - if (!cdev) - return type; - if (cdev->filetype != filetype_unknown) { - type = cdev->filetype; - goto cdev_close; + *type = cdev->filetype; + return 0; } buf = xzalloc(FILE_TYPE_SAFE_BUFSIZE); @@ -452,13 +478,12 @@ enum filetype cdev_detect_type(const char *name) if (ret < 0) goto err_out; - type = file_detect_type(buf, ret); + *type = file_detect_type(buf, ret); + ret = 0; err_out: free(buf); -cdev_close: - cdev_close(cdev); - return type; + return ret; } bool filetype_is_barebox_image(enum filetype ft) @@ -470,6 +495,8 @@ bool filetype_is_barebox_image(enum filetype ft) case filetype_ch_image_be: case filetype_layerscape_image: case filetype_layerscape_qspi_image: + case filetype_stm32_image_fsbl_v1: + case filetype_fip: return true; default: return false; diff --git a/common/firmware.c b/common/firmware.c index b87d7da38f..3c7960581f 100644 --- a/common/firmware.c +++ b/common/firmware.c @@ -272,17 +272,18 @@ int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware) firmwarefd = open(firmware, O_RDONLY); if (firmwarefd < 0) { - printf("could not open %s: %s\n", firmware, - errno_str()); + printf("could not open %s: %m\n", firmware); ret = firmwarefd; goto out; } - type = file_name_detect_type(firmware); + ret = file_name_detect_type(firmware, &type); + if (ret) + goto out; devicefd = open(dst, O_WRONLY); if (devicefd < 0) { - printf("could not open %s: %s\n", dst, errno_str()); + printf("could not open %s: %m\n", dst); ret = devicefd; goto out; } @@ -305,6 +306,38 @@ out: return ret; } +/* + * request_firmware - load a firmware to a device + */ +int request_firmware(const struct firmware **out, const char *fw_name, struct device *dev) +{ + char fw_path[PATH_MAX + 1]; + struct firmware *fw; + int ret; + + fw = kzalloc(sizeof(struct firmware), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + snprintf(fw_path, sizeof(fw_path), "%s/%s", firmware_path, fw_name); + + ret = read_file_2(fw_path, &fw->size, (void *)&fw->data, FILESIZE_MAX); + if (ret) { + kfree(fw); + return ret; + } + + *out = fw; + + return 0; +} + +void release_firmware(const struct firmware *fw) +{ + kfree_const(fw->data); + kfree_const(fw); +} + static int firmware_init(void) { firmware_path = strdup("/env/firmware"); diff --git a/common/globalvar.c b/common/globalvar.c index 9e5a99f793..a83529f98f 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <malloc.h> #include <globalvar.h> @@ -14,12 +16,12 @@ static int nv_dirty; -struct device_d global_device = { +struct device global_device = { .name = "global", .id = DEVICE_ID_SINGLE, }; -struct device_d nv_device = { +struct device nv_device = { .name = "nv", .id = DEVICE_ID_SINGLE, }; @@ -99,7 +101,7 @@ static int nv_save(const char *name, const char *val) * This function initializes a newly created device parameter from the corresponding * nv.dev.<devname>.<paramname> variable. */ -void dev_param_init_from_nv(struct device_d *dev, const char *name) +void dev_param_init_from_nv(struct device *dev, const char *name) { char *nvname; const char *val; @@ -128,19 +130,19 @@ void dev_param_init_from_nv(struct device_d *dev, const char *name) /** * nvvar_device_dispatch - dispatch dev.<dev>.<param> name into device and parameter name * @name: The incoming name in the form dev.<dev>.<param> - * @dev: The returned device_d * belonging to <dev> + * @dev: The returned device * belonging to <dev> * @pname: the parameter name * - * Given a dev.<dev>.<param> string this function finds the device_d * belonging to + * Given a dev.<dev>.<param> string this function finds the device * belonging to * <dev> and the parameter name from <param>. * * Return: When incoming string does not belong to the device namespace (does not begin * with "dev." this function returns 0. A value > 0 is returned when the incoming string - * is in the device namespace and the string can be dispatched into a device_d * and a + * is in the device namespace and the string can be dispatched into a device * and a * parameter name. A negative error code is returned when the incoming string belongs to * the device namespace, but cannot be dispatched. */ -static int nvvar_device_dispatch(const char *name, struct device_d **dev, +static int nvvar_device_dispatch(const char *name, struct device **dev, const char **pname) { char *devname; @@ -175,7 +177,8 @@ static int nvvar_device_dispatch(const char *name, struct device_d **dev, return 1; } -static int nv_set(struct device_d *dev, struct param_d *p, const char *name, const char *val) +static int nv_set(struct device *dev, struct param_d *p, const char *name, + const char *val) { int ret; @@ -193,12 +196,13 @@ static int nv_set(struct device_d *dev, struct param_d *p, const char *name, con return 0; } -static const char *nv_param_get(struct device_d *dev, struct param_d *p) +static const char *nv_param_get(struct device *dev, struct param_d *p) { return p->value ? p->value : ""; } -static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val) +static int nv_param_set(struct device *dev, struct param_d *p, + const char *val) { int ret; @@ -212,7 +216,7 @@ static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val static int __nvvar_add(const char *name, const char *value) { struct param_d *p; - struct device_d *dev = NULL; + struct device *dev = NULL; const char *pname; int ret; @@ -373,7 +377,7 @@ int nvvar_load(void) return 0; } -static void device_param_print(struct device_d *dev) +static void device_param_print(struct device *dev) { struct param_d *param; @@ -450,9 +454,10 @@ void globalvar_set(const char *name, const char *val) dev_set_param(&global_device, name, val); } -static int globalvar_simple_set(struct device_d *dev, struct param_d *p, const char *val) +static int globalvar_simple_set(struct device *dev, struct param_d *p, + const char *val) { - struct device_d *rdev; + struct device *rdev; const char *pname = NULL; int ret; @@ -742,8 +747,9 @@ static void nv_exit(void) } predevshutdown_exitcall(nv_exit); -static int nv_global_param_complete(struct device_d *dev, struct string_list *sl, - char *instr, int eval) +static int nv_global_param_complete(struct device *dev, + struct string_list *sl, + char *instr, int eval) { struct param_d *param; int len; @@ -764,7 +770,7 @@ static int nv_global_param_complete(struct device_d *dev, struct string_list *sl int nv_complete(struct string_list *sl, char *instr) { - struct device_d *dev; + struct device *dev; struct param_d *param; char *str; int len; diff --git a/common/hush.c b/common/hush.c index d80df7a181..608c0e4937 100644 --- a/common/hush.c +++ b/common/hush.c @@ -112,7 +112,6 @@ #include <slice.h> #include <getopt.h> #include <libfile.h> -#include <libbb.h> #include <magicvar.h> #include <linux/list.h> #include <binfmt.h> @@ -618,6 +617,7 @@ static int builtin_exit(struct p_context *ctx, struct child_prog *child, static void remove_quotes_in_str(char *src) { char *trg = src; + bool in_double_quotes = false; while (*src) { if (*src == '\'') { @@ -630,6 +630,7 @@ static void remove_quotes_in_str(char *src) /* drop quotes */ if (*src == '"') { + in_double_quotes = !in_double_quotes; src++; continue; } @@ -655,6 +656,13 @@ static void remove_quotes_in_str(char *src) continue; } + /* replace '\ ' with ' ' */ + if (!in_double_quotes && *src == '\\' && *(src + 1) == ' ') { + *trg++ = ' '; + src += 2; + continue; + } + *trg++ = *src++; } *trg = 0; @@ -1829,7 +1837,7 @@ static char **make_list_in(char **inp, char *name) p3 = insert_var_value(inp[i]); p1 = p3; while (*p1) { - if ((*p1 == ' ')) { + if (*p1 == ' ') { p1++; continue; } diff --git a/common/image-fit.c b/common/image-fit.c index 38a372ff52..251fda97b3 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <stringlist.h> #include <rsa.h> +#include <uncompress.h> #include <image-fit.h> #define FDT_MAX_DEPTH 32 @@ -255,53 +256,44 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node, static int fit_check_rsa_signature(struct device_node *sig_node, enum hash_algo algo, void *hash) { - struct rsa_public_key *key; - const char *key_name; - char *key_path; - struct device_node *key_node; + const struct rsa_public_key *key; + const char *key_name = NULL; int sig_len; const char *sig_value; int ret; sig_value = of_get_property(sig_node, "value", &sig_len); if (!sig_value) { - pr_err("signature value not found in %s\n", sig_node->full_name); + pr_err("signature value not found in %pOF\n", sig_node); return -EINVAL; } - if (of_property_read_string(sig_node, "key-name-hint", &key_name)) { - pr_err("key name not found in %s\n", sig_node->full_name); - return -EINVAL; - } - - key = rsa_get_key(key_name); - if (IS_ERR(key)) { - key_path = xasprintf("/signature/key-%s", key_name); - key_node = of_find_node_by_path(key_path); - if (!key_node) { - pr_info("failed to find key node %s\n", key_path); - free(key_path); - return -ENOENT; + of_property_read_string(sig_node, "key-name-hint", &key_name); + if (key_name) { + key = rsa_get_key(key_name); + if (key) { + ret = rsa_verify(key, sig_value, sig_len, hash, algo); + if (!ret) + goto ok; } - free(key_path); + } - key = rsa_of_read_key(key_node); + for_each_rsa_key(key) { + if (key_name && !strcmp(key->key_name_hint, key_name)) + continue; - if (IS_ERR(key)) { - pr_info("failed to read key in %s\n", key_node->full_name); - return -ENOENT; - } + ret = rsa_verify(key, sig_value, sig_len, hash, algo); + if (!ret) + goto ok; } - ret = rsa_verify(key, sig_value, sig_len, hash, algo); - if (ret) - pr_err("image signature BAD\n"); - else - pr_info("image signature OK\n"); + pr_err("image signature BAD\n"); - rsa_key_free(key); + return -EBADMSG; +ok: + pr_info("image signature OK\n"); - return ret; + return 0; } /* @@ -318,12 +310,12 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit) if (of_property_read_u32_index(sig_node, "hashed-strings", 0, &hashed_strings_start)) { - pr_err("hashed-strings start not found in %s\n", sig_node->full_name); + pr_err("hashed-strings start not found in %pOF\n", sig_node); return -EINVAL; } if (of_property_read_u32_index(sig_node, "hashed-strings", 1, &hashed_strings_size)) { - pr_err("hashed-strings size not found in %s\n", sig_node->full_name); + pr_err("hashed-strings size not found in %pOF\n", sig_node); return -EINVAL; } @@ -331,7 +323,7 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit) string_list_init(&exc_props); if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) { - pr_err("hashed-nodes property not found in %s\n", sig_node->full_name); + pr_err("hashed-nodes property not found in %pOF\n", sig_node); ret = -EINVAL; goto out_sl; } @@ -389,30 +381,29 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image, hash = of_get_child_by_name(image, "hash@1"); if (!hash) { if (ret) - pr_err("image %s does not have hashes\n", - image->full_name); + pr_err("image %pOF does not have hashes\n", image); return ret; } value_read = of_get_property(hash, "value", &hash_len); if (!value_read) { - pr_err("%s: \"value\" property not found\n", hash->full_name); + pr_err("%pOF: \"value\" property not found\n", hash); return -EINVAL; } if (of_property_read_string(hash, "algo", &algo)) { - pr_err("%s: \"algo\" property not found\n", hash->full_name); + pr_err("%pOF: \"algo\" property not found\n", hash); return -EINVAL; } d = digest_alloc(algo); if (!d) { - pr_err("%s: unsupported algo %s\n", hash->full_name, algo); + pr_err("%pOF: unsupported algo %s\n", hash, algo); return -EINVAL; } if (hash_len != digest_length(d)) { - pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len); + pr_err("%pOF: invalid hash length %d\n", hash, hash_len); ret = -EINVAL; goto err_digest_free; } @@ -421,10 +412,10 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image, digest_update(d, data, data_len); if (digest_verify(d, value_read)) { - pr_info("%s: hash BAD\n", hash->full_name); + pr_info("%pOF: hash BAD\n", hash); ret = -EBADMSG; } else { - pr_info("%s: hash OK\n", hash->full_name); + pr_info("%pOF: hash OK\n", hash); ret = 0; } @@ -461,7 +452,7 @@ static int fit_image_verify_signature(struct fit_handle *handle, if (!sig_node) sig_node = of_get_child_by_name(image, "signature@1"); if (!sig_node) { - pr_err("Image %s has no signature\n", image->full_name); + pr_err("Image %pOF has no signature\n", image); return ret; } @@ -554,6 +545,7 @@ int fit_get_image_address(struct fit_handle *handle, void *configuration, { struct device_node *image; const char *unit = name; + const char *type; int ret; if (!address || !property || !name) @@ -563,11 +555,62 @@ int fit_get_image_address(struct fit_handle *handle, void *configuration, if (ret) return ret; + /* Treat type = "kernel_noload" as if entry/load address is missing */ + ret = of_property_read_string(image, "type", &type); + if (!ret && !strcmp(type, "kernel_noload")) + return -ENOENT; + ret = fit_get_address(image, property, address); return ret; } +static void fit_uncompress_error_fn(char *x) +{ + pr_err("%s\n", x); +} + +static int fit_handle_decompression(struct device_node *image, + const char *type, + const void **data, + int *data_len) +{ + const char *compression = NULL; + void *uc_data; + int ret; + + of_property_read_string(image, "compression", &compression); + if (!compression || !strcmp(compression, "none")) + return 0; + + if (!strcmp(type, "ramdisk")) { + pr_warn("compression != \"none\" for ramdisks is deprecated," + " please fix your .its file!\n"); + return 0; + } + + if (!IS_ENABLED(CONFIG_UNCOMPRESS)) { + pr_err("image has compression = \"%s\", but support not compiled in\n", + compression); + return -ENOSYS; + } + + ret = uncompress_buf_to_buf(*data, *data_len, &uc_data, + fit_uncompress_error_fn); + if (ret < 0) { + pr_err("data couldn't be decompressed\n"); + return ret; + } + + *data = uc_data; + *data_len = ret; + + /* associate buffer with FIT, so it's not leaked */ + __of_new_property(image, "uncompressed-data", uc_data, *data_len); + + return 0; +} + /** * fit_open_image - Open an image in a FIT image * @handle: The FIT image handle @@ -604,7 +647,7 @@ int fit_open_image(struct fit_handle *handle, void *configuration, of_property_read_string(image, "type", &type); if (!type) { - pr_err("No \"type\" property found in %s\n", image->full_name); + pr_err("No \"type\" property found in %pOF\n", image); return -EINVAL; } @@ -622,6 +665,10 @@ int fit_open_image(struct fit_handle *handle, void *configuration, if (ret < 0) return ret; + ret = fit_handle_decompression(image, type, &data, &data_len); + if (ret) + return ret; + *outdata = data; *outsize = data_len; @@ -649,27 +696,32 @@ static int fit_config_verify_signature(struct fit_handle *handle, struct device_ } for_each_child_of_node(conf_node, sig_node) { + if (!of_node_has_prefix(sig_node, "signature")) + continue; + if (handle->verbose) - of_print_nodes(sig_node, 0); + of_print_nodes(sig_node, 0, ~0); + ret = fit_verify_signature(sig_node, handle->fit); if (ret < 0) return ret; } if (ret < 0) { - pr_err("configuration '%s' does not have a signature\n", - conf_node->full_name); + pr_err("configuration '%pOF' does not have a signature\n", conf_node); return ret; } return ret; } -static int fit_find_compatible_unit(struct device_node *conf_node, +static int fit_find_compatible_unit(struct fit_handle *handle, + struct device_node *conf_node, const char **unit) { struct device_node *child = NULL; struct device_node *barebox_root; + int best_score = 0; const char *machine; int ret; @@ -682,13 +734,55 @@ static int fit_find_compatible_unit(struct device_node *conf_node, return -ENOENT; for_each_child_of_node(conf_node, child) { - if (of_device_is_compatible(child, machine)) { + int score = of_device_is_compatible(child, machine); + + if (!score && !of_property_present(child, "compatible") && + of_property_present(child, "fdt")) { + struct device_node *image; + const char *unit = "fdt"; + int data_len; + const void *data; + int ret; + + ret = fit_get_image(handle, child, &unit, &image); + if (ret) + goto next; + + data = of_get_property(image, "data", &data_len); + if (!data) { + ret = -EINVAL; + goto next; + } + + ret = fit_handle_decompression(image, "fdt", &data, &data_len); + if (ret) { + ret = -EILSEQ; + goto next; + } + + score = fdt_machine_is_compatible(data, data_len, machine); + + of_delete_property_by_name(image, "uncompressed-data"); +next: + if (ret) + pr_warn("skipping malformed configuration: %pOF (%pe)\n", + child, ERR_PTR(ret)); + } + + if (score > best_score) { + best_score = score; *unit = child->name; - pr_info("matching unit '%s' found\n", *unit); - return 0; + + if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE) + break; } } + if (best_score) { + pr_info("matching unit '%s' found\n", *unit); + return 0; + } + default_unit: pr_info("No match found. Trying default.\n"); if (of_property_read_string(conf_node, "default", unit) == 0) @@ -720,7 +814,7 @@ void *fit_open_configuration(struct fit_handle *handle, const char *name) if (name) { unit = name; } else { - ret = fit_find_compatible_unit(conf_node, &unit); + ret = fit_find_compatible_unit(handle, conf_node, &unit); if (ret) { pr_info("Couldn't get a valid configuration. Aborting.\n"); return ERR_PTR(ret); @@ -806,6 +900,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose, * @filename: The filename of the FIT image * @verbose: If true, be more verbose * @verify: The verify mode + * @max_size: maximum length to read from file * * This opens a FIT image found in @filename. The returned handle is used as * context for the other FIT functions. @@ -813,7 +908,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose, * Return: A handle to a FIT image or a ERR_PTR */ struct fit_handle *fit_open(const char *filename, bool verbose, - enum bootm_verify verify) + enum bootm_verify verify, loff_t max_size) { struct fit_handle *handle; int ret; @@ -824,8 +919,8 @@ struct fit_handle *fit_open(const char *filename, bool verbose, handle->verify = verify; ret = read_file_2(filename, &handle->size, &handle->fit_alloc, - FILESIZE_MAX); - if (ret) { + max_size); + if (ret && ret != -EFBIG) { pr_err("unable to read %s: %s\n", filename, strerror(-ret)); return ERR_PTR(ret); } diff --git a/common/imd-barebox.c b/common/imd-barebox.c index 06731d0600..7877c8de39 100644 --- a/common/imd-barebox.c +++ b/common/imd-barebox.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <image-metadata.h> #include <generated/compile.h> @@ -21,6 +23,16 @@ __BAREBOX_IMD_SECTION(.barebox_imd_end) = { .type = cpu_to_le32(IMD_TYPE_END), }; +#ifdef CONFIG_IMD_ENDIANNESS +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define IMD_ENDIANNESS "little" +#else +#define IMD_ENDIANNESS "big" +#endif +BAREBOX_IMD_TAG_STRING(imd_endianness_tag, IMD_TYPE_PARAMETER, + "endianness=" IMD_ENDIANNESS, 1); +#endif /* CONFIG_IMD_ENDIANNESS */ + BAREBOX_IMD_TAG_STRING(imd_build_tag, IMD_TYPE_BUILD, UTS_VERSION, 1); BAREBOX_IMD_TAG_STRING(imd_release_tag, IMD_TYPE_RELEASE, UTS_RELEASE, 1); BAREBOX_IMD_TAG_STRING(imd_buildsystem_version_tag, IMD_TYPE_BUILDSYSTEM, BUILDSYSTEM_VERSION, 1); diff --git a/common/imd.c b/common/imd.c index e1d5733c6b..1100e6878a 100644 --- a/common/imd.c +++ b/common/imd.c @@ -21,6 +21,12 @@ static inline void read_file_2_free(void *buf) { free(buf); } + +static int imd_read_file(const char *filename, size_t *size, void **outbuf, + bool allow_mmap) +{ + return read_file_2(filename, size, outbuf, 0x100000); +} #endif /* @@ -317,7 +323,7 @@ static int imd_calculate_crc32(void *input, const struct imd_header *imd_start, length = ALIGN(length, 4); length += sizeof(struct imd_header); - if (imd_read_type(imd) == IMD_TYPE_CRC32) { + if (imd_is_crc32(imd_read_type(imd))) { *imd_crc = (struct imd_header *)imd; debug("Found crc token at %d\n", end_ofs); break; @@ -434,6 +440,7 @@ int imd_command(int argc, char *argv[]) char *str; uint32_t checksum = 0; uint32_t verify = 0; + bool allow_mmap = true; imd_command_verbose = 0; @@ -457,6 +464,7 @@ int imd_command(int argc, char *argv[]) break; case 'c': checksum = 1; + allow_mmap = false; break; case 'V': verify = 1; @@ -473,7 +481,7 @@ int imd_command(int argc, char *argv[]) filename = argv[optind]; - ret = read_file_2(filename, &size, &buf, 0x100000); + ret = imd_read_file(filename, &size, &buf, allow_mmap); if (ret && ret != -EFBIG) return -errno; diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c index 3b07d539ee..0d46192720 100644 --- a/common/imx-bbu-nand-fcb.c +++ b/common/imx-bbu-nand-fcb.c @@ -14,6 +14,8 @@ #include <linux/sizes.h> #include <bbu.h> #include <fs.h> +#include <command.h> +#include <complete.h> #include <linux/mtd/mtd-abi.h> #include <linux/mtd/nand_mxs.h> #include <linux/mtd/mtd.h> @@ -23,28 +25,27 @@ #include <linux/bitops.h> #include <io.h> #include <crc.h> -#include <mach/generic.h> #include <mtd/mtd-peb.h> #include <soc/imx/imx-nand-bcb.h> +#ifdef CONFIG_ARCH_IMX +#include <mach/imx/imx6.h> +#include <mach/imx/generic.h> +#else +#include <mach/mxs/generic.h> +#endif -#ifdef CONFIG_ARCH_IMX6 -#include <mach/imx6.h> static inline int fcb_is_bch_encoded(void) { return cpu_is_mx6ul() || cpu_is_mx6ull(); } -#else -static inline int fcb_is_bch_encoded(void) -{ - return 0; -} -#endif struct imx_nand_fcb_bbu_handler { struct bbu_handler handler; void (*fcb_create)(struct imx_nand_fcb_bbu_handler *imx_handler, struct fcb_block *fcb, struct mtd_info *mtd); + int (*fcb_read)(struct mtd_info *mtd, int block, struct fcb_block **retfcb); + int (*fcb_write)(struct mtd_info *mtd, int block, struct fcb_block *fcb); enum filetype filetype; }; @@ -123,7 +124,7 @@ static void encode_bch_ecc(void *buf, struct fcb_block *fcb, int eccbits) free_bch(bch); } -static struct fcb_block *read_fcb_bch(void *rawpage, int eccbits) +static struct fcb_block *fcb_decode_bch(void *rawpage, int eccbits) { int i, j, ret, errbit, m = 13; int blocksize = 128; @@ -227,7 +228,7 @@ static uint32_t calc_chksum(void *buf, size_t size) return ~chksum; } -static struct fcb_block *read_fcb_hamming_13_8(void *rawpage) +static struct fcb_block *fcb_decode_hamming_13_8(void *rawpage) { int i; int bitflips = 0, bit_to_flip; @@ -282,57 +283,57 @@ static __maybe_unused void dump_fcb(void *buf) { struct fcb_block *fcb = buf; - pr_debug("Checksum: 0x%08x\n", fcb->Checksum); - pr_debug("FingerPrint: 0x%08x\n", fcb->FingerPrint); - pr_debug("Version: 0x%08x\n", fcb->Version); - pr_debug("DataSetup: 0x%02x\n", fcb->DataSetup); - pr_debug("DataHold: 0x%02x\n", fcb->DataHold); - pr_debug("AddressSetup: 0x%02x\n", fcb->AddressSetup); - pr_debug("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME); - pr_debug("NandTimingState: 0x%02x\n", fcb->NandTimingState); - pr_debug("REA: 0x%02x\n", fcb->REA); - pr_debug("RLOH: 0x%02x\n", fcb->RLOH); - pr_debug("RHOH: 0x%02x\n", fcb->RHOH); - pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize); - pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize); - pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock); - pr_debug("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs); - pr_debug("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie); - pr_debug("CellType: 0x%08x\n", fcb->CellType); - pr_debug("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType); - pr_debug("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size); - pr_debug("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize); - pr_debug("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType); - pr_debug("MetadataBytes: 0x%08x\n", fcb->MetadataBytes); - pr_debug("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage); - pr_debug("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK); - pr_debug("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK); - pr_debug("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK); - pr_debug("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK); - pr_debug("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK); - pr_debug("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK); - pr_debug("EraseThreshold: 0x%08x\n", fcb->EraseThreshold); - pr_debug("BootPatch: 0x%08x\n", fcb->BootPatch); - pr_debug("PatchSectors: 0x%08x\n", fcb->PatchSectors); - pr_debug("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage); - pr_debug("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage); - pr_debug("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1); - pr_debug("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2); - pr_debug("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress); - pr_debug("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte); - pr_debug("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit); - pr_debug("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset); - pr_debug("BCHType: 0x%08x\n", fcb->BCHType); - pr_debug("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency); - pr_debug("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay); - pr_debug("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay); - pr_debug("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay); - pr_debug("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause); - pr_debug("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause); - pr_debug("TMSpeed: 0x%08x\n", fcb->TMSpeed); - pr_debug("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout); - pr_debug("DISBBM: 0x%08x\n", fcb->DISBBM); - pr_debug("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData); + printf("Checksum: 0x%08x\n", fcb->Checksum); + printf("FingerPrint: 0x%08x\n", fcb->FingerPrint); + printf("Version: 0x%08x\n", fcb->Version); + printf("DataSetup: 0x%02x\n", fcb->DataSetup); + printf("DataHold: 0x%02x\n", fcb->DataHold); + printf("AddressSetup: 0x%02x\n", fcb->AddressSetup); + printf("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME); + printf("NandTimingState: 0x%02x\n", fcb->NandTimingState); + printf("REA: 0x%02x\n", fcb->REA); + printf("RLOH: 0x%02x\n", fcb->RLOH); + printf("RHOH: 0x%02x\n", fcb->RHOH); + printf("PageDataSize: 0x%08x\n", fcb->PageDataSize); + printf("TotalPageSize: 0x%08x\n", fcb->TotalPageSize); + printf("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock); + printf("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs); + printf("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie); + printf("CellType: 0x%08x\n", fcb->CellType); + printf("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType); + printf("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size); + printf("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize); + printf("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType); + printf("MetadataBytes: 0x%08x\n", fcb->MetadataBytes); + printf("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage); + printf("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK); + printf("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK); + printf("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK); + printf("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK); + printf("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK); + printf("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK); + printf("EraseThreshold: 0x%08x\n", fcb->EraseThreshold); + printf("BootPatch: 0x%08x\n", fcb->BootPatch); + printf("PatchSectors: 0x%08x\n", fcb->PatchSectors); + printf("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage); + printf("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage); + printf("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1); + printf("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2); + printf("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress); + printf("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte); + printf("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit); + printf("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset); + printf("BCHType: 0x%08x\n", fcb->BCHType); + printf("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency); + printf("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay); + printf("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay); + printf("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay); + printf("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause); + printf("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause); + printf("TMSpeed: 0x%08x\n", fcb->TMSpeed); + printf("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout); + printf("DISBBM: 0x%08x\n", fcb->DISBBM); + printf("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData); } static __maybe_unused ssize_t raw_read_page(struct mtd_info *mtd, void *dst, loff_t offset) @@ -367,7 +368,7 @@ static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset) return ret; } -static int read_fcb(struct mtd_info *mtd, int num, struct fcb_block **retfcb) +static int fcb_read_hamming_13_8(struct mtd_info *mtd, int block, struct fcb_block **retfcb) { int ret; struct fcb_block *fcb; @@ -377,19 +378,71 @@ static int read_fcb(struct mtd_info *mtd, int num, struct fcb_block **retfcb) rawpage = xmalloc(mtd->writesize + mtd->oobsize); - ret = raw_read_page(mtd, rawpage, mtd->erasesize * num); + ret = raw_read_page(mtd, rawpage, mtd->erasesize * block); if (ret) { - pr_err("Cannot read block %d\n", num); + pr_err("Cannot read block %d\n", block); goto err; } - if (fcb_is_bch_encoded()) - fcb = read_fcb_bch(rawpage, 40); - else - fcb = read_fcb_hamming_13_8(rawpage); + fcb = fcb_decode_hamming_13_8(rawpage); + if (IS_ERR(fcb)) { + pr_err("Cannot decode fcb on block %d\n", block); + ret = PTR_ERR(fcb); + goto err; + } + + *retfcb = fcb; + ret = 0; +err: + free(rawpage); + + return ret; +} + +static int fcb_write_hamming_13_8(struct mtd_info *mtd, int block, struct fcb_block *fcb) +{ + void *fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize); + int ret; + + memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); + /* + * Set the first and second byte of OOB data to 0xFF, not 0x00. These + * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since + * the FCB is mostly written to the first page in a block, a scan for + * factory bad blocks will detect these blocks as bad, e.g. when + * function nand_scan_bbt() is executed to build a new bad block table. + */ + memset(fcb_raw_page + mtd->writesize, 0xFF, 2); + + encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512); + + ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize); + + free(fcb_raw_page); + + return ret; +} + +static int fcb_read_bch(struct mtd_info *mtd, int block, struct fcb_block **retfcb) +{ + int ret; + struct fcb_block *fcb; + void *rawpage; + + *retfcb = NULL; + + rawpage = xmalloc(mtd->writesize + mtd->oobsize); + + ret = raw_read_page(mtd, rawpage, mtd->erasesize * block); + if (ret) { + pr_err("Cannot read block %d\n", block); + goto err; + } + + fcb = fcb_decode_bch(rawpage, 40); if (IS_ERR(fcb)) { - pr_err("Cannot read fcb on block %d\n", num); + pr_err("Cannot decode fcb on block %d\n", block); ret = PTR_ERR(fcb); goto err; } @@ -402,6 +455,31 @@ err: return ret; } +static int fcb_write_bch(struct mtd_info *mtd, int block, struct fcb_block *fcb) +{ + void *fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize); + int ret; + + /* 40 bit BCH, for i.MX6UL(L) */ + encode_bch_ecc(fcb_raw_page + 32, fcb, 40); + + /* + * Set the first and second byte of OOB data to 0xFF, not 0x00. These + * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since + * the FCB is mostly written to the first page in a block, a scan for + * factory bad blocks will detect these blocks as bad, e.g. when + * function nand_scan_bbt() is executed to build a new bad block table. + */ + memset(fcb_raw_page + mtd->writesize, 0xFF, 2); + + ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize); + + free(fcb_raw_page); + + return ret; +} + + static int fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler, struct fcb_block *fcb, struct mtd_info *mtd) { @@ -438,6 +516,9 @@ static int fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler, imx_handler->fcb_create(imx_handler, fcb, mtd); + fcb->DISBBM = 0; + fcb->disbbm_search = 0; + fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); return 0; @@ -651,7 +732,7 @@ static void imx28_dbbt_create(struct dbbt_block *dbbt, int num_bad_blocks) * imx_bbu_write_fcb - Write FCB and DBBT raw data to the device * @mtd: The mtd Nand device * @block: The block to write to - * @fcb_raw_page: The raw FCB data + * @fcb: FCB * @dbbt_data_page: The DBBT data * * This function writes the FCB/DBBT data to the block given in @block @@ -660,7 +741,8 @@ static void imx28_dbbt_create(struct dbbt_block *dbbt, int num_bad_blocks) * * return: 0 on success or a negative error code otherwise. */ -static int imx_bbu_write_fcb(struct mtd_info *mtd, int block, void *fcb_raw_page, +static int imx_bbu_write_fcb(struct imx_nand_fcb_bbu_handler *imx_handler, + struct mtd_info *mtd, int block, struct fcb_block *fcb, void *dbbt_data_page) { struct dbbt_block *dbbt; @@ -682,7 +764,7 @@ again: if (ret) return ret; - ret = raw_write_page(mtd, fcb_raw_page, block * mtd->erasesize); + ret = imx_handler->fcb_write(mtd, block, fcb); if (ret) { pr_err("Writing FCB on block %d failed with %s\n", block, strerror(-ret)); @@ -843,13 +925,14 @@ out: * * return: 0 if the FCB/DBBT are valid, a negative error code otherwise */ -static int fcb_dbbt_check(struct mtd_info *mtd, int num, struct fcb_block *fcb) +static int fcb_dbbt_check(struct imx_nand_fcb_bbu_handler *imx_handler, + struct mtd_info *mtd, int num, struct fcb_block *fcb) { int ret; struct fcb_block *f; int pages_per_block = mtd->erasesize / mtd->writesize; - ret = read_fcb(mtd, num, &f); + ret = imx_handler->fcb_read(mtd, num, &f); if (ret) return ret; @@ -885,11 +968,11 @@ out: * * return: 0 for success or a negative error code otherwise. */ -static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb) +static int imx_bbu_write_fcbs_dbbts(struct imx_nand_fcb_bbu_handler *imx_handler, + struct mtd_info *mtd, struct fcb_block *fcb) { void *dbbt = NULL; int i, ret, valid = 0; - void *fcb_raw_page; /* * The DBBT search start page is configurable in the FCB block. @@ -899,28 +982,8 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb) if (fcb->DBBTSearchAreaStartAddress != 1) return -EINVAL; - fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize); - - if (fcb_is_bch_encoded()) { - /* 40 bit BCH, for i.MX6UL(L) */ - encode_bch_ecc(fcb_raw_page + 32, fcb, 40); - } else { - memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); - encode_hamming_13_8(fcb_raw_page + 12, - fcb_raw_page + 12 + 512, 512); - } - dbbt = dbbt_data_create(mtd); - /* - * Set the first and second byte of OOB data to 0xFF, not 0x00. These - * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since - * the FCB is mostly written to the first page in a block, a scan for - * factory bad blocks will detect these blocks as bad, e.g. when - * function nand_scan_bbt() is executed to build a new bad block table. - */ - memset(fcb_raw_page + mtd->writesize, 0xFF, 2); - pr_info("Writing FCBs/DBBTs with primary/secondary Firmwares at pages %d/%d\n", fcb->Firmware1_startingPage, fcb->Firmware2_startingPage); @@ -928,7 +991,7 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb) if (mtd_peb_is_bad(mtd, i)) continue; - if (!fcb_dbbt_check(mtd, i, fcb)) { + if (!fcb_dbbt_check(imx_handler, mtd, i, fcb)) { valid++; pr_info("FCB/DBBT on block %d still valid\n", i); continue; @@ -936,14 +999,13 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, struct fcb_block *fcb) pr_info("Writing FCB/DBBT on block %d\n", i); - ret = imx_bbu_write_fcb(mtd, i, fcb_raw_page, dbbt); + ret = imx_bbu_write_fcb(imx_handler, mtd, i, fcb, dbbt); if (ret) pr_err("Writing FCB/DBBT %d failed with: %s\n", i, strerror(-ret)); else valid++; } - free(fcb_raw_page); free(dbbt); if (!valid) @@ -1155,7 +1217,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat enum filetype filetype; unsigned num_blocks_fw, fw_size; int used = 0; - int fw_orig_len; + int fw_orig_len = 0; int used_refresh = 0, unused_refresh = 0; if (data->image) { @@ -1183,7 +1245,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat } for (i = 0; i < 4; i++) { - read_fcb(mtd, i, &fcb); + imx_handler->fcb_read(mtd, i, &fcb); if (fcb) break; } @@ -1326,7 +1388,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat * just written as primary firmware. From now on the new * firmware will be booted. */ - ret = imx_bbu_write_fcbs_dbbts(mtd, fcb); + ret = imx_bbu_write_fcbs_dbbts(imx_handler, mtd, fcb); if (ret < 0) goto out; @@ -1347,7 +1409,7 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat */ if (ret > 0) { pr_info("New bad blocks detected, writing FCBs/DBBTs again\n"); - ret = imx_bbu_write_fcbs_dbbts(mtd, fcb); + ret = imx_bbu_write_fcbs_dbbts(imx_handler, mtd, fcb); if (ret < 0) goto out; } @@ -1380,6 +1442,14 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags) imx_handler->fcb_create = imx6_fcb_create; imx_handler->filetype = filetype_arm_barebox; + if (fcb_is_bch_encoded()) { + imx_handler->fcb_read = fcb_read_bch; + imx_handler->fcb_write = fcb_write_bch; + } else { + imx_handler->fcb_read = fcb_read_hamming_13_8; + imx_handler->fcb_write = fcb_write_hamming_13_8; + } + handler = &imx_handler->handler; handler->devicefile = "nand0.barebox"; handler->name = name; @@ -1393,9 +1463,6 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags) return ret; } -#ifdef CONFIG_ARCH_IMX28 -#include <mach/imx28-regs.h> - #define GPMI_TIMING0 0x00000070 #define GPMI_TIMING0_ADDRESS_SETUP_MASK (0xff << 16) #define GPMI_TIMING0_ADDRESS_SETUP_OFFSET 16 @@ -1413,18 +1480,31 @@ int imx6_bbu_nand_register_handler(const char *name, unsigned long flags) #define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24 #define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16) #define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16 -#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12) -#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12 -#define BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0xfff +#define MX28_BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12) +#define MX28_BCH_FLASHLAYOUT0_ECC0_OFFSET 12 +#define BCH_FLASHLAYOUT0_ECC0_MASK (0x1f << 11) +#define BCH_FLASHLAYOUT0_ECC0_OFFSET 11 +#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_MASK BIT(10) +#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET 10 +#define BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0x3ff #define BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET 0 +#define MX28_BCH_FLASHLAYOUT0_DATA0_SIZE_MASK 0xfff #define BCH_FLASH0LAYOUT1 0x00000090 #define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16) #define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16 -#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12) -#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12 -#define BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0xfff +#define BCH_FLASHLAYOUT1_ECCN_MASK (0x1f << 11) +#define BCH_FLASHLAYOUT1_ECCN_OFFSET 11 +#define MX28_BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12) +#define MX28_BCH_FLASHLAYOUT1_ECCN_OFFSET 12 +#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK BIT(10) +#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET 10 #define BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET 0 +#define BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0x3ff +#define MX28_BCH_FLASHLAYOUT1_DATAN_SIZE_MASK 0xfff + +#ifdef CONFIG_ARCH_IMX28 +#include <mach/mxs/imx28-regs.h> static void imx28_fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler, struct fcb_block *fcb, struct mtd_info *mtd) @@ -1454,6 +1534,8 @@ int imx28_bbu_nand_register_handler(const char *name, unsigned long flags) imx_handler = xzalloc(sizeof(*imx_handler)); imx_handler->fcb_create = imx28_fcb_create; + imx_handler->fcb_read = fcb_read_hamming_13_8; + imx_handler->fcb_write = fcb_write_hamming_13_8; imx_handler->filetype = filetype_mxs_bootstream; @@ -1470,3 +1552,159 @@ int imx28_bbu_nand_register_handler(const char *name, unsigned long flags) return ret; } #endif + +static int imx7_fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb) +{ + struct fcb_block *fcb = xzalloc(mtd->writesize); + int ret; + + ret = mxs_nand_read_fcb_bch62(block, fcb, sizeof(*fcb)); + if (ret) + free(fcb); + else + *retfcb = fcb; + + return ret; +} + +#ifdef CONFIG_ARCH_IMX7 +#include <mach/imx/imx7-regs.h> + +static void imx7_fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler, + struct fcb_block *fcb, struct mtd_info *mtd) +{ + void __iomem *bch_regs = IOMEM(MX7_BCH_BASE); + u32 fl0, fl1; + + /* Also hardcoded in kobs-ng */ + fcb->DataSetup = 10; + fcb->DataHold = 7; + fcb->AddressSetup = 15; + fcb->DSAMPLE_TIME = 6; + + fl0 = readl(bch_regs + BCH_FLASH0LAYOUT0); + fcb->MetadataBytes = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE); + fcb->NumEccBlocksPerPage = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS); + fcb->EccBlock0Size = 4 * BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE); + fcb->EccBlock0EccType = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0); + + fl1 = readl(bch_regs + BCH_FLASH0LAYOUT1); + fcb->EccBlockNSize = 4 * BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE); + fcb->EccBlockNEccType = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN); + fcb->BCHType = BF_VAL(fl1, BCH_FLASHLAYOUT1_GF13_0_GF14_1); +} + +static int imx7_fcb_write(struct mtd_info *mtd, int block, struct fcb_block *fcb) +{ + return mxs_nand_write_fcb_bch62(block, fcb, sizeof(*fcb)); +} + +int imx7_bbu_nand_register_handler(const char *name, unsigned long flags) +{ + struct imx_nand_fcb_bbu_handler *imx_handler; + struct bbu_handler *handler; + int ret; + + imx_handler = xzalloc(sizeof(*imx_handler)); + imx_handler->fcb_create = imx7_fcb_create; + imx_handler->fcb_read = imx7_fcb_read; + imx_handler->fcb_write = imx7_fcb_write; + imx_handler->filetype = filetype_arm_barebox; + + handler = &imx_handler->handler; + handler->devicefile = "nand0.barebox"; + handler->name = name; + handler->flags = flags | BBU_HANDLER_CAN_REFRESH; + handler->handler = imx_bbu_nand_update; + + ret = bbu_register_handler(handler); + if (ret) + free(handler); + + return ret; +} +#endif + +static void dump_fcb_n(struct fcb_block **fcbs, int n) +{ + int i; + + if (!n || !fcbs[n]) + goto skip_compare; + + for (i = 0; i < n; i++) { + if (!fcbs[i] || !fcbs[n]) + continue; + + if (!memcmp(fcbs[i], fcbs[n], sizeof(struct fcb_block))) { + printf("FCB block#%d: same as FCB block#%d\n", n, i); + return; + } + } + +skip_compare: + if (fcbs[n]) { + printf("FCB block#%d:\n", n); + dump_fcb(fcbs[n]); + } else { + printf("FCB block#%d: NULL\n", n); + } +} + +static int fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb) +{ + if (cpu_is_mx7()) + return imx7_fcb_read(mtd, block, retfcb); + else if (fcb_is_bch_encoded()) + return fcb_read_bch(mtd, block, retfcb); + else + return fcb_read_hamming_13_8(mtd, block, retfcb); +} + +static int cmd_fcb(int argc, char *argv[]) +{ + struct cdev *cdev; + struct mtd_info *mtd; + struct fcb_block *fcb[4] = {}; + int i, ret; + + cdev = cdev_open_by_name("nand0", O_RDONLY); + if (!cdev) { + printf("Cannot open nand0\n"); + return COMMAND_ERROR; + } + + mtd = cdev->mtd; + if (!mtd) { + ret = COMMAND_ERROR; + goto out; + } + + for (i = 0; i < 4; i++) + fcb_read(mtd, i, &fcb[i]); + + for (i = 0; i < 4; i++) + dump_fcb_n(fcb, i); + + for (i = 0; i < 4; i++) + free(fcb[i]); + + ret = 0; + +out: + cdev_close(cdev); + + return ret; +} + +BAREBOX_CMD_HELP_START(fcb) +BAREBOX_CMD_HELP_TEXT("Dump FCB as found on /dev/nand0") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(fcb) + .cmd = cmd_fcb, + BAREBOX_CMD_DESC("Dump Flash Control Block (FCB)") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_fcb_help) + BAREBOX_CMD_COMPLETE(empty_complete) +BAREBOX_CMD_END diff --git a/common/kallsyms.c b/common/kallsyms.c index f641903147..3c5904f8a8 100644 --- a/common/kallsyms.c +++ b/common/kallsyms.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <init.h> #include <kallsyms.h> @@ -163,10 +165,10 @@ static unsigned long get_symbol_pos(unsigned long addr, * It resides in a module. * - We also guarantee that modname will be valid until rescheduled. */ -static const char *kallsyms_lookup(unsigned long addr, - unsigned long *symbolsize, - unsigned long *offset, - char **modname, char *namebuf) +const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname, char *namebuf) { namebuf[KSYM_NAME_LEN - 1] = 0; namebuf[0] = 0; diff --git a/common/machine_id.c b/common/machine_id.c index 6480806cd2..e670886d85 100644 --- a/common/machine_id.c +++ b/common/machine_id.c @@ -13,30 +13,120 @@ #define MACHINE_ID_LENGTH 32 +static bool __machine_id_initialized; static void *__machine_id_hashable; static size_t __machine_id_hashable_length; +const void *machine_id_get_hashable(size_t *len) +{ + *len = __machine_id_hashable_length; + return __machine_id_hashable; +} +/** + * machine_id_set_hashable - Provide per-board unique data + * @hashable: Buffer + * @len: size of buffer + * + * The data supplied to the last call of this function prior to + * late_initcall will be hashed and stored into global.machine_id, + * which can be later used for fixup into the kernel command line + * or for deriving application specific unique IDs via + * machine_id_get_app_specific(). + */ void machine_id_set_hashable(const void *hashable, size_t len) { - __machine_id_hashable = xmemdup(hashable, len); __machine_id_hashable_length = len; } +/** + * machine_id_get_app_specific - Generates an application-specific UUID + * @result: UUID output of the function + * @...: pairs of (const void *, size_t) arguments of data to factor + * into the UUID followed by a NULL sentinel value. + * + * Combines the machine ID with the application specific varargs data + * to arrive at an application-specific and board-specific UUID that is + * stable and unique. + * + * The function returns 0 if a UUID was successfully written into @result + * and a negative error code otherwise. + */ +int machine_id_get_app_specific(uuid_t *result, ...) +{ + static u8 hmac[SHA256_DIGEST_SIZE]; + const void *data; + size_t size; + va_list args; + struct digest *d; + int ret; + + if (!__machine_id_initialized) + return -ENODATA; + + d = digest_alloc("hmac(sha256)"); + if (!d) + return -ENOSYS; + + ret = digest_set_key(d, __machine_id_hashable, __machine_id_hashable_length); + if (ret) + goto out; + + ret = digest_init(d); + if (ret) + goto out; + + ret = -ENODATA; + + va_start(args, result); + + while ((data = va_arg(args, const void *))) { + size = va_arg(args, size_t); + + ret = digest_update(d, data, size); + if (ret) + break; + } + + va_end(args); + + if (ret) + goto out; + + ret = digest_final(d, hmac); + if (ret) + goto out; + + /* Take only the first half. */ + memcpy(result, hmac, min(sizeof(hmac), sizeof(*result))); + + uuid_make_v4(result); + +out: + digest_free(d); + + return ret; +} + static int machine_id_set_globalvar(void) { struct digest *digest = NULL; unsigned char machine_id[SHA1_DIGEST_SIZE]; char hex_machine_id[MACHINE_ID_LENGTH]; char *env_machine_id; - int ret = 0; + int ret; /* nothing to do if no hashable information provided */ if (!__machine_id_hashable) - goto out; + return 0; digest = digest_alloc_by_algo(HASH_ALGO_SHA1); + if (!digest) { + ret = -EOPNOTSUPP; + goto out; + } + ret = digest_init(digest); if (ret) goto out; @@ -56,10 +146,9 @@ static int machine_id_set_globalvar(void) env_machine_id = basprintf("%.*s", MACHINE_ID_LENGTH, hex_machine_id); globalvar_add_simple("machine_id", env_machine_id); free(env_machine_id); + __machine_id_initialized = true; out: - globalvar_add_simple("machine_id", NULL); - digest_free(digest); return ret; diff --git a/common/meminfo.c b/common/meminfo.c index d239fde582..3ceb0ba4cf 100644 --- a/common/meminfo.c +++ b/common/meminfo.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <init.h> #include <memory.h> @@ -10,8 +12,10 @@ static int display_meminfo(void) ulong mend = mem_malloc_end(); ulong msize = mend - mstart + 1; - pr_debug("barebox code: 0x%p -> 0x%p\n", _stext, _etext - 1); - pr_debug("bss segment: 0x%p -> 0x%p\n", __bss_start, __bss_stop - 1); + if (!IS_ENABLED(CONFIG_SANDBOX)) { + pr_debug("barebox code: 0x%lx -> 0x%lx\n", (ulong)_stext, (ulong)_etext - 1); + pr_debug("bss segment: 0x%lx -> 0x%lx\n", (ulong)__bss_start, (ulong)__bss_stop - 1); + } pr_info("malloc space: 0x%08lx -> 0x%08lx (size %s)\n", mstart, mend, size_human_readable(msize)); return 0; diff --git a/common/memory.c b/common/memory.c index 95995bb6e3..583843cc34 100644 --- a/common/memory.c +++ b/common/memory.c @@ -3,6 +3,8 @@ * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix */ +#define pr_fmt(fmt) "memory: " fmt + #include <common.h> #include <memory.h> #include <of.h> @@ -12,6 +14,8 @@ #include <asm-generic/memory_layout.h> #include <asm/sections.h> #include <malloc.h> +#include <of.h> +#include <mmu.h> /* * Begin and End of memory area for malloc(), and current "brk" @@ -53,9 +57,9 @@ void mem_malloc_init(void *start, void *end) mem_malloc_initialized = 1; } -#if !defined __SANDBOX__ static int mem_malloc_resource(void) { +#if !defined __SANDBOX__ /* * Normally it's a bug when one of these fails, * but we have some setups where some of these @@ -77,13 +81,14 @@ static int mem_malloc_resource(void) (unsigned long)&__bss_start, (unsigned long)&__bss_stop - (unsigned long)&__bss_start); +#endif #ifdef STACK_BASE request_sdram_region("stack", STACK_BASE, STACK_SIZE); #endif + return 0; } coredevice_initcall(mem_malloc_resource); -#endif static void *sbrk_no_zero(ptrdiff_t increment) { @@ -145,6 +150,7 @@ int barebox_add_memory_bank(const char *name, resource_size_t start, struct resource newres = { .start = start, .end = start + size - 1, + .flags = IORESOURCE_MEM, }; for_each_memory_bank(bank) { @@ -158,6 +164,8 @@ int barebox_add_memory_bank(const char *name, resource_size_t start, if (IS_ERR(res)) return PTR_ERR(res); + res->flags = IORESOURCE_MEM; + bank = xzalloc(sizeof(*bank)); bank->res = res; @@ -180,21 +188,23 @@ static int add_mem_devices(void) return 0; } -mmu_initcall(add_mem_devices); +postmem_initcall(add_mem_devices); /* * Request a region from the registered sdram */ -struct resource *request_sdram_region(const char *name, resource_size_t start, - resource_size_t size) +struct resource *__request_sdram_region(const char *name, unsigned flags, + resource_size_t start, resource_size_t size) { struct memory_bank *bank; + flags |= IORESOURCE_MEM; + for_each_memory_bank(bank) { struct resource *res; - res = __request_region(bank->res, name, start, - start + size - 1); + res = __request_region(bank->res, start, start + size - 1, + name, flags); if (!IS_ERR(res)) return res; } @@ -202,6 +212,31 @@ struct resource *request_sdram_region(const char *name, resource_size_t start, return NULL; } +/* use for secure firmware to inhibit speculation */ +struct resource *reserve_sdram_region(const char *name, resource_size_t start, + resource_size_t size) +{ + struct resource *res; + + if (!IS_ALIGNED(start, PAGE_SIZE)) { + pr_err("%s: %s start is not page aligned\n", __func__, name); + start = ALIGN_DOWN(start, PAGE_SIZE); + } + + if (!IS_ALIGNED(size, PAGE_SIZE)) { + pr_err("%s: %s size is not page aligned\n", __func__, name); + size = ALIGN(size, PAGE_SIZE); + } + + res = __request_sdram_region(name, IORESOURCE_BUSY, start, size); + if (!res) + return NULL; + + remap_range((void *)start, size, MAP_UNCACHED); + + return res; +} + int release_sdram_region(struct resource *res) { return release_region(res); diff --git a/common/memory_display.c b/common/memory_display.c index fbb8bbb6fa..c0ca469703 100644 --- a/common/memory_display.c +++ b/common/memory_display.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <errno.h> #include <abort.h> @@ -121,9 +123,9 @@ int __pr_memory_display(int level, const void *addr, loff_t offs, unsigned nbyte } while (nbytes > 0); - va_end(args); ret = 0; out: + va_end(args); return ret; } @@ -132,4 +134,4 @@ int memory_display(const void *addr, loff_t offs, unsigned nbytes, int size, int swab) { return pr_memory_display(-1, addr, offs, nbytes, size, swab); -}
\ No newline at end of file +} diff --git a/common/memtest.c b/common/memtest.c index d47e4a672e..aa16d94eed 100644 --- a/common/memtest.c +++ b/common/memtest.c @@ -160,7 +160,7 @@ static void mem_test_report_failure(const char *failure_description, } int mem_test_bus_integrity(resource_size_t _start, - resource_size_t _end) + resource_size_t _end, unsigned int flags) { static const uint64_t bitpattern[] = { 0x0000000000000001ULL, /* single bit */ @@ -190,7 +190,8 @@ int mem_test_bus_integrity(resource_size_t _start, dummy = start + 1; num_words = (_end - _start + 1)/sizeof(resource_size_t); - printf("Starting data line test.\n"); + if (flags & MEMTEST_VERBOSE) + printf("Starting data line test.\n"); /* * Data line test: write a pattern to the first @@ -294,7 +295,8 @@ int mem_test_bus_integrity(resource_size_t _start, */ start[0] = anti_pattern; - printf("Check for address bits stuck high.\n"); + if (flags & MEMTEST_VERBOSE) + printf("Check for address bits stuck high.\n"); /* * Check for address bits stuck high. @@ -313,8 +315,8 @@ int mem_test_bus_integrity(resource_size_t _start, */ start[0] = pattern; - printf("Check for address bits stuck " - "low or shorted.\n"); + if (flags & MEMTEST_VERBOSE) + printf("Check for address bits stuck low or shorted.\n"); /* * Check for address bits stuck low or shorted. @@ -340,7 +342,7 @@ int mem_test_bus_integrity(resource_size_t _start, return 0; } -static int update_progress(resource_size_t offset) +static int update_progress(resource_size_t offset, unsigned flags) { /* Only check every 4k to reduce overhead */ if (offset & (SZ_4K - 1)) @@ -349,12 +351,14 @@ static int update_progress(resource_size_t offset) if (ctrlc()) return -EINTR; - show_progress(offset); + if (flags & MEMTEST_VERBOSE) + show_progress(offset); return 0; } -int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) +int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end, + unsigned flags) { volatile resource_size_t *start, num_words, offset, temp, anti_pattern; int ret; @@ -368,8 +372,12 @@ int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) start = (resource_size_t *)_start; num_words = (_end - _start + 1)/sizeof(resource_size_t); - printf("Starting moving inversions test of RAM:\n" - "Fill with address, compare, fill with inverted address, compare again\n"); + if (flags & MEMTEST_VERBOSE) { + printf("Starting moving inversions test of RAM:\n" + "Fill with address, compare, fill with inverted address, compare again\n"); + + init_progression_bar(3 * num_words); + } /* * Description: Test the integrity of a physical @@ -382,11 +390,9 @@ int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) * selected by the caller. */ - init_progression_bar(3 * num_words); - /* Fill memory with a known pattern */ for (offset = 0; offset < num_words; offset++) { - ret = update_progress(offset); + ret = update_progress(offset, flags); if (ret) return ret; start[offset] = offset + 1; @@ -394,7 +400,7 @@ int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) /* Check each location and invert it for the second pass */ for (offset = 0; offset < num_words; offset++) { - ret = update_progress(num_words + offset); + ret = update_progress(num_words + offset, flags); if (ret) return ret; @@ -413,7 +419,7 @@ int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) /* Check each location for the inverted pattern and zero it */ for (offset = 0; offset < num_words; offset++) { - ret = update_progress(2 * num_words + offset); + ret = update_progress(2 * num_words + offset, flags); if (ret) return ret; @@ -430,10 +436,12 @@ int mem_test_moving_inversions(resource_size_t _start, resource_size_t _end) start[offset] = 0; } - show_progress(3 * num_words); + if (flags & MEMTEST_VERBOSE) { + show_progress(3 * num_words); - /* end of progressbar */ - printf("\n"); + /* end of progressbar */ + printf("\n"); + } return 0; } diff --git a/common/menutree.c b/common/menutree.c index 9a14005ea2..751350d754 100644 --- a/common/menutree.c +++ b/common/menutree.c @@ -34,14 +34,7 @@ static void menutree_action(struct menu *m, struct menu_entry *me) static void setenv_bool(const char *var, bool val) { - const char *str; - - if (val) - str = "1"; - else - str = "0"; - - setenv(var, str); + pr_setenv(var, "%d", val); } static void menutree_box(struct menu *m, struct menu_entry *me) @@ -87,7 +80,6 @@ int menutree(const char *path, int toplevel) glob_t g = {}; int i; char *globpath, *display; - size_t size; menu = menu_alloc(); @@ -100,7 +92,7 @@ int menutree(const char *path, int toplevel) } globpath = basprintf("%s/title", path); - display = read_file(globpath, &size); + display = read_file(globpath, NULL); free(globpath); if (!display) { eprintf("no title found in %s/title\n", path); diff --git a/common/misc.c b/common/misc.c index 226635f0d4..04ff4e6eb5 100644 --- a/common/misc.c +++ b/common/misc.c @@ -13,6 +13,7 @@ #include <led.h> #include <of.h> #include <restart.h> +#include <poweroff.h> #include <linux/stringify.h> int errno; @@ -105,16 +106,10 @@ const char *strerror(int errnum) } EXPORT_SYMBOL(strerror); -const char *errno_str(void) -{ - return strerror(errno); -} -EXPORT_SYMBOL(errno_str); - void perror(const char *s) { #ifdef CONFIG_ERRNO_MESSAGES - printf("%s: %s\n", s, errno_str()); + printf("%s: %m\n", s); #else printf("%s returned with %d\n", s, errno); #endif @@ -149,6 +144,8 @@ EXPORT_SYMBOL(barebox_get_model); BAREBOX_MAGICVAR(global.model, "Product name of this hardware"); static char *hostname; +static char *serial_number; +static char *of_machine_compatible; /* * The hostname is supposed to be the shortname of a board. It should @@ -179,23 +176,84 @@ EXPORT_SYMBOL(barebox_set_hostname_no_overwrite); BAREBOX_MAGICVAR(global.hostname, "shortname of the board. Also used as hostname for DHCP requests"); -void __noreturn panic(const char *fmt, ...) +void barebox_set_serial_number(const char *__serial_number) { - va_list args; - va_start(args, fmt); - vprintf(fmt, args); + globalvar_add_simple_string("serial_number", &serial_number); + + free(serial_number); + serial_number = xstrdup(__serial_number); +} + +const char *barebox_get_serial_number(void) +{ + if (!serial_number || *serial_number == '\0') + return NULL; + return serial_number; +} + +BAREBOX_MAGICVAR(global.serial_number, "Board serial number"); + +void barebox_set_of_machine_compatible(const char *__compatible) +{ + free(of_machine_compatible); + of_machine_compatible = xstrdup(__compatible); +} + +const char *barebox_get_of_machine_compatible(void) +{ + if (!of_machine_compatible || *of_machine_compatible == '\0') + return NULL; + return of_machine_compatible; +} + +static int of_kernel_init(void) +{ + globalvar_add_simple_string("of.kernel.add_machine_compatible", + &of_machine_compatible); + + return 0; +} +device_initcall(of_kernel_init); + +BAREBOX_MAGICVAR(global.of.kernel.add_machine_compatible, "Additional machine/board compatible"); + +static void __noreturn do_panic(bool stacktrace, const char *fmt, va_list ap) +{ + vprintf(fmt, ap); putchar('\n'); - va_end(args); - dump_stack(); + if (stacktrace) + dump_stack(); led_trigger(LED_TRIGGER_PANIC, TRIGGER_ENABLE); - if (IS_ENABLED(CONFIG_PANIC_HANG)) { + if (IS_ENABLED(CONFIG_PANIC_HANG)) hang(); - } else { - udelay(100000); /* allow messages to go out */ + + udelay(100000); /* allow messages to go out */ + + if (IS_ENABLED(CONFIG_PANIC_POWEROFF)) + poweroff_machine(); + else restart_machine(); - } +} + +void __noreturn panic(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + do_panic(true, fmt, args); + va_end(args); } EXPORT_SYMBOL(panic); + +void __noreturn panic_no_stacktrace(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + do_panic(false, fmt, args); + va_end(args); +} +EXPORT_SYMBOL(panic_no_stacktrace); diff --git a/common/module.lds.S b/common/module.lds.S index 76f3b6d1bb..b2d685670b 100644 --- a/common/module.lds.S +++ b/common/module.lds.S @@ -15,7 +15,7 @@ * */ -#include <asm-generic/barebox.lds.h> +#include <asm/barebox.lds.h> SECTIONS { diff --git a/common/oftree.c b/common/oftree.c index 962fff244d..c12b3cfb16 100644 --- a/common/oftree.c +++ b/common/oftree.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <environment.h> #include <fdt.h> @@ -122,14 +124,10 @@ void of_print_cmdline(struct device_node *root) static int of_fixup_bootargs_bootsource(struct device_node *root, struct device_node *chosen) { - char *alias_name = bootsource_get_alias_name(); struct device_node *bootsource; int ret = 0; - if (!alias_name) - return 0; - - bootsource = of_find_node_by_alias(root, alias_name); + bootsource = bootsource_of_node_get(root); /* * If kernel DTB doesn't have the appropriate alias set up, * give up and exit early. No error is reported. @@ -138,7 +136,6 @@ static int of_fixup_bootargs_bootsource(struct device_node *root, ret = of_set_property(chosen, "bootsource", bootsource->full_name, strlen(bootsource->full_name) + 1, true); - free(alias_name); return ret; } @@ -203,7 +200,17 @@ static int of_fixup_bootargs(struct device_node *root, void *unused) struct device_node *node; int err; int instance = reset_source_get_instance(); - struct device_d *dev; + struct device *dev; + const char *serialno; + const char *compat; + + serialno = barebox_get_serial_number(); + if (serialno) + of_property_write_string(root, "serial-number", serialno); + + compat = barebox_get_of_machine_compatible(); + if (compat) + of_prepend_machine_compatible(root, compat); node = of_create_node(root, "/chosen"); if (!node) @@ -221,10 +228,10 @@ static int of_fixup_bootargs(struct device_node *root, void *unused) dev = reset_source_get_device(); - if (dev && dev->device_node) { + if (dev && dev->of_node) { phandle phandle; - phandle = of_node_create_phandle(dev->device_node); + phandle = of_node_create_phandle(dev->of_node); err = of_property_write_u32(node, "reset-source-device", phandle); @@ -259,6 +266,49 @@ static int of_register_bootargs_fixup(void) } late_initcall(of_register_bootargs_fixup); +int of_fixup_reserved_memory(struct device_node *root, void *_res) +{ + struct resource *res = _res; + struct device_node *node, *child; + struct property *pp; + unsigned addr_n_cells = sizeof(void *) / sizeof(__be32), + size_n_cells = sizeof(void *) / sizeof(__be32); + unsigned rangelen = 0; + __be32 reg[4]; + int ret; + + node = of_get_child_by_name(root, "reserved-memory") ?: of_new_node(root, "reserved-memory"); + + ret = of_property_read_u32(node, "#address-cells", &addr_n_cells); + if (ret) + of_property_write_u32(node, "#address-cells", addr_n_cells); + + ret = of_property_read_u32(node, "#size-cells", &size_n_cells); + if (ret) + of_property_write_u32(node, "#size-cells", size_n_cells); + + pp = of_find_property(node, "ranges", &rangelen); + if (!pp) { + of_new_property(node, "ranges", NULL, 0); + } else if (rangelen) { + pr_warn("reserved-memory ranges not 1:1 mapped. Aborting fixup\n"); + return -EINVAL; + } + + child = of_get_child_by_name(node, res->name) ?: of_new_node(node, res->name); + + if (res->flags & IORESOURCE_BUSY) + of_property_write_bool(child, "no-map", true); + + of_write_number(reg, res->start, addr_n_cells); + of_write_number(reg + addr_n_cells, resource_size(res), size_n_cells); + + of_set_property(child, "reg", reg, + (addr_n_cells + size_n_cells) * sizeof(*reg), true); + + return 0; +} + struct of_fixup_status_data { const char *path; bool status; @@ -295,13 +345,12 @@ int of_register_set_status_fixup(const char *path, bool status) return of_register_fixup(of_fixup_status, (void *)data); } -struct of_fixup { - int (*fixup)(struct device_node *, void *); - void *context; - struct list_head list; -}; +LIST_HEAD(of_fixup_list); -static LIST_HEAD(of_fixup_list); +static inline bool of_fixup_disabled(struct of_fixup *fixup) +{ + return fixup->disabled; +} int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context) { @@ -338,7 +387,7 @@ int of_unregister_fixup(int (*fixup)(struct device_node *, void *), * Apply registered fixups for the given fdt. The fdt must have * enough free space to apply the fixups. */ -int of_fix_tree(struct device_node *node) +void of_fix_tree(struct device_node *node) { struct of_fixup *of_fixup; int ret; @@ -346,13 +395,14 @@ int of_fix_tree(struct device_node *node) of_overlay_load_firmware_clear(); list_for_each_entry(of_fixup, &of_fixup_list, list) { + if (of_fixup_disabled(of_fixup)) + continue; + ret = of_fixup->fixup(node, of_fixup->context); if (ret) pr_warn("Failed to fixup node in %pS: %s\n", of_fixup->fixup, strerror(-ret)); } - - return 0; } /* @@ -361,30 +411,27 @@ int of_fix_tree(struct device_node *node) * It increases the size of the tree and applies the registered * fixups. */ -struct fdt_header *of_get_fixed_tree(struct device_node *node) +struct fdt_header *of_get_fixed_tree(const struct device_node *node) { - int ret; struct fdt_header *fdt = NULL; - struct device_node *freenp = NULL; + struct device_node *np; if (!node) { node = of_get_root_node(); if (!node) return NULL; - - freenp = node = of_dup(node); - if (!node) - return NULL; } - ret = of_fix_tree(node); - if (ret) - goto out; + np = of_dup(node); + + if (!np) + return NULL; - fdt = of_flatten_dtb(node); + of_fix_tree(np); -out: - of_delete_node(freenp); + fdt = of_flatten_dtb(np); + + of_delete_node(np); return fdt; } @@ -403,7 +450,7 @@ int of_autoenable_device_by_path(char *path) struct device_node *node; int ret; - node = of_find_node_by_name(NULL, path); + node = of_find_node_by_name_address(NULL, path); if (!node) node = of_find_node_by_path(path); @@ -440,7 +487,7 @@ int of_autoenable_i2c_by_component(char *path) if (!IS_ENABLED(CONFIG_I2C)) return -ENODEV; - node = of_find_node_by_name(NULL, path); + node = of_find_node_by_name_address(NULL, path); if (!node) node = of_find_node_by_path(path); if (!node) @@ -476,3 +523,34 @@ int of_autoenable_i2c_by_component(char *path) return ret; } + +int of_prepend_machine_compatible(struct device_node *root, const char *compat) +{ + int cclen = 0, clen = strlen(compat) + 1; + const char *curcompat; + void *buf; + + if (!root) { + root = of_get_root_node(); + if (!root) + return -ENODEV; + } + + if (of_device_is_compatible(root, compat)) + return 0; + + curcompat = of_get_property(root, "compatible", &cclen); + + buf = xzalloc(cclen + clen); + + memcpy(buf, compat, clen); + + if (curcompat) + memcpy(buf + clen, curcompat, cclen); + + of_set_property(root, "compatible", buf, cclen + clen, true); + + free(buf); + + return 0; +} diff --git a/common/optee.c b/common/optee.c index d542dde118..7fe93e4419 100644 --- a/common/optee.c +++ b/common/optee.c @@ -3,21 +3,59 @@ #define pr_fmt(fmt) "optee: " fmt #include <tee/optee.h> -#include <printk.h> -#include <asm-generic/errno.h> +#include <linux/printk.h> +#include <linux/errno.h> -int optee_verify_header(struct optee_header *hdr) +static u64 optee_membase = U64_MAX; + +int optee_verify_header(const struct optee_header *hdr) { + if (IS_ERR_OR_NULL(hdr)) + return -EINVAL; + if (hdr->magic != OPTEE_MAGIC) { - pr_err("Invalid header magic 0x%08x, expected 0x%08x\n", - hdr->magic, OPTEE_MAGIC); + pr_debug("Invalid header magic 0x%08x, expected 0x%08x\n", + hdr->magic, OPTEE_MAGIC); + return -EINVAL; + } + + if (hdr->version != OPTEE_VERSION_V1) { + pr_err("Only V1 headers are supported right now\n"); return -EINVAL; } - if (hdr->arch != OPTEE_ARCH_ARM32 || hdr->init_load_addr_hi) { - pr_err("Only 32bit supported\n"); + if (IS_ENABLED(CPU_V7) && + (hdr->arch != OPTEE_ARCH_ARM32 || hdr->init_load_addr_hi)) { + pr_err("Wrong OP-TEE Arch for ARM v7 CPU\n"); + return -EINVAL; + } + + if (IS_ENABLED(CPU_V8) && hdr->arch != OPTEE_ARCH_ARM64) { + pr_err("Wrong OP-TEE Arch for ARM v8 CPU\n"); return -EINVAL; } return 0; } + +int optee_get_membase(u64 *membase) +{ + if (optee_membase == U64_MAX) + return -EINVAL; + + *membase = optee_membase; + + return 0; +} + +void optee_set_membase(const struct optee_header *hdr) +{ + int ret; + + ret = optee_verify_header(hdr); + if (ret) + return; + + optee_membase = (u64)hdr->init_load_addr_hi << 32; + optee_membase |= hdr->init_load_addr_lo; +} diff --git a/common/parser.c b/common/parser.c index 584d4b0efe..387cd64c42 100644 --- a/common/parser.c +++ b/common/parser.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <common.h> #include <command.h> #include <password.h> diff --git a/common/partitions.c b/common/partitions.c index d80878e065..17c2f1eb28 100644 --- a/common/partitions.c +++ b/common/partitions.c @@ -15,8 +15,7 @@ #include <disks.h> #include <filetype.h> #include <linux/err.h> - -#include "partitions/parser.h" +#include <partitions.h> static LIST_HEAD(partition_parser_list); @@ -27,30 +26,33 @@ static LIST_HEAD(partition_parser_list); * @param no Partition number * @return 0 on success */ -static int register_one_partition(struct block_device *blk, - struct partition *part, int no) +static int register_one_partition(struct block_device *blk, struct partition *part) { char *partition_name; int ret; - uint64_t start = part->first_sec * SECTOR_SIZE; - uint64_t size = part->size * SECTOR_SIZE; struct cdev *cdev; + struct devfs_partition partinfo = { + .offset = part->first_sec * SECTOR_SIZE, + .size = part->size * SECTOR_SIZE, + }; - partition_name = basprintf("%s.%d", blk->cdev.name, no); + partition_name = basprintf("%s.%d", blk->cdev.name, part->num); if (!partition_name) return -ENOMEM; + + partinfo.name = partition_name; + dev_dbg(blk->dev, "Registering partition %s on drive %s (partuuid=%s)\n", partition_name, blk->cdev.name, part->partuuid); - cdev = devfs_add_partition(blk->cdev.name, - start, size, 0, partition_name); + cdev = cdevfs_add_partition(&blk->cdev, &partinfo); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); goto out; } - cdev->flags |= DEVFS_PARTITION_FROM_TABLE; - - cdev->dos_partition_type = part->dos_partition_type; + cdev->flags |= DEVFS_PARTITION_FROM_TABLE | part->flags; + cdev->typeflags |= part->typeflags; + cdev->typeuuid = part->typeuuid; strcpy(cdev->partuuid, part->partuuid); free(partition_name); @@ -100,42 +102,157 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf) return NULL; } -/** - * Try to collect partition information on the given block device - * @param blk Block device to examine - * @return 0 most of the time, negative value else - * - * It is not a failure if no partition information is found - */ -int parse_partition_table(struct block_device *blk) +struct partition_desc *partition_table_new(struct block_device *blk, const char *type) { struct partition_desc *pdesc; - int i; - int rc = 0; struct partition_parser *parser; + + list_for_each_entry(parser, &partition_parser_list, list) { + if (!strcmp(parser->name, type)) + goto found; + } + + pr_err("Cannot find partition parser \"%s\"\n", type); + + return ERR_PTR(-ENOSYS); + +found: + pdesc = parser->create(blk); + if (IS_ERR(pdesc)) + return ERR_CAST(pdesc); + + pdesc->parser = parser; + + return pdesc; +} + +struct partition_desc *partition_table_read(struct block_device *blk) +{ + struct partition_parser *parser; + struct partition_desc *pdesc = NULL; uint8_t *buf; + int ret; - pdesc = xzalloc(sizeof(*pdesc)); buf = malloc(2 * SECTOR_SIZE); - rc = block_read(blk, buf, 0, 2); - if (rc != 0) { - dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(rc)); - goto on_error; + ret = block_read(blk, buf, 0, 2); + if (ret != 0) { + dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(ret)); + goto err; } parser = partition_parser_get_by_filetype(buf); if (!parser) - goto on_error; + goto err; + + pdesc = parser->parse(buf, blk); + if (!pdesc) + goto err; + + pdesc->parser = parser; +err: + free(buf); + + return pdesc; +} - parser->parse(buf, blk, pdesc); +int partition_table_write(struct partition_desc *pdesc) +{ + if (!pdesc->parser->write) + return -ENOSYS; + + return pdesc->parser->write(pdesc); +} + +static bool overlap(uint64_t s1, uint64_t e1, uint64_t s2, uint64_t e2) +{ + if (e1 < s2) + return false; + if (s1 > e2) + return false; + return true; +} - if (!pdesc->used_entries) - goto on_error; +int partition_create(struct partition_desc *pdesc, const char *name, + const char *fs_type, uint64_t lba_start, uint64_t lba_end) +{ + struct partition *part; + + if (!pdesc->parser->mkpart) + return -ENOSYS; + + if (lba_end < lba_start) { + pr_err("lba_end < lba_start: %llu < %llu\n", lba_end, lba_start); + return -EINVAL; + } + + if (lba_end >= pdesc->blk->num_blocks) { + pr_err("lba_end exceeds device: %llu >= %llu\n", lba_end, pdesc->blk->num_blocks); + return -EINVAL; + } + + list_for_each_entry(part, &pdesc->partitions, list) { + if (overlap(part->first_sec, + part->first_sec + part->size - 1, + lba_start, lba_end)) { + pr_err("new partition %llu-%llu overlaps with partition %s (%llu-%llu)\n", + lba_start, lba_end, part->name, part->first_sec, + part->first_sec + part->size - 1); + return -EINVAL; + } + } + + return pdesc->parser->mkpart(pdesc, name, fs_type, lba_start, lba_end); +} + +int partition_remove(struct partition_desc *pdesc, int num) +{ + struct partition *part; + + if (!pdesc->parser->rmpart) + return -ENOSYS; + + list_for_each_entry(part, &pdesc->partitions, list) { + if (part->num == num) + return pdesc->parser->rmpart(pdesc, part); + } + + pr_err("Partition %d doesn't exist\n", num); + return -ENOENT; +} + +void partition_table_free(struct partition_desc *pdesc) +{ + pdesc->parser->partition_free(pdesc); +} + +void partition_desc_init(struct partition_desc *pd, struct block_device *blk) +{ + pd->blk = blk; + INIT_LIST_HEAD(&pd->partitions); +} + +/** + * Try to collect partition information on the given block device + * @param blk Block device to examine + * @return 0 most of the time, negative value else + * + * It is not a failure if no partition information is found + */ +int parse_partition_table(struct block_device *blk) +{ + int i; + int rc = 0; + struct partition *part; + struct partition_desc *pdesc; + + pdesc = partition_table_read(blk); + if (!pdesc) + return 0; /* at least one partition description found */ - for (i = 0; i < pdesc->used_entries; i++) { - rc = register_one_partition(blk, &pdesc->parts[i], i); + list_for_each_entry(part, &pdesc->partitions, list) { + rc = register_one_partition(blk, part); if (rc != 0) dev_err(blk->dev, "Failed to register partition %d on %s (%d)\n", @@ -144,15 +261,61 @@ int parse_partition_table(struct block_device *blk) rc = 0; } -on_error: - free(buf); - free(pdesc); + partition_table_free(pdesc); + return rc; } +int reparse_partition_table(struct block_device *blk) +{ + struct cdev *cdev = &blk->cdev; + struct cdev *c, *tmp; + + list_for_each_entry(c, &cdev->partitions, partition_entry) { + if (c->open) { + pr_warn("%s is busy, will continue to use old partition table\n", c->name); + return -EBUSY; + } + } + + list_for_each_entry_safe(c, tmp, &cdev->partitions, partition_entry) { + if (c->flags & DEVFS_PARTITION_FROM_TABLE) + cdevfs_del_partition(c); + } + + return parse_partition_table(blk); +} + int partition_parser_register(struct partition_parser *p) { list_add_tail(&p->list, &partition_parser_list); return 0; } + +/** + * cdev_unallocated_space - return unallocated space + * cdev: The cdev + * + * This function returns the space that is not allocated by any partition + * at the start of a device. + * + * Return: The unallocated space at the start of the device in bytes + */ +loff_t cdev_unallocated_space(struct cdev *cdev) +{ + struct cdev *partcdev; + loff_t start; + + if (!cdev) + return 0; + + start = cdev->size; + + list_for_each_entry(partcdev, &cdev->partitions, partition_entry) { + if (partcdev->offset < start) + start = partcdev->offset; + } + + return start; +} diff --git a/common/partitions/Kconfig b/common/partitions/Kconfig index be9405a649..3bbcceedc1 100644 --- a/common/partitions/Kconfig +++ b/common/partitions/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + config PARTITION_DISK depends on PARTITION depends on BLOCK @@ -17,6 +19,7 @@ config PARTITION_DISK_EFI depends on PARTITION_DISK select CRC32 select PRINTF_UUID + select PARTITION_DISK_DOS if PARTITION_MANIPULATION bool "EFI: GPT partition support" help Add support to handle partitions in GUID Partition Table style. diff --git a/common/partitions/Makefile b/common/partitions/Makefile index 2b0c5b4b9c..d304b6f8d5 100644 --- a/common/partitions/Makefile +++ b/common/partitions/Makefile @@ -1,2 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_PARTITION_DISK_DOS) += dos.o obj-$(CONFIG_PARTITION_DISK_EFI) += efi.o diff --git a/common/partitions/dos.c b/common/partitions/dos.c index 6c76aac371..8e4edd885b 100644 --- a/common/partitions/dos.c +++ b/common/partitions/dos.c @@ -15,11 +15,26 @@ #include <common.h> #include <disks.h> #include <init.h> +#include <stdlib.h> #include <asm/unaligned.h> #include <linux/err.h> +#include <partitions.h> -#include "parser.h" +struct dos_partition_desc { + struct partition_desc pd; + uint32_t signature; +}; +struct dos_partition { + struct partition part; + bool extended; + bool logical; + + uint8_t boot_indicator; + uint8_t chs_begin[3]; + uint8_t type; + uint8_t chs_end[3]; +}; enum { /* These three have identical behaviour; use the second one if DOS FDISK gets @@ -106,17 +121,18 @@ static int dos_get_disk_signature(struct param_d *p, void *_priv) return 0; } -static void dos_extended_partition(struct block_device *blk, struct partition_desc *pd, +static void dos_extended_partition(struct block_device *blk, struct dos_partition_desc *dpd, struct partition *partition, uint32_t signature) { uint8_t *buf = malloc(SECTOR_SIZE); uint32_t ebr_sector = partition->first_sec; struct partition_entry *table = (struct partition_entry *)&buf[0x1be]; - unsigned partno = 5; + unsigned partno = 4; + struct dos_partition *dpart; + struct partition *pentry; - while (pd->used_entries < ARRAY_SIZE(pd->parts)) { + while (1) { int rc, i; - int n = pd->used_entries; dev_dbg(blk->dev, "expect EBR in sector %x\n", ebr_sector); @@ -139,15 +155,25 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de } /* /sanity checks */ + dpart = xzalloc(sizeof(*dpart)); + dpart->logical = true; + dpart->boot_indicator = table[0].boot_indicator; + memcpy(dpart->chs_begin, table[0].chs_begin, sizeof(table[0].chs_begin)); + dpart->type = table[0].type; + memcpy(dpart->chs_end, table[0].chs_end, sizeof(table[0].chs_end)); + + pentry = &dpart->part; + /* the first entry defines the extended partition */ - pd->parts[n].first_sec = ebr_sector + + pentry->first_sec = ebr_sector + get_unaligned_le32(&table[0].partition_start); - pd->parts[n].size = get_unaligned_le32(&table[0].partition_size); - pd->parts[n].dos_partition_type = table[0].type; - if (signature) - sprintf(pd->parts[n].partuuid, "%08x-%02u", - signature, partno); - pd->used_entries++; + pentry->size = get_unaligned_le32(&table[0].partition_size); + pentry->dos_partition_type = table[0].type; + pentry->num = partno; + sprintf(pentry->partuuid, "%08x-%02u", signature, partno + 1); + + list_add_tail(&pentry->list, &dpd->pd.partitions); + partno++; /* the second entry defines the start of the next ebr if != 0 */ @@ -163,6 +189,15 @@ out: return; } +static void extract_flags(const struct partition_entry *p, + struct partition *pentry) +{ + if (p->boot_indicator == 0x80) + pentry->flags |= DEVFS_PARTITION_BOOTABLE_LEGACY; + if (p->type == 0xef) + pentry->flags |= DEVFS_PARTITION_BOOTABLE_ESP; +} + /** * Check if a DOS like partition describes this block device * @param blk Block device to register to @@ -171,53 +206,72 @@ out: * It seems at least on ARM this routine cannot use temp. stack space for the * sector. So, keep the malloc/free. */ -static void dos_partition(void *buf, struct block_device *blk, - struct partition_desc *pd) +static struct partition_desc *dos_partition(void *buf, struct block_device *blk) { struct partition_entry *table; - struct partition pentry; + struct dos_partition *dpart; + struct partition *pentry; struct partition *extended_partition = NULL; uint8_t *buffer = buf; int i; struct disk_signature_priv *dsp; uint32_t signature = get_unaligned_le32(buf + 0x1b8); + struct dos_partition_desc *dpd; + + sprintf(blk->cdev.diskuuid, "%08x", signature); + + blk->cdev.flags |= DEVFS_IS_MBR_PARTITIONED; table = (struct partition_entry *)&buffer[446]; + dpd = xzalloc(sizeof(*dpd)); + partition_desc_init(&dpd->pd, blk); + for (i = 0; i < 4; i++) { - pentry.first_sec = get_unaligned_le32(&table[i].partition_start); - pentry.size = get_unaligned_le32(&table[i].partition_size); - pentry.dos_partition_type = table[i].type; - - if (pentry.first_sec != 0) { - int n = pd->used_entries; - pd->parts[n].first_sec = pentry.first_sec; - pd->parts[n].size = pentry.size; - pd->parts[n].dos_partition_type = pentry.dos_partition_type; - if (signature) - sprintf(pd->parts[n].partuuid, "%08x-%02d", - signature, i + 1); - pd->used_entries++; - - if (is_extended_partition(&pentry)) { - if (!extended_partition) - extended_partition = &pd->parts[n]; - else - /* - * An DOS MBR must only contain a single - * extended partition. Just ignore all - * but the first. - */ - dev_warn(blk->dev, "Skipping additional extended partition\n"); - } + uint64_t first_sec = get_unaligned_le32(&table[i].partition_start); - } else { + if (first_sec == 0) { dev_dbg(blk->dev, "Skipping empty partition %d\n", i); + continue; + } + + dpart = xzalloc(sizeof(*dpart)); + dpart->boot_indicator = table[i].boot_indicator; + memcpy(dpart->chs_begin, table[i].chs_begin, sizeof(table[i].chs_begin)); + dpart->type = table[i].type; + memcpy(dpart->chs_end, table[i].chs_end, sizeof(table[i].chs_end)); + + pentry = &dpart->part; + + pentry->first_sec = first_sec; + pentry->size = get_unaligned_le32(&table[i].partition_size); + pentry->dos_partition_type = table[i].type; + extract_flags(&table[i], pentry); + pentry->num = i; + + sprintf(pentry->partuuid, "%08x-%02d", signature, i + 1); + dpd->signature = signature; + + if (is_extended_partition(pentry)) { + dpart->extended = true; + pentry->size = 2; + + if (!extended_partition) + extended_partition = pentry; + else + /* + * An DOS MBR must only contain a single + * extended partition. Just ignore all + * but the first. + */ + dev_warn(blk->dev, "Skipping additional extended partition\n"); } + + list_add_tail(&pentry->list, &dpd->pd.partitions); } if (extended_partition) - dos_extended_partition(blk, pd, extended_partition, signature); + dos_extended_partition(blk, dpd, extended_partition, signature); dsp = xzalloc(sizeof(*dsp)); dsp->blk = blk; @@ -235,11 +289,219 @@ static void dos_partition(void *buf, struct block_device *blk, dev_add_param_uint32(blk->dev, "nt_signature", dos_set_disk_signature, dos_get_disk_signature, &dsp->signature, "%08x", dsp); + + return &dpd->pd; +} + +static void dos_partition_free(struct partition_desc *pd) +{ + struct partition *part, *tmp; + + list_for_each_entry_safe(part, tmp, &pd->partitions, list) { + struct dos_partition *dpart = container_of(part, struct dos_partition, part); + + free(dpart); + } + + free(pd); +} + +static __maybe_unused struct partition_desc *dos_partition_create_table(struct block_device *blk) +{ + struct dos_partition_desc *dpd = xzalloc(512); + + partition_desc_init(&dpd->pd, blk); + + dpd->signature = random32(); + + return &dpd->pd; +} + +static int fs_type_to_type(const char *fstype) +{ + unsigned long type; + int ret; + + if (!strcmp(fstype, "ext2")) + return 0x83; + if (!strcmp(fstype, "ext3")) + return 0x83; + if (!strcmp(fstype, "ext4")) + return 0x83; + if (!strcmp(fstype, "fat16")) + return 0xe; + if (!strcmp(fstype, "fat32")) + return 0xc; + + ret = kstrtoul(fstype, 16, &type); + if (ret) + return ret; + + if (type > 0xff) + return -EINVAL; + + return type; +} + +static __maybe_unused int dos_partition_mkpart(struct partition_desc *pd, + const char *name, const char *fs_type, + uint64_t start_lba, uint64_t end_lba) +{ + struct dos_partition *dpart; + struct partition *part, *part_extended = NULL; + int npart = 0, npart_logical = 0; + uint64_t size; + int mask_free = 0xf; + int type = fs_type_to_type(fs_type); + + if (type < 0) { + pr_err("invalid fs_type \"%s\"\n", fs_type); + return -EINVAL; + } + + if (start_lba < 1) { + pr_err("invalid start LBA, minimum is 1\n"); + return -EINVAL; + } + + list_for_each_entry(part, &pd->partitions, list) { + dpart = container_of(part, struct dos_partition, part); + + mask_free &= ~(1 << npart); + + if (dpart->extended) + part_extended = part; + if (dpart->logical) + npart_logical++; + else + npart++; + } + + if (!strcmp(name, "extended")) { + if (part_extended) { + pr_err("extended partition already exists\n"); + return -ENOSPC; + } + goto create; + } else if (!strcmp(name, "primary")) { + if (npart == 4) { + pr_err("Can't create any more partitions\n"); + return -ENOSPC; + } + + goto create; + } else if (!strcmp(name, "logical")) { + if (!part_extended) { + pr_err("No extended partition\n"); + return -EINVAL; + } + + if (npart_logical >= 4) { + pr_err("Can't create any more partitions\n"); + return -ENOSPC; + } + + pr_err("Can't create logical partitions yet\n"); + return -EINVAL; + } else { + pr_err("Invalid name \"%s\"\n", name); + return -EINVAL; + } + + return 0; + +create: + dpart = xzalloc(sizeof(*dpart)); + part = &dpart->part; + + if (start_lba > UINT_MAX) + return -ENOSPC; + size = end_lba - start_lba + 1; + + if (size > UINT_MAX) + return -ENOSPC; + + dpart->type = fs_type_to_type(fs_type); + + part->first_sec = start_lba; + part->size = size; + part->num = ffs(mask_free); + + list_add_tail(&part->list, &pd->partitions); + + return 0; +} + +static __maybe_unused int dos_partition_rmpart(struct partition_desc *pd, struct partition *part) +{ + struct dos_partition *dpart = container_of(part, struct dos_partition, part); + + list_del(&part->list); + free(dpart); + + return 0; +} + +static __maybe_unused int dos_partition_write(struct partition_desc *pd) +{ + struct dos_partition_desc *dpd = container_of(pd, struct dos_partition_desc, pd); + struct dos_partition *dpart; + struct partition *part; + void *buf; + struct partition_entry *table, *entry; + int ret; + + list_for_each_entry(part, &pd->partitions, list) { + dpart = container_of(part, struct dos_partition, part); + if (dpart->logical) { + pr_err("Cannot write tables with logical partitions yet\n"); + return -EINVAL; + } + } + + buf = read_mbr(pd->blk); + if (!buf) + return -EIO; + + put_unaligned_le32(dpd->signature, buf + 0x1b8); + + table = buf + 0x1be; + memset(table, 0x0, sizeof(*table) * 4); + + *(u8 *)(buf + 0x1fe) = 0x55; + *(u8 *)(buf + 0x1ff) = 0xaa; + + list_for_each_entry(part, &pd->partitions, list) { + dpart = container_of(part, struct dos_partition, part); + + entry = &table[part->num - 1]; + + entry->boot_indicator = dpart->boot_indicator; + memcpy(entry->chs_begin, dpart->chs_begin, sizeof(entry->chs_begin)); + entry->type = dpart->type; + memcpy(entry->chs_end, dpart->chs_end, sizeof(entry->chs_end)); + put_unaligned_le32(part->first_sec, &entry->partition_start); + put_unaligned_le32(part->size, &entry->partition_size); + } + + ret = block_write(pd->blk, buf, 0, 1); + + free(buf); + + return ret; } static struct partition_parser dos = { .parse = dos_partition, + .partition_free = dos_partition_free, +#ifdef CONFIG_PARTITION_MANIPULATION + .create = dos_partition_create_table, + .mkpart = dos_partition_mkpart, + .rmpart = dos_partition_rmpart, + .write = dos_partition_write, +#endif .type = filetype_mbr, + .name = "msdos", }; static int dos_partition_init(void) diff --git a/common/partitions/efi.c b/common/partitions/efi.c index 135b08901a..829360da6e 100644 --- a/common/partitions/efi.c +++ b/common/partitions/efi.c @@ -19,8 +19,19 @@ #include <crc.h> #include <linux/ctype.h> -#include "efi.h" -#include "parser.h" +#include <efi/partition.h> +#include <partitions.h> + +struct efi_partition_desc { + struct partition_desc pd; + gpt_header *gpt; + gpt_entry *ptes; +}; + +struct efi_partition { + struct partition part; + gpt_entry *pte; +}; static const int force_gpt = IS_ENABLED(CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE); @@ -80,6 +91,7 @@ static gpt_entry *alloc_read_gpt_entries(struct block_device *blk, if (!count) return NULL; + pte = kzalloc(count, GFP_KERNEL); if (!pte) return NULL; @@ -154,8 +166,8 @@ static int is_gpt_valid(struct block_device *blk, u64 lba, /* Check the GPT header signature */ if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { - dev_dbg(blk->dev, "GUID Partition Table Header signature is wrong:" - "0x%llX != 0x%llX\n", + dev_dbg(blk->dev, "GUID Partition Table Header signature at LBA" + "%llu is wrong: 0x%llX != 0x%llX\n", lba, (unsigned long long)le64_to_cpu((*gpt)->signature), (unsigned long long)GPT_HEADER_SIGNATURE); goto fail; @@ -206,7 +218,8 @@ static int is_gpt_valid(struct block_device *blk, u64 lba, le32_to_cpu((*gpt)->sizeof_partition_entry)); if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { - dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed.\n"); + dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed: 0x%08x 0x%08x\n", + crc, le32_to_cpu((*gpt)->partition_entry_array_crc32)); goto fail_ptes; } @@ -232,7 +245,7 @@ static int is_gpt_valid(struct block_device *blk, u64 lba, static inline int is_pte_valid(const gpt_entry *pte, const u64 lastlba) { - if ((!efi_guidcmp(pte->partition_type_guid, EFI_NULL_GUID)) || + if (guid_is_null(&pte->partition_type_guid) || le64_to_cpu(pte->starting_lba) > lastlba || le64_to_cpu(pte->ending_lba) > lastlba) return 0; @@ -249,7 +262,8 @@ is_pte_valid(const gpt_entry *pte, const u64 lastlba) * */ static void -compare_gpts(struct device_d *dev, gpt_header *pgpt, gpt_header *agpt, u64 lastlba) +compare_gpts(struct device *dev, gpt_header *pgpt, gpt_header *agpt, + u64 lastlba) { int error_found = 0; if (!pgpt || !agpt) @@ -286,7 +300,7 @@ compare_gpts(struct device_d *dev, gpt_header *pgpt, gpt_header *agpt, u64 lastl (unsigned long long)le64_to_cpu(agpt->last_usable_lba)); error_found++; } - if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { + if (!guid_equal(&pgpt->disk_guid, &agpt->disk_guid)) { dev_warn(dev, "GPT:disk_guids don't match.\n"); error_found++; } @@ -430,20 +444,53 @@ static void part_set_efi_name(gpt_entry *pte, char *dest) dest[i] = 0; } -static void efi_partition(void *buf, struct block_device *blk, - struct partition_desc *pd) +static void extract_flags(const gpt_entry *p, struct partition *pentry) +{ + static const guid_t system_guid = PARTITION_SYSTEM_GUID; + + if (p->attributes.required_to_function) + pentry->flags |= DEVFS_PARTITION_REQUIRED; + if (p->attributes.no_block_io_protocol) + pentry->flags |= DEVFS_PARTITION_NO_EXPORT; + if (p->attributes.legacy_bios_bootable) + pentry->flags |= DEVFS_PARTITION_BOOTABLE_LEGACY; + + if (guid_equal(&p->partition_type_guid, &system_guid)) + pentry->flags |= DEVFS_PARTITION_BOOTABLE_ESP; + + pentry->typeflags = p->attributes.type_guid_specific; +} + +static void part_get_efi_name(gpt_entry *pte, const char *src) +{ + int i; + int len = strlen(src); + + for (i = 0; i < GPT_PARTNAME_MAX_SIZE ; i++) { + if (i <= len) + pte->partition_name[i] = src[i]; + else + pte->partition_name[i] = 0; + } +} + +static struct partition_desc *efi_partition(void *buf, struct block_device *blk) { gpt_header *gpt = NULL; gpt_entry *ptes = NULL; int i = 0; int nb_part; + struct efi_partition *epart; struct partition *pentry; + struct efi_partition_desc *epd; - if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes) { - kfree(gpt); - kfree(ptes); - return; - } + if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes) + return NULL; + + snprintf(blk->cdev.diskuuid, sizeof(blk->cdev.diskuuid), "%pUl", &gpt->disk_guid); + dev_add_param_string_fixed(blk->dev, "guid", blk->cdev.diskuuid); + + blk->cdev.flags |= DEVFS_IS_GPT_PARTITIONED; nb_part = le32_to_cpu(gpt->num_partition_entries); @@ -453,25 +500,286 @@ static void efi_partition(void *buf, struct block_device *blk, nb_part = MAX_PARTITION; } + epd = xzalloc(sizeof(*epd)); + partition_desc_init(&epd->pd, blk); + + epd->gpt = gpt; + epd->ptes = ptes; + for (i = 0; i < nb_part; i++) { if (!is_pte_valid(&ptes[i], last_lba(blk))) { dev_dbg(blk->dev, "Invalid pte %d\n", i); - return; + continue; } - pentry = &pd->parts[pd->used_entries]; + epart = xzalloc(sizeof(*epart)); + epart->pte = &ptes[i]; + pentry = &epart->part; + extract_flags(&ptes[i], pentry); pentry->first_sec = le64_to_cpu(ptes[i].starting_lba); pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec; pentry->size++; part_set_efi_name(&ptes[i], pentry->name); snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid); - pd->used_entries++; + pentry->typeuuid = ptes[i].partition_type_guid; + pentry->num = i; + list_add_tail(&pentry->list, &epd->pd.partitions); + } + + return &epd->pd; +} + +static void efi_partition_free(struct partition_desc *pd) +{ + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd); + struct partition *part, *tmp; + + list_for_each_entry_safe(part, tmp, &pd->partitions, list) { + struct efi_partition *epart = container_of(part, struct efi_partition, part); + + free(epart); + } + + free(epd->ptes); + free(epd->gpt); + free(epd); +} + +static __maybe_unused struct partition_desc *efi_partition_create_table(struct block_device *blk) +{ + struct efi_partition_desc *epd = xzalloc(sizeof(*epd)); + gpt_header *gpt; + + partition_desc_init(&epd->pd, blk); + + epd->gpt = xzalloc(512); + gpt = epd->gpt; + + gpt->signature = cpu_to_le64(GPT_HEADER_SIGNATURE); + gpt->revision = cpu_to_le32(0x100); + gpt->header_size = cpu_to_le32(sizeof(*gpt)); + gpt->my_lba = cpu_to_le64(1); + gpt->alternate_lba = cpu_to_le64(last_lba(blk)); + gpt->first_usable_lba = cpu_to_le64(34); + gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - 34);; + generate_random_guid((unsigned char *)&gpt->disk_guid); + gpt->partition_entry_lba = cpu_to_le64(2); + gpt->num_partition_entries = cpu_to_le32(128); + gpt->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry)); + + pr_info("Created new disk label with GUID %pU\n", &gpt->disk_guid); + + epd->ptes = xzalloc(128 * sizeof(gpt_entry)); + + return &epd->pd; +} + +static guid_t partition_linux_data_guid = PARTITION_LINUX_DATA_GUID; +static guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID; +static guid_t partition_barebox_env_guid = PARTITION_BAREBOX_ENVIRONMENT_GUID; + +static const guid_t *fs_type_to_guid(const char *fstype) +{ + if (!strcmp(fstype, "ext2")) + return &partition_linux_data_guid; + if (!strcmp(fstype, "ext3")) + return &partition_linux_data_guid; + if (!strcmp(fstype, "ext4")) + return &partition_linux_data_guid; + if (!strcmp(fstype, "fat16")) + return &partition_basic_data_guid; + if (!strcmp(fstype, "fat32")) + return &partition_basic_data_guid; + if (!strcmp(fstype, "bbenv")) + return &partition_barebox_env_guid; + + return NULL; +} + +static __maybe_unused int efi_partition_mkpart(struct partition_desc *pd, + const char *name, const char *fs_type, + uint64_t start_lba, uint64_t end_lba) +{ + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd); + struct efi_partition *epart; + struct partition *part; + gpt_header *gpt = epd->gpt; + int num_parts = le32_to_cpu(gpt->num_partition_entries); + gpt_entry *pte; + int i; + const guid_t *guid; + + if (start_lba < 34) { + pr_err("invalid start LBA %lld, minimum is 34\n", start_lba); + return -EINVAL; } + + if (end_lba >= last_lba(pd->blk) - 33) { + pr_err("invalid end LBA %lld, maximum is %lld\n", start_lba, + last_lba(pd->blk) - 33); + return -EINVAL; + } + + for (i = 0; i < num_parts; i++) { + if (!is_pte_valid(&epd->ptes[i], last_lba(pd->blk))) + break; + } + + if (i == num_parts) { + pr_err("partition table is full\n"); + return -ENOSPC; + } + + guid = fs_type_to_guid(fs_type); + if (!guid) { + pr_err("Unknown fs type %s\n", fs_type); + return -EINVAL; + } + + pte = &epd->ptes[i]; + epart = xzalloc(sizeof(*epart)); + part = &epart->part; + + part->first_sec = start_lba; + part->size = end_lba - start_lba + 1; + part->typeuuid = *guid; + + pte->partition_type_guid = *guid; + generate_random_guid((unsigned char *)&pte->unique_partition_guid); + pte->starting_lba = cpu_to_le64(start_lba); + pte->ending_lba = cpu_to_le64(end_lba); + part_get_efi_name(pte, name); + part_set_efi_name(pte, part->name); + part->num = i; + + list_add_tail(&part->list, &pd->partitions); + + return 0; +} + +static __maybe_unused int efi_partition_rmpart(struct partition_desc *pd, struct partition *part) +{ + struct efi_partition *epart = container_of(part, struct efi_partition, part); + + memset(epart->pte, 0, sizeof(*epart->pte)); + + list_del(&part->list); + free(epart); + + return 0; +} + +static int efi_protective_mbr(struct block_device *blk) +{ + struct partition_desc *pdesc; + int ret; + + pdesc = partition_table_new(blk, "msdos"); + if (IS_ERR(pdesc)) { + printf("Error: Cannot create partition table: %pe\n", pdesc); + return PTR_ERR(pdesc); + } + + ret = partition_create(pdesc, "primary", "0xee", 1, last_lba(blk)); + if (ret) { + pr_err("Cannot create partition: %pe\n", ERR_PTR(ret)); + goto out; + } + + ret = partition_table_write(pdesc); + if (ret) { + pr_err("Cannot write partition: %pe\n", ERR_PTR(ret)); + goto out; + } +out: + partition_table_free(pdesc); + + return ret; +} + +static __maybe_unused int efi_partition_write(struct partition_desc *pd) +{ + struct block_device *blk = pd->blk; + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd); + gpt_header *gpt = epd->gpt, *altgpt; + int ret; + uint32_t count; + uint64_t from, size; + + if (le32_to_cpu(gpt->num_partition_entries) != 128) { + /* + * This is not yet properly implemented. At least writing of the + * alternative GPT is not correctly implemented for this case as + * we can't assume that the partition entries are written at + * last_lba() - 32, we would have to calculate that from the number + * of partition entries. + */ + pr_err("num_partition_entries is != 128. This is not yet supported for writing\n"); + return -EINVAL; + } + + count = le32_to_cpu(gpt->num_partition_entries) * + le32_to_cpu(gpt->sizeof_partition_entry); + + gpt->my_lba = cpu_to_le64(1); + gpt->partition_entry_array_crc32 = cpu_to_le32(efi_crc32( + (const unsigned char *)epd->ptes, count)); + gpt->header_crc32 = 0; + gpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)gpt, + le32_to_cpu(gpt->header_size))); + + ret = efi_protective_mbr(blk); + if (ret) + return ret; + + ret = block_write(blk, gpt, 1, 1); + if (ret) + goto err_block_write; + + from = le64_to_cpu(gpt->partition_entry_lba); + size = count / GPT_BLOCK_SIZE; + + ret = block_write(blk, epd->ptes, from, size); + if (ret) + goto err_block_write; + + altgpt = xmemdup(gpt, SECTOR_SIZE); + + altgpt->alternate_lba = cpu_to_le64(1); + altgpt->my_lba = cpu_to_le64(last_lba(blk)); + altgpt->partition_entry_lba = cpu_to_le64(last_lba(blk) - 32); + altgpt->header_crc32 = 0; + altgpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)altgpt, + le32_to_cpu(altgpt->header_size))); + ret = block_write(blk, altgpt, last_lba(blk), 1); + + free(altgpt); + + if (ret) + goto err_block_write; + ret = block_write(blk, epd->ptes, last_lba(blk) - 32, 32); + if (ret) + goto err_block_write; + + return 0; + +err_block_write: + pr_err("Cannot write to block device: %pe\n", ERR_PTR(ret)); + + return ret; } static struct partition_parser efi_partition_parser = { .parse = efi_partition, + .partition_free = efi_partition_free, +#ifdef CONFIG_PARTITION_MANIPULATION + .create = efi_partition_create_table, + .mkpart = efi_partition_mkpart, + .rmpart = efi_partition_rmpart, + .write = efi_partition_write, +#endif .type = filetype_gpt, + .name = "gpt", }; static int efi_partition_init(void) diff --git a/common/partitions/efi.h b/common/partitions/efi.h deleted file mode 100644 index a9b10c1266..0000000000 --- a/common/partitions/efi.h +++ /dev/null @@ -1,119 +0,0 @@ -/************************************************************ - * EFI GUID Partition Table - * Per Intel EFI Specification v1.02 - * http://developer.intel.com/technology/efi/efi.htm - * - * By Matt Domsch <Matt_Domsch@dell.com> Fri Sep 22 22:15:56 CDT 2000 - * Copyright 2000,2001 Dell Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - ************************************************************/ - -#ifndef FS_PART_EFI_H_INCLUDED -#define FS_PART_EFI_H_INCLUDED - -#include <efi.h> - -#define MSDOS_MBR_SIGNATURE 0xaa55 -#define EFI_PMBR_OSTYPE_EFI 0xEF -#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE - -#define GPT_BLOCK_SIZE 512 -#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL -#define GPT_HEADER_REVISION_V1 0x00010000 -#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 - -#define PARTITION_SYSTEM_GUID \ - EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ - 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) -#define LEGACY_MBR_PARTITION_GUID \ - EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ - 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) -#define PARTITION_MSFT_RESERVED_GUID \ - EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ - 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) -#define PARTITION_BASIC_DATA_GUID \ - EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ - 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) -#define PARTITION_LINUX_RAID_GUID \ - EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ - 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) -#define PARTITION_LINUX_SWAP_GUID \ - EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ - 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) -#define PARTITION_LINUX_LVM_GUID \ - EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ - 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) - -/* based on linux/include/genhd.h */ -struct legacy_partition { - unsigned char boot_ind; /* 0x80 - active */ - unsigned char head; /* starting head */ - unsigned char sector; /* starting sector */ - unsigned char cyl; /* starting cylinder */ - unsigned char sys_ind; /* What partition type */ - unsigned char end_head; /* end head */ - unsigned char end_sector; /* end sector */ - unsigned char end_cyl; /* end cylinder */ - __le32 start_sect; /* starting sector counting from 0 */ - __le32 nr_sects; /* nr of sectors in partition */ -} __attribute__ ((packed)); - -/* based on linux/fs/partitions/efi.h */ -typedef struct _gpt_header { - __le64 signature; - __le32 revision; - __le32 header_size; - __le32 header_crc32; - __le32 reserved1; - __le64 my_lba; - __le64 alternate_lba; - __le64 first_usable_lba; - __le64 last_usable_lba; - efi_guid_t disk_guid; - __le64 partition_entry_lba; - __le32 num_partition_entries; - __le32 sizeof_partition_entry; - __le32 partition_entry_array_crc32; - - /* The rest of the logical block is reserved by UEFI and must be zero. - * EFI standard handles this by: - * - * uint8_t reserved2[ BlockSize - 92 ]; - */ -} __attribute__ ((packed)) gpt_header; - -typedef struct _gpt_entry_attributes { - u64 required_to_function:1; - u64 reserved:47; - u64 type_guid_specific:16; -} __attribute__ ((packed)) gpt_entry_attributes; - -#define GPT_PARTNAME_MAX_SIZE (72 / sizeof (efi_char16_t)) -typedef struct _gpt_entry { - efi_guid_t partition_type_guid; - efi_guid_t unique_partition_guid; - __le64 starting_lba; - __le64 ending_lba; - gpt_entry_attributes attributes; - efi_char16_t partition_name[GPT_PARTNAME_MAX_SIZE]; -} __attribute__ ((packed)) gpt_entry; - -typedef struct _legacy_mbr { - u8 boot_code[440]; - __le32 unique_mbr_signature; - __le16 unknown; - struct legacy_partition partition_record[4]; - __le16 signature; -} __attribute__ ((packed)) legacy_mbr; - -#endif /* _DISK_PART_EFI_H */ diff --git a/common/partitions/parser.h b/common/partitions/parser.h deleted file mode 100644 index 69508932b3..0000000000 --- a/common/partitions/parser.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2013 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * Under GPLv2 only - */ - -#ifndef __PARTITIONS_PARSER_H__ -#define __PARTITIONS_PARSER_H__ - -#include <block.h> -#include <filetype.h> -#include <linux/list.h> - -#define MAX_PARTITION 128 -#define MAX_PARTITION_NAME 38 - -struct partition { - char name[MAX_PARTITION_NAME]; - u8 dos_partition_type; - char partuuid[MAX_PARTUUID_STR]; - uint64_t first_sec; - uint64_t size; -}; - -struct partition_desc { - int used_entries; - struct partition parts[MAX_PARTITION]; -}; - -struct partition_parser { - void (*parse)(void *buf, struct block_device *blk, struct partition_desc *pd); - enum filetype type; - - struct list_head list; -}; - -int partition_parser_register(struct partition_parser *p); - -#endif /* __PARTITIONS_PARSER_H__ */ diff --git a/common/password.c b/common/password.c index aea7c7ff5d..55b2d1093a 100644 --- a/common/password.c +++ b/common/password.c @@ -148,8 +148,7 @@ static unsigned char to_hexa(unsigned char c) static int read_default_passwd(unsigned char *sum, size_t length) { - int i = 0; - int len = strlen(default_passwd); + int len, i = 0; unsigned char *buf = (unsigned char *)default_passwd; unsigned char c; @@ -159,6 +158,7 @@ static int read_default_passwd(unsigned char *sum, size_t length) if (!sum || length < 1) return -EINVAL; + len = strlen(default_passwd); for (i = 0; i < len && length > 0; i++) { c = buf[i]; i++; diff --git a/common/pe.c b/common/pe.c new file mode 100644 index 0000000000..5c33665dfa --- /dev/null +++ b/common/pe.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PE image loader + * + * based partly on wine code + * + * Copyright (c) 2016 Alexander Graf + */ + +#define pr_fmt(fmt) "pe: " fmt + +#include <common.h> +#include <pe.h> +#include <linux/sizes.h> +#include <libfile.h> +#include <memory.h> +#include <linux/err.h> + +static int machines[] = { +#if defined(__aarch64__) + IMAGE_FILE_MACHINE_ARM64, +#elif defined(__arm__) + IMAGE_FILE_MACHINE_ARM, + IMAGE_FILE_MACHINE_THUMB, + IMAGE_FILE_MACHINE_ARMNT, +#endif + +#if defined(__x86_64__) + IMAGE_FILE_MACHINE_AMD64, +#elif defined(__i386__) + IMAGE_FILE_MACHINE_I386, +#endif + +#if defined(__riscv) && (__riscv_xlen == 32) + IMAGE_FILE_MACHINE_RISCV32, +#endif + +#if defined(__riscv) && (__riscv_xlen == 64) + IMAGE_FILE_MACHINE_RISCV64, +#endif + 0 }; + +/** + * pe_loader_relocate() - relocate PE binary + * + * @rel: pointer to the relocation table + * @rel_size: size of the relocation table in bytes + * @pe_reloc: actual load address of the image + * @pref_address: preferred load address of the image + * Return: status code + */ +static int pe_loader_relocate(const IMAGE_BASE_RELOCATION *rel, + unsigned long rel_size, void *pe_reloc, + unsigned long pref_address) +{ + unsigned long delta = (unsigned long)pe_reloc - pref_address; + const IMAGE_BASE_RELOCATION *end; + int i; + + if (delta == 0) + return 0; + + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + while (rel < end && rel->SizeOfBlock) { + const uint16_t *relocs = (const uint16_t *)(rel + 1); + i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t); + while (i--) { + uint32_t offset = (uint32_t)(*relocs & 0xfff) + + rel->VirtualAddress; + int type = *relocs >> PAGE_SHIFT; + uint64_t *x64 = pe_reloc + offset; + uint32_t *x32 = pe_reloc + offset; + uint16_t *x16 = pe_reloc + offset; + + switch (type) { + case IMAGE_REL_BASED_ABSOLUTE: + break; + case IMAGE_REL_BASED_HIGH: + *x16 += ((uint32_t)delta) >> 16; + break; + case IMAGE_REL_BASED_LOW: + *x16 += (uint16_t)delta; + break; + case IMAGE_REL_BASED_HIGHLOW: + *x32 += (uint32_t)delta; + break; + case IMAGE_REL_BASED_DIR64: + *x64 += (uint64_t)delta; + break; +#ifdef __riscv + case IMAGE_REL_BASED_RISCV_HI20: + *x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) | + (*x32 & 0x00000fff); + break; + case IMAGE_REL_BASED_RISCV_LOW12I: + case IMAGE_REL_BASED_RISCV_LOW12S: + /* We know that we're 4k aligned */ + if (delta & 0xfff) { + pr_err("Unsupported reloc offset\n"); + return -ENOEXEC; + } + break; +#endif + default: + pr_err("Unknown Relocation off %x type %x\n", + offset, type); + return -ENOEXEC; + } + relocs++; + } + rel = (const IMAGE_BASE_RELOCATION *)relocs; + } + return 0; +} + +/** + * pe_check_header() - check if a memory buffer contains a PE-COFF image + * + * @buffer: buffer to check + * @size: size of buffer + * @nt_header: on return pointer to NT header of PE-COFF image + * Return: 0 if the buffer contains a PE-COFF image + */ +static int pe_check_header(void *buffer, size_t size, void **nt_header) +{ + struct mz_hdr *dos = buffer; + IMAGE_NT_HEADERS32 *nt; + + if (size < sizeof(*dos)) + return -EINVAL; + + /* Check for DOS magix */ + if (dos->magic != MZ_MAGIC) + return -EINVAL; + + /* + * Check if the image section header fits into the file. Knowing that at + * least one section header follows we only need to check for the length + * of the 64bit header which is longer than the 32bit header. + */ + if (size < dos->peaddr + sizeof(IMAGE_NT_HEADERS32)) + return -EINVAL; + nt = (IMAGE_NT_HEADERS32 *)((u8 *)buffer + dos->peaddr); + + /* Check for PE-COFF magic */ + if (nt->FileHeader.magic != PE_MAGIC) + return -EINVAL; + + if (nt_header) + *nt_header = nt; + + return 0; +} + +/** + * section_size() - determine size of section + * + * The size of a section in memory if normally given by VirtualSize. + * If VirtualSize is not provided, use SizeOfRawData. + * + * @sec: section header + * Return: size of section in memory + */ +static u32 section_size(IMAGE_SECTION_HEADER *sec) +{ + if (sec->Misc.VirtualSize) + return sec->Misc.VirtualSize; + else + return sec->SizeOfRawData; +} + +struct pe_image *pe_open_buf(void *bin, size_t pe_size) +{ + struct pe_image *pe; + int i; + int supported = 0; + int ret; + + pe = calloc(1, sizeof(*pe)); + if (!pe) + return ERR_PTR(-ENOMEM); + + ret = pe_check_header(bin, pe_size, (void **)&pe->nt); + if (ret) { + pr_err("Not a PE-COFF file\n"); + ret = -ENOEXEC; + goto err; + } + + for (i = 0; machines[i]; i++) + if (machines[i] == pe->nt->FileHeader.machine) { + supported = 1; + break; + } + + if (!supported) { + pr_err("Machine type 0x%04x is not supported\n", + pe->nt->FileHeader.machine); + ret = -ENOEXEC; + goto err; + } + + pe->num_sections = pe->nt->FileHeader.sections; + pe->sections = (void *)&pe->nt->OptionalHeader + + pe->nt->FileHeader.opt_hdr_size; + + if (pe_size < ((void *)pe->sections + sizeof(pe->sections[0]) * pe->num_sections - bin)) { + pr_err("Invalid number of sections: %d\n", pe->num_sections); + ret = -ENOEXEC; + goto err; + } + + pe->bin = bin; + + return pe; +err: + return ERR_PTR(ret); +} + +struct pe_image *pe_open(const char *filename) +{ + struct pe_image *pe; + size_t size; + void *bin; + + bin = read_file(filename, &size); + if (!bin) + return ERR_PTR(-errno); + + pe = pe_open_buf(bin, size); + if (IS_ERR(pe)) + free(bin); + + return pe; +} + +static struct resource *pe_alloc(size_t virt_size) +{ + resource_size_t start, end; + int ret; + + ret = memory_bank_first_find_space(&start, &end); + if (ret) + return NULL; + + start = ALIGN(start, SZ_64K); + + if (start + virt_size > end) + return NULL; + + return request_sdram_region("pe-code", start, virt_size); +} + +unsigned long pe_get_mem_size(struct pe_image *pe) +{ + unsigned long virt_size = 0; + int i; + + /* Calculate upper virtual address boundary */ + for (i = pe->num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = &pe->sections[i]; + + virt_size = max_t(unsigned long, virt_size, + sec->VirtualAddress + section_size(sec)); + } + + /* Read 32/64bit specific header bits */ + if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) { + IMAGE_NT_HEADERS64 *nt64 = (void *)pe->nt; + struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader; + + virt_size = ALIGN(virt_size, opt->section_align); + } else if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) { + struct pe32_opt_hdr *opt = &pe->nt->OptionalHeader; + + virt_size = ALIGN(virt_size, opt->section_align); + } else { + pr_err("Invalid optional header magic %x\n", + pe->nt->OptionalHeader.magic); + return 0; + } + + return virt_size; +} + +int pe_load(struct pe_image *pe) +{ + int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC; + uint64_t image_base; + unsigned long virt_size; + unsigned long rel_size; + const IMAGE_BASE_RELOCATION *rel; + struct mz_hdr *dos; + struct resource *code; + void *pe_reloc; + int i; + + virt_size = pe_get_mem_size(pe); + if (!virt_size) + return -ENOEXEC; + + /* Read 32/64bit specific header bits */ + if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) { + IMAGE_NT_HEADERS64 *nt64 = (void *)pe->nt; + struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader; + image_base = opt->image_base; + pe->image_type = opt->subsys; + + code = pe_alloc(virt_size); + if (!code) + return -ENOMEM; + + pe_reloc = (void *)code->start; + + pe->entry = code->start + opt->entry_point; + rel_size = nt64->DataDirectory[rel_idx].size; + rel = pe_reloc + nt64->DataDirectory[rel_idx].virtual_address; + } else if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) { + struct pe32_opt_hdr *opt = &pe->nt->OptionalHeader; + image_base = opt->image_base; + pe->image_type = opt->subsys; + virt_size = ALIGN(virt_size, opt->section_align); + + code = pe_alloc(virt_size); + if (!code) + return -ENOMEM; + + pe_reloc = (void *)code->start; + + pe->entry = code->start + opt->entry_point; + rel_size = pe->nt->DataDirectory[rel_idx].size; + rel = pe_reloc + pe->nt->DataDirectory[rel_idx].virtual_address; + } else { + pr_err("Invalid optional header magic %x\n", + pe->nt->OptionalHeader.magic); + return -ENOEXEC; + } + + /* Copy PE headers */ + memcpy(pe_reloc, pe->bin, + sizeof(*dos) + + sizeof(*pe->nt) + + pe->nt->FileHeader.opt_hdr_size + + pe->num_sections * sizeof(IMAGE_SECTION_HEADER)); + + /* Load sections into RAM */ + for (i = pe->num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = &pe->sections[i]; + u32 copy_size = section_size(sec); + + if (copy_size > sec->SizeOfRawData) { + copy_size = sec->SizeOfRawData; + memset(pe_reloc + sec->VirtualAddress, 0, + sec->Misc.VirtualSize); + } + + memcpy(pe_reloc + sec->VirtualAddress, + pe->bin + sec->PointerToRawData, + copy_size); + } + + /* Run through relocations */ + if (pe_loader_relocate(rel, rel_size, pe_reloc, + (unsigned long)image_base) != 0) { + release_sdram_region(code); + return -EINVAL; + } + + pe->code = code; + + return 0; +} + +void pe_close(struct pe_image *pe) +{ + release_sdram_region(pe->code); + free(pe->bin); + free(pe); +} diff --git a/common/ratp/Kconfig b/common/ratp/Kconfig index 25150addfd..43720e30a0 100644 --- a/common/ratp/Kconfig +++ b/common/ratp/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config CONSOLE_RATP bool @@ -28,4 +29,4 @@ config RATP_CMD_GPIO depends on GENERIC_GPIO prompt "RATP GPIO support" help - This option adds support for GPIO get/set/direction commands via RATP.
\ No newline at end of file + This option adds support for GPIO get/set/direction commands via RATP. diff --git a/common/ratp/Makefile b/common/ratp/Makefile index 71288bcb8c..e37ccca1fa 100644 --- a/common/ratp/Makefile +++ b/common/ratp/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-y += ratp.o obj-y += ping.o obj-y += getenv.o diff --git a/common/ratp/getenv.c b/common/ratp/getenv.c index 7b38d2e363..9617ab1b43 100644 --- a/common/ratp/getenv.c +++ b/common/ratp/getenv.c @@ -32,6 +32,9 @@ static int ratp_cmd_getenv(const struct ratp_bb *req, int req_len, value = getenv(varname); free(varname); + if (!value) + value = ""; + dlen = strlen(value); *rsp_len = sizeof(struct ratp_bb) + dlen; diff --git a/common/ratp/i2c.c b/common/ratp/i2c.c index c14bbbf9b9..404ddd2ece 100644 --- a/common/ratp/i2c.c +++ b/common/ratp/i2c.c @@ -162,7 +162,7 @@ out: *rsp = (struct ratp_bb *)i2c_read_rsp; *rsp_len = i2c_read_rsp_len; - return ret; + return 0; } BAREBOX_RATP_CMD_START(I2C_READ) @@ -271,7 +271,7 @@ out: *rsp = (struct ratp_bb *)i2c_write_rsp; *rsp_len = sizeof(*i2c_write_rsp); - return ret; + return 0; } BAREBOX_RATP_CMD_START(I2C_WRITE) diff --git a/common/ratp/md.c b/common/ratp/md.c index 3e258c59a0..8221afaebc 100644 --- a/common/ratp/md.c +++ b/common/ratp/md.c @@ -68,8 +68,10 @@ static int do_ratp_mem_md(const char *filename, char *buf = NULL; fd = open_and_lseek(filename, O_RWSIZE_1 | O_RDONLY, start); - if (fd < 0) + if (fd < 0) { + printf("Could not open \"%s\": %m\n", filename); return -errno; + } map = memmap(fd, PROT_READ); if (map != MAP_FAILED) { diff --git a/common/ratp/mw.c b/common/ratp/mw.c index 8945799f1d..87dc8cb95c 100644 --- a/common/ratp/mw.c +++ b/common/ratp/mw.c @@ -133,6 +133,7 @@ static int ratp_cmd_mw(const struct ratp_bb *req, int req_len, fd = open_and_lseek(path, O_RWSIZE_1 | O_WRONLY, addr); if (fd < 0) { + printf("Could not open \"%s\": %m\n", path); ret = -errno; goto out; } diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c index 424c9406d2..fddb286e01 100644 --- a/common/ratp/ratp.c +++ b/common/ratp/ratp.c @@ -486,7 +486,7 @@ int barebox_ratp(struct console_device *cdev) ctx->cdev = cdev; ctx->have_synch = 1; - ret = ratp_establish(&ctx->ratp, false, 100); + ret = ratp_establish(&ctx->ratp, false, 1000); if (ret < 0) goto out; diff --git a/common/reset_source.c b/common/reset_source.c index 3554cbd0fb..f28be90dcb 100644 --- a/common/reset_source.c +++ b/common/reset_source.c @@ -25,7 +25,7 @@ static const char * const reset_src_names[] = { static enum reset_src_type reset_source; static unsigned int reset_source_priority; static int reset_source_instance; -static struct device_d *reset_source_device; +static struct device *reset_source_device; enum reset_src_type reset_source_get(void) { @@ -45,13 +45,13 @@ int reset_source_get_instance(void) } EXPORT_SYMBOL(reset_source_get_instance); -struct device_d *reset_source_get_device(void) +struct device *reset_source_get_device(void) { return reset_source_device; } EXPORT_SYMBOL(reset_source_get_device); -static void __reset_source_set(struct device_d *dev, +static void __reset_source_set(struct device *dev, enum reset_src_type st, unsigned int priority, int instance) { @@ -74,9 +74,11 @@ void reset_source_set_prinst(enum reset_src_type st, } EXPORT_SYMBOL(reset_source_set_prinst); -void reset_source_set_device(struct device_d *dev, enum reset_src_type st) +void reset_source_set_device(struct device *dev, enum reset_src_type st) { - int priority = of_get_reset_source_priority(dev->device_node); + unsigned int priority = RESET_SOURCE_DEFAULT_PRIORITY; + + of_property_read_u32(dev->of_node, "reset-source-priority", &priority); __reset_source_set(dev, st, priority, -1); } @@ -92,19 +94,3 @@ static int reset_source_init(void) return 0; } coredevice_initcall(reset_source_init); - -/** - * of_get_reset_source_priority() - get the desired reset source priority from - * device tree - * @node: The device_node to read the property from - * - * return: The priority - */ -unsigned int of_get_reset_source_priority(struct device_node *node) -{ - unsigned int priority = RESET_SOURCE_DEFAULT_PRIORITY; - - of_property_read_u32(node, "reset-source-priority", &priority); - - return priority; -} diff --git a/common/resource.c b/common/resource.c index f96cb94b50..8678609229 100644 --- a/common/resource.c +++ b/common/resource.c @@ -28,8 +28,8 @@ static int init_resource(struct resource *res, const char *name) * resources. */ struct resource *__request_region(struct resource *parent, - const char *name, resource_size_t start, - resource_size_t end) + resource_size_t start, resource_size_t end, + const char *name, unsigned flags) { struct resource *r, *new; @@ -73,15 +73,16 @@ struct resource *__request_region(struct resource *parent, } ok: - debug("%s ok: 0x%08llx:0x%08llx\n", __func__, + debug("%s ok: 0x%08llx:0x%08llx flags=0x%x\n", __func__, (unsigned long long)start, - (unsigned long long)end); + (unsigned long long)end, flags); new = xzalloc(sizeof(*new)); init_resource(new, name); new->start = start; new->end = end; new->parent = parent; + new->flags = flags; list_add_tail(&new->sibling, &r->sibling); return new; @@ -138,7 +139,7 @@ struct resource iomem_resource = { struct resource *request_iomem_region(const char *name, resource_size_t start, resource_size_t end) { - return __request_region(&iomem_resource, name, start, end); + return __request_region(&iomem_resource, start, end, name, 0); } /* The root resource for the whole io-mapped io space */ @@ -157,7 +158,7 @@ struct resource *request_ioport_region(const char *name, { struct resource *res; - res = __request_region(&ioport_resource, name, start, end); + res = __request_region(&ioport_resource, start, end, name, 0); if (IS_ERR(res)) return ERR_CAST(res); diff --git a/common/restart.c b/common/restart.c index b6f2bbf25b..35cfb54251 100644 --- a/common/restart.c +++ b/common/restart.c @@ -27,6 +27,14 @@ int restart_handler_register(struct restart_handler *rst) if (!rst->priority) rst->priority = RESTART_DEFAULT_PRIORITY; + if (rst->of_node) { + of_property_read_u32(rst->of_node, "restart-priority", + &rst->priority); + if (of_property_read_bool(rst->of_node, + "barebox,restart-warm-bootrom")) + rst->flags |= RESTART_FLAG_WARM_BOOTROM; + } + list_add_tail(&rst->list, &restart_handler_list); pr_debug("registering restart handler \"%s\" with priority %d\n", @@ -66,9 +74,9 @@ int restart_handler_register_fn(const char *name, } /** - * restart_handler_get_by_name() - reset the whole system + * restart_handler_get_by_name() - get highest priority `name' */ -struct restart_handler *restart_handler_get_by_name(const char *name) +struct restart_handler *restart_handler_get_by_name(const char *name, int flags) { struct restart_handler *rst = NULL, *tmp; unsigned int priority = 0; @@ -76,6 +84,8 @@ struct restart_handler *restart_handler_get_by_name(const char *name) list_for_each_entry(tmp, &restart_handler_list, list) { if (name && tmp->name && strcmp(name, tmp->name)) continue; + if (flags && (tmp->flags & flags) != flags) + continue; if (tmp->priority > priority) { priority = tmp->priority; rst = tmp; @@ -92,7 +102,7 @@ void __noreturn restart_machine(void) { struct restart_handler *rst; - rst = restart_handler_get_by_name(NULL); + rst = restart_handler_get_by_name(NULL, 0); if (rst) { pr_debug("%s: using restart handler %s\n", __func__, rst->name); console_flush(); @@ -102,21 +112,6 @@ void __noreturn restart_machine(void) hang(); } -/** - * of_get_restart_priority() - get the desired restart priority from device tree - * @node: The device_node to read the property from - * - * return: The priority - */ -unsigned int of_get_restart_priority(struct device_node *node) -{ - unsigned int priority = RESTART_DEFAULT_PRIORITY; - - of_property_read_u32(node, "restart-priority", &priority); - - return priority; -} - /* * restart_handlers_print - print informations about all restart handlers */ @@ -124,6 +119,10 @@ void restart_handlers_print(void) { struct restart_handler *tmp; - list_for_each_entry(tmp, &restart_handler_list, list) - printf("%-20s %6d\n", tmp->name, tmp->priority); + list_for_each_entry(tmp, &restart_handler_list, list) { + printf("%-20s %6d ", tmp->name, tmp->priority); + if (tmp->flags & RESTART_FLAG_WARM_BOOTROM) + putchar('W'); + putchar('\n'); + } } diff --git a/common/serdev.c b/common/serdev.c index 4bf11b1618..5399a20627 100644 --- a/common/serdev.c +++ b/common/serdev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <serdev.h> @@ -131,7 +132,7 @@ static int serdev_device_reader_receive_buf(struct serdev_device *serdev, const unsigned char *buf, size_t size) { - struct device_d *dev = serdev->dev; + struct device *dev = serdev->dev; struct serdev_device_reader *r = dev->priv; const size_t room = min(r->capacity - r->len, size); @@ -185,7 +186,7 @@ int serdev_device_reader_open(struct serdev_device *serdev, size_t capacity) int serdev_device_read(struct serdev_device *serdev, unsigned char *buf, size_t count, unsigned long timeout) { - struct device_d *dev = serdev->dev; + struct device *dev = serdev->dev; struct serdev_device_reader *r = dev->priv; int ret; diff --git a/common/startup.c b/common/startup.c index f72902fc53..47b70a7756 100644 --- a/common/startup.c +++ b/common/startup.c @@ -37,6 +37,8 @@ #include <linux/ctype.h> #include <watchdog.h> #include <glob.h> +#include <net.h> +#include <efi/efi-mode.h> #include <bselftest.h> extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[], @@ -53,7 +55,7 @@ static int mount_root(void) mkdir("/tmp", 0); mount("none", "devfs", "/dev", NULL); - if (IS_ENABLED(CONFIG_FS_EFIVARFS)) { + if (IS_ENABLED(CONFIG_FS_EFIVARFS) && efi_is_payload()) { mkdir("/efivars", 0); mount("none", "efivarfs", "/efivars", NULL); } @@ -68,70 +70,6 @@ static int mount_root(void) fs_initcall(mount_root); #endif -#ifdef CONFIG_ENV_HANDLING -static bool region_overlap(loff_t starta, loff_t lena, - loff_t startb, loff_t lenb) -{ - if (starta + lena <= startb) - return 0; - if (startb + lenb <= starta) - return 0; - return 1; -} - -static int check_overlap(const char *path) -{ - struct cdev *cenv, *cdisk, *cpart; - const char *name; - - name = devpath_to_name(path); - - if (name == path) - /* - * no /dev/ in front, so *path is some file. No need to - * check further. - */ - return 0; - - cenv = cdev_by_name(name); - if (!cenv) - return -EINVAL; - - if (cenv->mtd) - return 0; - - cdisk = cenv->master; - - if (!cdisk) - return 0; - - list_for_each_entry(cpart, &cdisk->partitions, partition_entry) { - if (cpart == cenv) - continue; - - if (region_overlap(cpart->offset, cpart->size, - cenv->offset, cenv->size)) - goto conflict; - } - - return 0; - -conflict: - pr_err("Environment partition (0x%08llx-0x%08llx) " - "overlaps with partition %s (0x%08llx-0x%08llx), not using it\n", - cenv->offset, cenv->offset + cenv->size - 1, - cpart->name, - cpart->offset, cpart->offset + cpart->size - 1); - - return -EINVAL; -} -#else -static int check_overlap(const char *path) -{ - return 0; -} -#endif - static int load_environment(void) { const char *default_environment_path; @@ -143,11 +81,7 @@ static int load_environment(void) defaultenv_load("/env", 0); if (IS_ENABLED(CONFIG_ENV_HANDLING)) { - ret = check_overlap(default_environment_path); - if (ret) - default_environment_path_set(NULL); - else - envfs_load(default_environment_path, "/env", 0); + envfs_load(default_environment_path, "/env", 0); } else { if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT)) pr_notice("No support for persistent environment. Using default environment\n"); @@ -241,7 +175,8 @@ enum autoboot_state do_autoboot_countdown(void) if (autoboot_state != AUTOBOOT_UNKNOWN) return autoboot_state; - if (IS_ENABLED(CONFIG_CONSOLE_DISABLE_INPUT)) { + if (!console_get_first_active() && + global_autoboot_state != AUTOBOOT_ABORT) { printf("\nNon-interactive console, booting system\n"); return autoboot_state = AUTOBOOT_BOOT; } @@ -375,6 +310,9 @@ static int run_init(void) if (autoboot == AUTOBOOT_BOOT) run_command("boot"); + if (IS_ENABLED(CONFIG_NET)) + eth_open_all(); + if (autoboot == AUTOBOOT_MENU) run_command(MENUFILE); diff --git a/common/state/Makefile b/common/state/Makefile index fcf9add52c..93215dd069 100644 --- a/common/state/Makefile +++ b/common/state/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-y += state.o obj-y += state_variables.o obj-y += backend_format_dtb.o diff --git a/common/state/backend_bucket_circular.c b/common/state/backend_bucket_circular.c index 735510e0d3..2ac5bf6a82 100644 --- a/common/state/backend_bucket_circular.c +++ b/common/state/backend_bucket_circular.c @@ -62,7 +62,7 @@ struct state_backend_storage_bucket_circular { #endif /* For outputs */ - struct device_d *dev; + struct device *dev; }; /* @@ -456,7 +456,7 @@ static int bucket_circular_is_block_bad(struct state_backend_storage_bucket_circ } #endif -int state_backend_bucket_circular_create(struct device_d *dev, const char *path, +int state_backend_bucket_circular_create(struct device *dev, const char *path, struct state_backend_storage_bucket **bucket, unsigned int eraseblock, ssize_t writesize, diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c index 4522f0170f..03c752d6fe 100644 --- a/common/state/backend_bucket_direct.c +++ b/common/state/backend_bucket_direct.c @@ -17,7 +17,7 @@ #include <libfile.h> #include <linux/kernel.h> #include <malloc.h> -#include <printk.h> +#include <linux/printk.h> #include "state.h" @@ -29,7 +29,7 @@ struct state_backend_storage_bucket_direct { int fd; - struct device_d *dev; + struct device *dev; }; struct __attribute__((__packed__)) state_backend_storage_bucket_direct_meta { @@ -52,7 +52,7 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket struct state_backend_storage_bucket_direct *direct = get_bucket_direct(bucket); struct state_backend_storage_bucket_direct_meta meta; - uint32_t read_len; + uint32_t read_len, header_len = 0; void *buf; int ret; @@ -72,13 +72,15 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket return -EINVAL; } + + header_len = sizeof(meta); } else { if (meta.magic != ~0 && !!meta.magic) bucket->wrong_magic = 1; if (!IS_ENABLED(CONFIG_STATE_BACKWARD_COMPATIBLE)) { - dev_err(direct->dev, "No meta data header found\n"); dev_dbg(direct->dev, "Enable backward compatibility or increase stride size\n"); - return -EINVAL; + return dev_err_state_init(direct->dev, meta.magic ? -EINVAL : -ENOMEDIUM, + "No meta data header found\n"); } read_len = direct->max_size; if (lseek(direct->fd, direct->offset, SEEK_SET) != @@ -87,12 +89,16 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket -errno); return -errno; } + } buf = xmalloc(read_len); if (!buf) return -ENOMEM; + dev_dbg(direct->dev, "Read state from %lld length %d\n", (long long) direct->offset, + header_len + read_len); + ret = read_full(direct->fd, buf, read_len); if (ret < 0) { dev_err(direct->dev, "Failed to read from file, %d\n", ret); @@ -112,6 +118,7 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket { struct state_backend_storage_bucket_direct *direct = get_bucket_direct(bucket); + size_t header_len = 0; int ret; struct state_backend_storage_bucket_direct_meta meta; @@ -129,6 +136,8 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket dev_err(direct->dev, "Failed to write metadata to file, %d\n", ret); return ret; } + + header_len = sizeof(meta); } else { if (!IS_ENABLED(CONFIG_STATE_BACKWARD_COMPATIBLE)) { dev_dbg(direct->dev, "Too small stride size: must skip metadata! Increase stride size\n"); @@ -148,6 +157,9 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket return ret; } + dev_dbg(direct->dev, "Written state to offset %lld length %zu data length %zu\n", + (long long)direct->offset, len + header_len, len); + return 0; } @@ -162,14 +174,14 @@ static void state_backend_bucket_direct_free(struct free(direct); } -int state_backend_bucket_direct_create(struct device_d *dev, const char *path, +int state_backend_bucket_direct_create(struct device *dev, const char *path, struct state_backend_storage_bucket **bucket, - off_t offset, ssize_t max_size) + off_t offset, ssize_t max_size, bool readonly) { int fd; struct state_backend_storage_bucket_direct *direct; - fd = open(path, O_RDWR); + fd = open(path, readonly ? O_RDONLY : O_RDWR); if (fd < 0) { dev_err(dev, "Failed to open file '%s', %d\n", path, -errno); return -errno; diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c index d0fc948859..b41b896aac 100644 --- a/common/state/backend_format_dtb.c +++ b/common/state/backend_format_dtb.c @@ -28,7 +28,7 @@ struct state_backend_format_dtb { struct device_node *root; /* For outputs */ - struct device_d *dev; + struct device *dev; }; static inline struct state_backend_format_dtb *get_format_dtb(struct @@ -131,7 +131,7 @@ static void state_backend_format_dtb_free(struct state_backend_format *format) } int backend_format_dtb_create(struct state_backend_format **format, - struct device_d *dev) + struct device *dev) { struct state_backend_format_dtb *dtb; diff --git a/common/state/backend_format_raw.c b/common/state/backend_format_raw.c index ea962606cc..105f7dd444 100644 --- a/common/state/backend_format_raw.c +++ b/common/state/backend_format_raw.c @@ -16,14 +16,12 @@ */ #include <common.h> -#include <common.h> #include <crypto/keystore.h> #include <digest.h> #include <linux/kernel.h> #include <malloc.h> #include <crc.h> #include <of.h> -#include <crc.h> #include "state.h" @@ -34,7 +32,7 @@ struct state_backend_format_raw { unsigned int digest_length; /* For outputs */ - struct device_d *dev; + struct device *dev; char *secret_name; int needs_secret; @@ -115,11 +113,10 @@ static int backend_format_raw_verify(struct state_backend_format *format, header = (struct backend_raw_header *)buf; crc = crc32(0, header, sizeof(*header) - sizeof(uint32_t)); - if (crc != header->header_crc) { - dev_err(backend_raw->dev, "Error, invalid header crc in raw format, calculated 0x%08x, found 0x%08x\n", + if (crc != header->header_crc) + return dev_err_state_init(backend_raw->dev, header->header_crc ? -EINVAL : -ENOMEDIUM, + "header crc in raw format, calculated 0x%08x, found 0x%08x\n", crc, header->header_crc); - return -EINVAL; - } if (magic && magic != header->magic) { dev_err(backend_raw->dev, "Error, invalid magic in raw format 0x%08x, should be 0x%08x\n", @@ -302,7 +299,7 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw, int backend_format_raw_create(struct state_backend_format **format, struct device_node *node, const char *secret_name, - struct device_d *dev) + struct device *dev) { struct state_backend_format_raw *raw; int ret; diff --git a/common/state/backend_storage.c b/common/state/backend_storage.c index fe7e89e8fb..df81902bf7 100644 --- a/common/state/backend_storage.c +++ b/common/state/backend_storage.c @@ -21,7 +21,7 @@ #include <linux/mtd/mtd-abi.h> #include <sys/stat.h> #include <malloc.h> -#include <printk.h> +#include <linux/printk.h> #include "state.h" @@ -144,6 +144,7 @@ int state_storage_read(struct state_backend_storage *storage, enum state_flags flags) { struct state_backend_storage_bucket *bucket, *bucket_used = NULL; + int zerobuckets = 0, totalbuckets = 0; int ret; dev_dbg(storage->dev, "Checking redundant buckets...\n"); @@ -152,30 +153,35 @@ int state_storage_read(struct state_backend_storage *storage, * one we want to use. */ list_for_each_entry(bucket, &storage->buckets, bucket_list) { + totalbuckets++; + ret = bucket->read(bucket, &bucket->buf, &bucket->len); - if (ret == -EUCLEAN) + if (ret == -EUCLEAN) { bucket->needs_refresh = 1; - else if (ret) + } else if (ret) { + if (ret == -ENOMEDIUM) + zerobuckets++; continue; + } /* * Verify the buffer crcs. The buffer length is passed in the len argument, * .verify overwrites it with the length actually used. */ ret = format->verify(format, magic, bucket->buf, &bucket->len, flags); - if (!ret && !bucket_used) + if (ret == -ENOMEDIUM) + zerobuckets++; + else if (!ret && !bucket_used) bucket_used = bucket; - if (ret) + else if (ret) dev_info(storage->dev, "Ignoring broken bucket %d@0x%08llx...\n", bucket->num, (long long) bucket->offset); } dev_dbg(storage->dev, "Checking redundant buckets finished.\n"); - if (!bucket_used) { - dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); - - return -ENOENT; - } + if (!bucket_used) + return dev_err_state_init(storage->dev, zerobuckets == totalbuckets ? -ENOMEDIUM : -ENOENT, + "no valid state copy in any bucket\n"); dev_info(storage->dev, "Using bucket %d@0x%08llx\n", bucket_used->num, (long long) bucket_used->offset); @@ -326,7 +332,7 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage offset = storage->offset + n * stridesize; ret = state_backend_bucket_direct_create(storage->dev, storage->path, &bucket, offset, - stridesize); + stridesize, storage->readonly); if (ret) { dev_warn(storage->dev, "Failed to create direct bucket at '%s' offset %lld\n", storage->path, (long long) offset); diff --git a/common/state/state.c b/common/state/state.c index 469ee62d40..219309d715 100644 --- a/common/state/state.c +++ b/common/state/state.c @@ -21,8 +21,10 @@ #include <fs.h> #include <crc.h> #include <init.h> +#include <block.h> #include <linux/err.h> #include <linux/list.h> +#include <linux/uuid.h> #include <linux/mtd/mtd-abi.h> #include <malloc.h> @@ -101,8 +103,8 @@ static int state_do_load(struct state *state, enum state_flags flags) ret = state_storage_read(&state->storage, state->format, state->magic, &buf, &len, flags); if (ret) { - dev_err(&state->dev, "Failed to read state with format %s, %d\n", - state->format->name, ret); + dev_err_state_init(&state->dev, ret, "format %s read failed\n", + state->format->name); goto out; } @@ -333,8 +335,10 @@ static int state_convert_node_variable(struct state *state, goto out; return 0; - out_free:free(name); - out: return ret; +out_free: + free(name); +out: + return ret; } struct device_node *state_to_node(struct state *state, @@ -361,7 +365,8 @@ struct device_node *state_to_node(struct state *state, } return root; - out: of_delete_node(root); +out: + of_delete_node(root); return ERR_PTR(ret); } @@ -556,12 +561,12 @@ static int of_state_fixup(struct device_node *root, void *ctx) goto out; /* delete existing node */ - if (node) - of_delete_node(node); + of_delete_node(node); return 0; - out: of_delete_node(new_node); +out: + of_delete_node(new_node); return ret; } @@ -578,6 +583,22 @@ void state_release(struct state *state) free(state); } +#ifdef __BAREBOX__ +static char *cdev_to_devpath(struct cdev *cdev, off_t *offset, size_t *size) +{ + /* + * We only accept partitions exactly mapping the barebox-state, + * but dt-utils may need to set non-zero values here + */ + *offset = 0; + *size = 0; + + return basprintf("/dev/%s", cdev->name); +} +#endif + +static guid_t barebox_state_partition_guid = BAREBOX_STATE_PARTITION_GUID; + /* * state_new_from_node - create a new state instance from a device_node * @@ -594,12 +615,13 @@ struct state *state_new_from_node(struct device_node *node, bool readonly) const char *alias; uint32_t stridesize; struct device_node *partition_node; - off_t offset = 0; - size_t size = 0; + struct cdev *cdev; + off_t offset; + size_t size; alias = of_alias_get(node); if (!alias) { - pr_err("State node %s does not have an alias in the /aliases/ node\n", node->full_name); + pr_err("State node %pOF does not have an alias in the /aliases/ node\n", node); return ERR_PTR(-EINVAL); } @@ -614,15 +636,8 @@ struct state *state_new_from_node(struct device_node *node, bool readonly) goto out_release_state; } -#ifdef __BAREBOX__ - ret = of_partition_ensure_probed(partition_node); - if (ret) - goto out_release_state; - - ret = of_find_path_by_node(partition_node, &state->backend_path, 0); -#else - ret = of_get_devicepath(partition_node, &state->backend_path, &offset, &size); -#endif + cdev = of_cdev_find(partition_node); + ret = PTR_ERR_OR_ZERO(cdev); if (ret) { if (ret != -EPROBE_DEFER) dev_err(&state->dev, "state failed to parse path to backend: %s\n", @@ -630,6 +645,22 @@ struct state *state_new_from_node(struct device_node *node, bool readonly) goto out_release_state; } + /* Is the backend referencing an on-disk partitionable block device? */ + if (cdev_is_block_disk(cdev)) { + cdev = cdev_find_child_by_gpt_typeuuid(cdev, &barebox_state_partition_guid); + if (IS_ERR(cdev)) { + ret = -EINVAL; + goto out_release_state; + } + + pr_debug("%pOF: backend GPT partition looked up via PartitionTypeGUID\n", + node); + } + + state->backend_path = cdev_to_devpath(cdev, &offset, &size); + + pr_debug("%pOF: backend resolved to %s\n", node, state->backend_path); + state->backend_reproducible_name = of_get_reproducible_name(partition_node); ret = of_property_read_string(node, "backend-type", &backend_type); @@ -653,14 +684,14 @@ struct state *state_new_from_node(struct device_node *node, bool readonly) if (ret) goto out_release_state; + if (readonly) + state_backend_set_readonly(state); + ret = state_storage_init(state, state->backend_path, offset, size, stridesize, storage_type); if (ret) goto out_release_state; - if (readonly) - state_backend_set_readonly(state); - ret = state_from_node(state, node, 1); if (ret) { goto out_release_state; @@ -704,10 +735,12 @@ struct state *state_by_name(const char *name) * * @node The of node of the state instance */ -struct state *state_by_node(const struct device_node *node) +struct state *state_by_node(struct device_node *node) { struct state *state; + of_device_ensure_probed(node); + list_for_each_entry(state, &state_list, list) { if (!strcmp(state->of_path, node->full_name)) return state; @@ -716,6 +749,22 @@ struct state *state_by_node(const struct device_node *node) return NULL; } +/* + * state_by_alias - find a state instance by alias + * + * @name The DT alias of the state instance + */ +struct state *state_by_alias(const char *alias) +{ + struct device_node *node; + + node = of_find_node_by_alias(NULL, alias); + if (!node) + return NULL; + + return state_by_node(node); +} + int state_read_mac(struct state *state, const char *name, u8 *buf) { struct state_variable *svar; diff --git a/common/state/state.h b/common/state/state.h index 1881d92ea7..f0c5b1de41 100644 --- a/common/state/state.h +++ b/common/state/state.h @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <linux/types.h> #include <linux/list.h> +#include <linux/err.h> #include <driver.h> struct state; @@ -87,7 +90,7 @@ struct state_backend_storage { struct list_head buckets; /* For outputs */ - struct device_d *dev; + struct device *dev; const char *name; @@ -102,7 +105,7 @@ struct state_backend_storage { struct state { struct list_head list; /* Entry to enqueue on list of states */ - struct device_d dev; + struct device dev; char *of_path; const char *name; uint32_t magic; @@ -199,21 +202,21 @@ struct device_node *state_to_node(struct state *state, enum state_convert conv); int backend_format_raw_create(struct state_backend_format **format, struct device_node *node, const char *secret_name, - struct device_d *dev); + struct device *dev); int backend_format_dtb_create(struct state_backend_format **format, - struct device_d *dev); + struct device *dev); int state_storage_init(struct state *state, const char *path, off_t offset, size_t max_size, uint32_t stridesize, const char *storagetype); void state_storage_set_readonly(struct state_backend_storage *storage); void state_add_var(struct state *state, struct state_variable *var); struct variable_type *state_find_type_by_name(const char *name); -int state_backend_bucket_circular_create(struct device_d *dev, const char *path, +int state_backend_bucket_circular_create(struct device *dev, const char *path, struct state_backend_storage_bucket **bucket, unsigned int eraseblock, ssize_t writesize, struct mtd_info_user *mtd_uinfo); -int state_backend_bucket_cached_create(struct device_d *dev, +int state_backend_bucket_cached_create(struct device *dev, struct state_backend_storage_bucket *raw, struct state_backend_storage_bucket **out); struct state_variable *state_find_var(struct state *state, const char *name); @@ -221,9 +224,9 @@ struct digest *state_backend_format_raw_get_digest(struct state_backend_format *format); void state_backend_set_readonly(struct state *state); void state_storage_free(struct state_backend_storage *storage); -int state_backend_bucket_direct_create(struct device_d *dev, const char *path, +int state_backend_bucket_direct_create(struct device *dev, const char *path, struct state_backend_storage_bucket **bucket, - off_t offset, ssize_t max_size); + off_t offset, ssize_t max_size, bool readonly); int state_storage_write(struct state_backend_storage *storage, const void * buf, ssize_t len); int state_storage_read(struct state_backend_storage *storage, @@ -266,3 +269,16 @@ static inline int state_string_copy_to_raw(struct state_string *string, return 0; } + +#ifdef DEBUG +#define MSG_STATE_ZERO_INIT MSG_INFO +#else +#define MSG_STATE_ZERO_INIT MSG_DEBUG +#endif + +#define dev_err_state_init(dev, ret, fmt, ...) ({ \ + int __ret = (ret); \ + __dev_printf(__ret == -ENOMEDIUM ? MSG_STATE_ZERO_INIT : MSG_ERR, \ + (dev), "init error: %pe: " fmt, ERR_PTR(__ret), ##__VA_ARGS__); \ + __ret; \ +}) diff --git a/common/state/state_variables.c b/common/state/state_variables.c index 66c66f38bd..77946206cd 100644 --- a/common/state/state_variables.c +++ b/common/state/state_variables.c @@ -21,7 +21,7 @@ #include <linux/types.h> #include <malloc.h> #include <net.h> -#include <printk.h> +#include <linux/printk.h> #include <of.h> #include <stdio.h> @@ -180,6 +180,8 @@ static int state_enum32_export(struct state_variable *var, str += sprintf(str, "%s", enum32->names[i]) + 1; ret = of_set_property(node, "names", prop, len, 1); + if (ret) + return ret; free(prop); @@ -261,7 +263,8 @@ static struct state_variable *state_enum32_create(struct state *state, } return &enum32->var; - out: for (i--; i >= 0; i--) +out: + for (i--; i >= 0; i--) free((char *)enum32->names[i]); free(enum32->names); free(enum32); @@ -327,7 +330,8 @@ static struct state_variable *state_mac_create(struct state *state, } return &mac->var; - out: free(mac); +out: + free(mac); return ERR_PTR(ret); } @@ -442,7 +446,8 @@ static struct state_variable *state_string_create(struct state *state, } return &string->var; - out: free(string); +out: + free(string); return ERR_PTR(ret); } diff --git a/common/tlsf.c b/common/tlsf.c index 4247a9d3c7..ba2ed367c0 100644 --- a/common/tlsf.c +++ b/common/tlsf.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -28,13 +30,8 @@ enum tlsf_public /* Private constants: do not modify. */ enum tlsf_private { -#if defined (TLSF_64BIT) /* All allocation sizes and addresses are aligned to 8 bytes. */ ALIGN_SIZE_LOG2 = 3, -#else - /* All allocation sizes and addresses are aligned to 4 bytes. */ - ALIGN_SIZE_LOG2 = 2, -#endif ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2), /* @@ -99,6 +96,8 @@ tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT); /* Ensure we've properly tuned our sizes. */ tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT); +tlsf_static_assert(ALIGN_SIZE >= CONFIG_MALLOC_ALIGNMENT); + /* ** Data structures and associated constants. */ @@ -120,6 +119,7 @@ typedef struct block_header_t /* The size of this block, excluding the block header. */ size_t size; + u32 : BYTES_TO_BITS(ALIGN_SIZE - sizeof(size_t)); /* Next and previous free blocks. */ struct block_header_t* next_free; @@ -132,28 +132,29 @@ typedef struct block_header_t ** - bit 0: whether block is busy or free ** - bit 1: whether previous block is busy or free */ -static const size_t block_header_free_bit = 1 << 0; -static const size_t block_header_prev_free_bit = 1 << 1; +#define block_header_free_bit (1 << 0) +#define block_header_prev_free_bit (1 << 1) /* ** The size of the block header exposed to used blocks is the size field. ** The prev_phys_block field is stored *inside* the previous free block. */ -static const size_t block_header_overhead = sizeof(size_t); +#define block_header_shift offsetof(block_header_t, size) +#define block_header_overhead ALIGN_SIZE /* User data starts directly after the size field in a used block. */ -static const size_t block_start_offset = - offsetof(block_header_t, size) + sizeof(size_t); +#define block_start_offset (block_header_shift + block_header_overhead) /* ** A free block must be large enough to store its header minus the size of ** the prev_phys_block field, and no larger than the number of addressable ** bits for FL_INDEX. */ -static const size_t block_size_min = - sizeof(block_header_t) - sizeof(block_header_t*); -static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX; +#define block_size_min (sizeof(block_header_t) - sizeof(block_header_t*)) +#define block_size_max (tlsf_cast(size_t, 1) << FL_INDEX_MAX) +tlsf_static_assert(block_size_min % ALIGN_SIZE == 0); +tlsf_static_assert(block_size_max % ALIGN_SIZE == 0); /* The TLSF control structure. */ typedef struct control_t @@ -164,10 +165,12 @@ typedef struct control_t /* Bitmaps for free lists. */ unsigned int fl_bitmap; unsigned int sl_bitmap[FL_INDEX_COUNT]; + u32 : BYTES_TO_BITS(ALIGN_SIZE - sizeof(size_t)); /* Head of free lists. */ block_header_t* blocks[FL_INDEX_COUNT][SL_INDEX_COUNT]; } control_t; +tlsf_static_assert(sizeof(control_t) % ALIGN_SIZE == 0); /* A type used for casting when doing pointer arithmetic. */ typedef ptrdiff_t tlsfptr_t; @@ -251,7 +254,7 @@ static block_header_t* block_prev(const block_header_t* block) static block_header_t* block_next(const block_header_t* block) { block_header_t* next = offset_to_block(block_to_ptr(block), - block_size(block) - block_header_overhead); + block_size(block) - block_header_shift); tlsf_assert(!block_is_last(block)); return next; } @@ -311,7 +314,7 @@ static size_t adjust_request_size(size_t size, size_t align) const size_t aligned = align_up(size, align); /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */ - if (aligned < block_size_max) + if (aligned >= size && aligned < block_size_max) { adjust = tlsf_max(aligned, block_size_min); } @@ -462,7 +465,7 @@ static block_header_t* block_split(block_header_t* block, size_t size) { /* Calculate the amount of space left in the remaining block. */ block_header_t* remaining = - offset_to_block(block_to_ptr(block), size - block_header_overhead); + offset_to_block(block_to_ptr(block), size - block_header_shift); const size_t remain_size = block_size(block) - (size + block_header_overhead); @@ -728,7 +731,7 @@ void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) { tlsf_walker pool_walker = walker ? walker : default_walker; block_header_t* block = - offset_to_block(pool, -(int)block_header_overhead); + offset_to_block(pool, -(int)block_header_shift); while (block && !block_is_last(block)) { @@ -763,11 +766,11 @@ int tlsf_check_pool(pool_t pool) /* ** Size of the TLSF structures in a given memory block passed to -** tlsf_create, equal to the size of a control_t +** tlsf_create, equal to aligned size of a control_t */ size_t tlsf_size(void) { - return sizeof(control_t); + return align_up(sizeof(control_t), ALIGN_SIZE); } size_t tlsf_align_size(void) @@ -834,7 +837,7 @@ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) ** so that the prev_phys_block field falls outside of the pool - ** it will never be used. */ - block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block = offset_to_block(mem, -(tlsfptr_t)block_header_shift); block_set_size(block, pool_bytes); block_set_free(block); block_set_prev_used(block); @@ -852,7 +855,7 @@ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) void tlsf_remove_pool(tlsf_t tlsf, pool_t pool) { control_t* control = tlsf_cast(control_t*, tlsf); - block_header_t* block = offset_to_block(pool, -(int)block_header_overhead); + block_header_t* block = offset_to_block(pool, -(int)block_header_shift); int fl = 0, sl = 0; @@ -940,7 +943,12 @@ void* tlsf_malloc(tlsf_t tlsf, size_t size) { control_t* control = tlsf_cast(control_t*, tlsf); const size_t adjust = adjust_request_size(size, ALIGN_SIZE); - block_header_t* block = block_locate_free(control, adjust); + block_header_t* block; + + if (!adjust) + return NULL; + + block = block_locate_free(control, adjust); return block_prepare_used(control, block, adjust, size); } @@ -967,10 +975,15 @@ void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) */ const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust; - block_header_t* block = block_locate_free(control, aligned_size); + block_header_t* block; + + if (!adjust || !size_with_gap) + return NULL; + + block = block_locate_free(control, aligned_size); /* This can't be a static assert. */ - tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); + tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_shift); if (block) { @@ -1057,6 +1070,9 @@ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) tlsf_assert(!block_is_free(block) && "block already marked as free"); + if (!adjust) + return NULL; + /* ** If the next block is used, or when combined with the current ** block, does not offer enough space, we must reallocate and copy. diff --git a/common/tlsfbits.h b/common/tlsfbits.h index edbac80636..8633fb5959 100644 --- a/common/tlsfbits.h +++ b/common/tlsfbits.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #ifndef INCLUDED_tlsfbits #define INCLUDED_tlsfbits diff --git a/common/ubiformat.c b/common/ubiformat.c index e10ce31ce6..d8399ad9d6 100644 --- a/common/ubiformat.c +++ b/common/ubiformat.c @@ -444,7 +444,7 @@ static int format(struct ubiformat_args *args, struct mtd_info *mtd, } if (!args->quiet && !args->verbose) - printf("\n"); + printf("\rubiformat: formatted all eraseblocks -- 100 %% complete\n"); if (!novtbl) { if (eb1 == -1 || eb2 == -1) { @@ -745,6 +745,7 @@ int ubiformat_write(struct mtd_info *mtd, const void *buf, size_t count, while (count) { size_t now = mtd->erasesize - offset_in_peb; + int new_len; if (now > count) now = count; @@ -780,7 +781,8 @@ int ubiformat_write(struct mtd_info *mtd, const void *buf, size_t count, } } - ret = mtd_peb_write(mtd, buf, peb, offset_in_peb, now); + new_len = drop_ffs(mtd, buf, now); + ret = mtd_peb_write(mtd, buf, peb, offset_in_peb, new_len); if (ret < 0) return ret; diff --git a/common/uimage.c b/common/uimage.c index 42e9d9023f..cc9e5e510a 100644 --- a/common/uimage.c +++ b/common/uimage.c @@ -29,17 +29,19 @@ static inline int uimage_is_multi_image(struct uimage_handle *handle) void uimage_print_contents(struct uimage_handle *handle) { struct image_header *hdr = &handle->header; -#if defined(CONFIG_TIMESTAMP) - struct rtc_time tm; -#endif + printf(" Image Name: %.*s\n", IH_NMLEN, hdr->ih_name); -#if defined(CONFIG_TIMESTAMP) - printf(" Created: "); - to_tm(hdr->ih_time, &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); -#endif + + if (IS_ENABLED(CONFIG_TIMESTAMP)) { + struct rtc_time tm; + + printf(" Created: "); + to_tm(hdr->ih_time, &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); + } + #if defined(CONFIG_BOOTM_SHOW_TYPE) printf(" OS: %s\n", image_get_os_name(hdr->ih_os)); printf(" Architecture: %s\n", image_get_arch_name(hdr->ih_arch)); @@ -98,7 +100,7 @@ struct uimage_handle *uimage_open(const char *filename) fd = open(filename, O_RDONLY); if (fd < 0) { - printf("could not open: %s\n", errno_str()); + printf("could not open: %m\n"); free(copy); return NULL; } @@ -109,7 +111,7 @@ struct uimage_handle *uimage_open(const char *filename) handle->copy = copy; if (read(fd, header, sizeof(*header)) < 0) { - printf("could not read: %s\n", errno_str()); + printf("could not read: %m\n"); goto err_out; } @@ -216,23 +218,23 @@ EXPORT_SYMBOL(uimage_close); static int uimage_fd; -static int uimage_fill(void *buf, unsigned int len) +static long uimage_fill(void *buf, unsigned long len) { return read_full(uimage_fd, buf, len); } -static int uncompress_copy(unsigned char *inbuf_unused, int len, - int(*fill)(void*, unsigned int), - int(*flush)(void*, unsigned int), +static int uncompress_copy(unsigned char *inbuf_unused, long len, + long(*fill)(void*, unsigned long), + long(*flush)(void*, unsigned long), unsigned char *outbuf_unused, - int *pos, + long *pos, void(*error_fn)(char *x)) { int ret; void *buf = xmalloc(PAGE_SIZE); while (len) { - int now = min(len, PAGE_SIZE); + int now = min_t(long, len, PAGE_SIZE); ret = fill(buf, now); if (ret < 0) goto err; @@ -293,17 +295,17 @@ EXPORT_SYMBOL(uimage_verify); * Load a uimage, flushing output to flush function */ int uimage_load(struct uimage_handle *handle, unsigned int image_no, - int(*flush)(void*, unsigned int)) + long(*flush)(void*, unsigned long)) { image_header_t *hdr = &handle->header; struct uimage_handle_data *iha; int ret; loff_t off; - int (*uncompress_fn)(unsigned char *inbuf, int len, - int(*fill)(void*, unsigned int), - int(*flush)(void*, unsigned int), + int (*uncompress_fn)(unsigned char *inbuf, long len, + long(*fill)(void*, unsigned long), + long(*flush)(void*, unsigned long), unsigned char *output, - int *pos, + long *pos, void(*error)(char *x)); if (image_no >= handle->nb_data_entries) @@ -334,7 +336,7 @@ static void *uimage_buf; static size_t uimage_size; static struct resource *uimage_resource; -static int uimage_sdram_flush(void *buf, unsigned int len) +static long uimage_sdram_flush(void *buf, unsigned long len) { if (uimage_size + len > resource_size(uimage_resource)) { resource_size_t start = uimage_resource->start; diff --git a/common/usbgadget.c b/common/usbgadget.c index e8c9f7d236..3713551163 100644 --- a/common/usbgadget.c +++ b/common/usbgadget.c @@ -12,14 +12,15 @@ #include <getopt.h> #include <fs.h> #include <xfuncs.h> -#include <usb/usbserial.h> -#include <usb/dfu.h> -#include <usb/gadget-multi.h> +#include <linux/usb/usbserial.h> +#include <linux/usb/dfu.h> +#include <linux/usb/gadget-multi.h> #include <globalvar.h> #include <magicvar.h> #include <system-partitions.h> static int autostart; +static int nv_loaded; static int acm; static char *dfu_function; @@ -27,16 +28,14 @@ static inline struct file_list *get_dfu_function(void) { if (dfu_function && *dfu_function) return file_list_parse_null(dfu_function); - if (!system_partitions_empty()) - return system_partitions_get(); - return NULL; + return system_partitions_get_null(); } int usbgadget_register(const struct usbgadget_funcs *funcs) { int ret; int flags = funcs->flags; - struct device_d *dev; + struct device *dev; struct f_multi_opts *opts; opts = xzalloc(sizeof(*opts)); @@ -54,7 +53,7 @@ int usbgadget_register(const struct usbgadget_funcs *funcs) opts->ums_opts.files = file_list_parse_null(funcs->ums_opts); if (IS_ENABLED(CONFIG_USB_GADGET_MASS_STORAGE) && file_list_empty(opts->ums_opts.files)) { file_list_free(opts->ums_opts.files); - opts->ums_opts.files = system_partitions_get(); + opts->ums_opts.files = system_partitions_get_null(); } } @@ -100,13 +99,20 @@ err: return ret; } -static int usbgadget_autostart_set(struct param_d *param, void *ctx) +static int usbgadget_do_autostart(void) { struct usbgadget_funcs funcs = {}; static bool started; int err; - if (!autostart || started) + if (!IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) + return 0; + + /* + * We want to run this function exactly once when the + * environment is loaded and autostart is requested + */ + if (!nv_loaded || !autostart || started) return 0; if (get_fastboot_bbu()) @@ -123,19 +129,38 @@ static int usbgadget_autostart_set(struct param_d *param, void *ctx) return err; } +static int usbgadget_autostart_set(struct param_d *param, void *ctx) +{ + usbgadget_do_autostart(); + + return 0; +} + +void usbgadget_autostart(bool enable) +{ + autostart = enable; + + usbgadget_do_autostart(); +} + static int usbgadget_globalvars_init(void) { globalvar_add_simple_bool("usbgadget.acm", &acm); globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function); + if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) + globalvar_add_bool("usbgadget.autostart", usbgadget_autostart_set, + &autostart, NULL); return 0; } -device_initcall(usbgadget_globalvars_init); +coredevice_initcall(usbgadget_globalvars_init); static int usbgadget_autostart_init(void) { - if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) - globalvar_add_bool("usbgadget.autostart", usbgadget_autostart_set, &autostart, NULL); + nv_loaded = true; + + usbgadget_do_autostart(); + return 0; } postenvironment_initcall(usbgadget_autostart_init); diff --git a/common/version.c b/common/version.c index 54cec5335d..0cac5ee609 100644 --- a/common/version.c +++ b/common/version.c @@ -1,9 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <generated/compile.h> #include <generated/utsrelease.h> const char version_string[] = - "barebox " UTS_RELEASE " " UTS_VERSION "\n"; + "barebox " UTS_RELEASE " " UTS_VERSION; EXPORT_SYMBOL(version_string); const char release_string[] = @@ -18,9 +20,9 @@ EXPORT_SYMBOL(buildsystem_version_string); void barebox_banner (void) { printf("\n\n"); - pr_info("%s", version_string); + pr_info("%s\n", version_string); if (strlen(buildsystem_version_string) > 0) - pr_info("Buildsystem version: %s", buildsystem_version_string); + pr_info("Buildsystem version: %s\n", buildsystem_version_string); printf("\n\n"); pr_info("Board: %s\n", barebox_get_model()); } |