summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/.gitignore2
-rw-r--r--common/Kconfig736
-rw-r--r--common/Makefile35
-rw-r--r--common/bbu.c241
-rw-r--r--common/binfmt.c9
-rw-r--r--common/block.c130
-rw-r--r--common/blspec.c350
-rw-r--r--common/boards/Kconfig16
-rw-r--r--common/boards/Makefile5
-rw-r--r--common/boards/phytec/Makefile5
-rw-r--r--common/boards/phytec/phytec-som-detection.c209
-rw-r--r--common/boards/phytec/phytec-som-imx8m-detection.c151
-rw-r--r--common/boards/qemu-virt/Makefile13
-rw-r--r--common/boards/qemu-virt/board.c90
-rw-r--r--common/boards/qemu-virt/fitimage-pubkey.dts7
-rw-r--r--common/boards/qemu-virt/qemu-virt-flash.dtso99
-rw-r--r--common/boards/qemu-virt/qemu-virt-flash.h22
-rw-r--r--common/boards/tq/Makefile1
-rw-r--r--common/boards/tq/tq_eeprom.c139
-rw-r--r--common/boot.c83
-rw-r--r--common/bootargs.c17
-rw-r--r--common/bootchooser.c70
-rw-r--r--common/booti.c93
-rw-r--r--common/bootm.c449
-rw-r--r--common/bootsource.c159
-rw-r--r--common/bthread.c246
-rw-r--r--common/calloc.c2
-rw-r--r--common/clock.c30
-rw-r--r--common/command.c12
-rw-r--r--common/complete.c152
-rw-r--r--common/console.c107
-rw-r--r--common/console_common.c144
-rw-r--r--common/console_countdown.c13
-rw-r--r--common/console_simple.c9
-rw-r--r--common/date.c12
-rw-r--r--common/ddr1_dimm_params.c323
-rw-r--r--common/ddr2_dimm_params.c326
-rw-r--r--common/ddr3_dimm_params.c328
-rw-r--r--common/ddr4_dimm_params.c355
-rw-r--r--common/ddr_spd.c620
-rw-r--r--common/deep-probe.c41
-rw-r--r--common/dlmalloc.c36
-rw-r--r--common/dummy_malloc.c23
-rw-r--r--common/efi-devicepath.c1195
-rw-r--r--common/efi-guid.c93
-rw-r--r--common/efi/Makefile3
-rw-r--r--common/efi/efi-image.c289
-rw-r--r--common/efi/efi.c481
-rw-r--r--common/efi/env-efi/network/eth0-discover5
-rw-r--r--common/elf.c281
-rw-r--r--common/env.c72
-rw-r--r--common/envfs-core.c17
-rw-r--r--common/environment.c101
-rw-r--r--common/fastboot.c1001
-rw-r--r--common/file-list.c175
-rw-r--r--common/filetype.c102
-rw-r--r--common/firmware.c152
-rw-r--r--common/globalvar.c167
-rw-r--r--common/hush.c69
-rw-r--r--common/image-fit.c326
-rw-r--r--common/image.c12
-rw-r--r--common/imd-barebox.c13
-rw-r--r--common/imd.c49
-rw-r--r--common/imx-bbu-nand-fcb.c570
-rw-r--r--common/kallsyms.c12
-rw-r--r--common/machine_id.c105
-rw-r--r--common/meminfo.c8
-rw-r--r--common/memory.c128
-rw-r--r--common/memory_display.c6
-rw-r--r--common/memsize.c14
-rw-r--r--common/memtest.c55
-rw-r--r--common/menu.c12
-rw-r--r--common/menutree.c25
-rw-r--r--common/misc.c110
-rw-r--r--common/module.c80
-rw-r--r--common/module.lds.S2
-rw-r--r--common/oftree.c224
-rw-r--r--common/optee.c52
-rw-r--r--common/parser.c4
-rw-r--r--common/partitions.c251
-rw-r--r--common/partitions/Kconfig3
-rw-r--r--common/partitions/Makefile2
-rw-r--r--common/partitions/dos.c380
-rw-r--r--common/partitions/efi.c357
-rw-r--r--common/partitions/efi.h119
-rw-r--r--common/partitions/parser.h39
-rw-r--r--common/password.c28
-rw-r--r--common/pe.c379
-rw-r--r--common/poller.c97
-rw-r--r--common/poweroff.c11
-rw-r--r--common/ratp/Kconfig3
-rw-r--r--common/ratp/Makefile2
-rw-r--r--common/ratp/getenv.c3
-rw-r--r--common/ratp/i2c.c4
-rw-r--r--common/ratp/md.c4
-rw-r--r--common/ratp/mw.c1
-rw-r--r--common/ratp/ratp.c179
-rw-r--r--common/reset_source.c39
-rw-r--r--common/resource.c51
-rw-r--r--common/restart.c67
-rw-r--r--common/s_record.c12
-rw-r--r--common/sched.c26
-rw-r--r--common/serdev.c12
-rw-r--r--common/slice.c335
-rw-r--r--common/startup.c209
-rw-r--r--common/state/Makefile2
-rw-r--r--common/state/backend_bucket_circular.c4
-rw-r--r--common/state/backend_bucket_direct.c28
-rw-r--r--common/state/backend_format_dtb.c6
-rw-r--r--common/state/backend_format_raw.c17
-rw-r--r--common/state/backend_storage.c28
-rw-r--r--common/state/state.c96
-rw-r--r--common/state/state.h32
-rw-r--r--common/state/state_variables.c13
-rw-r--r--common/system-partitions.c44
-rw-r--r--common/tlsf.c476
-rw-r--r--common/tlsf_malloc.c17
-rw-r--r--common/tlsfbits.h2
-rw-r--r--common/ubiformat.c17
-rw-r--r--common/uimage.c81
-rw-r--r--common/usbgadget.c197
-rw-r--r--common/version.c12
-rw-r--r--common/workqueue.c61
123 files changed, 10895 insertions, 4771 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 460ac487cb..0000dac874 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1,7 +1,6 @@
-config DEFCONFIG_LIST
- string
- option defconfig_list
- default "arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)"
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "common/boards/Kconfig"
config GREGORIAN_CALENDER
bool
@@ -15,7 +14,7 @@ config HAS_MODULES
config HAS_CACHE
bool
help
- This allows you do run "make ARCH=sandbox allyesconfig".
+ This allows you to run "make ARCH=sandbox allyesconfig".
Drivers that depend on a cache implementation can depend on this
config, so that you don't get a compilation error.
@@ -23,11 +22,16 @@ config HAS_CACHE
config HAS_DMA
bool
help
- This allows you do run "make ARCH=sandbox allyesconfig".
+ This allows you to run "make ARCH=sandbox allyesconfig".
Drivers that depend on a DMA implementation can depend on this
config, so that you don't get a compilation error.
+config ARCH_HAS_SJLJ
+ bool
+ help
+ Architecture has support implemented for setjmp()/longjmp()/initjmp()
+
config GENERIC_GPIO
bool
@@ -37,10 +41,6 @@ config BLOCK
config BLOCK_WRITE
bool
-config ELF
- bool "ELF Support" if COMPILE_TEST
- depends on MIPS || COMPILE_TEST
-
config FILETYPE
bool
@@ -73,23 +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 FILE_LIST
- 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
@@ -107,10 +96,13 @@ config USBGADGET_START
bool
depends on CMD_USBGADGET || USB_GADGET_AUTOSTART
select ENVIRONMENT_VARIABLES
- select FILE_LIST
default y
config BOOT
+ select GLOB
+ bool
+
+config FASTBOOT_BASE
bool
menu "General Settings"
@@ -152,6 +144,9 @@ config MEMINFO
bool "display memory info"
default y
+config MEMTEST
+ bool
+
config ENVIRONMENT_VARIABLES
bool "environment variables support"
@@ -193,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
@@ -209,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"
@@ -284,6 +277,11 @@ config MALLOC_SIZE
hex
default 0x400000
prompt "malloc area size"
+
+config MALLOC_ALIGNMENT
+ hex
+ default 8
+
endmenu
config BROKEN
@@ -310,18 +308,33 @@ config MALLOC_DUMMY
memory is never freed. This is suitable for well tested noninteractive
environments only.
+config MALLOC_LIBC
+ bool "libc malloc"
+ depends on SANDBOX
+ help
+ select this option to use the libc malloc implementation in the sandbox.
+ This is benefecial for testing with external memory integrity tools.
+
endchoice
config MODULES
depends on HAS_MODULES
depends on EXPERIMENTAL
bool "module support"
+ modules
help
This option enables support for loadable modules via insmod. Module
support is quite experimental at the moment. There is no convenient
way to compile modules and the list of exported symbols to actually
make use of modules is short to nonexistent
+config HAVE_MOD_ARCH_SPECIFIC
+ bool
+ help
+ The arch uses struct mod_arch_specific to store data. Many arches
+ just need a simple module loader without arch specific data - those
+ should not enable this.
+
config KALLSYMS
depends on HAS_KALLSYMS
bool "kallsyms"
@@ -330,7 +343,7 @@ config KALLSYMS
This is useful to print a nice backtrace when an exception occurs.
config RELOCATABLE
- depends on PPC || ARM
+ depends on PPC || ARM || RISCV
bool "generate relocatable barebox binary"
help
A non relocatable barebox binary will run at it's compiled in
@@ -340,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
@@ -384,6 +421,7 @@ choice
select PARAMETER
select BINFMT
select STDDEV
+ select GLOB
help
Enable hush support. This is the most advanced shell available
for barebox.
@@ -394,6 +432,8 @@ choice
select COMMAND_SUPPORT
select PARAMETER
select STDDEV
+ select CMD_SETENV
+ select GLOB
help
simple shell. No if/then, no return values from commands, no loops
@@ -508,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"
@@ -526,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
@@ -593,13 +623,27 @@ 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
+ select ELF
+ prompt "elf loading support"
+ help
+ Add support to load elf file with bootm.
+
config BOOTM_FITIMAGE
bool
prompt "FIT image support"
select FITIMAGE
depends on BOOTM && ARM
help
- Support using Flattened Image Tree (FIT) Images. FIT is an image
+ Support using Flattened Image Tree (FIT) images. FIT is an image
format introduced by U-Boot. A FIT image contains one or multiple
kernels, device trees and initrds. The FIT image itself is a flattened
device tree binary. Have a look at the u-boot source tree
@@ -618,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"
@@ -627,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"
@@ -645,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
@@ -664,26 +730,50 @@ config FLEXIBLE_BOOTARGS
to replace parts of the bootargs string without reconstructing it
completely.
+config MMCBLKDEV_ROOTARG
+ bool
+ prompt "Support 'root=mmcblkXpN' cmdline appending"
+ depends on FLEXIBLE_BOOTARGS
+ depends on MCI
+ depends on OFTREE
+ help
+ Enable this option to append 'root=mmcblkXpN' to the cmdline instead
+ of 'root=PARTUUID=XYZ'. Don't enable this option if your used linux
+ kernel doesn't contain commit [1]. The first linux kernel release
+ containing that commit is v5.10-rc1.
+
+ 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
+ tree use the same mmc aliases.
+
+ [1] fa2d0aa96941 ("mmc: core: Allow setting slot index via device tree
+ alias")
+
config BAREBOX_UPDATE
bool "In-system barebox update infrastructure"
+config SYSTEM_PARTITIONS
+ bool "Generic system partitions support"
+ depends on GLOBALVAR
+ help
+ System partitions are a generic way for boards to specify the
+ partitions that should be exported for flashing.
+ Board drivers that set this directly will select this option
+ automatically.
+ Say y here if this should be configurable over the
+ global.system.partitions device parameter as well.
+
config IMD
select CRC32
bool "barebox metadata support"
-config IMD_TARGET
- bool "build bareboximd target tool"
+config IMD_ENDIANNESS
+ bool "add endianness record to metadata"
depends on IMD
- depends on !SANDBOX
-
-config KERNEL_INSTALL_TARGET
- bool
- depends on !SANDBOX
- prompt "Build kernel-install utility for the target"
- help
- Enable this to compile the kernel-install script using the cross
- compiler. The utility for the target will be under
- scripts/kernel-install-target
+ depends on SYS_SUPPORTS_LITTLE_ENDIAN && SYS_SUPPORTS_BIG_ENDIAN
+ default y
choice
prompt "console support"
@@ -750,6 +840,27 @@ 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
+ help
+ If enabled, all consoles are initially configured to not accept any input,
+ making the consoles effectively non-interactive.
+ The active device parameter can be used to override this on a per-console
+ basis.
+ CAUTION: this will also disable input devices by default, since they are
+ registered as consoles.
+
config PBL_CONSOLE
depends on PBL_IMAGE
depends on !CONSOLE_NONE
@@ -767,6 +878,9 @@ config PARTITION
bool
prompt "Enable Partitions"
+config PARTITION_MANIPULATION
+ bool
+
source "common/partitions/Kconfig"
config ENV_HANDLING
@@ -780,6 +894,7 @@ config ENV_HANDLING
startup) will bring them back. If unsure, say yes.
config DEFAULT_ENVIRONMENT
+ select CRC32
bool
default y if ENV_HANDLING
prompt "Compile in default environment"
@@ -797,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"
@@ -849,6 +965,13 @@ config DEFAULT_ENVIRONMENT_GENERIC_NEW
select NET_CMD_IFUP if NET
select CMD_IP_ROUTE_GET if NET
select CMD_HOST if NET
+ help
+ With this option barebox will use the files found under
+ defaultenv/defaultenv-2-base/ in the source tree as a template for
+ the defaultenv. The directories specified in DEFAULT_ENVIRONMENT_PATH
+ will be added to the default environment. If a file is present in
+ both locations, the file from DEFAULT_ENVIRONMENT_PATH will overwrite
+ that from the template.
config DEFAULT_ENVIRONMENT_GENERIC
bool "Generic environment template (old version)"
@@ -862,9 +985,12 @@ config DEFAULT_ENVIRONMENT_GENERIC
select CMD_CRC_CMP
select CMD_GLOBAL
help
- With this option barebox will use the generic default
- environment found under defaultenv/ in the src tree.
- The Directory given with DEFAULT_ENVIRONMENT_PATH
+ Note: this option is not recommended for new boards; use
+ DEFAULT_ENVIRONMENT_GENERIC_NEW instead.
+
+ With this option barebox will use the old generic default environment
+ found under defaultenv/defaultenv-1/ in the source tree.
+ The directory given with DEFAULT_ENVIRONMENT_PATH
will be added to the default environment. This should
at least contain a /env/config file.
This will be able to overwrite the files from defaultenv.
@@ -874,40 +1000,56 @@ config DEFAULT_ENVIRONMENT_GENERIC_NEW_MENU
depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
depends on CMD_MENUTREE
default y
+ help
+ Extend the defaultenv template with a menu that is displayed at boot.
+ The menu files are taken from defaultenv/defaultenv-2-menu/.
config DEFAULT_ENVIRONMENT_GENERIC_NEW_DFU
bool
depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
depends on USB_GADGET_DFU
default y
+ help
+ Extend the defaultenv template with the 'dfu' boot entry, which
+ allows uploading the kernel and oftree over USB via the dfu protocol.
+
+config DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE
+ bool "Generic reboot-mode handlers in the environment"
+ depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
+ depends on REBOOT_MODE
+
+config DEFAULT_ENVIRONMENT_GENERIC_NEW_IKCONFIG
+ bool "Ship .config as /env/data/config"
+ depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
+ help
+ This option embeds the used barebox Kconfig .config file into the
+ environment as /env/data/config. This will increase barebox' image
+ size. If unsure, say n here.
config DEFAULT_ENVIRONMENT_PATH
string
depends on DEFAULT_ENVIRONMENT
prompt "Default environment path"
help
- Space separated list of paths the default environment will be taken from.
- Relative paths will be relative to the barebox Toplevel dir, but absolute
- paths are fine as well.
-
-config BAREBOXENV_TARGET
- bool
- depends on !SANDBOX
- prompt "build bareboxenv tool for target"
- help
- 'bareboxenv' is a tool to access the barebox environment from a running Linux
- system. Say yes here to build it for the target.
+ Space separated list of paths from which the default environment will
+ be taken. Relative paths will be relative to the barebox top-level
+ directory, but absolute paths are fine as well.
-config BAREBOXCRC32_TARGET
+config HAS_SCHED
bool
- prompt "build bareboxcrc32 tool for target"
- depends on !SANDBOX
- help
- 'bareboxcrc32' is a userspacetool to generate the crc32 checksums the same way
- barebox does. Say yes here to build it for the target.
config POLLER
bool "generic polling infrastructure"
+ select HAS_SCHED
+
+config BTHREAD
+ bool "barebox co-operative (green) thread infrastructure"
+ select HAS_SCHED
+ 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
+ execute actions, like checking for link up or feeding a watchdog.
config STATE
bool "generic state infrastructure"
@@ -915,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"
@@ -923,7 +1072,7 @@ config STATE_CRYPTO
select DIGEST
select DIGEST_HMAC_GENERIC
help
- This options enables HMAC based authentication support for
+ This option enables HMAC based authentication support for
the state's header and data. This means the state framework
can verify both the data integrity and the authentication of
the state's header and data.
@@ -959,39 +1108,92 @@ config RESET_SOURCE
useful for any kind of system recovery or repair.
config MACHINE_ID
- bool "pass machine-id to kernel"
+ bool "compute unique machine-id"
depends on FLEXIBLE_BOOTARGS
- depends on SHA1
+ depends on HAVE_DIGEST_SHA1
help
- Sets the linux.bootargs.machine_id global variable with a value of
- systemd.machine_id=UID. The UID is a persistent device-specific
- id. It is a hash over device-specific information provided by various
- sources.
+ Compute a persistent machine-specific id and store it to $global.machine_id.
+ The id is a hash of device-specific information added via
+ machine_id_set_hashable(). If multiple sources are available, the
+ information provided by the last call prior to the late initcall
+ set_machine_id() is used to generate the machine id from. Thus when
+ updating barebox the machine id might change.
- Note: if multiple sources provide hashable device-specific information
- (via machine_id_set_hashable()) the information provided by the last call
- prior to the late initcall set_machine_id() is used to generate the
- machine id from. Thus when updating barebox the machine id might change.
+ global.bootm.provide_machine_id may be used to automatically set
+ the linux.bootargs.machine_id global variable with a value of
+ systemd.machine_id=${global.machine_id}
Note: if no hashable information is available no machine id will be passed
to the kernel.
+config SYSTEMD_OF_WATCHDOG
+ bool "inform devicetree-enabled kernel of used watchdog"
+ depends on WATCHDOG && OFTREE && FLEXIBLE_BOOTARGS
+ help
+ Sets the linux.bootargs.dyn.watchdog global variable with a value of
+ systemd.watchdog-device=/dev/WDOG if barebox succeeded in enabling
+ the watchdog WDOG prior to boot. WDOG is the alias of the watchdog
+ in the kernel device tree. If the kernel is booted without a device
+ tree or with one that lacks aliases, nothing is added.
+
+config EXTERNAL_DTS_FRAGMENTS
+ string "external dts file fragments"
+ depends on OFTREE
+ help
+ List of dts fragment files that will be appended to Barebox's device
+ tree(s) source when building the dtb file(s). If multiple files are
+ listed, they will be appended in order. Relative filenames will use
+ the dtc include search path.
+
+ A preprocessor macro based on the name of the main dts will be
+ defined, which allows the dts fragments to be based on which image of
+ a multi image build they are being used in. Given the dts filename
+ used for a board is "foo-board.dts" the external dts usage can be
+ limited to that board with
+
+ #ifdef foo_board_dts
+ ...
+ #endif
+
+ It is not intended that this is put into Barebox' defconfig files.
+ Instead, it's an external build system's job, like Yocto or buildroot,
+ to add dts fragments from outside the Barebox source tree into the
+ Barebox build.
+
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.
@@ -1003,12 +1205,38 @@ 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 boards lowlevel file.
+ Requires explicit support in the board's lowlevel file.
+
+endmenu
+
+if FASTBOOT_BASE
+
+menu "Android Fastboot"
+
+config FASTBOOT_SPARSE
+ bool
+ select IMAGE_SPARSE
+ prompt "Enable Fastboot sparse image support"
+ help
+ Sparse images are a way for the fastboot protocol to write
+ images that are bigger than the available memory. If unsure,
+ say yes here.
+
+config FASTBOOT_CMD_OEM
+ bool
+ prompt "Enable OEM commands"
+ help
+ This option enables the fastboot "oem" group of commands. They allow to
+ executing arbitrary barebox commands and may be disabled in secure
+ environments.
endmenu
+endif
+
endmenu
menu "Debugging"
@@ -1066,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
@@ -1073,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.
@@ -1080,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.
@@ -1087,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.
@@ -1094,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.
@@ -1101,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.
@@ -1108,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.
@@ -1115,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.
@@ -1122,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.
@@ -1129,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.
@@ -1136,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.
@@ -1143,20 +1405,35 @@ 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.
-config DEBUG_IMX8MQ_UART
- bool "i.MX8MQ Debug UART"
- depends on ARCH_IMX8MQ
+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.MX8MQ.
+ 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.
@@ -1164,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.
@@ -1171,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.
@@ -1178,16 +1457,50 @@ 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.
-config DEBUG_ROCKCHIP_UART
- bool "RK3xxx Debug UART"
- depends on ARCH_ROCKCHIP
+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.
+
+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.
+
+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 RK3XXX.
+ 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"
@@ -1203,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.
@@ -1220,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.
@@ -1227,11 +1549,78 @@ 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
+ select DEBUG_LL_NS16550
+
+config DEBUG_STARFIVE
+ bool "Starfive ns16550 serial0 port"
+ 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
+
+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
+ bool
+ help
+ Selected by RISC-V platforms that use ns16550 for debug_ll
+
config DEBUG_IMX_UART_PORT
int "i.MX Debug UART Port Selection" if DEBUG_IMX1_UART || \
DEBUG_IMX21_UART || \
@@ -1242,9 +1631,9 @@ config DEBUG_IMX_UART_PORT
DEBUG_IMX51_UART || \
DEBUG_IMX53_UART || \
DEBUG_IMX6Q_UART || \
- DEBUG_IMX6SL_UART || \
DEBUG_IMX7D_UART || \
- DEBUG_IMX8MQ_UART || \
+ DEBUG_IMX8M_UART || \
+ DEBUG_IMX9_UART || \
DEBUG_VF610_UART
default 1
depends on ARCH_IMX
@@ -1252,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
@@ -1266,7 +1665,11 @@ config DEBUG_OMAP_UART_PORT
AM33XX: 0 - 2
config DEBUG_ROCKCHIP_UART_PORT
- int "RK3xxx UART debug port" if DEBUG_ROCKCHIP_UART
+ int "RK3xxx UART debug port" if DEBUG_ROCKCHIP_RK3188_UART || \
+ DEBUG_ROCKCHIP_RK3288_UART || \
+ DEBUG_ROCKCHIP_RK3568_UART || \
+ DEBUG_ROCKCHIP_RK3588_UART || \
+ DEBUG_ROCKCHIP_RK3399_UART
default 2
depends on ARCH_ROCKCHIP
help
@@ -1312,24 +1715,75 @@ 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/removes"
+ select CONSOLE_FLUSH_LINE_BREAK
+ help
+ 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"
depends on ARM && (!CPU_32v4T && !ARCH_TEGRA)
help
- If this enabled, barebox will be compiled with BKPT instruction
+ 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"
-config KASAN
- bool "KASAN: runtime memory debugger"
- depends on HAVE_ARCH_KASAN
+config ASAN
+ bool "ASAN: runtime memory debugger"
+ depends on HAVE_ARCH_ASAN
help
- Enables KASAN (KernelAddressSANitizer) - runtime memory debugger,
+ Enables ASAN (AddressSANitizer) - runtime memory debugger,
designed to find out-of-bounds accesses and use-after-free bugs.
config COMPILE_TEST
@@ -1345,6 +1799,20 @@ 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
@@ -1354,5 +1822,11 @@ config DDR_SPD
bool
select CRC_ITU_T
-config HAVE_ARCH_KASAN
+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 c14af692f9..96498790b3 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -1,42 +1,56 @@
+# 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-$(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
+obj-y += workqueue.o
obj-$(CONFIG_MACHINE_ID) += machine_id.o
obj-$(CONFIG_AUTO_COMPLETE) += complete.o
obj-y += version.o
obj-$(CONFIG_BAREBOX_UPDATE) += bbu.o
+obj-$(CONFIG_SYSTEM_PARTITIONS) += system-partitions.o
obj-$(CONFIG_BINFMT) += binfmt.o
obj-$(CONFIG_BLOCK) += block.o
obj-$(CONFIG_BLSPEC) += blspec.o
-obj-$(CONFIG_BOOTM) += bootm.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
-obj-$(CONFIG_FILETYPE) += filetype.o
+obj-pbl-$(CONFIG_FILETYPE) += filetype.o
CFLAGS_filetype.o = -I$(srctree)/arch/
+CFLAGS_filetype.pbl.o = -I$(srctree)/arch/
obj-$(CONFIG_FLEXIBLE_BOOTARGS) += bootargs.o
obj-$(CONFIG_GLOBALVAR) += globalvar.o
obj-$(CONFIG_GREGORIAN_CALENDER) += date.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o
obj-$(CONFIG_MALLOC_TLSF) += tlsf_malloc.o tlsf.o calloc.o
+KASAN_SANITIZE_tlsf.o := n
obj-$(CONFIG_MALLOC_DUMMY) += dummy_malloc.o calloc.o
obj-$(CONFIG_MEMINFO) += meminfo.o
obj-$(CONFIG_MENU) += menu.o
@@ -45,7 +59,9 @@ extra-$(CONFIG_MODULES) += module.lds
obj-$(CONFIG_OFTREE) += oftree.o
obj-$(CONFIG_PARTITION_DISK) += partitions.o partitions/
obj-$(CONFIG_PASSWORD) += password.o
+obj-$(CONFIG_HAS_SCHED) += sched.o
obj-$(CONFIG_POLLER) += poller.o
+obj-$(CONFIG_BTHREAD) += bthread.o
obj-$(CONFIG_RESET_SOURCE) += reset_source.o
obj-$(CONFIG_SHELL_HUSH) += hush.o
obj-$(CONFIG_SHELL_SIMPLE) += parser.o
@@ -55,20 +71,17 @@ 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-$(CONFIG_FILE_LIST) += file-list.o
+obj-y += file-list.o
obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_UBIFORMAT) += ubiformat.o
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
@@ -95,7 +108,7 @@ endif # CONFIG_PASSWORD
# dependencies on generated files need to be listed explicitly
$(obj)/version.o: include/generated/compile.h
$(obj)/imd-barebox.o: include/generated/compile.h
-$(obj)/pbl-imd-barebox.o: include/generated/compile.h
+$(obj)/imd-barebox.pbl.o: include/generated/compile.h
# compile.h changes depending on hostname, generation number, etc,
# so we regenerate it always.
diff --git a/common/bbu.c b/common/bbu.c
index f284c341b9..ba2566acdf 100644
--- a/common/bbu.c
+++ b/common/bbu.c
@@ -1,17 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bbu.c - barebox update functions
*
* Copyright (c) 2012 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 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.
*/
+
+#define pr_fmt(fmt) "bbu: " fmt
+
#include <common.h>
#include <bbu.h>
#include <linux/list.h>
@@ -24,22 +19,48 @@
#include <malloc.h>
#include <linux/stat.h>
#include <image-metadata.h>
+#include <environment.h>
+#include <file-list.h>
static LIST_HEAD(bbu_image_handlers);
-int bbu_handlers_iterate(int (*fn)(struct bbu_handler *, void *), void *ctx)
+static void append_bbu_entry(const char *_name,
+ const char *devicefile,
+ struct file_list *files)
+{
+ char *name;
+
+ name = basprintf("bbu-%s", _name);
+
+ if (file_list_add_entry(files, name, devicefile, 0))
+ pr_warn("duplicate partition name %s\n", name);
+
+ free(name);
+}
+
+void bbu_append_handlers_to_file_list(struct file_list *files)
{
struct bbu_handler *handler;
list_for_each_entry(handler, &bbu_image_handlers, list) {
- int ret;
+ const char *cdevname;
+ struct stat s;
+ char *devpath;
- ret = fn(handler, ctx);
- if (ret)
- return ret;
- }
+ cdevname = devpath_to_name(handler->devicefile);
+ device_detect_by_name(cdevname);
- return 0;
+ 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);
+ }
}
int bbu_force(struct bbu_data *data, const char *fmt, ...)
@@ -74,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();
@@ -133,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;
@@ -170,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;
@@ -190,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;
@@ -199,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;
@@ -283,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);
@@ -321,7 +447,7 @@ static int bbu_std_file_handler(struct bbu_handler *handler,
return fd;
ret = protect(fd, data->len, 0, 0);
- if (ret && ret != -ENOSYS) {
+ if (ret && (ret != -ENOSYS) && (ret != -ENOTSUPP)) {
printf("unprotecting %s failed with %s\n", data->devicefile,
strerror(-ret));
goto err_close;
@@ -348,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
@@ -378,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 f2ff624587..6a1e9fc83e 100644
--- a/common/binfmt.c
+++ b/common/binfmt.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * GPL v2
*/
#include <common.h>
@@ -16,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 c522310dcf..c7ca4fb403 100644
--- a/common/block.c
+++ b/common/block.c
@@ -1,33 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* block.c - simple block layer
*
* Copyright (c) 2011 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 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.
- *
*/
#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);
/* a chunk of contiguous data */
struct chunk {
void *data; /* data buffer */
- int block_start; /* first block in this chunk */
+ sector_t block_start; /* first block in this chunk */
int dirty; /* need to write back to device */
int num; /* number of chunk, debugging only */
struct list_head list;
@@ -37,7 +28,7 @@ struct chunk {
static int writebuffer_io_len(struct block_device *blk, struct chunk *chunk)
{
- return min(blk->rdbufsize, blk->num_blocks - chunk->block_start);
+ return min_t(blkcnt_t, blk->rdbufsize, blk->num_blocks - chunk->block_start);
}
/*
@@ -73,14 +64,14 @@ static int writebuffer_flush(struct block_device *blk)
* get the chunk containing a given block. Will return NULL if the
* block is not cached, the chunk otherwise.
*/
-static struct chunk *chunk_get_cached(struct block_device *blk, int block)
+static struct chunk *chunk_get_cached(struct block_device *blk, sector_t block)
{
struct chunk *chunk;
list_for_each_entry(chunk, &blk->buffered_blocks, list) {
if (block >= chunk->block_start &&
block < chunk->block_start + blk->rdbufsize) {
- dev_dbg(blk->dev, "%s: found %d in %d\n", __func__,
+ dev_dbg(blk->dev, "%s: found %llu in %d\n", __func__,
block, chunk->num);
/*
* move most recently used entry to the head of the list
@@ -97,7 +88,7 @@ static struct chunk *chunk_get_cached(struct block_device *blk, int block)
* Get the data pointer for a given block. Will return NULL if
* the block is not cached, the data pointer otherwise.
*/
-static void *block_get_cached(struct block_device *blk, int block)
+static void *block_get_cached(struct block_device *blk, sector_t block)
{
struct chunk *chunk;
@@ -144,7 +135,7 @@ static struct chunk *get_chunk(struct block_device *blk)
* not cached already. By definition block_get_cached() for
* the same block will succeed after this call.
*/
-static int block_cache(struct block_device *blk, int block)
+static int block_cache(struct block_device *blk, sector_t block)
{
struct chunk *chunk;
int ret;
@@ -155,7 +146,7 @@ static int block_cache(struct block_device *blk, int block)
chunk->block_start = block & ~blk->blkmask;
- dev_dbg(blk->dev, "%s: %d to %d\n", __func__, chunk->block_start,
+ dev_dbg(blk->dev, "%s: %llu to %d\n", __func__, chunk->block_start,
chunk->num);
if (chunk->block_start * BLOCKSIZE(blk) >= blk->discard_start &&
@@ -181,7 +172,7 @@ static int block_cache(struct block_device *blk, int block)
* Get the data for a block, either from the cache or from
* the device.
*/
-static void *block_get(struct block_device *blk, int block)
+static void *block_get(struct block_device *blk, sector_t block)
{
void *outdata;
int ret;
@@ -209,9 +200,9 @@ static ssize_t block_op_read(struct cdev *cdev, void *buf, size_t count,
{
struct block_device *blk = cdev->priv;
unsigned long mask = BLOCKSIZE(blk) - 1;
- unsigned long block = offset >> blk->blockbits;
+ sector_t block = offset >> blk->blockbits;
size_t icount = count;
- int blocks;
+ blkcnt_t blocks;
if (offset & mask) {
size_t now = BLOCKSIZE(blk) - (offset & mask);
@@ -261,7 +252,7 @@ static ssize_t block_op_read(struct cdev *cdev, void *buf, size_t count,
* Put data into a block. This only overwrites the data in the
* cache and marks the corresponding chunk as dirty.
*/
-static int block_put(struct block_device *blk, const void *buf, int block)
+static int block_put(struct block_device *blk, const void *buf, sector_t block)
{
struct chunk *chunk;
void *data;
@@ -286,9 +277,21 @@ static ssize_t block_op_write(struct cdev *cdev, const void *buf, size_t count,
{
struct block_device *blk = cdev->priv;
unsigned long mask = BLOCKSIZE(blk) - 1;
- unsigned long block = offset >> blk->blockbits;
+ sector_t block = offset >> blk->blockbits;
size_t icount = count;
- int blocks, ret;
+ 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);
@@ -347,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)
{
@@ -369,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);
@@ -388,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);
@@ -403,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;
}
@@ -428,24 +459,63 @@ int blockdevice_unregister(struct block_device *blk)
return 0;
}
-int block_read(struct block_device *blk, void *buf, int block, int num_blocks)
+int block_read(struct block_device *blk, void *buf, sector_t block, blkcnt_t num_blocks)
{
int ret;
ret = cdev_read(&blk->cdev, buf,
num_blocks << blk->blockbits,
- (loff_t)block << blk->blockbits, 0);
+ block << blk->blockbits, 0);
return ret < 0 ? ret : 0;
}
-int block_write(struct block_device *blk, void *buf, int block, int num_blocks)
+int block_write(struct block_device *blk, void *buf, sector_t block, blkcnt_t num_blocks)
{
int ret;
ret = cdev_write(&blk->cdev, buf,
num_blocks << blk->blockbits,
- (loff_t)block << blk->blockbits, 0);
+ block << blk->blockbits, 0);
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 1a6d581f5a..23a24c63db 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -1,15 +1,4 @@
-/*
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0-or-later
#define pr_fmt(fmt) "blspec: " fmt
#include <environment.h>
@@ -26,6 +15,7 @@
#include <libbb.h>
#include <init.h>
#include <bootm.h>
+#include <glob.h>
#include <net.h>
#include <fs.h>
#include <of.h>
@@ -43,76 +33,33 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
val ? strlen(val) + 1 : 0, 1);
}
-static int blspec_apply_oftree_overlay(char *file, const char *abspath,
- int dryrun)
+static int blspec_overlay_fixup(struct device_node *root, void *ctx)
{
- int ret = 0;
- struct fdt_header *fdt;
- struct device_node *overlay;
- char *path;
- char *firmware_path;
-
- path = basprintf("%s/%s", abspath, file);
-
- fdt = read_file(path, NULL);
- if (!fdt) {
- pr_warn("unable to read \"%s\"\n", path);
- ret = -EINVAL;
- goto out;
- }
-
- overlay = of_unflatten_dtb(fdt);
- free(fdt);
- if (IS_ERR(overlay)) {
- ret = PTR_ERR(overlay);
- goto out;
- }
-
- if (dryrun) {
- pr_info("dry run: skip overlay %s\n", path);
- of_delete_node(overlay);
- goto out;
- }
+ struct blspec_entry *entry = ctx;
+ const char *overlays;
+ char *overlay;
+ char *sep, *freep;
- /*
- * Unfortunately the device tree overlay contains only the filename of
- * the firmware and relies on the firmware search paths to find the
- * actual file. Use /lib/firmware in the Linux root directory and hope
- * for the best.
- */
- firmware_path = basprintf("%s/%s", abspath, "/lib/firmware");
- ret = of_firmware_load_overlay(overlay, firmware_path);
- free(firmware_path);
- if (ret) {
- pr_warn("failed to load firmware: skip overlay \"%s\"\n", path);
- of_delete_node(overlay);
- goto out;
- }
+ overlays = blspec_entry_var_get(entry, "devicetree-overlay");
- ret = of_register_overlay(overlay);
- if (ret) {
- pr_warn("cannot register devicetree overlay \"%s\"\n", path);
- of_delete_node(overlay);
- }
+ sep = freep = xstrdup(overlays);
-out:
- free(path);
+ while ((overlay = strsep(&sep, " "))) {
+ char *path;
- return ret;
-}
+ if (!*overlay)
+ continue;
-static void blspec_apply_oftree_overlays(const char *overlays,
- const char *abspath, int dryrun)
-{
- char *overlay;
- char *sep, *freep;
+ path = basprintf("%s/%s", entry->rootpath, overlay);
- sep = freep = xstrdup(overlays);
+ of_overlay_apply_file(root, path, false);
- while ((overlay = strsep(&sep, " ")))
- blspec_apply_oftree_overlay(overlay, abspath, dryrun);
+ free(path);
+ }
free(freep);
+
+ return 0;
}
/*
@@ -129,20 +76,20 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
const char *abspath, *devicetree, *options, *initrd, *linuximage;
const char *overlays;
const char *appendroot;
+ char *old_fws, *fws;
struct bootm_data data = {
- .initrd_address = UIMAGE_INVALID_ADDRESS,
- .os_address = UIMAGE_SOME_ADDRESS,
- .verbose = verbose,
.dryrun = dryrun,
};
globalvar_set_match("linux.bootargs.dyn.", "");
- globalvar_set_match("bootm.image", "");
- globalvar_set_match("bootm.oftree", "");
- globalvar_set_match("bootm.initrd", "");
+ globalvar_set("bootm.image", "");
+ globalvar_set("bootm.oftree", "");
+ globalvar_set("bootm.initrd", "");
bootm_data_init_defaults(&data);
+ data.verbose = max(verbose, data.verbose);
+
devicetree = blspec_entry_var_get(entry, "devicetree");
initrd = blspec_entry_var_get(entry, "initrd");
options = blspec_entry_var_get(entry, "options");
@@ -168,7 +115,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
}
if (overlays)
- blspec_apply_oftree_overlays(overlays, abspath, dryrun);
+ of_register_fixup(blspec_overlay_fixup, entry);
if (initrd)
data.initrd_file = basprintf("%s/%s", abspath, initrd);
@@ -192,9 +139,27 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
(entry->cdev && entry->cdev->dev) ?
dev_name(entry->cdev->dev) : "none");
+ of_overlay_set_basedir(abspath);
+
+ old_fws = firmware_get_searchpath();
+ if (old_fws && *old_fws)
+ fws = basprintf("%s/lib/firmware:%s", abspath, old_fws);
+ else
+ fws = basprintf("%s/lib/firmware", abspath);
+ firmware_set_searchpath(fws);
+ free(fws);
+
ret = bootm_boot(&data);
if (ret)
pr_err("Booting failed\n");
+
+ if (overlays)
+ of_unregister_fixup(blspec_overlay_fixup, entry);
+
+ of_overlay_set_basedir("/");
+ firmware_set_searchpath(old_fws);
+ free(old_fws);
+
err_out:
free((char *)data.oftree_file);
free((char *)data.initrd_file);
@@ -298,6 +263,18 @@ static struct blspec_entry *blspec_entry_open(struct bootentries *bootentries,
val = end;
+ if (!strcmp(name, "options")) {
+ /* If there was a previous "options" key given, prepend its value
+ * (as per spec). */
+ const char *prev_val = blspec_entry_var_get(entry, name);
+ if (prev_val) {
+ char *opts = xasprintf("%s %s", prev_val, val);
+ blspec_entry_var_set(entry, name, opts);
+ free(opts);
+ continue;
+ }
+ }
+
blspec_entry_var_set(entry, name, val);
}
@@ -339,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))
@@ -364,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;
@@ -436,7 +413,7 @@ out:
if (ret)
free(mountpath);
- return ret ? ERR_PTR(ret) : mountpath;
+ return ret ? NULL : mountpath;
}
/*
@@ -449,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;
@@ -482,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);
- 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;
}
@@ -537,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
*
@@ -546,114 +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 = xstrdup(blspec_entry_var_get(entry, "title"));
- 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;
@@ -670,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);
@@ -725,7 +666,7 @@ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev)
found += ret;
}
- rootpath = cdev_mount_default(cdev, NULL);
+ rootpath = cdev_mount(cdev);
if (!IS_ERR(rootpath)) {
ret = blspec_scan_directory(bootentries, rootpath);
if (ret > 0)
@@ -744,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;
@@ -752,7 +693,7 @@ int blspec_scan_devices(struct bootentries *bootentries)
device_detect(dev);
for_each_block_device(bdev) {
- struct cdev *cdev = &bdev->cdev;
+ struct cdev *cdev;
list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list) {
ret = blspec_scan_cdev(bootentries, cdev);
@@ -772,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;
@@ -788,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;
@@ -836,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);
@@ -860,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);
@@ -867,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 f546fce62c..cbfe6649b3 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -1,30 +1,18 @@
-/*
- * 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; version 2.
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0-only
-#include <environment.h>
-#include <bootchooser.h>
+#include <boot.h>
#include <globalvar.h>
#include <magicvar.h>
#include <watchdog.h>
#include <command.h>
#include <readkey.h>
#include <common.h>
-#include <blspec.h>
#include <libgen.h>
-#include <malloc.h>
#include <bootm.h>
#include <glob.h>
#include <init.h>
#include <menu.h>
-#include <fs.h>
+#include <unistd.h>
#include <linux/stat.h>
@@ -87,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;
}
@@ -106,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);
}
@@ -119,12 +107,26 @@ void boot_set_watchdog_timeout(unsigned int timeout)
boot_watchdog_timeout = timeout;
}
+static struct watchdog *boot_enabled_watchdog;
+
+struct watchdog *boot_get_enabled_watchdog(void)
+{
+ return boot_enabled_watchdog;
+}
+
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");
@@ -136,25 +138,28 @@ static int init_boot(void)
}
late_initcall(init_boot);
-BAREBOX_MAGICVAR_NAMED(global_watchdog_timeout, global.boot.watchdog_timeout,
+BAREBOX_MAGICVAR(global.boot.watchdog_timeout,
"Watchdog enable timeout in seconds before booting");
int boot_entry(struct bootentry *be, int verbose, int dryrun)
{
int ret;
- printf("Booting entry '%s'\n", be->title);
+ pr_info("Booting entry '%s'\n", be->title);
if (IS_ENABLED(CONFIG_WATCHDOG) && boot_watchdog_timeout) {
- ret = watchdog_set_timeout(watchdog_get_default(),
- boot_watchdog_timeout);
- if (ret)
+ boot_enabled_watchdog = watchdog_get_default();
+
+ ret = watchdog_set_timeout(boot_enabled_watchdog, boot_watchdog_timeout);
+ if (ret) {
pr_warn("Failed to enable watchdog: %s\n", strerror(-ret));
+ boot_enabled_watchdog = NULL;
+ }
}
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;
}
@@ -185,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;
@@ -274,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
@@ -315,13 +325,15 @@ 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)) {
- printf("no menu support available\n");
+ pr_warn("no menu support available\n");
return;
}
@@ -330,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));
@@ -338,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;
@@ -354,11 +372,10 @@ void bootsources_list(struct bootentries *bootentries)
{
struct bootentry *entry;
- printf("%-20s\n", "title");
- printf("%-20s\n", "------");
+ printf("title\n------\n");
bootentries_for_each_entry(bootentries, entry)
- printf("%-20s %s\n", entry->title, entry->description);
+ printf("%s\n\t%s\n", entry->title, entry->description);
}
-BAREBOX_MAGICVAR_NAMED(global_boot_default, global.boot.default, "default boot order");
+BAREBOX_MAGICVAR(global.boot.default, "default boot order");
diff --git a/common/bootargs.c b/common/bootargs.c
index 97d0e15eaf..c49136609f 100644
--- a/common/bootargs.c
+++ b/common/bootargs.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bootargs.c - concatenate Linux bootargs
*
* Copyright (c) 2012 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 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.
- *
*/
#include <common.h>
#include <boot.h>
@@ -84,6 +75,6 @@ int linux_bootargs_overwrite(const char *bootargs)
return 0;
}
-BAREBOX_MAGICVAR_NAMED(global_linux_bootargs_, global.linux.bootargs.*, "Linux bootargs variables");
-BAREBOX_MAGICVAR_NAMED(global_linux_mtdparts_, global.linux.mtdparts.*, "Linux mtdparts variables");
-BAREBOX_MAGICVAR_NAMED(global_linux_blkdevparts_, global.linux.blkdevparts.*, "Linux blkdevparts variables");
+BAREBOX_MAGICVAR(global.linux.bootargs.*, "Linux bootargs variables");
+BAREBOX_MAGICVAR(global.linux.mtdparts.*, "Linux mtdparts variables");
+BAREBOX_MAGICVAR(global.linux.blkdevparts.*, "Linux blkdevparts variables");
diff --git a/common/bootchooser.c b/common/bootchooser.c
index c08db03eba..022e225165 100644
--- a/common/bootchooser.c
+++ b/common/bootchooser.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
* Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
- *
- * 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.
*/
#define pr_fmt(fmt) "bootchooser: " fmt
@@ -22,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>
@@ -86,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;
@@ -137,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;
@@ -151,14 +143,12 @@ static int pr_setenv(struct bootchooser *bc, const char *fmt, ...)
if (!str)
return -ENOMEM;
- val = strchr(str, '=');
+ val = parse_assignment(str);
if (!val) {
ret = -EINVAL;
goto err;
}
- *val++ = '\0';
-
oldval = getenv(str);
if (!oldval || strcmp(oldval, val)) {
if (bc->state)
@@ -173,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;
@@ -269,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);
@@ -450,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;
@@ -508,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;
@@ -926,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[] = {
@@ -954,21 +952,17 @@ static int bootchooser_init(void)
}
device_initcall(bootchooser_init);
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_disable_on_zero_attempts,
- global.bootchooser.disable_on_zero_attempts,
- "bootchooser: Disable target when remaining attempts counter reaches 0");
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_retry,
- global.bootchooser.retry,
- "bootchooser: Try again when booting a target fails");
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_targets,
- global.bootchooser.targets,
- "bootchooser: Space separated list of target names");
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_default_attempts,
- global.bootchooser.default_attempts,
- "bootchooser: Default number of attempts for a target");
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_default_priority,
- global.bootchooser.default_priority,
- "bootchooser: Default priority for a target");
-BAREBOX_MAGICVAR_NAMED(global_bootchooser_state_prefix,
- global.bootchooser.state_prefix,
- "bootchooser: state name prefix, empty for nv backend");
+BAREBOX_MAGICVAR(global.bootchooser.disable_on_zero_attempts,
+ "bootchooser: Disable target when remaining attempts counter reaches 0");
+BAREBOX_MAGICVAR(global.bootchooser.retry,
+ "bootchooser: Try again when booting a target fails");
+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,
+ "bootchooser: state name prefix, empty for nv backend");
diff --git a/common/booti.c b/common/booti.c
new file mode 100644
index 0000000000..e745ff6963
--- /dev/null
+++ b/common/booti.c
@@ -0,0 +1,93 @@
+// 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;
+ unsigned long text_offset, image_size, kernel;
+ unsigned long image_end;
+ int ret;
+ void *fdt;
+
+ text_offset = le64_to_cpup(kernel_header + 8);
+ image_size = le64_to_cpup(kernel_header + 16);
+
+ kernel = get_kernel_address(data->os_address, 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)
+ return ERR_PTR(ret);
+
+ 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)
+ return ERR_PTR(ret);
+
+ image_end += resource_size(data->initrd_res);
+ image_end = PAGE_ALIGN(image_end);
+ }
+
+ devicetree = image_end;
+
+ fdt = bootm_get_devicetree(data);
+ if (IS_ERR(fdt))
+ return fdt;
+
+ ret = bootm_load_devicetree(data, fdt, devicetree);
+
+ free(fdt);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ *oftree = devicetree;
+ }
+
+ printf("Loaded kernel to 0x%08lx", kernel);
+ if (oftree)
+ printf(", devicetree at %pa", oftree);
+ printf("\n");
+
+ return (void *)kernel;
+}
diff --git a/common/bootm.c b/common/bootm.c
index 8fec1ee34d..4cc88eed76 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -1,15 +1,4 @@
-/*
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <common.h>
#include <bootm.h>
@@ -23,6 +12,8 @@
#include <environment.h>
#include <linux/stat.h>
#include <magicvar.h>
+#include <uncompress.h>
+#include <zero_page.h>
static LIST_HEAD(handler_list);
@@ -51,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;
@@ -58,12 +50,14 @@ void bootm_data_init_defaults(struct bootm_data *data)
{
data->initrd_address = UIMAGE_INVALID_ADDRESS;
data->os_address = UIMAGE_SOME_ADDRESS;
+ data->os_entry = UIMAGE_SOME_ADDRESS;
data->oftree_file = getenv_nonempty("global.bootm.oftree");
data->tee_file = getenv_nonempty("global.bootm.tee");
data->os_file = getenv_nonempty("global.bootm.image");
getenv_ul("global.bootm.image.loadaddr", &data->os_address);
getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address);
data->initrd_file = getenv_nonempty("global.bootm.initrd");
+ data->root_dev = getenv_nonempty("global.bootm.root_dev");
data->verify = bootm_get_verify_mode();
data->appendroot = bootm_appendroot;
data->provide_machine_id = bootm_provide_machine_id;
@@ -77,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",
@@ -120,13 +119,13 @@ int bootm_load_os(struct image_data *data, unsigned long load_address)
data->os_res = request_sdram_region("kernel",
load_address, kernel_size);
if (!data->os_res) {
- printf("unable to request SDRAM region for kernel at"
- "0x%08llx-0x%08llx\n",
+ pr_err("unable to request SDRAM region for kernel at"
+ " 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;
}
@@ -143,6 +142,9 @@ int bootm_load_os(struct image_data *data, unsigned long load_address)
return 0;
}
+ if (IS_ENABLED(CONFIG_ELF) && data->elf)
+ return elf_load(data->elf);
+
if (data->os_file) {
data->os_res = file_to_sdram(data->os_file, load_address);
if (!data->os_res)
@@ -181,7 +183,7 @@ static int bootm_open_initrd_uimage(struct image_data *data)
if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) {
ret = uimage_verify(data->initrd);
if (ret) {
- printf("Checking data crc failed with %s\n",
+ pr_err("Checking data crc failed with %s\n",
strerror(-ret));
return ret;
}
@@ -228,36 +230,38 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address)
ret = fit_open_image(data->os_fit, data->fit_config, "ramdisk",
&initrd, &initrd_size);
-
+ if (ret) {
+ pr_err("Cannot open ramdisk image in FIT image: %s\n",
+ strerror(-ret));
+ return ret;
+ }
data->initrd_res = request_sdram_region("initrd",
load_address,
initrd_size);
if (!data->initrd_res) {
- printf("unable to request SDRAM region for initrd at"
+ pr_err("unable to request SDRAM region for initrd at"
"0x%08llx-0x%08llx\n",
(unsigned long long)load_address,
(unsigned long long)load_address + initrd_size - 1);
return -ENOMEM;
}
memcpy((void *)load_address, initrd, initrd_size);
- printf("Loaded initrd from FIT image\n");
+ pr_info("Loaded initrd from FIT image\n");
goto done1;
}
- type = file_name_detect_type(data->initrd_file);
-
- if ((int)type < 0) {
- printf("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) {
int num;
ret = bootm_open_initrd_uimage(data);
if (ret) {
- printf("loading initrd failed with %s\n",
- strerror(-ret));
+ pr_err("loading initrd failed with %s\n",
+ strerror(-ret));
return ret;
}
@@ -277,13 +281,13 @@ int bootm_load_initrd(struct image_data *data, unsigned long load_address)
done:
- printf("Loaded initrd %s '%s'", file_type_to_string(type),
+ pr_info("Loaded initrd %s '%s'", file_type_to_string(type),
data->initrd_file);
if (type == filetype_uimage && data->initrd->header.ih_type == IH_TYPE_MULTI)
- printf(", multifile image %s", data->initrd_part);
- printf("\n");
+ pr_info(", multifile image %s", data->initrd_part);
+ pr_info("\n");
done1:
- printf("initrd is at %pa-%pa\n",
+ pr_info("initrd is at %pa-%pa\n",
&data->initrd_res->start,
&data->initrd_res->end);
@@ -299,7 +303,7 @@ static int bootm_open_oftree_uimage(struct image_data *data, size_t *size,
struct uimage_handle *of_handle;
int release = 0;
- printf("Loading devicetree from '%s'@%d\n", oftree, num);
+ pr_info("Loading devicetree from '%s'@%d\n", oftree, num);
if (!IS_ENABLED(CONFIG_BOOTM_OFTREE_UIMAGE))
return -EINVAL;
@@ -323,7 +327,7 @@ static int bootm_open_oftree_uimage(struct image_data *data, size_t *size,
ft = file_detect_type(*fdt, *size);
if (ft != filetype_oftree) {
- printf("%s is not an oftree but %s\n",
+ pr_err("%s is not an oftree but %s\n",
data->oftree_file, file_type_to_string(ft));
free(*fdt);
return -EINVAL;
@@ -362,16 +366,15 @@ void *bootm_get_devicetree(struct image_data *data)
if (ret)
return ERR_PTR(ret);
- data->of_root_node = of_unflatten_dtb(of_tree);
+ data->of_root_node = of_unflatten_dtb(of_tree, of_size);
} else if (data->oftree_file) {
size_t size;
- type = file_name_detect_type(data->oftree_file);
-
- if ((int)type < 0) {
- printf("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) {
@@ -379,7 +382,7 @@ void *bootm_get_devicetree(struct image_data *data)
ret = bootm_open_oftree_uimage(data, &size, &oftree);
break;
case filetype_oftree:
- printf("Loading devicetree from '%s'\n", data->oftree_file);
+ pr_info("Loading devicetree from '%s'\n", data->oftree_file);
ret = read_file_2(data->oftree_file, &size, (void *)&oftree,
FILESIZE_MAX);
break;
@@ -390,7 +393,7 @@ void *bootm_get_devicetree(struct image_data *data)
if (ret)
return ERR_PTR(ret);
- data->of_root_node = of_unflatten_dtb(oftree);
+ data->of_root_node = of_unflatten_dtb(oftree, size);
free(oftree);
@@ -401,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");
}
@@ -415,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);
@@ -450,7 +458,7 @@ int bootm_load_devicetree(struct image_data *data, void *fdt,
data->oftree_res = request_sdram_region("oftree", load_address,
fdt_size);
if (!data->oftree_res) {
- printf("unable to request SDRAM region for device tree at"
+ pr_err("unable to request SDRAM region for device tree at"
"0x%08llx-0x%08llx\n",
(unsigned long long)load_address,
(unsigned long long)load_address + fdt_size - 1);
@@ -460,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;
}
@@ -470,6 +480,8 @@ int bootm_get_os_size(struct image_data *data)
{
int ret;
+ if (data->elf)
+ return elf_get_mem_size(data->elf);
if (data->os)
return uimage_get_size(data->os, uimage_part_num(data->os_part));
if (data->os_fit)
@@ -497,7 +509,7 @@ static int bootm_open_os_uimage(struct image_data *data)
if (bootm_get_verify_mode() > BOOTM_VERIFY_NONE) {
ret = uimage_verify(data->os);
if (ret) {
- printf("Checking data crc failed with %s\n",
+ pr_err("Checking data crc failed with %s\n",
strerror(-ret));
return ret;
}
@@ -505,8 +517,8 @@ static int bootm_open_os_uimage(struct image_data *data)
uimage_print_contents(data->os);
- if (data->os->header.ih_arch != IH_ARCH) {
- printf("Unsupported Architecture 0x%x\n",
+ if (IH_ARCH == IH_ARCH_INVALID || data->os->header.ih_arch != IH_ARCH) {
+ pr_err("Unsupported Architecture 0x%x\n",
data->os->header.ih_arch);
return -EINVAL;
}
@@ -517,6 +529,88 @@ 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;
+
+ elf = elf_open(data->os_file);
+ if (IS_ERR(elf))
+ return PTR_ERR(elf);
+
+ pr_info("Entry Point: %08llx\n", elf->entry);
+
+ data->os_address = elf->entry;
+ data->elf = elf;
+
+ return 0;
+}
+
static void bootm_print_info(struct image_data *data)
{
if (data->os_res)
@@ -562,7 +656,7 @@ int bootm_boot(struct bootm_data *bootm_data)
size_t size;
if (!bootm_data->os_file) {
- printf("no image given\n");
+ pr_err("no image given\n");
return -ENOENT;
}
@@ -583,7 +677,7 @@ int bootm_boot(struct bootm_data *bootm_data)
ret = read_file_2(data->os_file, &size, &data->os_header, PAGE_SIZE);
if (ret < 0 && ret != -EFBIG) {
- printf("could not open %s: %s\n", data->os_file,
+ pr_err("could not open %s: %s\n", data->os_file,
strerror(-ret));
goto err_out;
}
@@ -593,7 +687,7 @@ int bootm_boot(struct bootm_data *bootm_data)
os_type = file_detect_type(data->os_header, PAGE_SIZE);
if (!data->force && os_type == filetype_unknown) {
- printf("Unknown OS filetype (try -f)\n");
+ pr_err("Unknown OS filetype (try -f)\n");
ret = -EINVAL;
goto err_out;
}
@@ -609,67 +703,91 @@ int bootm_boot(struct bootm_data *bootm_data)
data->initrd_file = NULL;
data->tee_file = NULL;
if (os_type != filetype_oftree) {
- printf("Signed boot and image is no FIT image, aborting\n");
+ pr_err("Signed boot and image is no FIT image, aborting\n");
ret = -EINVAL;
goto err_out;
}
}
- if (IS_ENABLED(CONFIG_FITIMAGE) && os_type == filetype_oftree) {
- struct fit_handle *fit;
-
- fit = fit_open(data->os_file, data->verbose, data->verify);
- if (IS_ERR(fit)) {
- printf("Loading FIT image %s failed with: %s\n", data->os_file,
- strerrorp(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)) {
- printf("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",
- &data->fit_kernel, &data->fit_kernel_size);
- if (ret)
- goto err_out;
+ switch (os_type) {
+ case filetype_oftree:
+ ret = bootm_open_fit(data);
+ break;
+ case filetype_uimage:
+ ret = bootm_open_os_uimage(data);
+ break;
+ case filetype_elf:
+ ret = bootm_open_elf(data);
+ break;
+ default:
+ ret = 0;
+ break;
}
- if (os_type == filetype_uimage) {
- ret = bootm_open_os_uimage(data);
- if (ret) {
- printf("Loading OS image failed with: %s\n",
- strerror(-ret));
- 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) {
char *rootarg;
- rootarg = path_get_linux_rootarg(data->os_file);
- if (!IS_ERR(rootarg)) {
- printf("Adding \"%s\" to Kernel commandline\n", rootarg);
+ if (bootm_data->root_dev) {
+ const char *root_dev_name = devpath_to_name(bootm_data->root_dev);
+ const struct cdev *root_cdev = cdev_by_name(root_dev_name);
+
+ rootarg = cdev_get_linux_rootarg(root_cdev);
+ if (!rootarg) {
+ rootarg = ERR_PTR(-EINVAL);
+
+ if (!root_cdev)
+ pr_err("no cdev found for %s, cannot set root= option\n",
+ root_dev_name);
+ else if (!root_cdev->partuuid[0])
+ pr_err("%s doesn't have a PARTUUID, cannot set root= option\n",
+ root_dev_name);
+ }
+ } else {
+ rootarg = path_get_linux_rootarg(data->os_file);
+ }
+
+ 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);
free(rootarg);
}
}
+ 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;
if (!machine_id) {
- printf("Providing machine id is enabled but no machine id set\n");
+ pr_err("Providing machine id is enabled but no machine id set\n");
ret = -EINVAL;
goto err_out;
}
@@ -679,22 +797,24 @@ int bootm_boot(struct bootm_data *bootm_data)
free(machine_id_bootarg);
}
- printf("\nLoading %s '%s'", file_type_to_string(os_type),
- data->os_file);
+ pr_info("\nLoading %s '%s'", file_type_to_string(os_type),
+ data->os_file);
if (os_type == filetype_uimage &&
data->os->header.ih_type == IH_TYPE_MULTI)
- printf(", multifile image %d", uimage_part_num(data->os_part));
- printf("\n");
+ pr_info(", multifile image %d", uimage_part_num(data->os_part));
+ pr_info("\n");
if (data->os_address == UIMAGE_SOME_ADDRESS)
data->os_address = UIMAGE_INVALID_ADDRESS;
+ if (data->os_entry == UIMAGE_SOME_ADDRESS)
+ data->os_entry = 0;
handler = bootm_find_handler(os_type, data);
if (!handler) {
- printf("no image handler found for image type %s\n",
- file_type_to_string(os_type));
+ pr_err("no image handler found for image type %s\n",
+ file_type_to_string(os_type));
if (os_type == filetype_uimage)
- printf("and OS type: %d\n", data->os->header.ih_os);
+ pr_err("and OS type: %d\n", data->os->header.ih_os);
ret = -ENODEV;
goto err_out;
}
@@ -706,7 +826,7 @@ int bootm_boot(struct bootm_data *bootm_data)
ret = handler->bootm(data);
if (data->dryrun)
- printf("Dryrun. Aborted\n");
+ pr_info("Dryrun. Aborted\n");
err_out:
if (data->os_res)
@@ -721,11 +841,14 @@ err_out:
uimage_close(data->initrd);
if (data->os)
uimage_close(data->os);
+ if (IS_ENABLED(CONFIG_ELF) && data->elf)
+ 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);
@@ -737,13 +860,101 @@ err_out:
return ret;
}
+static int do_bootm_compressed(struct image_data *img_data)
+{
+ struct bootm_data bootm_data = {
+ .oftree_file = img_data->oftree_file,
+ .initrd_file = img_data->initrd_file,
+ .tee_file = img_data->tee_file,
+ .verbose = img_data->verbose,
+ .verify = img_data->verify,
+ .force = img_data->force,
+ .dryrun = img_data->dryrun,
+ .initrd_address = img_data->initrd_address,
+ .os_address = img_data->os_address,
+ .os_entry = img_data->os_entry,
+ };
+ int from, to, ret;
+ char *dstpath;
+
+ from = open(img_data->os_file, O_RDONLY);
+ if (from < 0)
+ return -ENODEV;
+
+ dstpath = make_temp("bootm-compressed");
+ if (!dstpath) {
+ ret = -ENOMEM;
+ goto fail_from;
+ }
+
+ to = open(dstpath, O_CREAT | O_WRONLY);
+ if (to < 0) {
+ ret = -ENODEV;
+ goto fail_make_temp;
+ }
+
+ ret = uncompress_fd_to_fd(from, to, uncompress_err_stdout);
+ if (ret)
+ goto fail_to;
+
+ bootm_data.os_file = dstpath;
+ ret = bootm_boot(&bootm_data);
+
+fail_to:
+ close(to);
+ unlink(dstpath);
+fail_make_temp:
+ free(dstpath);
+fail_from:
+ close(from);
+ return ret;
+}
+
+static struct image_handler bzip2_bootm_handler = {
+ .name = "BZIP2 compressed file",
+ .bootm = do_bootm_compressed,
+ .filetype = filetype_bzip2,
+};
+
+static struct image_handler gzip_bootm_handler = {
+ .name = "GZIP compressed file",
+ .bootm = do_bootm_compressed,
+ .filetype = filetype_gzip,
+};
+
+static struct image_handler lzo_bootm_handler = {
+ .name = "LZO compressed file",
+ .bootm = do_bootm_compressed,
+ .filetype = filetype_lzo_compressed,
+};
+
+static struct image_handler lz4_bootm_handler = {
+ .name = "LZ4 compressed file",
+ .bootm = do_bootm_compressed,
+ .filetype = filetype_lz4_compressed,
+};
+
+static struct image_handler xz_bootm_handler = {
+ .name = "XZ compressed file",
+ .bootm = do_bootm_compressed,
+ .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);
globalvar_add_simple("bootm.image.loadaddr", NULL);
globalvar_add_simple("bootm.oftree", NULL);
+ 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);
@@ -758,18 +969,34 @@ static int bootm_init(void)
globalvar_add_simple_enum("bootm.verify", (unsigned int *)&bootm_verify_mode,
bootm_verify_names, ARRAY_SIZE(bootm_verify_names));
+
+ if (IS_ENABLED(CONFIG_BZLIB))
+ register_image_handler(&bzip2_bootm_handler);
+ if (IS_ENABLED(CONFIG_ZLIB))
+ register_image_handler(&gzip_bootm_handler);
+ if (IS_ENABLED(CONFIG_LZO_DECOMPRESS))
+ register_image_handler(&lzo_bootm_handler);
+ if (IS_ENABLED(CONFIG_LZ4_DECOMPRESS))
+ 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;
}
late_initcall(bootm_init);
BAREBOX_MAGICVAR(bootargs, "Linux kernel parameters");
-BAREBOX_MAGICVAR_NAMED(global_bootm_image, global.bootm.image, "bootm default boot image");
-BAREBOX_MAGICVAR_NAMED(global_bootm_image_loadaddr, global.bootm.image.loadaddr, "bootm default boot image loadaddr");
-BAREBOX_MAGICVAR_NAMED(global_bootm_initrd, global.bootm.initrd, "bootm default initrd");
-BAREBOX_MAGICVAR_NAMED(global_bootm_initrd_loadaddr, global.bootm.initrd.loadaddr, "bootm default initrd loadaddr");
-BAREBOX_MAGICVAR_NAMED(global_bootm_oftree, global.bootm.oftree, "bootm default oftree");
-BAREBOX_MAGICVAR_NAMED(global_bootm_tee, global.bootm.tee, "bootm default tee image");
-BAREBOX_MAGICVAR_NAMED(global_bootm_verify, global.bootm.verify, "bootm default verify level");
-BAREBOX_MAGICVAR_NAMED(global_bootm_verbose, global.bootm.verbose, "bootm default verbosity level (0=quiet)");
-BAREBOX_MAGICVAR_NAMED(global_bootm_appendroot, global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from");
-BAREBOX_MAGICVAR_NAMED(global_bootm_provide_machine_id, global.bootm.provide_machine_id, "If true, add systemd.machine_id= with value of global.machine_id to Kernel");
+BAREBOX_MAGICVAR(global.bootm.image, "bootm default boot image");
+BAREBOX_MAGICVAR(global.bootm.image.loadaddr, "bootm default boot image loadaddr");
+BAREBOX_MAGICVAR(global.bootm.initrd, "bootm default initrd");
+BAREBOX_MAGICVAR(global.bootm.initrd.loadaddr, "bootm default initrd loadaddr");
+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 fdbc4cd638..6808c9c51d 100644
--- a/common/bootsource.c
+++ b/common/bootsource.c
@@ -1,18 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- *
* Copyright (C) 2011 Marc Reilly <marc@cpdesign.com.au>
* Copyright (C) 2013 Marc Kleine-Budde <mkl@pengutronix.de>
- *
- * 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.
- *
*/
#include <common.h>
@@ -21,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",
@@ -44,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
*
@@ -69,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
@@ -107,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)
@@ -152,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
new file mode 100644
index 0000000000..d40c0b0f9e
--- /dev/null
+++ b/common/bthread.c
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Ahmad Fatoum, Pengutronix
+ *
+ * ASAN bookkeeping based on Qemu coroutine-ucontext.c
+ */
+
+/* To avoid future issues; fortify doesn't like longjmp up the call stack */
+#ifndef __NO_FORTIFY
+#define __NO_FORTIFY
+#endif
+
+#include <common.h>
+#include <bthread.h>
+#include <asm/setjmp.h>
+#include <linux/overflow.h>
+
+#if defined CONFIG_ASAN && !defined CONFIG_32BIT
+#define HAVE_FIBER_SANITIZER
+#endif
+
+static struct bthread {
+ void (*threadfn)(void *);
+ void *data;
+ char *name;
+ jmp_buf jmp_buf;
+ void *stack;
+ u32 stack_size;
+ struct list_head list;
+#ifdef HAVE_FIBER_SANITIZER
+ void *fake_stack_save;
+#endif
+ u8 awake :1;
+ u8 should_stop :1;
+ u8 should_clean :1;
+ u8 has_stopped :1;
+} main_thread = {
+ .list = LIST_HEAD_INIT(main_thread.list),
+ .name = "main",
+ .awake = true,
+};
+
+struct bthread *current = &main_thread;
+
+/*
+ * When using ASAN, it needs to be told when we switch stacks.
+ */
+static void start_switch_fiber(struct bthread *, bool terminate_old);
+static void finish_switch_fiber(struct bthread *);
+
+static void __noreturn bthread_trampoline(void)
+{
+ finish_switch_fiber(current);
+ bthread_reschedule();
+
+ current->threadfn(current->data);
+
+ bthread_suspend(current);
+ current->has_stopped = true;
+
+ current = &main_thread;
+ start_switch_fiber(current, true);
+ longjmp(current->jmp_buf, 1);
+}
+
+bool bthread_is_main(struct bthread *bthread)
+{
+ return bthread == &main_thread;
+}
+
+static void bthread_free(struct bthread *bthread)
+{
+ if (!bthread)
+ return;
+ free(bthread->stack);
+ free(bthread->name);
+ free(bthread);
+}
+
+const char *bthread_name(struct bthread *bthread)
+{
+ return bthread->name;
+}
+
+struct bthread *bthread_create(void (*threadfn)(void *), void *data,
+ const char *namefmt, ...)
+{
+ struct bthread *bthread;
+ va_list ap;
+ int len;
+
+ bthread = calloc(1, sizeof(*bthread));
+ if (!bthread)
+ goto err;
+
+ bthread->stack = memalign(16, CONFIG_STACK_SIZE);
+ if (!bthread->stack)
+ goto err;
+
+ bthread->stack_size = CONFIG_STACK_SIZE;
+ bthread->threadfn = threadfn;
+ bthread->data = data;
+
+ va_start(ap, namefmt);
+ len = vasprintf(&bthread->name, namefmt, ap);
+ va_end(ap);
+
+ if (len < 0)
+ goto err;
+
+ list_add_tail(&bthread->list, &current->list);
+
+ /* set up bthread context with the new stack */
+ initjmp(bthread->jmp_buf, bthread_trampoline,
+ bthread->stack + CONFIG_STACK_SIZE);
+
+ return bthread;
+err:
+ bthread_free(bthread);
+ return NULL;
+}
+
+void *bthread_data(struct bthread *bthread)
+{
+ return bthread->data;
+}
+
+void bthread_wake(struct bthread *bthread)
+{
+ bthread->awake = true;
+}
+
+void bthread_suspend(struct bthread *bthread)
+{
+ bthread->awake = false;
+}
+
+void bthread_cancel(struct bthread *bthread)
+{
+ bthread->should_stop = true;
+ bthread->should_clean = true;
+}
+
+void __bthread_stop(struct bthread *bthread)
+{
+ bthread->should_stop = true;
+
+ pr_debug("waiting for %s to stop\n", bthread->name);
+
+ while (!bthread->has_stopped)
+ bthread_reschedule();
+
+ list_del(&bthread->list);
+ bthread_free(bthread);
+}
+
+int bthread_should_stop(void)
+{
+ if (bthread_is_main(current))
+ return -EINTR;
+ bthread_reschedule();
+ return current->should_stop;
+}
+
+void bthread_info(void)
+{
+ struct bthread *bthread;
+
+ printf("Registered barebox threads:\n%s\n", current->name);
+
+ list_for_each_entry(bthread, &current->list, list)
+ printf("%s\n", bthread->name);
+}
+
+void bthread_reschedule(void)
+{
+ struct bthread *next, *tmp;
+
+ if (current == list_next_entry(current, list))
+ return;
+
+ list_for_each_entry_safe(next, tmp, &current->list, list) {
+ if (next->awake) {
+ pr_debug("switch %s -> %s\n", current->name, next->name);
+ bthread_schedule(next);
+ return;
+ }
+ if (next->has_stopped && next->should_clean) {
+ pr_debug("deleting %s\n", next->name);
+ list_del(&next->list);
+ bthread_free(next);
+ }
+ }
+}
+
+void bthread_schedule(struct bthread *to)
+{
+ struct bthread *from = current;
+ int ret;
+
+ start_switch_fiber(to, false);
+
+ ret = setjmp(from->jmp_buf);
+ if (ret == 0) {
+ current = to;
+ longjmp(to->jmp_buf, 1);
+ }
+
+ finish_switch_fiber(from);
+}
+
+#ifdef HAVE_FIBER_SANITIZER
+
+void __sanitizer_start_switch_fiber(void **fake_stack_save, const void *bottom, size_t size);
+void __sanitizer_finish_switch_fiber(void *fake_stack_save, const void **bottom_old, size_t *size_old);
+
+static void finish_switch_fiber(struct bthread *bthread)
+{
+ const void *bottom_old;
+ size_t size_old;
+
+ __sanitizer_finish_switch_fiber(bthread->fake_stack_save, &bottom_old, &size_old);
+
+ if (!main_thread.stack) {
+ main_thread.stack = (void *)bottom_old;
+ main_thread.stack_size = size_old;
+ }
+}
+
+static void start_switch_fiber(struct bthread *to, bool terminate_old)
+{
+ __sanitizer_start_switch_fiber(terminate_old ? &to->fake_stack_save : NULL,
+ to->stack, to->stack_size);
+}
+
+#else
+
+static void finish_switch_fiber(struct bthread *bthread)
+{
+}
+
+static void start_switch_fiber(struct bthread *to, bool terminate_old)
+{
+}
+
+#endif
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/clock.c b/common/clock.c
index 17b07abdc1..b300e5798a 100644
--- a/common/clock.c
+++ b/common/clock.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* clock.c - generic clocksource implementation
*
@@ -6,32 +7,16 @@
*
* Copyright (C) 2004, 2005 IBM, John Stultz (johnstul@us.ibm.com)
* Copyright (c) 2007 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 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.
- *
*/
#include <common.h>
#include <init.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
#include <clock.h>
-#include <poller.h>
+#include <sched.h>
static uint64_t time_ns;
-/*
- * The first timestamp when the clocksource is registered.
- * Useful for measuring the time spent in barebox.
- */
-uint64_t time_beginning;
-
static uint64_t dummy_read(void)
{
static uint64_t dummy_counter;
@@ -181,7 +166,7 @@ int is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
int ret = is_timeout_non_interruptible(start_ns, time_offset_ns);
if (time_offset_ns >= 100 * USECOND)
- poller_call();
+ resched();
return ret;
}
@@ -231,8 +216,13 @@ int init_clock(struct clocksource *cs)
return ret;
}
+ /*
+ * If clocksource is freerunning it might have been running for a while
+ * before barebox started, we only care about the time spent in barebox
+ * thus we must discard the clocksource cycles up to this exact moment:
+ */
+ cs->cycle_last = cs->read() & cs->mask;
current_clock = cs;
- time_beginning = get_time_ns();
return 0;
}
diff --git a/common/command.c b/common/command.c
index c7c0c4c103..014b85f9a3 100644
--- a/common/command.c
+++ b/common/command.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2000-2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
*/
/*
diff --git a/common/complete.c b/common/complete.c
index 919e5abc6a..3911535621 100644
--- a/common/complete.c
+++ b/common/complete.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* complete.c - functions for TAB completion
*
* Copyright (c) 2008 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 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.
- *
*/
#include <common.h>
@@ -23,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)
@@ -43,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);
@@ -102,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))
@@ -110,14 +123,7 @@ static int path_command_complete(struct string_list *sl, char *instr)
else
strcat(tmp, " ");
- /* This function is called
- * after command_complete,
- * so we check if a double
- * entry exist */
- if (string_list_contains
- (sl, tmp) == 0) {
- string_list_add_sorted(sl, tmp);
- }
+ string_list_add_sort_uniq(sl, tmp);
}
}
@@ -150,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));
@@ -169,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;
@@ -245,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);
@@ -266,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;
@@ -315,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;
}
@@ -330,12 +348,18 @@ 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;
}
+int env_param_noeval_complete(struct string_list *sl, char *instr)
+{
+ return env_param_complete(sl, instr, 0);
+}
+EXPORT_SYMBOL(env_param_noeval_complete);
+
static int tab_pressed = 0;
void complete_reset(void)
@@ -343,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);
@@ -402,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);
@@ -417,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 3375ecb7e5..5a0fd66ab3 100644
--- a/common/console.c
+++ b/common/console.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2000
* Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
- *
- * 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.
- *
*/
#include <config.h>
@@ -27,7 +17,7 @@
#include <clock.h>
#include <kfifo.h>
#include <module.h>
-#include <poller.h>
+#include <sched.h>
#include <ratp_bb.h>
#include <magicvar.h>
#include <globalvar.h>
@@ -175,12 +165,12 @@ int console_set_baudrate(struct console_device *cdev, unsigned baudrate)
int ret;
unsigned char c;
- if (!cdev->setbrg)
- return -ENOSYS;
-
if (cdev->baudrate == baudrate)
return 0;
+ if (!cdev->setbrg)
+ return -ENOSYS;
+
/*
* If the device is already active, change its baudrate.
* The baudrate of an inactive device will be set at activation time.
@@ -230,7 +220,22 @@ static void console_init_early(void)
initialized = CONSOLE_INITIALIZED_BUFFER;
}
-static void console_set_stdoutpath(struct console_device *cdev)
+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;
char *str;
@@ -238,26 +243,45 @@ static void console_set_stdoutpath(struct console_device *cdev)
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;
- str = basprintf("console=%s%d,%dn8", cdev->linux_console_name, id,
- cdev->baudrate);
+ str = basprintf("console=%s%d,%dn8", cdev->linux_console_name, id, baudrate);
globalvar_add_simple("linux.bootargs.console", str);
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++;
@@ -305,8 +329,9 @@ 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;
if (!serdev_node && initialized == CONSOLE_UNINITIALIZED)
console_init_early();
@@ -337,15 +362,24 @@ int console_register(struct console_device *newcdev)
if (serdev_node)
return of_platform_populate(serdev_node, NULL, dev);
+ if (newcdev->dev && of_device_is_stdout_path(newcdev->dev, &baudrate)) {
+ activate = CONSOLE_STDIOE;
+ console_set_stdoutpath(newcdev, baudrate);
+ }
+
+ console_add_earlycon_param(newcdev, baudrate);
+
if (newcdev->setbrg) {
- ret = newcdev->setbrg(newcdev, CONFIG_BAUDRATE);
+ ret = newcdev->setbrg(newcdev, baudrate);
if (ret)
return ret;
- newcdev->baudrate_param = newcdev->baudrate = CONFIG_BAUDRATE;
+ newcdev->baudrate_param = baudrate;
dev_add_param_uint32(dev, "baudrate", console_baudrate_set,
NULL, &newcdev->baudrate_param, "%u", newcdev);
}
+ newcdev->baudrate = baudrate;
+
if (newcdev->putc && !newcdev->puts)
newcdev->puts = __console_puts;
@@ -354,21 +388,18 @@ int console_register(struct console_device *newcdev)
if (IS_ENABLED(CONFIG_CONSOLE_ACTIVATE_FIRST)) {
if (list_empty(&console_list))
- activate = 1;
+ activate = CONSOLE_STDIOE;
} else if (IS_ENABLED(CONFIG_CONSOLE_ACTIVATE_ALL)) {
- activate = 1;
- }
-
- if (newcdev->dev && of_device_is_stdout_path(newcdev->dev)) {
- activate = 1;
- console_set_stdoutpath(newcdev);
+ activate = CONSOLE_STDIOE;
}
list_add_tail(&newcdev->list, &console_list);
+ if (IS_ENABLED(CONFIG_CONSOLE_DISABLE_INPUT))
+ activate &= ~CONSOLE_STDIN;
+
if (activate)
- console_set_active(newcdev, CONSOLE_STDIN |
- CONSOLE_STDOUT | CONSOLE_STDERR);
+ console_set_active(newcdev, activate);
/* expose console as device in fs */
newcdev->devfs.name = basprintf("%s%d", newcdev->class_dev.name,
@@ -395,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;
/*
@@ -589,15 +620,15 @@ int ctrlc(void)
{
int ret = 0;
+ resched();
+
if (!ctrlc_allowed)
return 0;
if (ctrlc_abort)
return 1;
- poller_call();
-
-#ifdef ARCH_HAS_CTRLC
+#ifdef CONFIG_ARCH_HAS_CTRLC
ret = arch_ctrlc();
#else
if (tstc() && getchar() == 3)
@@ -628,8 +659,8 @@ void console_ctrlc_forbid(void)
ctrlc_allowed = 0;
}
-BAREBOX_MAGICVAR_NAMED(global_console_ctrlc_allowed, global.console.ctrlc_allowed,
+BAREBOX_MAGICVAR(global.console.ctrlc_allowed,
"If true, scripts can be aborted with ctrl-c");
-BAREBOX_MAGICVAR_NAMED(global_linux_bootargs_console, global.linux.bootargs.console,
+BAREBOX_MAGICVAR(global.linux.bootargs.console,
"console= argument for Linux from the stdout-path property in /chosen node");
diff --git a/common/console_common.c b/common/console_common.c
index a174c2deed..0113a64138 100644
--- a/common/console_common.c
+++ b/common/console_common.c
@@ -1,54 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* based on code:
*
* (C) Copyright 2000 Paolo Scaffardi, AIRVENT SAM s.p.a -
* RIMINI(ITALY), arsenio@tin.it
- *
- * 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.
- *
*/
#include <common.h>
#include <fs.h>
#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 <asm-generic/div64.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--;
@@ -77,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;
@@ -86,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)
@@ -98,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;
@@ -121,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, ...)
@@ -135,7 +128,7 @@ int pr_print(int level, const char *fmt, ...)
return 0;
va_start(args, fmt);
- i = vsprintf(printbuffer, fmt, args);
+ i = vsnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
pr_puts(level, printbuffer);
@@ -143,23 +136,24 @@ 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;
char printbuffer[CFG_PBSIZE];
+ size_t size = sizeof(printbuffer);
if (!IS_ENABLED(CONFIG_LOGBUF) && level > barebox_loglevel)
return 0;
- if (dev->driver && dev->driver->name)
- ret += sprintf(printbuffer, "%s ", dev->driver->name);
+ if (dev && dev->driver && dev->driver->name)
+ ret += snprintf(printbuffer, size, "%s ", dev->driver->name);
- ret += sprintf(printbuffer + ret, "%s: ", dev_name(dev));
+ ret += snprintf(printbuffer + ret, size - ret, "%s: ", dev_name(dev));
va_start(args, format);
- ret += vsprintf(printbuffer + ret, format, args);
+ ret += vsnprintf(printbuffer + ret, size - ret, format, args);
va_end(args);
@@ -181,50 +175,73 @@ 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);
-void log_print(unsigned flags, unsigned levels)
+int log_writefile(const char *filepath)
+{
+ int ret = 0, nbytes = 0, fd = -1;
+ struct log_entry *log;
+
+ fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC);
+ if (fd < 0)
+ return -errno;
+
+ list_for_each_entry(log, &barebox_logbuf, list) {
+ ret = dputs(fd, log->msg);
+ if (ret < 0)
+ break;
+ nbytes += ret;
+ }
+
+ close(fd);
+ return ret < 0 ? ret : nbytes;
+}
+
+int log_print(unsigned flags, unsigned levels)
{
struct log_entry *log;
unsigned long last = 0;
list_for_each_entry(log, &barebox_logbuf, list) {
- uint64_t diff = log->timestamp - time_beginning;
- unsigned long difful;
+ uint64_t time_ns = log->timestamp;
+ unsigned long time;
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);
- do_div(diff, 1000);
- difful = diff;
-
- if (!log->timestamp)
- difful = 0;
+ /* convert ns to us */
+ do_div(time_ns, 1000);
+ time = time_ns;
if (flags & (BAREBOX_LOG_PRINT_TIME | BAREBOX_LOG_DIFF_TIME))
printf("[");
if (flags & BAREBOX_LOG_PRINT_TIME)
- printf("%10luus", difful);
+ printf("%10luus", time);
if (flags & BAREBOX_LOG_DIFF_TIME) {
- printf(" < %10luus", difful - last);
- last = difful;
+ printf(" < %10luus", time - last);
+ last = time;
}
if (flags & (BAREBOX_LOG_PRINT_TIME | BAREBOX_LOG_DIFF_TIME))
@@ -232,6 +249,8 @@ void log_print(unsigned flags, unsigned levels)
printf("%s", log->msg);
}
+
+ return 0;
}
int printf(const char *fmt, ...)
@@ -246,7 +265,7 @@ int printf(const char *fmt, ...)
* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
- i = vsprintf (printbuffer, fmt, args);
+ i = vsnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Print the string */
@@ -265,7 +284,7 @@ int vprintf(const char *fmt, va_list args)
* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
- i = vsprintf(printbuffer, fmt, args);
+ i = vsnprintf(printbuffer, sizeof(printbuffer), fmt, args);
/* Print the string */
puts(printbuffer);
@@ -274,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;
@@ -323,6 +342,23 @@ struct console_device *console_get_first_active(void)
}
EXPORT_SYMBOL(console_get_first_active);
+struct console_device *of_console_get_by_alias(const char *alias)
+{
+ struct device_node *node;
+ struct device *dev;
+
+ node = of_find_node_by_alias(NULL, alias);
+ if (!node)
+ return NULL;
+
+ dev = of_find_device_by_node(node);
+ if (!dev)
+ return NULL;
+
+ return console_get_by_dev(dev);
+}
+EXPORT_SYMBOL(of_console_get_by_alias);
+
#endif /* !CONFIG_CONSOLE_NONE */
int dprintf(int file, const char *fmt, ...)
@@ -336,7 +372,7 @@ int dprintf(int file, const char *fmt, ...)
* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
- vsprintf(printbuffer, fmt, args);
+ vsnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Print the string */
diff --git a/common/console_countdown.c b/common/console_countdown.c
index 74dc382795..e41641aca2 100644
--- a/common/console_countdown.c
+++ b/common/console_countdown.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* console_countdown - contdown on the console - interruptible by a keypress
*
* Copyright (c) 2007 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 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.
- *
*/
#include <clock.h>
@@ -64,7 +55,7 @@ int console_countdown(int timeout_s, unsigned flags, const char *keys,
goto out;
if (flags & CONSOLE_COUNTDOWN_ANYKEY)
goto out;
- if (flags & CONSOLE_COUNTDOWN_RETURN && key == '\n')
+ if (flags & CONSOLE_COUNTDOWN_RETURN && (key == '\n' || key == '\r'))
goto out;
if (flags & CONSOLE_COUNTDOWN_CTRLC && key == 3)
goto out;
diff --git a/common/console_simple.c b/common/console_simple.c
index 42224842c5..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)
@@ -92,7 +94,10 @@ int console_register(struct console_device *newcdev)
newcdev->setbrg(newcdev, newcdev->baudrate);
}
- newcdev->f_active = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR;
+ newcdev->f_active = CONSOLE_STDIOE;
+
+ if (IS_ENABLED(CONFIG_CONSOLE_DISABLE_INPUT))
+ newcdev->f_active &= ~CONSOLE_STDIN;
barebox_banner();
diff --git a/common/date.c b/common/date.c
index 1fea02cae0..69d82e2d52 100644
--- a/common/date.c
+++ b/common/date.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2001
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
*/
/**
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 23df3e7119..b7693f3fd2 100644
--- a/common/ddr_spd.c
+++ b/common/ddr_spd.c
@@ -1,14 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Freescale Semiconductor, Inc.
- *
- * 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.
*/
#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)
@@ -67,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
@@ -79,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;
@@ -98,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"
@@ -119,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) {
@@ -233,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;
@@ -248,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);
@@ -422,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,
@@ -443,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;
@@ -469,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;
@@ -482,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
new file mode 100644
index 0000000000..931e5a1770
--- /dev/null
+++ b/common/deep-probe.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "deep-probe: " fmt
+
+#include <common.h>
+#include <deep-probe.h>
+#include <of.h>
+
+enum deep_probe_state {
+ DEEP_PROBE_UNKONWN = -1,
+ DEEP_PROBE_NOT_SUPPORTED,
+ DEEP_PROBE_SUPPORTED
+};
+
+static enum deep_probe_state boardstate = DEEP_PROBE_UNKONWN;
+
+bool deep_probe_is_supported(void)
+{
+ struct deep_probe_entry *board;
+
+ if (boardstate > DEEP_PROBE_UNKONWN)
+ return boardstate;
+
+ /* determine boardstate */
+ for (board = &__barebox_deep_probe_start;
+ board != &__barebox_deep_probe_end; board++) {
+ const struct of_device_id *matches = board->device_id;
+
+ for (; matches->compatible; matches++) {
+ if (of_machine_is_compatible(matches->compatible)) {
+ boardstate = DEEP_PROBE_SUPPORTED;
+ pr_info("supported due to %s\n", matches->compatible);
+ return true;
+ }
+ }
+ }
+
+ boardstate = DEEP_PROBE_NOT_SUPPORTED;
+ return false;
+}
+EXPORT_SYMBOL_GPL(deep_probe_is_supported);
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 0120d9be2e..7a96afec76 100644
--- a/common/dummy_malloc.c
+++ b/common/dummy_malloc.c
@@ -1,25 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * 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 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.
- *
- *
- * As a special exception, if other files instantiate templates or use macros
- * or inline functions from this file, or you compile this file and link it
- * with other works to produce a work based on this file, this file does not
- * by itself cause the resulting work to be covered by the GNU General Public
- * License. However the source code for this file must still be made available
- * in accordance with section (3) of the GNU General Public License.
-
- * This exception does not invalidate any other reasons why a work based on
- * this file might be covered by the GNU General Public License.
*/
#include <common.h>
#include <malloc.h>
@@ -42,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 ef19969f93..0000000000
--- a/common/efi/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-y += efi.o
-obj-y += efi-image.o
-bbenv-y += env-efi
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.c b/common/efi/efi.c
deleted file mode 100644
index 6f55e3970e..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_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);
-
- 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 4733accb05..62f793010f 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -5,21 +5,32 @@
#include <common.h>
#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libfile.h>
#include <memory.h>
+#include <unistd.h>
+#include <zero_page.h>
+#include <linux/fs.h>
+#include <linux/list_sort.h>
struct elf_section {
struct list_head list;
struct resource *r;
+ void *phdr;
};
static int elf_request_region(struct elf_image *elf, resource_size_t start,
- resource_size_t size)
+ resource_size_t size, void *phdr)
{
struct list_head *list = &elf->list;
struct resource *r_new;
struct elf_section *r;
- r = xzalloc(sizeof(*r));
+ r = calloc(1, sizeof(*r));
+ if (!r)
+ return -ENOMEM;
+
r_new = request_sdram_region("elf_section", start, size);
if (!r_new) {
pr_err("Failed to request region: %pa %pa\n", &start, &size);
@@ -27,6 +38,7 @@ static int elf_request_region(struct elf_image *elf, resource_size_t start,
}
r->r = r_new;
+ r->phdr = phdr;
list_add_tail(&r->list, list);
return 0;
@@ -39,108 +51,297 @@ static void elf_release_regions(struct elf_image *elf)
list_for_each_entry_safe(r, r_tmp, list, list) {
release_sdram_region(r->r);
+ list_del(&r->list);
free(r);
}
}
-
-static int load_elf_phdr_segment(struct elf_image *elf, void *src,
- void *phdr)
+static int request_elf_segment(struct elf_image *elf, void *phdr)
{
- void *dst = (void *) elf_phdr_p_paddr(elf, 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;
- pr_debug("Loading phdr to 0x%p (%llu bytes)\n", dst, p_filesz);
+ if (dst < elf->low_addr)
+ elf->low_addr = dst;
+ if (dst + p_memsz > elf->high_addr)
+ elf->high_addr = dst + p_memsz;
+
+ pr_debug("Requesting segment 0x%p (%llu bytes)\n", dst, p_memsz);
- ret = elf_request_region(elf, (resource_size_t)dst, p_filesz);
+ ret = elf_request_region(elf, (resource_size_t)dst, p_memsz, phdr);
if (ret)
return ret;
- memcpy(dst, src, p_filesz);
+ return 0;
+}
+
+static int elf_section_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct elf_image *elf = priv;
+ struct elf_section *elf_a, *elf_b;
- if (p_filesz < p_memsz)
- memset(dst + p_filesz, 0x00,
- p_memsz - p_filesz);
+ if (a == b)
+ return 0;
- return 0;
+ elf_a = list_entry(a, struct elf_section, list);
+ elf_b = list_entry(b, struct elf_section, list);
+
+ return elf_phdr_p_offset(elf, elf_a->phdr) >
+ elf_phdr_p_offset(elf, elf_b->phdr);
+}
+
+static int load_elf_to_memory(struct elf_image *elf)
+{
+ void *dst;
+ int ret = 0, fd = -1;
+ u64 p_filesz, p_memsz, p_offset;
+ struct elf_section *r;
+ struct list_head *list = &elf->list;
+
+ 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);
+
+ pr_debug("Loading phdr offset 0x%llx to 0x%p (%llu bytes)\n",
+ p_offset, dst, p_filesz);
+
+ 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 ret >= 0 ? 0 : ret;
}
-static int load_elf_image_phdr(struct elf_image *elf)
+static int load_elf_image_segments(struct elf_image *elf)
{
- void *buf = elf->buf;
+ void *buf = elf->hdr_buf;
void *phdr = (void *) (buf + elf_hdr_e_phoff(elf, buf));
int i, ret;
- elf->entry = elf_hdr_e_entry(elf, buf);
+ /* File as already been loaded */
+ if (!list_empty(&elf->list))
+ return -EINVAL;
for (i = 0; i < elf_hdr_e_phnum(elf, buf) ; ++i) {
- void *src = buf + elf_phdr_p_offset(elf, phdr);
-
- ret = load_elf_phdr_segment(elf, src, phdr);
- /* in case of error elf_load_image() caller should clean up and
- * call elf_release_image() */
+ ret = request_elf_segment(elf, phdr);
if (ret)
- return ret;
+ goto elf_release_regions;
phdr += elf_size_of_phdr(elf);
}
+ /*
+ * Sort the list to avoid doing backward lseek while loading the elf
+ * segments from file to memory(some filesystems don't support it)
+ */
+ list_sort(elf, &elf->list, elf_section_cmp);
+
+ ret = load_elf_to_memory(elf);
+ if (ret)
+ goto elf_release_regions;
+
return 0;
+
+elf_release_regions:
+ elf_release_regions(elf);
+
+ return ret;
}
-static int elf_check_image(struct elf_image *elf)
+static int elf_check_image(struct elf_image *elf, void *buf)
{
- if (strncmp(elf->buf, ELFMAG, SELFMAG)) {
+ if (strncmp(buf, ELFMAG, SELFMAG)) {
pr_err("ELF magic not found.\n");
return -EINVAL;
}
- elf->class = ((char *) elf->buf)[EI_CLASS];
+ elf->class = ((char *) buf)[EI_CLASS];
- if (elf_hdr_e_type(elf, elf->buf) != ET_EXEC) {
+ if (elf_hdr_e_type(elf, buf) != ET_EXEC) {
pr_err("Non EXEC ELF image.\n");
return -ENOEXEC;
}
+ if (!elf_hdr_e_phnum(elf, buf)) {
+ pr_err("No phdr found.\n");
+ return -ENOEXEC;
+ }
+
return 0;
}
-struct elf_image *elf_load_image(void *buf)
+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)
{
- struct elf_image *elf;
int ret;
+ struct elf_image *elf;
- elf = xzalloc(sizeof(*elf));
+ elf = calloc(1, sizeof(*elf));
+ if (!elf)
+ return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&elf->list);
+ elf_init_struct(elf);
+
+ elf->hdr_buf = buf;
+ ret = elf_check_image(elf, buf);
+ if (ret) {
+ free(elf);
+ return ERR_PTR(-EINVAL);
+ }
- elf->buf = buf;
+ elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf);
- ret = elf_check_image(elf);
+ return elf;
+}
+
+static struct elf_image *elf_check_init(const char *filename)
+{
+ int ret, fd;
+ int hdr_size;
+ struct elf64_hdr hdr;
+ struct elf_image *elf;
+
+ elf = calloc(1, sizeof(*elf));
+ if (!elf)
+ return ERR_PTR(-ENOMEM);
+
+ 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: %m\n");
+ ret = -errno;
+ goto err_free_elf;
+ }
+
+ if (read_full(fd, &hdr, sizeof(hdr)) < 0) {
+ pr_err("could not read elf header: %m\n");
+ close(fd);
+ ret = -errno;
+ goto err_free_elf;
+ }
+ close(fd);
+
+ ret = elf_check_image(elf, &hdr);
if (ret)
- return ERR_PTR(ret);
+ goto err_free_elf;
- ret = load_elf_image_phdr(elf);
- if (ret) {
- elf_release_image(elf);
- return ERR_PTR(ret);
+ hdr_size = elf_hdr_e_phoff(elf, &hdr) +
+ elf_hdr_e_phnum(elf, &hdr) *
+ elf_hdr_e_phentsize(elf, &hdr);
+
+ /* Second pass is to read the whole elf header and program headers */
+ elf->hdr_buf = malloc(hdr_size);
+ if (!elf->hdr_buf) {
+ ret = -ENOMEM;
+ goto err_free_elf;
}
+ /*
+ * We must open the file again since some fs (tftp) do not support
+ * backward lseek operations
+ */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ 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: %m\n");
+ ret = -errno;
+ close(fd);
+ goto err_free_hdr_buf;
+ }
+ close(fd);
+
+ elf->filename = strdup(filename);
+ if (!elf->filename) {
+ ret = -ENOMEM;
+ goto err_free_hdr_buf;
+ }
+
+ elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf);
+
return elf;
+
+err_free_hdr_buf:
+ free(elf->hdr_buf);
+err_free_elf:
+ free(elf);
+
+ return ERR_PTR(ret);
}
-void elf_release_image(struct elf_image *elf)
+struct elf_image *elf_open(const char *filename)
+{
+ return elf_check_init(filename);
+}
+
+int elf_load(struct elf_image *elf)
+{
+ return load_elf_image_segments(elf);
+}
+
+void elf_close(struct elf_image *elf)
{
elf_release_regions(elf);
+ if (elf->filename) {
+ free(elf->hdr_buf);
+ free(elf->filename);
+ }
+
free(elf);
}
diff --git a/common/env.c b/common/env.c
index fbaaac4f2f..7a213cadb2 100644
--- a/common/env.c
+++ b/common/env.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* env.c - environment variables storage
*
* Copyright (c) 2007 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 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.
- *
*/
/**
@@ -89,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;
@@ -136,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;
@@ -224,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;
@@ -251,15 +242,20 @@ static int dev_setenv(const char *name, const char *val)
return -ENODEV;
}
+/**
+ * setenv - set environment variables
+ * @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);
int ret = 0;
struct list_head *list;
- if (value && !*value)
- value = NULL;
-
if (strchr(name, '.')) {
ret = dev_setenv(name, value);
if (ret)
@@ -280,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);
@@ -318,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 6e58f122ba..39cad0c16a 100644
--- a/common/environment.c
+++ b/common/environment.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2007 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 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.
- *
*/
/**
@@ -35,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
@@ -58,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(const char *path)
+{
+ free(default_environment_path);
+
+ default_environment_path = xstrdup(path);
+}
+
+static guid_t partition_barebox_env_guid = PARTITION_BAREBOX_ENVIRONMENT_GUID;
-void default_environment_path_set(char *path)
+/*
+ * 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)
{
- default_environment_path = path;
+ 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;
}
-char *default_environment_path_get(void)
+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;
}
@@ -306,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;
}
@@ -315,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;
}
@@ -323,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;
}
@@ -346,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;
}
@@ -394,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
new file mode 100644
index 0000000000..d8dabd89ab
--- /dev/null
+++ b/common/fastboot.c
@@ -0,0 +1,1001 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ *
+ * Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de>
+ * Ported to barebox
+ *
+ * Copyright 2020 Edmund Henniges <eh@emlix.com>
+ * Copyright 2020 Daniel Glöckner <dg@emlix.com>
+ * Split off of generic parts
+ */
+
+#define pr_fmt(fmt) "fastboot: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <ioctl.h>
+#include <bbu.h>
+#include <bootm.h>
+#include <fs.h>
+#include <init.h>
+#include <libfile.h>
+#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>
+#include <restart.h>
+#include <console_countdown.h>
+#include <image-sparse.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/mtd/mtd.h>
+#include <fastboot.h>
+#include <system-partitions.h>
+
+#define FASTBOOT_VERSION "0.4"
+
+static unsigned int fastboot_max_download_size;
+static int fastboot_bbu;
+static char *fastboot_partitions;
+
+struct fb_variable {
+ char *name;
+ char *value;
+ struct list_head list;
+};
+
+static void fb_setvar(struct fb_variable *var, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ var->value = bvasprintf(fmt, ap);
+ va_end(ap);
+}
+
+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;
+
+ va_start(ap, fmt);
+ var->name = bvasprintf(fmt, ap);
+ va_end(ap);
+
+ list_add_tail(&var->list, list);
+
+ return var;
+}
+
+static int fastboot_add_partition_variables(struct fastboot *fb, struct list_head *list,
+ struct file_list_entry *fentry)
+{
+ struct stat s;
+ size_t size = 0;
+ int fd, ret;
+ struct mtd_info_user mtdinfo;
+ char *type = NULL;
+ struct fb_variable *var;
+
+ ret = stat(fentry->filename, &s);
+ if (ret) {
+ device_detect_by_name(devpath_to_name(fentry->filename));
+ ret = stat(fentry->filename, &s);
+ }
+
+ 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";
+ goto out;
+ }
+
+ goto out;
+ }
+
+ fd = open(fentry->filename, O_RDWR);
+ if (fd < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = s.st_size;
+
+ ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+
+ close(fd);
+
+ if (!ret) {
+ switch (mtdinfo.type) {
+ case MTD_NANDFLASH:
+ type = "NAND-flash";
+ break;
+ case MTD_NORFLASH:
+ type = "NOR-flash";
+ break;
+ case MTD_UBIVOLUME:
+ type = "UBI";
+ break;
+ default:
+ type = "flash";
+ break;
+ }
+
+ goto out;
+ }
+
+ type = "basic";
+ ret = 0;
+
+out:
+ if (ret)
+ return ret;
+
+ var = fb_addvar(fb, list, "partition-size:%s", fentry->name);
+ fb_setvar(var, "%08zx", size);
+ var = fb_addvar(fb, list, "partition-type:%s", fentry->name);
+ fb_setvar(var, "%s", type);
+
+ return ret;
+}
+
+int fastboot_generic_init(struct fastboot *fb, bool export_bbu)
+{
+ struct fb_variable *var;
+
+ INIT_LIST_HEAD(&fb->variables);
+
+ var = fb_addvar(fb, &fb->variables, "version");
+ fb_setvar(var, "0.4");
+ var = fb_addvar(fb, &fb->variables, "bootloader-version");
+ fb_setvar(var, release_string);
+ if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) {
+ var = fb_addvar(fb, &fb->variables, "max-download-size");
+ fb_setvar(var, "%u", fastboot_max_download_size);
+ }
+
+ fb->tempname = make_temp("fastboot");
+ if (!fb->tempname)
+ return -ENOMEM;
+
+ if (!fb->files)
+ fb->files = file_list_new();
+ if (export_bbu)
+ bbu_append_handlers_to_file_list(fb->files);
+
+ return 0;
+}
+
+static void fastboot_free_variables(struct list_head *list)
+{
+ struct fb_variable *var, *tmp;
+
+ 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);
+
+ fb->active = false;
+}
+
+static struct fastboot *g_fb;
+
+void fastboot_generic_close(struct fastboot *fb)
+{
+ if (g_fb == fb)
+ g_fb = NULL;
+}
+
+/*
+ * A "oem exec bootm" or similar commands will stop barebox. Tell the
+ * fastboot command on the other side so that it doesn't run into a
+ * timeout.
+ */
+static void fastboot_shutdown(void)
+{
+ struct fastboot *fb = g_fb;
+
+ if (!fb || !fb->active)
+ return;
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "barebox shutting down");
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+early_exitcall(fastboot_shutdown);
+
+static char *fastboot_msg[] = {
+ [FASTBOOT_MSG_OKAY] = "OKAY",
+ [FASTBOOT_MSG_FAIL] = "FAIL",
+ [FASTBOOT_MSG_INFO] = "INFO",
+ [FASTBOOT_MSG_DATA] = "DATA",
+ [FASTBOOT_MSG_NONE] = "",
+};
+
+int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ char buf[64];
+ va_list ap;
+ int n;
+ const char *msg = fastboot_msg[type];
+
+ va_start(ap, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &ap;
+
+ n = snprintf(buf, 64, "%s%pV", msg, &vaf);
+
+ switch (type) {
+ case FASTBOOT_MSG_OKAY:
+ fb->active = false;
+ break;
+ case FASTBOOT_MSG_FAIL:
+ fb->active = false;
+ pr_err("%pV\n", &vaf);
+ break;
+ case FASTBOOT_MSG_INFO:
+ pr_info("%pV\n", &vaf);
+ break;
+ case FASTBOOT_MSG_NONE:
+ case FASTBOOT_MSG_DATA:
+ break;
+ }
+
+ va_end(ap);
+
+ if (n > 64)
+ n = 64;
+
+ return fb->write(fb, buf, n);
+}
+
+static void cb_reboot(struct fastboot *fb, const char *cmd)
+{
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+ restart_machine();
+}
+
+static int strcmp_l1(const char *s1, const char *s2)
+{
+ if (!s1 || !s2)
+ return -1;
+ return strncmp(s1, s2, strlen(s1));
+}
+
+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)
+ 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, "");
+ goto out;
+ }
+
+ list_for_each_entry(var, &fb->variables, list) {
+ if (!strcmp(cmd, var->name)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, var->value);
+ 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,
+ unsigned int len)
+{
+ int ret;
+
+ ret = write(fb->download_fd, buffer, len);
+ if (ret < 0)
+ return ret;
+
+ fb->download_bytes += len;
+ show_progress(fb->download_bytes);
+ return 0;
+}
+
+void fastboot_download_finished(struct fastboot *fb)
+{
+ close(fb->download_fd);
+ fb->download_fd = 0;
+
+ printf("\n");
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished",
+ fb->download_bytes);
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+void fastboot_abort(struct fastboot *fb)
+{
+ if (fb->download_fd > 0) {
+ close(fb->download_fd);
+ fb->download_fd = 0;
+ }
+
+ fb->active = false;
+
+ unlink(fb->tempname);
+}
+
+static void cb_download(struct fastboot *fb, const char *cmd)
+{
+ fb->download_size = simple_strtoul(cmd, NULL, 16);
+ fb->download_bytes = 0;
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...",
+ fb->download_size);
+
+ init_progression_bar(fb->download_size);
+
+ if (fb->download_fd > 0) {
+ pr_err("%s called and %s is still opened\n", __func__, fb->tempname);
+ close(fb->download_fd);
+ }
+
+ fb->download_fd = open(fb->tempname, O_WRONLY | O_CREAT | O_TRUNC);
+ if (fb->download_fd < 0) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "internal error");
+ return;
+ }
+
+ if (!fb->download_size)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "data invalid size");
+ else
+ fb->start_download(fb);
+}
+
+void fastboot_start_download_generic(struct fastboot *fb)
+{
+ fastboot_tx_print(fb, FASTBOOT_MSG_DATA, "%08x", fb->download_size);
+}
+
+static void __maybe_unused cb_boot(struct fastboot *fb, const char *opt)
+{
+ int ret;
+ struct bootm_data data = {
+ .initrd_address = UIMAGE_INVALID_ADDRESS,
+ .os_address = UIMAGE_SOME_ADDRESS,
+ };
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Booting kernel..\n");
+
+ globalvar_set_match("linux.bootargs.dyn.", "");
+ globalvar_set("bootm.image", "");
+
+ data.os_file = fb->tempname;
+
+ ret = bootm_boot(&data);
+
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "Booting failed: %s",
+ strerror(-ret));
+ else
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static struct mtd_info *get_mtd(struct fastboot *fb, const char *filename)
+{
+ int fd, ret;
+ struct mtd_info_user meminfo;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return ERR_PTR(-errno);
+
+ ret = ioctl(fd, MEMGETINFO, &meminfo);
+
+ close(fd);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ return meminfo.mtd;
+}
+
+static int do_ubiformat(struct fastboot *fb, struct mtd_info *mtd,
+ const char *file, size_t len)
+{
+ struct ubiformat_args args = {
+ .yes = 1,
+ .image = file,
+ .image_size = len,
+ };
+
+ if (!file)
+ args.novtbl = 1;
+
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "ubiformat is not available");
+ return -ENODEV;
+ }
+
+ return ubiformat(mtd, &args);
+}
+
+
+static int check_ubi(struct fastboot *fb, struct file_list_entry *fentry,
+ enum filetype filetype)
+{
+ struct mtd_info *mtd;
+
+ mtd = get_mtd(fb, fentry->filename);
+
+ /*
+ * Issue a warning when we are about to write a UBI image to a MTD device
+ * and the FILE_LIST_FLAG_UBI is not given as this means we loose all
+ * erase counters.
+ */
+ if (!IS_ERR(mtd) && filetype == filetype_ubi &&
+ !(fentry->flags & FILE_LIST_FLAG_UBI)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+ "writing UBI image to MTD device, "
+ "add the 'u' ");
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+ "flag to the partition description");
+ return 0;
+ }
+
+ if (!(fentry->flags & FILE_LIST_FLAG_UBI))
+ return 0;
+
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "ubiformat not available");
+ return -ENOSYS;
+ }
+
+ if (IS_ERR(mtd)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "UBI flag given on non-MTD device");
+ return -EINVAL;
+ }
+
+ if (filetype == filetype_ubi) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+ "This is a UBI image...");
+ return 1;
+ } else {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "This is no UBI image but %s",
+ file_type_to_string(filetype));
+ return -EINVAL;
+ }
+}
+
+static int fastboot_handle_sparse(struct fastboot *fb,
+ struct file_list_entry *fentry)
+{
+ struct sparse_image_ctx *sparse;
+ void *buf = NULL;
+ int ret, fd;
+ unsigned int flags = O_RDWR;
+ int bufsiz = SZ_128K;
+ struct stat s;
+ struct mtd_info *mtd = NULL;
+
+ ret = stat(fentry->filename, &s);
+ if (ret) {
+ if (fentry->flags & FILE_LIST_FLAG_CREATE)
+ flags |= O_CREAT;
+ else
+ return ret;
+ }
+
+ fd = open(fentry->filename, flags);
+ if (fd < 0)
+ return -errno;
+
+ ret = fstat(fd, &s);
+ if (ret)
+ goto out_close_fd;
+
+ sparse = sparse_image_open(fb->tempname);
+ if (IS_ERR(sparse)) {
+ pr_err("Cannot open sparse image\n");
+ ret = PTR_ERR(sparse);
+ goto out_close_fd;
+ }
+
+ if (S_ISREG(s.st_mode)) {
+ ret = ftruncate(fd, sparse_image_size(sparse));
+ if (ret)
+ goto out;
+ }
+
+ buf = malloc(bufsiz);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (fentry->flags & FILE_LIST_FLAG_UBI) {
+ mtd = get_mtd(fb, fentry->filename);
+ if (IS_ERR(mtd)) {
+ ret = PTR_ERR(mtd);
+ goto out;
+ }
+ }
+
+ while (1) {
+ size_t retlen;
+ loff_t pos;
+
+ ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen);
+ if (ret)
+ goto out;
+ if (!retlen)
+ break;
+
+ if (pos == 0) {
+ ret = check_ubi(fb, fentry, file_detect_type(buf, retlen));
+ if (ret < 0)
+ goto out;
+ }
+
+ if (fentry->flags & FILE_LIST_FLAG_UBI) {
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ if (pos == 0) {
+ ret = do_ubiformat(fb, mtd, NULL, 0);
+ if (ret)
+ goto out;
+ }
+
+ ret = ubiformat_write(mtd, buf, retlen, pos);
+ if (ret)
+ goto out;
+ } else {
+ discard_range(fd, retlen, pos);
+
+ pos = lseek(fd, pos, SEEK_SET);
+ if (pos == -1) {
+ ret = errno == EINVAL ? -ENOSPC : -errno;
+ goto out;
+ }
+
+ ret = write_full(fd, buf, retlen);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(buf);
+ sparse_image_close(sparse);
+out_close_fd:
+ close(fd);
+
+ return ret;
+}
+
+static void cb_flash(struct fastboot *fb, const char *cmd)
+{
+ struct file_list_entry *fentry;
+ int ret;
+ const char *filename = NULL;
+ enum filetype filetype;
+
+ 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);
+
+ fentry = file_list_entry_by_name(fb->files, cmd);
+
+ if (!fentry) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "No such partition: %s",
+ cmd);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (fb->cmd_flash) {
+ ret = fb->cmd_flash(fb, fentry, fb->tempname, fb->download_size);
+ if (ret != FASTBOOT_CMD_FALLTHROUGH)
+ goto out;
+ }
+
+ filename = fentry->filename;
+
+ if (filetype == filetype_android_sparse) {
+ if (!IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "sparse image not supported");
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = fastboot_handle_sparse(fb, fentry);
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "writing sparse image: %s",
+ strerror(-ret));
+
+ goto out;
+ }
+
+ ret = check_ubi(fb, fentry, filetype);
+ if (ret < 0)
+ goto out;
+
+ if (ret > 0) {
+ struct mtd_info *mtd;
+
+ mtd = get_mtd(fb, fentry->filename);
+
+ ret = do_ubiformat(fb, mtd, fb->tempname, fb->download_size);
+ if (ret) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "write partition: %s",
+ strerror(-ret));
+ goto out;
+ }
+
+ goto out;
+ }
+
+ 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 = {
+ .devicefile = filename,
+ .flags = BBU_FLAG_YES,
+ };
+
+ handler = bbu_find_handler_by_device(data.devicefile);
+ if (!handler)
+ goto copy;
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+ "This is a barebox image...");
+
+ ret = read_file_2(fb->tempname, &data.len, &buf,
+ fb->download_size);
+ if (ret) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "reading barebox");
+ goto out;
+ }
+
+ data.image = buf;
+ data.imagefile = fb->tempname;
+
+ ret = barebox_update(&data, handler);
+
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "update barebox: %s", strerror(-ret));
+
+ free(buf);
+
+ goto out;
+ }
+
+copy:
+ ret = copy_file(fb->tempname, filename, 1);
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "write partition: %s", strerror(-ret));
+
+out:
+ if (!ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+
+ unlink(fb->tempname);
+}
+
+static void cb_erase(struct fastboot *fb, const char *cmd)
+{
+ struct file_list_entry *fentry;
+ int ret;
+ const char *filename = NULL;
+ int fd;
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd);
+
+ file_list_for_each_entry(fb->files, fentry) {
+ if (!strcmp(cmd, fentry->name)) {
+ filename = fentry->filename;
+ break;
+ }
+ }
+
+ if (!filename) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "No such partition: %s", cmd);
+ return;
+ }
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-fd));
+
+ ret = erase(fd, ERASE_SIZE_ALL, 0);
+
+ close(fd);
+
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "cannot erase partition %s: %s",
+ filename, strerror(-ret));
+ else
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+struct cmd_dispatch_info {
+ char *cmd;
+ void (*cb)(struct fastboot *fb, const char *opt);
+};
+
+static void fb_run_command(struct fastboot *fb, const char *cmdbuf,
+ const struct cmd_dispatch_info *cmds, int num_commands)
+{
+ const struct cmd_dispatch_info *cmd;
+ int i;
+
+ console_countdown_abort();
+
+ for (i = 0; i < num_commands; i++) {
+ cmd = &cmds[i];
+
+ if (!strcmp_l1(cmd->cmd, cmdbuf)) {
+ cmd->cb(fb, cmdbuf + strlen(cmd->cmd));
+
+ return;
+ }
+ }
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "unknown command %s",
+ cmdbuf);
+}
+
+static void cb_oem_getenv(struct fastboot *fb, const char *cmd)
+{
+ const char *value;
+
+ pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+ value = getenv(cmd);
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_INFO, value ? value : "");
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static void cb_oem_setenv(struct fastboot *fb, const char *cmd)
+{
+ char *var = xstrdup(cmd);
+ char *value;
+ int ret;
+
+ pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+ value = parse_assignment(var);
+ if (!value) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = setenv(var, value);
+ if (ret)
+ goto out;
+
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+out:
+ free(var);
+
+ if (ret)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret));
+}
+
+static void cb_oem_exec(struct fastboot *fb, const char *cmd)
+{
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) {
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+ "no command support available");
+ return;
+ }
+
+ ret = run_command(cmd);
+ if (ret < 0)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret));
+ else if (ret > 0)
+ fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "");
+ else
+ fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = {
+ {
+ .cmd = "getenv ",
+ .cb = cb_oem_getenv,
+ }, {
+ .cmd = "setenv ",
+ .cb = cb_oem_setenv,
+ }, {
+ .cmd = "exec ",
+ .cb = cb_oem_exec,
+ },
+};
+
+static void __maybe_unused cb_oem(struct fastboot *fb, const char *cmd)
+{
+ pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+ fb_run_command(fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info));
+}
+
+static const struct cmd_dispatch_info cmd_dispatch_info[] = {
+ {
+ .cmd = "reboot",
+ .cb = cb_reboot,
+ }, {
+ .cmd = "getvar:",
+ .cb = cb_getvar,
+ }, {
+ .cmd = "download:",
+ .cb = cb_download,
+#if defined(CONFIG_BOOTM)
+ }, {
+ .cmd = "boot",
+ .cb = cb_boot,
+#endif
+ }, {
+ .cmd = "flash:",
+ .cb = cb_flash,
+ }, {
+ .cmd = "erase:",
+ .cb = cb_erase,
+#if defined(CONFIG_FASTBOOT_CMD_OEM)
+ }, {
+ .cmd = "oem ",
+ .cb = cb_oem,
+#endif
+ },
+};
+
+void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf)
+{
+ int ret;
+
+ g_fb = fb;
+ fb->active = true;
+
+ if (fb->cmd_exec) {
+ ret = fb->cmd_exec(fb, cmdbuf);
+ if (ret != FASTBOOT_CMD_FALLTHROUGH)
+ return;
+ }
+
+ fb_run_command(fb, cmdbuf, cmd_dispatch_info,
+ ARRAY_SIZE(cmd_dispatch_info));
+}
+
+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);
+ return system_partitions_get_null();
+}
+
+static int fastboot_globalvars_init(void)
+{
+ 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);
+
+ globalvar_alias_deprecated("usbgadget.fastboot_function",
+ "fastboot.partitions");
+ globalvar_alias_deprecated("usbgadget.fastboot_bbu",
+ "fastboot.bbu");
+ globalvar_alias_deprecated("usbgadget.fastboot_max_download_size",
+ "fastboot.max_download_size");
+
+ return 0;
+}
+
+device_initcall(fastboot_globalvars_init);
+
+BAREBOX_MAGICVAR(global.fastboot.max_download_size,
+ "Fastboot maximum download size");
+BAREBOX_MAGICVAR(global.fastboot.partitions,
+ "Partitions exported for update via fastboot");
+BAREBOX_MAGICVAR(global.fastboot.bbu,
+ "Export barebox update handlers via fastboot");
diff --git a/common/file-list.c b/common/file-list.c
index eb469cf9be..7ecc8d00bb 100644
--- a/common/file-list.c
+++ b/common/file-list.c
@@ -1,13 +1,4 @@
-/*
- * 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; version 2.
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "file_list: " fmt
@@ -15,7 +6,10 @@
#include <malloc.h>
#include <fs.h>
#include <file-list.h>
+#include <stringlist.h>
#include <linux/err.h>
+#include <driver.h>
+#include <block.h>
#define PARSE_DEVICE 0
#define PARSE_NAME 1
@@ -33,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;
@@ -44,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);
@@ -53,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));
@@ -95,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;
@@ -106,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;
}
@@ -115,19 +144,49 @@ 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);
}
-struct file_list *file_list_parse(const char *str)
+static const char *flags_to_str(int flags)
+{
+ static char str[sizeof "srcuo"];
+ char *s = str;;
+
+ if (flags & FILE_LIST_FLAG_SAFE)
+ *s++ = 's';
+ if (flags & FILE_LIST_FLAG_READBACK)
+ *s++ = 'r';
+ if (flags & FILE_LIST_FLAG_CREATE)
+ *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_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) {
@@ -146,10 +205,29 @@ out:
return ERR_PTR(ret);
}
+struct file_list *file_list_parse_null(const char *files)
+{
+ struct file_list *list;
+
+ if (!files)
+ return NULL;
+
+ list = file_list_parse(files);
+ if (IS_ERR(list)) {
+ pr_err("Parsing file list \"%s\" failed: %pe\n", files, list);
+ return NULL;
+ }
+
+ return list;
+}
+
void file_list_free(struct file_list *files)
{
struct file_list_entry *entry, *tmp;
+ if (!files)
+ return;
+
list_for_each_entry_safe(entry, tmp, &files->list, list) {
free(entry->name);
free(entry->filename);
@@ -158,3 +236,62 @@ void file_list_free(struct file_list *files)
free(files);
}
+
+struct file_list *file_list_dup(struct file_list *old)
+{
+ struct file_list_entry *old_entry;
+ struct file_list *new;
+
+ 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,
+ old_entry->flags); /* can't fail */
+ new->num_entries++;
+ }
+
+ return new;
+}
+
+char *file_list_to_str(const struct file_list *files)
+{
+ struct file_list_entry *entry;
+ struct string_list sl;
+ char *str;
+
+ if (!files)
+ return strdup("");
+
+ string_list_init(&sl);
+
+ list_for_each_entry(entry, &files->list, list) {
+ int ret = string_list_add_asprintf(&sl, "%s(%s)%s", entry->filename, entry->name,
+ flags_to_str(entry->flags));
+ if (ret) {
+ str = ERR_PTR(ret);
+ goto out;
+ }
+ }
+
+ str = string_list_join(&sl, ",");
+out:
+ string_list_free(&sl);
+
+ return str;
+}
+
+int file_list_detect_all(const struct file_list *files)
+{
+ struct file_list_entry *fentry;
+ struct stat s;
+ int i = 0;
+
+ list_for_each_entry(fentry, &files->list, list) {
+ if (stat(fentry->filename, &s))
+ continue;
+ if (device_detect_by_name(devpath_to_name(fentry->filename)) == 0)
+ i++;
+ }
+
+ return i;
+}
diff --git a/common/filetype.c b/common/filetype.c
index eda8ecb376..f922494500 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* filetype.c - detect filetypes
*
* Copyright (c) 2011 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 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.
*/
#include <common.h>
#include <filetype.h>
@@ -25,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 */
@@ -53,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" },
@@ -69,16 +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)
@@ -223,7 +226,7 @@ enum filetype is_fat_or_mbr(const unsigned char *sector, unsigned long *bootsec)
* first partition so we could check if there is a FAT boot
* sector there
*/
- *bootsec = get_unaligned_le16(&sector[MBR_Table + MBR_StartSector]);
+ *bootsec = get_unaligned_le32(&sector[MBR_Table + MBR_StartSector]);
return filetype_mbr;
}
@@ -250,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)
@@ -305,12 +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 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 &&
@@ -339,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;
@@ -357,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)
@@ -394,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);
@@ -416,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);
@@ -451,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)
@@ -469,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 609cf11822..3c7960581f 100644
--- a/common/firmware.c
+++ b/common/firmware.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Juergen Beisert <kernel@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 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.
*/
#include <firmware.h>
@@ -19,9 +11,13 @@
#include <libbb.h>
#include <libfile.h>
#include <fs.h>
+#include <globalvar.h>
+#include <magicvar.h>
#include <linux/list.h>
#include <linux/stat.h>
#include <linux/err.h>
+#include <uncompress.h>
+#include <filetype.h>
#define BUFSIZ 4096
@@ -69,13 +65,25 @@ struct firmware_mgr *firmwaremgr_find(const char *id)
* handler. This allows to retrieve the firmware handler with a phandle from
* the device tree.
*/
-struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+struct firmware_mgr *firmwaremgr_find_by_node(struct device_node *np)
{
struct firmware_mgr *mgr;
+ char *na, *nb;
- list_for_each_entry(mgr, &firmwaremgr_list, list)
- if (mgr->handler->dev->parent->device_node == np)
+ na = of_get_reproducible_name(np);
+
+ list_for_each_entry(mgr, &firmwaremgr_list, list) {
+ nb = of_get_reproducible_name(mgr->handler->device_node);
+ if (!strcmp(na, nb)) {
+ free(na);
+ free(nb);
return mgr;
+ }
+
+ free(nb);
+ }
+
+ free(na);
return NULL;
}
@@ -214,17 +222,129 @@ out:
return ret;
}
+static char *firmware_path;
+
+char *firmware_get_searchpath(void)
+{
+ return strdup(firmware_path);
+}
+
+void firmware_set_searchpath(const char *path)
+{
+ free(firmware_path);
+ firmware_path = strdup(path);
+}
+
+static bool file_exists(const char *filename)
+{
+ struct stat s;
+
+ return !stat(filename, &s);
+}
+
/*
* firmware_load_file - load a firmware to a device
*/
int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware)
{
- int ret;
- char *name = basprintf("/dev/%s", mgr->handler->id);
+ char *dst;
+ enum filetype type;
+ int ret = 0;
+ char *fw = NULL;
+ int firmwarefd = 0;
+ int devicefd = 0;
+
+ if (!firmware)
+ return -EINVAL;
+
+ if (!mgr->handler->id) {
+ pr_err("id not defined for handler\n");
+ return -ENODEV;
+ }
- ret = copy_file(firmware, name, 0);
+ dst = basprintf("/dev/%s", mgr->handler->id);
- free(name);
+ if (*firmware != '/') {
+ fw = find_path(firmware_path, firmware, file_exists);
+ if (fw)
+ firmware = fw;
+ }
+
+ firmwarefd = open(firmware, O_RDONLY);
+ if (firmwarefd < 0) {
+ printf("could not open %s: %m\n", firmware);
+ ret = firmwarefd;
+ goto out;
+ }
+
+ ret = file_name_detect_type(firmware, &type);
+ if (ret)
+ goto out;
+
+ devicefd = open(dst, O_WRONLY);
+ if (devicefd < 0) {
+ printf("could not open %s: %m\n", dst);
+ ret = devicefd;
+ goto out;
+ }
+
+ if (file_is_compressed_file(type))
+ ret = uncompress_fd_to_fd(firmwarefd, devicefd,
+ uncompress_err_stdout);
+ else
+ ret = copy_fd(firmwarefd, devicefd);
+
+out:
+ free(dst);
+ free(fw);
+
+ if (firmwarefd > 0)
+ close(firmwarefd);
+ if (devicefd > 0)
+ close(devicefd);
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");
+ globalvar_add_simple_string("firmware.path", &firmware_path);
+
+ return 0;
+}
+device_initcall(firmware_init);
+
+BAREBOX_MAGICVAR(global.firmware.path, "Firmware search path");
diff --git a/common/globalvar.c b/common/globalvar.c
index c87f2c9339..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,20 +177,17 @@ 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;
- if (!val) {
- if (p)
- free(p->value);
- return 0;
+ if (val) {
+ ret = dev_set_param(&global_device, name, val);
+ if (ret)
+ return ret;
}
- ret = dev_set_param(&global_device, name, val);
- if (ret)
- return ret;
-
if (p) {
free(p->value);
p->value = xstrdup(val);
@@ -197,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;
@@ -216,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;
@@ -293,6 +293,53 @@ int nvvar_remove(const char *name)
return ret;
}
+struct globalvar_deprecated {
+ char *newname;
+ char *oldname;
+ struct list_head list;
+};
+
+static LIST_HEAD(globalvar_deprecated_list);
+
+/*
+ * globalvar_alias_deprecated - add an alias
+ *
+ * @oldname: The old name for the variable
+ * @newname: The new name for the variable
+ *
+ * This function is a helper for globalvars that are renamed from one
+ * release to another. when a variable @oldname is found in the persistent
+ * environment a warning is issued and its value is written to @newname.
+ *
+ * Note that when both @oldname and @newname contain values then the values
+ * existing in @newname are overwritten.
+ */
+void globalvar_alias_deprecated(const char *oldname, const char *newname)
+{
+ struct globalvar_deprecated *gd;
+
+ gd = xzalloc(sizeof(*gd));
+ gd->newname = xstrdup(newname);
+ gd->oldname = xstrdup(oldname);
+ list_add_tail(&gd->list, &globalvar_deprecated_list);
+}
+
+static const char *globalvar_new_name(const char *oldname)
+{
+ struct globalvar_deprecated *gd;
+
+ list_for_each_entry(gd, &globalvar_deprecated_list, list) {
+ if (!strcmp(oldname, gd->oldname)) {
+ pr_warn("nv.%s is deprecated, converting to nv.%s\n", oldname,
+ gd->newname);
+ nv_dirty = 1;
+ return gd->newname;
+ }
+ }
+
+ return oldname;
+}
+
int nvvar_load(void)
{
char *val;
@@ -308,6 +355,8 @@ int nvvar_load(void)
return -ENOENT;
while ((d = readdir(dir))) {
+ const char *n;
+
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
@@ -316,10 +365,11 @@ int nvvar_load(void)
pr_debug("%s: Setting \"%s\" to \"%s\"\n",
__func__, d->d_name, val);
- ret = __nvvar_add(d->d_name, val);
+ n = globalvar_new_name(d->d_name);
+ ret = __nvvar_add(n, val);
if (ret)
pr_err("failed to create nv variable %s: %s\n",
- d->d_name, strerror(-ret));
+ n, strerror(-ret));
}
closedir(dir);
@@ -327,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;
@@ -399,9 +449,15 @@ void globalvar_set_match(const char *match, const char *val)
}
}
-static int globalvar_simple_set(struct device_d *dev, struct param_d *p, const char *val)
+void globalvar_set(const char *name, const char *val)
+{
+ dev_set_param(&global_device, name, 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;
@@ -514,7 +570,8 @@ int globalvar_add_simple_int(const char *name, int *value,
return 0;
}
-int globalvar_add_simple_bool(const char *name, int *value)
+int globalvar_add_simple_uint64(const char *name, u64 *value,
+ const char *format)
{
struct param_d *p;
int ret;
@@ -523,8 +580,30 @@ int globalvar_add_simple_bool(const char *name, int *value)
if (ret)
return ret;
- p = dev_add_param_bool(&global_device, name, NULL, NULL,
- value, NULL);
+ p = dev_add_param_uint64(&global_device, name, NULL, NULL,
+ value, format, NULL);
+
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ globalvar_nv_sync(name);
+
+ return 0;
+}
+
+int globalvar_add_bool(const char *name,
+ int (*set)(struct param_d *, void *),
+ int *value, void *priv)
+{
+ struct param_d *p;
+ int ret;
+
+ ret = globalvar_remove_unqualified(name);
+ if (ret)
+ return ret;
+
+ p = dev_add_param_bool(&global_device, name, set, NULL,
+ value, priv);
if (IS_ERR(p))
return PTR_ERR(p);
@@ -563,10 +642,7 @@ int globalvar_add_simple_bitmask(const char *name, unsigned long *value,
p = dev_add_param_bitmask(&global_device, name, NULL, NULL,
value, names, max, NULL);
- if (IS_ERR(p))
- return PTR_ERR(p);
-
- return 0;
+ return PTR_ERR_OR_ZERO(p);
}
int globalvar_add_simple_ip(const char *name, IPaddr_t *ip)
@@ -591,6 +667,8 @@ int globalvar_add_simple_ip(const char *name, IPaddr_t *ip)
static int globalvar_init(void)
{
+ const char *endianness;
+
register_device(&global_device);
if (IS_ENABLED(CONFIG_NVVAR))
@@ -598,11 +676,27 @@ static int globalvar_init(void)
globalvar_add_simple("version", UTS_RELEASE);
+ if (strlen(buildsystem_version_string) > 0)
+ globalvar_add_simple("buildsystem.version", buildsystem_version_string);
+
+#ifdef __BIG_ENDIAN
+ endianness = "big";
+#elif defined(__LITTLE_ENDIAN)
+ endianness = "little";
+#else
+#error "could not determine byte order"
+#endif
+
+ globalvar_add_simple("endianness", endianness);
+
return 0;
}
pure_initcall(globalvar_init);
-BAREBOX_MAGICVAR_NAMED(global_version, global.version, "The barebox version");
+BAREBOX_MAGICVAR(global.version, "The barebox version");
+BAREBOX_MAGICVAR(global.buildsystem.version,
+ "version of buildsystem barebox was built with");
+BAREBOX_MAGICVAR(global.endianness, "The barebox endianness");
/**
* nvvar_save - save NV variables to persistent environment
@@ -614,7 +708,7 @@ int nvvar_save(void)
{
struct param_d *param;
const char *env = default_environment_path_get();
- int ret;
+ int ret = 0;
#define TMPDIR "/.env.tmp"
if (!nv_dirty || !env)
return 0;
@@ -653,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;
@@ -675,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 c24b2c7cd2..608c0e4937 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* vi: set sw=8 ts=8: */
/*
* hush.c -- a prototype Bourne shell grammar parser
@@ -94,19 +95,6 @@
*
*/
-/*
- * 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.
- *
- */
-
#define pr_fmt(fmt) "hush: " fmt
#include <malloc.h> /* malloc, free, realloc*/
@@ -121,9 +109,9 @@
#include <libbb.h>
#include <password.h>
#include <glob.h>
+#include <slice.h>
#include <getopt.h>
#include <libfile.h>
-#include <libbb.h>
#include <magicvar.h>
#include <linux/list.h>
#include <binfmt.h>
@@ -460,7 +448,12 @@ static void get_user_input(struct in_str *i)
else
prompt = CONFIG_PROMPT_HUSH_PS2;
- n = readline(prompt, console_buffer, CONFIG_CBSIZE);
+ command_slice_release();
+
+ n = readline(prompt, console_buffer, CONFIG_CBSIZE - 1);
+
+ command_slice_acquire();
+
if (n == -1 ) {
i->interrupt = 1;
n = 0;
@@ -624,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 == '\'') {
@@ -636,6 +630,7 @@ static void remove_quotes_in_str(char *src)
/* drop quotes */
if (*src == '"') {
+ in_double_quotes = !in_double_quotes;
src++;
continue;
}
@@ -661,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;
@@ -792,16 +794,9 @@ static int run_pipe_real(struct p_context *ctx, struct pipe *pi)
* This junk is all to decide whether or not to export this
* variable. */
int export_me = 0;
- char *name, *value;
- name = xstrdup(child->argv[i]);
- hush_debug("Local environment set: %s\n", name);
- value = strchr(name, '=');
+ hush_debug("Local environment set: %s\n", child->argv[i]);
- if (value)
- *value = 0;
-
- free(name);
p = insert_var_value(child->argv[i]);
rcode = set_local_var(p, export_me);
if (rcode)
@@ -867,7 +862,7 @@ static int run_list_real(struct p_context *ctx, struct pipe *pi)
struct pipe *rpipe;
int flag_rep = 0;
int rcode=0, flag_skip=1;
- int flag_restore = 0;
+ int flag_restore = 0, flag_conditional = 0;
int if_code=0, next_if_code=0; /* need double-buffer to handle elif */
reserved_style rmode, skip_more_in_this_rmode = RES_XXXX;
@@ -979,6 +974,20 @@ static int run_list_real(struct p_context *ctx, struct pipe *pi)
return rcode; /* exit */
}
+ /* Conditional statements like "if", "elif", "while" and "until"
+ * return 1 if conditional is not met. This is standard behavior.
+ * However this does not mean that this value (1) should be
+ * returned as exit code, as it suggests generic error code.
+ * Catch this by raising a flag and check it later on.
+ */
+ if (rcode == 1) {
+ if (rmode == RES_IF || rmode == RES_ELIF ||
+ rmode == RES_WHILE || rmode == RES_UNTIL)
+ flag_conditional = 1;
+ else
+ flag_conditional = 0;
+ }
+
last_return_code = rcode;
if (rmode == RES_IF || rmode == RES_ELIF )
@@ -994,6 +1003,13 @@ static int run_list_real(struct p_context *ctx, struct pipe *pi)
(rcode != EXIT_SUCCESS && pi->followup == PIPE_AND) )
skip_more_in_this_rmode = rmode;
}
+
+ /* Substitute exit code in case flag_conditional is set. */
+ if (flag_conditional == 1 && last_return_code == 1) {
+ last_return_code = 0;
+ rcode = 0;
+ }
+
return rcode;
}
@@ -1118,12 +1134,11 @@ static int set_local_var(const char *s, int flg_export)
/* Assume when we enter this function that we are already in
* NAME=VALUE format. So the first order of business is to
* split 's' on the '=' into 'name' and 'value' */
- value = strchr(name, '=');
+ value = parse_assignment(name);
if (!value) {
free(name);
return -1;
}
- *value++ = 0;
remove_quotes_in_str(value);
@@ -1822,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 2681d62a9a..251fda97b3 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) Jan Lübbe, 2014
*
* This code is inspired by the U-Boot FIT image code.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "FIT: " fmt
@@ -33,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
@@ -242,11 +231,9 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node,
return ERR_PTR(-EINVAL);
}
- if (strcmp(algo_name, "sha1,rsa2048") == 0) {
+ if (strncmp(algo_name, "sha1,", 5) == 0) {
algo = HASH_ALGO_SHA1;
- } else if (strcmp(algo_name, "sha256,rsa2048") == 0) {
- algo = HASH_ALGO_SHA256;
- } else if (strcmp(algo_name, "sha256,rsa4096") == 0) {
+ } else if (strncmp(algo_name, "sha256,", 7) == 0) {
algo = HASH_ALGO_SHA256;
} else {
pr_err("unknown algo %s\n", algo_name);
@@ -269,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);
- 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);
+ pr_err("signature value not found in %pOF\n", sig_node);
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;
}
/*
@@ -332,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;
}
@@ -345,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;
}
@@ -385,7 +363,6 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image,
struct digest *d;
const char *algo;
const char *value_read;
- char *value_calc;
int hash_len, ret;
struct device_node *hash;
@@ -404,50 +381,44 @@ 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;
}
- value_calc = xmalloc(hash_len);
-
digest_init(d);
digest_update(d, data, data_len);
- digest_final(d, value_calc);
- if (memcmp(value_read, value_calc, hash_len)) {
- pr_info("%s: hash BAD\n", hash->full_name);
+ if (digest_verify(d, value_read)) {
+ 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;
}
- free(value_calc);
-
err_digest_free:
digest_free(d);
@@ -481,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;
}
@@ -517,6 +488,129 @@ int fit_has_image(struct fit_handle *handle, void *configuration,
return 1;
}
+static int fit_get_address(struct device_node *image, const char *property,
+ unsigned long *addr)
+{
+ const __be32 *cell;
+ int len = 0;
+
+ cell = of_get_property(image, property, &len);
+ if (!cell)
+ return -EINVAL;
+ if (len > sizeof(*addr))
+ return -ENOTSUPP;
+
+ *addr = (unsigned long)of_read_number(cell, len / sizeof(*cell));
+ return 0;
+}
+
+static int
+fit_get_image(struct fit_handle *handle, void *configuration,
+ const char **unit, struct device_node **image)
+{
+ struct device_node *conf_node = configuration;
+
+ if (conf_node) {
+ if (of_property_read_string(conf_node, *unit, unit)) {
+ pr_err("No image named '%s'\n", *unit);
+ return -ENOENT;
+ }
+ }
+
+ *image = of_get_child_by_name(handle->images, *unit);
+ if (!*image)
+ return -ENOENT;
+
+ return 0;
+}
+
+/**
+ * fit_get_image_address - Get an address from an image in a FIT image
+ * @handle: The FIT image handle
+ * @name: The name of the image to open
+ * @property: The name of the address to get (for example "load" or "entry")
+ * @address: The address given by the image
+ *
+ * Try to parse the @property in the image @name as an address. @configuration
+ * holds the cookie returned from fit_open_configuration() if the image is
+ * opened as part of a configuration, or NULL if the image is opened without a
+ * configuration. If it exists the value will be returned in @address. Otherwise
+ * @address won't be changed.
+ *
+ * Return: 0 for success, negative error code otherwise
+ */
+int fit_get_image_address(struct fit_handle *handle, void *configuration,
+ const char *name, const char *property,
+ unsigned long *address)
+{
+ struct device_node *image;
+ const char *unit = name;
+ const char *type;
+ int ret;
+
+ if (!address || !property || !name)
+ return -EINVAL;
+
+ ret = fit_get_image(handle, configuration, &unit, &image);
+ 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
@@ -539,31 +633,21 @@ int fit_open_image(struct fit_handle *handle, void *configuration,
unsigned long *outsize)
{
struct device_node *image;
- const char *unit, *type = NULL, *desc= "(no description)";
+ const char *unit = name, *type = NULL, *desc= "(no description)";
const void *data;
int data_len;
int ret = 0;
- struct device_node *conf_node = configuration;
- if (conf_node) {
- if (of_property_read_string(conf_node, name, &unit)) {
- pr_err("No image named '%s'\n", name);
- return -ENOENT;
- }
- } else {
- unit = name;
- }
-
- image = of_get_child_by_name(handle->images, unit);
- if (!image)
- return -ENOENT;
+ ret = fit_get_image(handle, configuration, &unit, &image);
+ if (ret)
+ return ret;
of_property_read_string(image, "description", &desc);
pr_info("image '%s': '%s'\n", unit, desc);
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;
}
@@ -573,7 +657,7 @@ int fit_open_image(struct fit_handle *handle, void *configuration,
return -EINVAL;
}
- if (conf_node)
+ if (configuration)
ret = fit_verify_hash(handle, image, data, data_len);
else
ret = fit_image_verify_signature(handle, image, data, data_len);
@@ -581,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;
@@ -608,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;
@@ -641,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)
@@ -679,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);
@@ -707,7 +842,7 @@ static int fit_do_open(struct fit_handle *handle)
const char *desc = "(no description)";
struct device_node *root;
- root = of_unflatten_dtb_const(handle->fit);
+ root = of_unflatten_dtb_const(handle->fit, handle->size);
if (IS_ERR(root))
return PTR_ERR(root);
@@ -765,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.
@@ -772,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;
@@ -783,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/image.c b/common/image.c
index 8199e2df17..c9a99ace78 100644
--- a/common/image.c
+++ b/common/image.c
@@ -1,19 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2008 Semihalf
*
* (C) Copyright 2000-2006
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
*/
#ifdef __BAREBOX__
diff --git a/common/imd-barebox.c b/common/imd-barebox.c
index e5cdfd1aed..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,17 @@ __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);
BAREBOX_IMD_CRC(imd_crc32, 0x0, 1);
diff --git a/common/imd.c b/common/imd.c
index 96496514a5..1100e6878a 100644
--- a/common/imd.c
+++ b/common/imd.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2014 Sascha Hauer, Pengutronix
- *
- * 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.
- *
*/
#ifdef __BAREBOX__
#include <common.h>
@@ -27,6 +17,16 @@ int imd_command_setenv(const char *variable_name, const char *value)
return -ENOSYS;
}
#endif
+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
/*
@@ -168,6 +168,9 @@ static struct imd_type_names imd_types[] = {
}, {
.type = IMD_TYPE_CRC32,
.name = "crc32",
+ }, {
+ .type = IMD_TYPE_BUILDSYSTEM,
+ .name = "buildsystem version",
},
};
@@ -312,6 +315,7 @@ static int imd_calculate_crc32(void *input, const struct imd_header *imd_start,
const struct imd_header *imd;
int length;
int end_ofs = (char *)imd_start - (char *)input + sizeof(char) * 8;
+ *imd_crc = NULL;
/* search the checksum imd token */
imd_for_each(imd_start, imd) {
@@ -319,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;
@@ -412,7 +416,7 @@ int imd_verify_crc32(void *buf, size_t size)
*p, crc);
return -EILSEQ;
} else if (*p != crc && !imd_crc32_is_valid(*flags)) {
- printf("CRC: is invalid, but the checksum tag is not enabled\n");
+ debug("CRC: is invalid, but the checksum tag is not enabled\n");
return -EINVAL;
} else {
printf("CRC: valid\n");
@@ -436,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;
@@ -459,6 +464,7 @@ int imd_command(int argc, char *argv[])
break;
case 'c':
checksum = 1;
+ allow_mmap = false;
break;
case 'V':
verify = 1;
@@ -475,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;
@@ -485,10 +491,15 @@ int imd_command(int argc, char *argv[])
goto out;
}
- if (checksum)
- imd_write_crc32(buf, imd_start, filename, size);
- if (verify)
- imd_verify_crc32(buf, size);
+ if (checksum) {
+ ret = imd_write_crc32(buf, imd_start, filename, size);
+ goto out;
+ }
+
+ if (verify) {
+ ret = imd_verify_crc32(buf, size);
+ goto out;
+ }
if (type == IMD_TYPE_INVALID) {
imd_for_each(imd_start, imd) {
@@ -543,6 +554,6 @@ int imd_command(int argc, char *argv[])
ret = 0;
out:
- free(buf);
+ read_file_2_free(buf);
return ret;
}
diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c
index 0629ae5b73..0d46192720 100644
--- a/common/imx-bbu-nand-fcb.c
+++ b/common/imx-bbu-nand-fcb.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014 Sascha Hauer, Pengutronix
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation.
- *
*/
#define pr_fmt(fmt) "imx-bbu-nand-fcb: " fmt
@@ -28,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>
@@ -37,95 +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 dbbt_block {
- uint32_t Checksum;
- uint32_t FingerPrint;
- uint32_t Version;
- uint32_t numberBB; /* reserved on i.MX6 */
- uint32_t DBBTNumOfPages;
-};
-
-struct fcb_block {
- uint32_t Checksum; /* First fingerprint in first byte */
- uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
- uint32_t Version; /* 3rd fingerprint at byte 8 */
- uint8_t DataSetup;
- uint8_t DataHold;
- uint8_t AddressSetup;
- uint8_t DSAMPLE_TIME;
- /* These are for application use only and not for ROM. */
- uint8_t NandTimingState;
- uint8_t REA;
- uint8_t RLOH;
- uint8_t RHOH;
- uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
- uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
- uint32_t SectorsPerBlock; /* Number of 2K sections per block */
- uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
- uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
- uint32_t CellType; /* MLC or SLC */
- uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
- uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
- uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
- uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
- uint32_t MetadataBytes; /* Metadata size - BCH */
- uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
- uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
- uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
- uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
- uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
- uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
- uint32_t MetadataBytesSDK; /* Metadata size - BCH */
- uint32_t EraseThreshold; /* To set into BCH_MODE register */
- uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
- uint32_t PatchSectors; /* Size of patch in sectors */
- uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
- uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
- uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
- uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
- uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
- uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
- /* this will be swapped with metadata[0] to complete page data. */
- uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
- /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
- /* the start bit of bad block marker byte with in BadBlockMarkerByte */
- uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
- uint32_t BCHType;
-
- uint32_t TMTiming2_ReadLatency;
- uint32_t TMTiming2_PreambleDelay;
- uint32_t TMTiming2_CEDelay;
- uint32_t TMTiming2_PostambleDelay;
- uint32_t TMTiming2_CmdAddPause;
- uint32_t TMTiming2_DataPause;
- uint32_t TMSpeed;
- uint32_t TMTiming1_BusyTimeout;
-
- uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
- uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
-};
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;
};
@@ -204,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;
@@ -308,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;
@@ -363,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)
@@ -448,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;
@@ -458,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;
}
@@ -483,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)
{
@@ -519,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;
@@ -582,7 +582,8 @@ static int imx_bbu_firmware_start_block(struct mtd_info *mtd, int num)
* @num: The slot number (0 or 1)
*
* This returns the start page for a firmware slot, to be written into the
- * Firmwaren_startingPage field in the FCB.
+ * Firmwaren_startingPage field in the FCB or a negative error code in case
+ * of a failure.
*/
static int imx_bbu_firmware_fcb_start_page(struct mtd_info *mtd, int num)
{
@@ -593,6 +594,11 @@ static int imx_bbu_firmware_fcb_start_page(struct mtd_info *mtd, int num)
blocksleft = imx_bbu_firmware_max_blocks(mtd);
+ if (blocksleft <= 0) {
+ pr_err("partition size too small for both firmwares\n");
+ return -ENOMEM;
+ }
+
/*
* The ROM only checks for a bad block when advancing the read position,
* but not if the initial block is good, hence we cannot directly point
@@ -726,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
@@ -735,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;
@@ -757,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));
@@ -918,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;
@@ -960,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.
@@ -974,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);
@@ -1003,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;
@@ -1011,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)
@@ -1230,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) {
@@ -1258,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;
}
@@ -1338,8 +1325,17 @@ static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *dat
free(fcb);
fcb = xzalloc(sizeof(*fcb));
- fcb->Firmware1_startingPage = imx_bbu_firmware_fcb_start_page(mtd, !used);
- fcb->Firmware2_startingPage = imx_bbu_firmware_fcb_start_page(mtd, used);
+
+ ret = imx_bbu_firmware_fcb_start_page(mtd, !used);
+ if (ret < 0)
+ goto out;
+ fcb->Firmware1_startingPage = ret;
+
+ ret = imx_bbu_firmware_fcb_start_page(mtd, used);
+ if (ret < 0)
+ goto out;
+ fcb->Firmware2_startingPage = ret;
+
fcb->PagesInFirmware1 = fw_size / mtd->writesize;
fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
@@ -1392,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;
@@ -1413,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;
}
@@ -1446,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;
@@ -1459,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
@@ -1479,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)
@@ -1520,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;
@@ -1536,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 e15dec5dfc..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>
@@ -15,7 +17,7 @@ extern const unsigned long kallsyms_markers[] __attribute__((weak));
static inline int is_kernel_text(unsigned long addr)
{
- if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext))
+ if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
return 1;
return 0;
}
@@ -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 e678bb7fe8..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;
}
-static int machine_id_set_bootarg(void)
+/**
+ * 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,14 +146,13 @@ static int machine_id_set_bootarg(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;
}
-late_initcall(machine_id_set_bootarg);
+late_initcall(machine_id_set_globalvar);
-BAREBOX_MAGICVAR_NAMED(global_machine_id, global.machine_id, "Persistent device-specific, hexadecimal, 32-character id");
+BAREBOX_MAGICVAR(global.machine_id, "Persistent device-specific, hexadecimal, 32-character id");
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 114958fbcf..583843cc34 100644
--- a/common/memory.c
+++ b/common/memory.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * memory.c
- *
* Copyright (c) 2011 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 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.
- *
*/
+#define pr_fmt(fmt) "memory: " fmt
+
#include <common.h>
#include <memory.h>
#include <of.h>
@@ -23,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"
@@ -43,7 +36,7 @@ unsigned long mem_malloc_end(void)
#ifdef CONFIG_MALLOC_TLSF
#include <tlsf.h>
-tlsf_pool tlsf_mem_pool;
+tlsf_t tlsf_mem_pool;
#endif
int mem_malloc_initialized;
@@ -59,14 +52,14 @@ void mem_malloc_init(void *start, void *end)
malloc_end = (unsigned long)end;
malloc_brk = malloc_start;
#ifdef CONFIG_MALLOC_TLSF
- tlsf_mem_pool = tlsf_create(start, end - start + 1);
+ tlsf_mem_pool = tlsf_create_with_pool(start, end - start + 1);
#endif
mem_malloc_initialized = 1;
}
-#if !defined __SANDBOX__ && !defined CONFIG_EFI_BOOTUP
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
@@ -88,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)
{
@@ -122,19 +116,59 @@ void *sbrk(ptrdiff_t increment)
LIST_HEAD(memory_banks);
+static int barebox_grow_memory_bank(struct memory_bank *bank, const char *name,
+ const struct resource *newres)
+{
+ struct resource *res;
+ resource_size_t bank_end = bank->res->end;
+
+ if (newres->start < bank->start) {
+ res = request_iomem_region(name, newres->start, bank->start - 1);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+ __merge_regions(name, bank->res, res);
+ }
+
+ if (bank_end < newres->end) {
+ res = request_iomem_region(name, bank_end + 1, newres->end);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+ __merge_regions(name, bank->res, res);
+ }
+
+ bank->start = newres->start;
+ bank->size = resource_size(bank->res);
+
+ return 0;
+}
+
int barebox_add_memory_bank(const char *name, resource_size_t start,
resource_size_t size)
{
- struct memory_bank *bank = xzalloc(sizeof(*bank));
- struct device_d *dev;
+ struct memory_bank *bank;
+ struct resource *res;
+ struct resource newres = {
+ .start = start,
+ .end = start + size - 1,
+ .flags = IORESOURCE_MEM,
+ };
- bank->res = request_iomem_region(name, start, start + size - 1);
- if (IS_ERR(bank->res))
- return PTR_ERR(bank->res);
+ for_each_memory_bank(bank) {
+ if (resource_contains(bank->res, &newres))
+ return 0;
+ if (resource_contains(&newres, bank->res))
+ return barebox_grow_memory_bank(bank, name, &newres);
+ }
+
+ res = request_iomem_region(name, start, start + size - 1);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
- dev = add_mem_device(name, start, size, IORESOURCE_MEM_WRITEABLE);
+ res->flags = IORESOURCE_MEM;
- bank->dev = dev;
+ bank = xzalloc(sizeof(*bank));
+
+ bank->res = res;
bank->start = start;
bank->size = size;
@@ -143,19 +177,34 @@ int barebox_add_memory_bank(const char *name, resource_size_t start,
return 0;
}
+static int add_mem_devices(void)
+{
+ struct memory_bank *bank;
+
+ for_each_memory_bank(bank) {
+ add_mem_device(bank->res->name, bank->start, bank->size,
+ IORESOURCE_MEM_WRITEABLE);
+ }
+
+ return 0;
+}
+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;
}
@@ -163,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/memsize.c b/common/memsize.c
index 915ab87b34..de4d8df18a 100644
--- a/common/memsize.c
+++ b/common/memsize.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2004
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
*/
#include <common.h>
@@ -58,7 +48,7 @@ long get_ram_size(volatile long *base, long maxsize)
*addr = 0;
sync ();
- if ((val = *addr) != 0) {
+ if (*addr != 0) {
/* Restore the original data before leaving the function.
*/
sync ();
diff --git a/common/memtest.c b/common/memtest.c
index 09cfa8a347..aa16d94eed 100644
--- a/common/memtest.c
+++ b/common/memtest.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* memtest.c
*
@@ -5,16 +6,6 @@
*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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 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.
- *
*/
#include <progress.h>
@@ -169,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 */
@@ -199,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
@@ -303,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.
@@ -322,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.
@@ -349,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))
@@ -358,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;
@@ -377,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
@@ -391,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;
@@ -403,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;
@@ -422,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;
@@ -439,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/menu.c b/common/menu.c
index 089dab8a11..4007c476c3 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) Copyright 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * 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; version 2 of
- * the License.
- *
- * 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.
- *
*/
#include <common.h>
diff --git a/common/menutree.c b/common/menutree.c
index 400d1a6939..751350d754 100644
--- a/common/menutree.c
+++ b/common/menutree.c
@@ -1,13 +1,4 @@
-/*
- * 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 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.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#include <environment.h>
#include <libbb.h>
@@ -43,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)
@@ -93,10 +77,9 @@ int menutree(const char *path, int toplevel)
struct stat s;
char *box;
struct menutree *mt;
- glob_t g;
+ glob_t g = {};
int i;
char *globpath, *display;
- size_t size;
menu = menu_alloc();
@@ -109,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 1c7f937608..04ff4e6eb5 100644
--- a/common/misc.c
+++ b/common/misc.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) Copyright 2000-2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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 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.
- *
*/
#include <common.h>
@@ -22,6 +13,8 @@
#include <led.h>
#include <of.h>
#include <restart.h>
+#include <poweroff.h>
+#include <linux/stringify.h>
int errno;
EXPORT_SYMBOL(errno);
@@ -29,7 +22,7 @@ EXPORT_SYMBOL(errno);
const char *strerror(int errnum)
{
- static char errno_string[10];
+ static char errno_string[sizeof("error -2147483648")];
#ifdef CONFIG_ERRNO_MESSAGES
char *str;
@@ -113,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
@@ -154,9 +141,11 @@ const char *barebox_get_model(void)
}
EXPORT_SYMBOL(barebox_get_model);
-BAREBOX_MAGICVAR_NAMED(global_model, global.model, "Product name of this hardware");
+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
@@ -184,26 +173,87 @@ void barebox_set_hostname_no_overwrite(const char *__hostname)
}
EXPORT_SYMBOL(barebox_set_hostname_no_overwrite);
-BAREBOX_MAGICVAR_NAMED(global_hostname, global.hostname,
+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.c b/common/module.c
index 829c120007..b669ec09c9 100644
--- a/common/module.c
+++ b/common/module.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2002 Richard Henderson
* Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
- *
- * 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.
- *
*/
#include <common.h>
@@ -176,6 +166,42 @@ static void layout_sections( struct module *mod,
debug("core_size: %ld\n", mod->core_size);
}
+int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
+ Elf_Shdr *sechdrs,
+ char *secstrings,
+ struct module *mod)
+{
+ return 0;
+}
+
+static void register_module_cmds(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex)
+{
+ Elf32_Sym *sym;
+ unsigned int numsyms;
+ unsigned int i;
+ struct command * const *cmd_start = NULL;
+ struct command * const *cmd_end = NULL;
+ struct command * const *cmd;
+
+ numsyms = sechdrs[symindex].sh_size / sizeof(Elf32_Sym);
+ sym = (void *)sechdrs[symindex].sh_addr;
+
+ for (i = 0; i < numsyms; i++) {
+ if (strcmp(strtab + sym[i].st_name, MODULE_SYMBOL_PREFIX "__barebox_cmd_start") == 0)
+ cmd_start = (struct command * const *)sym[i].st_value;
+
+ if (strcmp(strtab + sym[i].st_name, MODULE_SYMBOL_PREFIX "__barebox_cmd_end") == 0)
+ cmd_end = (struct command * const *)sym[i].st_value;
+ }
+
+ if (cmd_start && cmd_end) {
+ debug("found __barebox_cmd_start at 0x%08x\n", (uint32_t)cmd_start);
+ for (cmd = cmd_start; cmd != cmd_end; cmd++) {
+ register_command(*cmd);
+ }
+ }
+}
+
LIST_HEAD(module_list);
struct module * load_module(void *mod_image, unsigned long len)
@@ -183,8 +209,6 @@ struct module * load_module(void *mod_image, unsigned long len)
struct module *module = NULL;
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
Elf32_Shdr *sechdrs; /* Section header structure pointer */
- Elf32_Sym *sym;
- unsigned int numsyms;
char *strtab = 0; /* String table pointer */
int i; /* Loop counter */
unsigned int strindex = 0;
@@ -193,7 +217,6 @@ struct module * load_module(void *mod_image, unsigned long len)
char *secstrings;
void *ptr = NULL;
int err;
- int cmdindex;
if (len < sizeof(*ehdr))
return NULL;
@@ -246,6 +269,12 @@ struct module * load_module(void *mod_image, unsigned long len)
goto cleanup;
}
+ /* Allow arches to frob section contents and sizes. */
+ err = module_frob_arch_sections(ehdr, sechdrs,
+ secstrings, module);
+ if (err < 0)
+ goto cleanup;
+
/* Determine total sizes, and put offsets in sh_entsize. For now
this is done generically; there doesn't appear to be any
special cases for the architectures. */
@@ -285,25 +314,10 @@ struct module * load_module(void *mod_image, unsigned long len)
apply_relocate_add(sechdrs, strtab, symindex, i, module);
}
- numsyms = sechdrs[symindex].sh_size / sizeof(Elf32_Sym);
- sym = (void *)sechdrs[symindex].sh_addr;
-
- cmdindex = find_sec(ehdr, sechdrs, secstrings, ".barebox_cmd");
- if (cmdindex) {
- struct command *cmd =(struct command *)sechdrs[cmdindex].sh_addr;
- for (i = 0; i < sechdrs[cmdindex].sh_size / sizeof(struct command); i++) {
- register_command(cmd);
- cmd++;
- }
- }
-
- for (i = 0; i < numsyms; i++) {
- if (!strcmp(strtab + sym[i].st_name, MODULE_SYMBOL_PREFIX "init_module")) {
- printf("found init_module() at 0x%08x\n", sym[i].st_value);
- module->init = (void *)sym[i].st_value;
- }
- }
+ register_module_cmds(sechdrs, strtab, symindex);
+ /* Module has been moved */
+ module = (void *)sechdrs[modindex].sh_addr;
list_add_tail(&module->list, &module_list);
return module;
@@ -311,8 +325,6 @@ struct module * load_module(void *mod_image, unsigned long len)
cleanup:
if (ptr)
free(ptr);
- if (module)
- free(module);
return NULL;
}
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 36906e86fc..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>
@@ -14,6 +16,9 @@
#include <bootsource.h>
#include <i2c/i2c.h>
#include <reset_source.h>
+#include <watchdog.h>
+#include <globalvar.h>
+#include <magicvar.h>
#define MAX_LEVEL 32 /* how deeply nested we will go */
@@ -113,20 +118,16 @@ void of_print_cmdline(struct device_node *root)
cmdline = of_get_property(node, "bootargs", NULL);
- printf("commandline: %s\n", cmdline);
+ pr_info("commandline: %s\n", cmdline);
}
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.
@@ -135,17 +136,40 @@ 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;
}
-static int of_fixup_bootargs(struct device_node *root, void *unused)
+static void watchdog_build_bootargs(struct watchdog *watchdog, struct device_node *root)
+{
+ int alias_id;
+ char *buf;
+
+ if (!watchdog)
+ return;
+
+ alias_id = watchdog_get_alias_id_from(watchdog, root);
+ if (alias_id < 0)
+ return;
+
+ buf = basprintf("systemd.watchdog-device=/dev/watchdog%d", alias_id);
+ if (!buf)
+ return;
+
+ globalvar_add_simple("linux.bootargs.dyn.watchdog", buf);
+ free(buf);
+}
+
+static int bootargs_append = 0;
+BAREBOX_MAGICVAR(global.linux.bootargs_append, "append to original oftree bootargs");
+
+static int of_write_bootargs(struct device_node *node)
{
- struct device_node *node;
const char *str;
- int err;
- int instance = reset_source_get_instance();
- struct device_d *dev;
+ char *buf = NULL;
+ int ret;
+
+ if (IS_ENABLED(CONFIG_SYSTEMD_OF_WATCHDOG))
+ watchdog_build_bootargs(boot_get_enabled_watchdog(), of_get_parent(node));
str = linux_bootargs_get();
if (!str)
@@ -155,13 +179,46 @@ static int of_fixup_bootargs(struct device_node *root, void *unused)
if (strlen(str) == 0)
return 0;
+ if (bootargs_append) {
+ const char *oldstr;
+
+ ret = of_property_read_string(node, "bootargs", &oldstr);
+ if (!ret) {
+ str = buf = basprintf("%s %s", oldstr, str);
+ if (!buf)
+ return -ENOMEM;
+ }
+ }
+
+ ret = of_property_write_string(node, "bootargs", str);
+ free(buf);
+ return ret;
+}
+
+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 *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)
return -ENOMEM;
of_property_write_string(node, "barebox-version", release_string);
- err = of_property_write_string(node, "bootargs", str);
+ err = of_write_bootargs(node);
if (err)
return err;
@@ -171,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);
@@ -182,15 +239,76 @@ static int of_fixup_bootargs(struct device_node *root, void *unused)
return err;
}
- return of_fixup_bootargs_bootsource(root, node);
+ err = of_fixup_bootargs_bootsource(root, node);
+ if (err)
+ return err;
+
+ if (IS_ENABLED(CONFIG_RISCV)) {
+ const char *hartid;
+
+ hartid = getenv("global.hartid");
+ if (hartid) {
+ unsigned long id;
+
+ err = kstrtoul(hartid, 10, &id);
+ if (!err)
+ err = of_property_write_u32(node, "boot-hartid", id);
+ }
+ }
+
+ return err;
}
static int of_register_bootargs_fixup(void)
{
+ globalvar_add_simple_bool("linux.bootargs_append", &bootargs_append);
return of_register_fixup(of_fixup_bootargs, NULL);
}
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;
@@ -227,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)
{
@@ -270,19 +387,22 @@ 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;
+ 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;
}
/*
@@ -291,10 +411,10 @@ 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;
+ struct fdt_header *fdt = NULL;
+ struct device_node *np;
if (!node) {
node = of_get_root_node();
@@ -302,14 +422,17 @@ struct fdt_header *of_get_fixed_tree(struct device_node *node)
return NULL;
}
- ret = of_fix_tree(node);
- if (ret)
- return NULL;
+ np = of_dup(node);
- fdt = of_flatten_dtb(node);
- if (!fdt)
+ if (!np)
return NULL;
+ of_fix_tree(np);
+
+ fdt = of_flatten_dtb(np);
+
+ of_delete_node(np);
+
return fdt;
}
@@ -327,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);
@@ -364,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)
@@ -400,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 fb9ef42e7f..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>
@@ -270,7 +272,7 @@ int run_shell(void)
login();
for (;;) {
- len = readline (CONFIG_PROMPT, console_buffer, CONFIG_CBSIZE);
+ len = readline (CONFIG_PROMPT, console_buffer, CONFIG_CBSIZE - 1);
if (len > 0)
strcpy (lastcommand, console_buffer);
diff --git a/common/partitions.c b/common/partitions.c
index 4162e86804..5b861c40fc 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009...2011 Juergen Beisert, Pengutronix
- *
- * 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.
- *
- *
*/
/**
* @file
* @brief Generic support for partition tables on disk like media
- *
- * @todo Support for disks larger than 4 GiB
- * @todo Reliable size detection for BIOS based disks (on x86 only)
*/
#include <common.h>
#include <malloc.h>
@@ -28,10 +14,8 @@
#include <asm/unaligned.h>
#include <disks.h>
#include <filetype.h>
-#include <dma.h>
#include <linux/err.h>
-
-#include "partitions/parser.h"
+#include <partitions.h>
static LIST_HEAD(partition_parser_list);
@@ -42,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);
@@ -115,42 +102,154 @@ 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 = dma_alloc(SECTOR_SIZE * 2);
+ buf = malloc(2 * SECTOR_SIZE);
- rc = block_read(blk, buf, 0, 2);
- if (rc != 0) {
- dev_err(blk->dev, "Cannot read MBR/partition table\n");
- 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;
- parser->parse(buf, blk, pdesc);
+ pdesc = parser->parse(buf, blk);
+ pdesc->parser = parser;
+err:
+ free(buf);
- if (!pdesc->used_entries)
- goto on_error;
+ return 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;
+}
+
+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",
@@ -159,15 +258,61 @@ int parse_partition_table(struct block_device *blk)
rc = 0;
}
-on_error:
- dma_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 488c2936f7..8e4edd885b 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -15,12 +15,26 @@
#include <common.h>
#include <disks.h>
#include <init.h>
+#include <stdlib.h>
#include <asm/unaligned.h>
-#include <dma.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
@@ -37,34 +51,9 @@ static inline int is_extended_partition(struct partition *p)
p->dos_partition_type == LINUX_EXTENDED_PARTITION);
}
-/**
- * Guess the size of the disk, based on the partition table entries
- * @param dev device to create partitions for
- * @param table partition table
- * @return sector count
- */
-static uint64_t disk_guess_size(struct device_d *dev,
- struct partition_entry *table)
-{
- uint64_t size = 0;
- int i;
-
- for (i = 0; i < 4; i++) {
- if (get_unaligned_le32(&table[i].partition_start) != 0) {
- uint64_t part_end = get_unaligned_le32(&table[i].partition_start) +
- get_unaligned_le32(&table[i].partition_size);
-
- if (size < part_end)
- size = part_end;
- }
- }
-
- return size;
-}
-
static void *read_mbr(struct block_device *blk)
{
- void *buf = dma_alloc(SECTOR_SIZE);
+ void *buf = malloc(SECTOR_SIZE);
int ret;
ret = block_read(blk, buf, 0, 1);
@@ -132,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 = dma_alloc(SECTOR_SIZE);
+ 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);
@@ -165,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 */
@@ -185,10 +185,19 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
}
out:
- dma_free(buf);
+ free(buf);
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
@@ -197,57 +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];
- /* valid for x86 BIOS based disks only */
- if (IS_ENABLED(CONFIG_DISK_BIOS) && blk->num_blocks == 0)
- blk->num_blocks = disk_guess_size(blk->dev, table);
+ 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;
@@ -265,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 f20fd0d9b9..9df40e3c15 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -16,12 +16,22 @@
#include <disks.h>
#include <init.h>
#include <asm/unaligned.h>
-#include <dma.h>
#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);
@@ -81,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;
@@ -155,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;
@@ -207,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;
}
@@ -233,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;
@@ -250,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)
@@ -287,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++;
}
@@ -431,45 +444,343 @@ 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 = NULL;
- 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)
+ goto out;
+
+ 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);
- for (i = 0; i < MAX_PARTITION && i < nb_part; i++) {
+
+ if (nb_part > MAX_PARTITION) {
+ dev_warn(blk->dev, "GPT has more partitions than we support (%d) > max partition number (%d)\n",
+ nb_part, MAX_PARTITION);
+ 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);
}
+out:
- if (i > MAX_PARTITION)
- dev_warn(blk->dev, "num_partition_entries (%d) > max partition number (%d)\n",
- nb_part, MAX_PARTITION);
+ 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 8ad134a9aa..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 8
-#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 a2a9c4cd6c..55b2d1093a 100644
--- a/common/password.c
+++ b/common/password.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * 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 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.
- *
*/
#include <common.h>
@@ -27,6 +18,7 @@
#include <init.h>
#include <stdlib.h>
#include <globalvar.h>
+#include <crypto.h>
#include <generated/passwd.h>
#include <crypto/pbkdf2.h>
@@ -156,14 +148,17 @@ 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;
+ if (ARRAY_SIZE(default_passwd) == 1)
+ return -ENOSYS;
+
if (!sum || length < 1)
return -EINVAL;
+ len = strlen(default_passwd);
for (i = 0; i < len && length > 0; i++) {
c = buf[i];
i++;
@@ -179,7 +174,6 @@ static int read_default_passwd(unsigned char *sum, size_t length)
return 0;
}
-EXPORT_SYMBOL(read_default_passwd);
static int read_env_passwd(unsigned char *sum, size_t length)
{
@@ -318,7 +312,7 @@ static int check_passwd(unsigned char *passwd, size_t length)
if (ret)
goto err;
- if (strncmp(passwd1_sum, key, keylen) == 0)
+ if (!crypto_memneq(passwd1_sum, key, keylen))
ret = 1;
} else {
ret = digest_digest(d, passwd, length, passwd1_sum);
@@ -326,7 +320,7 @@ static int check_passwd(unsigned char *passwd, size_t length)
if (ret)
goto err;
- if (strncmp(passwd1_sum, passwd2_sum, hash_len) == 0)
+ if (!crypto_memneq(passwd1_sum, passwd2_sum, hash_len))
ret = 1;
}
@@ -447,7 +441,7 @@ static int login_global_init(void)
}
late_initcall(login_global_init);
-BAREBOX_MAGICVAR_NAMED(global_login_fail_command, global.login.fail_command,
+BAREBOX_MAGICVAR(global.login.fail_command,
"command to run when password entry failed");
-BAREBOX_MAGICVAR_NAMED(global_login_timeout, global.login.timeout,
+BAREBOX_MAGICVAR(global.login.timeout,
"timeout to type the password");
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/poller.c b/common/poller.c
index 32795b641f..0409d3cf81 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -1,8 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 Marc Kleine-Budde <mkl@pengutronix.de>
- *
- * This file is released under the GPLv2
- *
*/
#include <common.h>
@@ -14,13 +12,19 @@
#include <clock.h>
static LIST_HEAD(poller_list);
-static int poller_active;
+static int __poller_active;
+
+bool poller_active(void)
+{
+ return __poller_active;
+}
-int poller_register(struct poller_struct *poller)
+int poller_register(struct poller_struct *poller, const char *name)
{
if (poller->registered)
return -EBUSY;
+ poller->name = xstrdup(name);
list_add_tail(&poller->list, &poller_list);
poller->registered = 1;
@@ -35,6 +39,7 @@ int poller_unregister(struct poller_struct *poller)
list_del(&poller->list);
poller->registered = 0;
+ free(poller->name);
return 0;
}
@@ -92,12 +97,12 @@ int poller_call_async(struct poller_async *pa, uint64_t delay_ns,
return 0;
}
-int poller_async_register(struct poller_async *pa)
+int poller_async_register(struct poller_async *pa, const char *name)
{
pa->poller.func = poller_async_callback;
pa->active = 0;
- return poller_register(&pa->poller);
+ return poller_register(&pa->poller, name);
}
int poller_async_unregister(struct poller_async *pa)
@@ -109,13 +114,81 @@ void poller_call(void)
{
struct poller_struct *poller, *tmp;
- if (poller_active)
- return;
-
- poller_active = 1;
+ __poller_active = 1;
list_for_each_entry_safe(poller, tmp, &poller_list, list)
poller->func(poller);
- poller_active = 0;
+ __poller_active = 0;
+}
+
+#if defined CONFIG_CMD_POLLER
+
+#include <command.h>
+#include <getopt.h>
+
+static void poller_time(void)
+{
+ uint64_t start = get_time_ns();
+ int i = 0;
+
+ /*
+ * How many times we can run the registered pollers in one second?
+ *
+ * A low number here may point to problems with pollers taking too
+ * much time.
+ */
+ while (!is_timeout(start, SECOND))
+ i++;
+
+ printf("%d poller calls in 1s\n", i);
+}
+
+static void poller_info(void)
+{
+ struct poller_struct *poller;
+
+ printf("Registered pollers:\n");
+
+ if (list_empty(&poller_list)) {
+ printf("<none>\n");
+ return;
+ }
+
+ list_for_each_entry(poller, &poller_list, list)
+ printf("%s\n", poller->name);
+}
+
+BAREBOX_CMD_HELP_START(poller)
+BAREBOX_CMD_HELP_TEXT("print info about registered pollers")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-i", "Print information about registered pollers")
+BAREBOX_CMD_HELP_OPT ("-t", "measure how many pollers we run in 1s")
+BAREBOX_CMD_HELP_END
+
+static int do_poller(int argc, char *argv[])
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "it")) > 0) {
+ switch (opt) {
+ case 'i':
+ poller_info();
+ return 0;
+ case 't':
+ poller_time();
+ return 0;
+ }
+ }
+
+ return COMMAND_ERROR_USAGE;
}
+
+BAREBOX_CMD_START(poller)
+ .cmd = do_poller,
+ BAREBOX_CMD_DESC("print info about registered pollers")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_poller_help)
+BAREBOX_CMD_END
+#endif
diff --git a/common/poweroff.c b/common/poweroff.c
index 4ce04d158f..65909c6141 100644
--- a/common/poweroff.c
+++ b/common/poweroff.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 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 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.
- *
*/
#define pr_fmt(fmt) "poweroff: " fmt
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 ca751a30eb..fddb286e01 100644
--- a/common/ratp/ratp.c
+++ b/common/ratp/ratp.c
@@ -15,18 +15,18 @@
#include <common.h>
#include <command.h>
-#include <kfifo.h>
#include <malloc.h>
#include <init.h>
#include <ratp.h>
-#include <command.h>
#include <byteorder.h>
#include <environment.h>
#include <kfifo.h>
#include <poller.h>
+#include <work.h>
#include <linux/sizes.h>
#include <ratp_bb.h>
#include <fs.h>
+#include <console_countdown.h>
LIST_HEAD(ratp_command_list);
EXPORT_SYMBOL(ratp_command_list);
@@ -50,6 +50,11 @@ struct ratp_ctx {
struct ratp_bb_pkt *fs_rx;
struct poller_struct poller;
+ struct work_queue wq;
+
+ bool console_registered;
+ bool poller_registered;
+ bool wq_registered;
};
static int compare_ratp_command(struct list_head *a, struct list_head *b)
@@ -97,11 +102,24 @@ static int init_ratp_command_list(void)
late_initcall(init_ratp_command_list);
+static bool console_exists(struct console_device *cdev)
+{
+ struct console_device *cs;
+
+ list_for_each_entry(cs, &console_list, list)
+ if (cs == cdev)
+ return true;
+ return false;
+}
+
static int console_recv(struct ratp *r, uint8_t *data)
{
struct ratp_ctx *ctx = container_of(r, struct ratp_ctx, ratp);
struct console_device *cdev = ctx->cdev;
+ if (!console_exists(cdev))
+ return -ENODEV;
+
if (ctx->have_synch) {
ctx->have_synch = 0;
*data = 0x01;
@@ -123,6 +141,9 @@ static int console_send(struct ratp *r, void *pkt, int len)
const uint8_t *buf = pkt;
int i;
+ if (!console_exists(cdev))
+ return -ENODEV;
+
for (i = 0; i < len; i++)
cdev->putc(cdev, buf[i]);
@@ -175,9 +196,14 @@ static int ratp_bb_send_command_return(struct ratp_ctx *ctx, uint32_t errno)
return ret;
}
-static char *ratp_command;
static struct ratp_ctx *ratp_ctx;
+struct ratp_work {
+ struct work_struct work;
+ struct ratp_ctx *ctx;
+ char *command;
+};
+
static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len)
{
const struct ratp_bb *rbb = buf;
@@ -186,6 +212,7 @@ static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len)
int ret = 0;
uint16_t type = be16_to_cpu(rbb->type);
struct ratp_command *cmd;
+ struct ratp_work *rw;
/* See if there's a command registered to this type */
cmd = find_ratp_request(type);
@@ -203,12 +230,16 @@ static int ratp_bb_dispatch(struct ratp_ctx *ctx, const void *buf, int len)
switch (type) {
case BB_RATP_TYPE_COMMAND:
- if (!IS_ENABLED(CONFIG_CONSOLE_RATP) || ratp_command)
+ if (!IS_ENABLED(CONFIG_CONSOLE_RATP))
return 0;
- ratp_command = xstrndup((const char *)rbb->data, dlen);
- ratp_ctx = ctx;
- pr_debug("got command: %s\n", ratp_command);
+ rw = xzalloc(sizeof(*rw));
+ rw->ctx = ctx;
+ rw->command = xstrndup((const char *)rbb->data, dlen);
+
+ wq_queue_work(&ctx->wq, &rw->work);
+
+ pr_debug("got command: %s\n", rw->command);
break;
@@ -279,41 +310,20 @@ static void ratp_console_putc(struct console_device *cdev, char c)
kfifo_putc(ctx->console_transmit_fifo, c);
}
-static int ratp_console_register(struct ratp_ctx *ctx)
+static void ratp_command_run(struct work_struct *w)
{
+ struct ratp_work *rw = container_of(w, struct ratp_work, work);
+ struct ratp_ctx *ctx = rw->ctx;
int ret;
- ctx->ratp_console.tstc = ratp_console_tstc;
- ctx->ratp_console.puts = ratp_console_puts;
- ctx->ratp_console.putc = ratp_console_putc;
- ctx->ratp_console.getc = ratp_console_getc;
- ctx->ratp_console.devname = "ratpconsole";
- ctx->ratp_console.devid = DEVICE_ID_SINGLE;
-
- ret = console_register(&ctx->ratp_console);
- if (ret) {
- pr_err("registering failed with %s\n", strerror(-ret));
- return ret;
- }
-
- return 0;
-}
+ pr_debug("running command: %s\n", rw->command);
-void barebox_ratp_command_run(void)
-{
- int ret;
-
- if (!ratp_command)
- return;
+ ret = run_command(rw->command);
- pr_debug("running command: %s\n", ratp_command);
+ free(rw->command);
+ free(rw);
- ret = run_command(ratp_command);
-
- free(ratp_command);
- ratp_command = NULL;
-
- ratp_bb_send_command_return(ratp_ctx, ret);
+ ratp_bb_send_command_return(ctx, ret);
}
static const char *ratpfs_mount_path;
@@ -328,15 +338,30 @@ int barebox_ratp_fs_mount(const char *path)
return 0;
}
-static void ratp_console_unregister(struct ratp_ctx *ctx)
+static void ratp_unregister(struct ratp_ctx *ctx)
{
int ret;
- console_set_active(&ctx->ratp_console, 0);
- poller_unregister(&ctx->poller);
+ if (ctx->console_registered)
+ console_unregister(&ctx->ratp_console);
+
+ if (ctx->poller_registered)
+ poller_unregister(&ctx->poller);
+
+ if (ctx->wq_registered)
+ wq_unregister(&ctx->wq);
+
ratp_close(&ctx->ratp);
console_set_active(ctx->cdev, ctx->old_active);
- ctx->cdev = NULL;
+
+ if (ctx->console_recv_fifo)
+ kfifo_free(ctx->console_recv_fifo);
+
+ if (ctx->console_transmit_fifo)
+ kfifo_free(ctx->console_transmit_fifo);
+
+ free(ctx);
+ ratp_ctx = NULL;
if (ratpfs_mount_path) {
ret = umount(ratpfs_mount_path);
@@ -358,6 +383,7 @@ static void ratp_poller(struct poller_struct *poller)
ret = ratp_poll(&ctx->ratp);
if (ret == -EINTR)
goto out;
+
if (ratp_closed(&ctx->ratp))
goto out;
@@ -372,7 +398,7 @@ static void ratp_poller(struct poller_struct *poller)
return;
out:
- ratp_console_unregister(ctx);
+ ratp_unregister(ctx);
}
int barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx)
@@ -400,7 +426,6 @@ int barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx)
start = get_time_ns();
while (!ctx->fs_rx) {
- poller_call();
if (ratp_closed(&ctx->ratp))
return -EIO;
if (is_timeout(start, 10 * SECOND))
@@ -414,32 +439,46 @@ int barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx)
return 0;
}
+static void ratp_work_cancel(struct work_struct *w)
+{
+ struct ratp_work *rw = container_of(w, struct ratp_work, work);
+
+ free(rw->command);
+ free(rw);
+}
+
int barebox_ratp(struct console_device *cdev)
{
int ret;
struct ratp_ctx *ctx;
- struct ratp *ratp;
if (!cdev->getc || !cdev->putc)
return -EINVAL;
- if (ratp_ctx) {
- ctx = ratp_ctx;
- } else {
- ctx = xzalloc(sizeof(*ctx));
- ratp_ctx = ctx;
- ctx->ratp.send = console_send;
- ctx->ratp.recv = console_recv;
- ctx->console_recv_fifo = kfifo_alloc(512);
- ctx->console_transmit_fifo = kfifo_alloc(SZ_128K);
- ctx->poller.func = ratp_poller;
- ratp_console_register(ctx);
- }
-
- if (ctx->cdev)
+ if (ratp_ctx)
return -EBUSY;
- ratp = &ctx->ratp;
+ ctx = xzalloc(sizeof(*ctx));
+ ratp_ctx = ctx;
+ ctx->ratp.send = console_send;
+ ctx->ratp.recv = console_recv;
+ ctx->console_recv_fifo = kfifo_alloc(512);
+ ctx->console_transmit_fifo = kfifo_alloc(SZ_128K);
+ ctx->poller.func = ratp_poller;
+ ctx->ratp_console.tstc = ratp_console_tstc;
+ ctx->ratp_console.puts = ratp_console_puts;
+ ctx->ratp_console.putc = ratp_console_putc;
+ ctx->ratp_console.getc = ratp_console_getc;
+ ctx->ratp_console.devname = "ratpconsole";
+ ctx->ratp_console.devid = DEVICE_ID_SINGLE;
+
+ ret = console_register(&ctx->ratp_console);
+ if (ret) {
+ pr_err("registering console failed with %s\n", strerror(-ret));
+ return ret;
+ }
+
+ ctx->console_registered = true;
ctx->old_active = console_get_active(cdev);
console_set_active(cdev, 0);
@@ -447,31 +486,37 @@ int barebox_ratp(struct console_device *cdev)
ctx->cdev = cdev;
ctx->have_synch = 1;
- ret = ratp_establish(ratp, false, 100);
+ ret = ratp_establish(&ctx->ratp, false, 1000);
if (ret < 0)
goto out;
- ret = poller_register(&ctx->poller);
+ ctx->wq.fn = ratp_command_run;
+ ctx->wq.cancel = ratp_work_cancel;
+ wq_register(&ctx->wq);
+ ctx->wq_registered = true;
+
+ ret = poller_register(&ctx->poller, "ratp");
if (ret)
- goto out1;
+ goto out;
+
+ ctx->poller_registered = true;
+
+ console_countdown_abort();
console_set_active(&ctx->ratp_console, CONSOLE_STDOUT | CONSOLE_STDERR |
CONSOLE_STDIN);
return 0;
-out1:
- ratp_close(ratp);
out:
- console_set_active(ctx->cdev, ctx->old_active);
- ctx->cdev = NULL;
+ ratp_unregister(ctx);
return ret;
}
static void barebox_ratp_close(void)
{
- if (ratp_ctx && ratp_ctx->cdev)
- ratp_console_unregister(ratp_ctx);
+ if (ratp_ctx)
+ ratp_unregister(ratp_ctx);
}
predevshutdown_exitcall(barebox_ratp_close);
diff --git a/common/reset_source.c b/common/reset_source.c
index 343dc08444..f28be90dcb 100644
--- a/common/reset_source.c
+++ b/common/reset_source.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2012 Juergen Beisert - <kernel@pengutronix.de>
- *
- * 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.
*/
#define pr_fmt(fmt) "reset-source: " fmt
@@ -34,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)
{
@@ -54,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)
{
@@ -83,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);
}
@@ -101,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 62497e7a54..8678609229 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* resource.c - barebox resource management
*
* Copyright (c) 2011 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 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.
*/
#include <common.h>
#include <malloc.h>
@@ -36,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;
@@ -69,25 +61,28 @@ struct resource *__request_region(struct resource *parent,
goto ok;
if (start > r->end)
continue;
- debug("%s: 0x%08llx:0x%08llx conflicts with 0x%08llx:0x%08llx\n",
+ debug("%s: 0x%08llx:0x%08llx (%s) conflicts with 0x%08llx:0x%08llx (%s)\n",
__func__,
(unsigned long long)start,
(unsigned long long)end,
+ name,
(unsigned long long)r->start,
- (unsigned long long)r->end);
+ (unsigned long long)r->end,
+ r->name);
return ERR_PTR(-EBUSY);
}
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;
@@ -108,6 +103,28 @@ int release_region(struct resource *res)
return 0;
}
+
+/*
+ * merge two adjacent sibling regions.
+ */
+int __merge_regions(const char *name,
+ struct resource *resa, struct resource *resb)
+{
+ if (!resource_adjacent(resa, resb))
+ return -EINVAL;
+
+ if (resa->start < resb->start)
+ resa->end = resb->end;
+ else
+ resa->start = resb->start;
+
+ free((char *)resa->name);
+ resa->name = xstrdup(name);
+ release_region(resb);
+
+ return 0;
+}
+
/* The root resource for the whole memory-mapped io space */
struct resource iomem_resource = {
.start = 0,
@@ -122,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 */
@@ -141,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 b19ae54657..35cfb54251 100644
--- a/common/restart.c
+++ b/common/restart.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 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 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.
- *
*/
#define pr_fmt(fmt) "restart: " fmt
@@ -19,6 +10,7 @@
#include <of.h>
static LIST_HEAD(restart_handler_list);
+static unsigned resetidx;
/**
* restart_handler_register() - register a handler for restarting the system
@@ -31,20 +23,30 @@ static LIST_HEAD(restart_handler_list);
int restart_handler_register(struct restart_handler *rst)
{
if (!rst->name)
- rst->name = RESTART_DEFAULT_NAME;
+ rst->name = basprintf("reset%u", resetidx);
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",
rst->name, rst->priority);
+ resetidx++;
return 0;
}
/**
* restart_handler_register_fn() - register a handler function
+ * @name: restart method name or NULL if name should be auto-generated
* @restart_fn: The restart function
*
* convenience wrapper for restart_handler_register() to register a handler
@@ -52,13 +54,15 @@ int restart_handler_register(struct restart_handler *rst)
*
* return: 0 for success or negative error code
*/
-int restart_handler_register_fn(void (*restart_fn)(struct restart_handler *))
+int restart_handler_register_fn(const char *name,
+ void (*restart_fn)(struct restart_handler *))
{
struct restart_handler *rst;
int ret;
rst = xzalloc(sizeof(*rst));
+ rst->name = xstrdup(name);
rst->restart = restart_fn;
ret = restart_handler_register(rst);
@@ -70,20 +74,35 @@ int restart_handler_register_fn(void (*restart_fn)(struct restart_handler *))
}
/**
- * restart_machine() - reset the whole system
+ * restart_handler_get_by_name() - get highest priority `name'
*/
-void __noreturn restart_machine(void)
+struct restart_handler *restart_handler_get_by_name(const char *name, int flags)
{
struct restart_handler *rst = NULL, *tmp;
unsigned int priority = 0;
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;
}
}
+ return rst;
+}
+
+/**
+ * restart_machine() - reset the whole system
+ */
+void __noreturn restart_machine(void)
+{
+ struct restart_handler *rst;
+
+ rst = restart_handler_get_by_name(NULL, 0);
if (rst) {
pr_debug("%s: using restart handler %s\n", __func__, rst->name);
console_flush();
@@ -93,17 +112,17 @@ 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
+/*
+ * restart_handlers_print - print informations about all restart handlers
*/
-unsigned int of_get_restart_priority(struct device_node *node)
+void restart_handlers_print(void)
{
- unsigned int priority = RESTART_DEFAULT_PRIORITY;
-
- of_property_read_u32(node, "restart-priority", &priority);
+ struct restart_handler *tmp;
- return 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/s_record.c b/common/s_record.c
index 1806890242..95fa890b69 100644
--- a/common/s_record.c
+++ b/common/s_record.c
@@ -1,17 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * 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.
- *
*/
#include <common.h>
diff --git a/common/sched.c b/common/sched.c
new file mode 100644
index 0000000000..02582b2c5e
--- /dev/null
+++ b/common/sched.c
@@ -0,0 +1,26 @@
+/* SPDX License Identifier: GPL-2.0 */
+
+#include <bthread.h>
+#include <poller.h>
+#include <work.h>
+#include <slice.h>
+#include <sched.h>
+
+void resched(void)
+{
+ bool run_workqueues = !slice_acquired(&command_slice);
+
+ if (poller_active())
+ return;
+
+ command_slice_acquire();
+
+ if (run_workqueues) {
+ wq_do_all_works();
+ bthread_reschedule();
+ }
+
+ poller_call();
+
+ command_slice_release();
+}
diff --git a/common/serdev.c b/common/serdev.c
index c87b8a8c17..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>
@@ -73,7 +74,7 @@ int serdev_device_open(struct serdev_device *serdev)
if (!serdev->fifo)
return -ENOMEM;
- ret = poller_async_register(&serdev->poller);
+ ret = poller_async_register(&serdev->poller, "serdev");
if (ret)
return ret;
@@ -84,10 +85,7 @@ int serdev_device_open(struct serdev_device *serdev)
p = dev_add_param_uint64(serdev->dev, "polling_interval",
serdev_device_set_polling_interval, NULL,
&serdev->polling_interval, "%llu", serdev);
- if (IS_ERR(p))
- return PTR_ERR(p);
-
- return 0;
+ return PTR_ERR_OR_ZERO(p);
}
unsigned int serdev_device_set_baudrate(struct serdev_device *serdev,
@@ -134,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);
@@ -188,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/slice.c b/common/slice.c
new file mode 100644
index 0000000000..9d7e0d16cf
--- /dev/null
+++ b/common/slice.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "slice: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <slice.h>
+
+/*
+ * slices, the barebox idea of locking
+ *
+ * barebox has pollers which execute code in the background whenever one of the
+ * delay functions (udelay, mdelay, ...) or is_timeout() are called. This
+ * introduces resource problems when some device triggers a poller by calling
+ * a delay function and then the poller code calls into the same device again.
+ *
+ * As an example consider a I2C GPIO expander which drives a LED which shall
+ * be used as a heartbeat LED:
+ *
+ * poller -> LED on/off -> GPIO high/low -> I2C transfer
+ *
+ * The I2C controller has a timeout loop using is_timeout() and thus can trigger
+ * a poller run. With this the following can happen during an unrelated I2C
+ * transfer:
+ *
+ * I2C transfer -> is_timeout() -> poller -> LED on/off -> GPIO high/low -> I2C transfer
+ *
+ * We end up with issuing an I2C transfer during another I2C transfer and
+ * things go downhill.
+ *
+ * Due to the lack of interrupts we can't do real locking in barebox. We use
+ * a mechanism called slices instead. A slice describes a resource to which
+ * other slices can be attached. Whenever a slice is needed it must be acquired.
+ * Acquiring a slice never fails, it just increases the acquired counter of
+ * the slice and its dependent slices. when a slice shall be used inside a
+ * poller it must first be tested if the slice is already in use. If it is,
+ * we can't do the operation on the slice now and must return and hope that
+ * we have more luck in the next poller call.
+ *
+ * slices can be attached other slices as dependencies. In the example above
+ * LED driver would add a dependency to the GPIO controller and the GPIO driver
+ * would add a dependency to the I2C bus:
+ *
+ * GPIO driver probe:
+ *
+ * slice_depends_on(&gpio->slice, i2c_device_slice(i2cdev));
+ *
+ * LED driver probe:
+ *
+ * slice_depends_on(&led->slice, gpio_slice(gpio));
+ *
+ * The GPIO code would call slice_acquire(&gpio->slice) before doing any
+ * operation on the GPIO chip providing this GPIO, likewise the I2C core
+ * would call slice_acquire(&i2cbus->slice) before doing an operation on
+ * this I2C bus.
+ *
+ * The heartbeat poller code would call slice_acquired(led_slice(led)) and
+ * only continue when the slice is not acquired.
+ */
+
+/*
+ * slices are not required to have a device and thus a name, but it really
+ * helps debugging when it has one.
+ */
+static const char *slice_name(struct slice *slice)
+{
+ return slice->name;
+}
+
+static void __slice_acquire(struct slice *slice, int add)
+{
+ slice->acquired += add;
+
+ if (slice->acquired < 0) {
+ pr_err("Slice %s acquired count drops below zero\n",
+ slice_name(slice));
+ dump_stack();
+ slice->acquired = 0;
+ return;
+ }
+}
+
+/**
+ * slice_acquire: acquire a slice
+ * @slice: The slice to acquire
+ *
+ * This acquires a slice and its dependencies
+ */
+void slice_acquire(struct slice *slice)
+{
+ __slice_acquire(slice, 1);
+}
+
+/**
+ * slice_release: release a slice
+ * @slice: The slice to release
+ *
+ * This releases a slice and its dependencies
+ */
+void slice_release(struct slice *slice)
+{
+ __slice_acquire(slice, -1);
+}
+
+/**
+ * slice_acquired: test if a slice is acquired
+ * @slice: The slice to test
+ *
+ * This tests if a slice is acquired. Returns true if it is, false otherwise
+ */
+bool slice_acquired(struct slice *slice)
+{
+ struct slice_entry *se;
+ bool ret = false;
+
+ if (slice->acquired > 0)
+ return true;
+
+ if (slice->acquired < 0) {
+ pr_err("Recursive dependency detected in slice %s\n",
+ slice_name(slice));
+ panic("Cannot continue");
+ }
+
+ slice->acquired = -1;
+
+ list_for_each_entry(se, &slice->deps, list)
+ if (slice_acquired(se->slice)) {
+ ret = true;
+ break;
+ }
+
+ slice->acquired = 0;
+
+ return ret;
+}
+
+static void __slice_debug_acquired(struct slice *slice, int level)
+{
+ struct slice_entry *se;
+
+ pr_debug("%*s%s: %d\n", level * 4, "",
+ slice_name(slice),
+ slice->acquired);
+
+ list_for_each_entry(se, &slice->deps, list)
+ __slice_debug_acquired(se->slice, level + 1);
+}
+
+/**
+ * slice_debug_acquired: print the acquired state of a slice
+ *
+ * @slice: The slice to print
+ *
+ * This prints the acquired state of a slice and its dependencies.
+ */
+void slice_debug_acquired(struct slice *slice)
+{
+ if (!slice_acquired(slice))
+ return;
+
+ __slice_debug_acquired(slice, 0);
+}
+
+/**
+ * slice_depends_on: Add a dependency to a slice
+ *
+ * @slice: The slice to add the dependency to
+ * @dep: The slice @slice depends on
+ *
+ * This makes @slice dependent on @dep. In other words, acquiring @slice
+ * will lead to also acquiring @dep.
+ */
+void slice_depends_on(struct slice *slice, struct slice *dep)
+{
+ struct slice_entry *se = xzalloc(sizeof(*se));
+
+ pr_debug("Adding dependency %s (%d) to slice %s (%d)\n",
+ slice_name(dep), dep->acquired,
+ slice_name(slice), slice->acquired);
+
+ se->slice = dep;
+
+ list_add_tail(&se->list, &slice->deps);
+}
+
+static LIST_HEAD(slices);
+
+/**
+ * slice_init - initialize a slice before usage
+ * @slice: The slice to initialize
+ * @name: The name of the slice
+ *
+ * This initializes a slice before usage. @name should be a meaningful name, when
+ * a device context is available to the caller it is recommended to pass its
+ * dev_name() as name argument.
+ */
+void slice_init(struct slice *slice, const char *name)
+{
+ INIT_LIST_HEAD(&slice->deps);
+ slice->name = xstrdup(name);
+ list_add_tail(&slice->list, &slices);
+}
+
+/**
+ * slice_exit: Remove a slice
+ * @slice: The slice to remove the dependency from
+ */
+void slice_exit(struct slice *slice)
+{
+ struct slice *s;
+ struct slice_entry *se, *tmp;
+
+ list_del(&slice->list);
+
+ /* remove our dependencies */
+ list_for_each_entry_safe(se, tmp, &slice->deps, list) {
+ list_del(&se->list);
+ free(se);
+ }
+
+ /* remove this slice from other slices dependencies */
+ list_for_each_entry(s, &slices, list) {
+ list_for_each_entry_safe(se, tmp, &s->deps, list) {
+ if (se->slice == slice) {
+ list_del(&se->list);
+ free(se);
+ }
+ }
+ }
+
+ free(slice->name);
+}
+
+struct slice command_slice;
+
+/**
+ * command_slice_acquire - acquire the command slice
+ *
+ * The command slice is acquired by default. It is only released
+ * at certain points we know it's safe to execute code in the
+ * background, like when the shell is waiting for input.
+ */
+void command_slice_acquire(void)
+{
+ slice_acquire(&command_slice);
+}
+
+/**
+ * command_slice_release - release the command slice
+ */
+void command_slice_release(void)
+{
+ slice_release(&command_slice);
+}
+
+static int command_slice_init(void)
+{
+ slice_init(&command_slice, "command");
+ slice_acquire(&command_slice);
+ return 0;
+}
+
+pure_initcall(command_slice_init);
+
+#if defined CONFIG_CMD_SLICE
+
+#include <command.h>
+#include <getopt.h>
+
+static void slice_info(void)
+{
+ struct slice *slice;
+ struct slice_entry *se;
+
+ list_for_each_entry(slice, &slices, list) {
+ printf("slice %s: acquired=%d\n",
+ slice_name(slice), slice->acquired);
+ list_for_each_entry(se, &slice->deps, list)
+ printf(" dep: %s\n", slice_name(se->slice));
+ }
+}
+
+static int __slice_acquire_name(const char *name, int add)
+{
+ struct slice *slice;
+
+ list_for_each_entry(slice, &slices, list) {
+ if (!strcmp(slice->name, name)) {
+ __slice_acquire(slice, add);
+ return 0;
+ }
+ }
+
+ printf("No such slice: %s\n", name);
+
+ return 1;
+}
+
+BAREBOX_CMD_HELP_START(slice)
+BAREBOX_CMD_HELP_TEXT("slice debugging command")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-i", "Print information about slices")
+BAREBOX_CMD_HELP_OPT ("-a <devname>", "acquire a slice")
+BAREBOX_CMD_HELP_OPT ("-r <devname>", "release a slice")
+BAREBOX_CMD_HELP_END
+
+static int do_slice(int argc, char *argv[])
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "a:r:i")) > 0) {
+ switch (opt) {
+ case 'a':
+ return __slice_acquire_name(optarg, 1);
+ case 'r':
+ return __slice_acquire_name(optarg, -1);
+ case 'i':
+ slice_info();
+ return 0;
+ }
+ }
+
+ return COMMAND_ERROR_USAGE;
+}
+
+BAREBOX_CMD_START(slice)
+ .cmd = do_slice,
+ BAREBOX_CMD_DESC("debug slices")
+ BAREBOX_CMD_OPTS("[-ari]")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_slice_help)
+BAREBOX_CMD_END
+#endif
diff --git a/common/startup.c b/common/startup.c
index 511675ed55..47b70a7756 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2002-2006
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
@@ -5,17 +6,6 @@
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
- *
- * 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.
- *
*/
#ifdef CONFIG_DEBUG_INITCALLS
@@ -34,15 +24,22 @@
#include <debug_ll.h>
#include <fs.h>
#include <errno.h>
+#include <slice.h>
#include <linux/stat.h>
#include <envfs.h>
#include <magicvar.h>
+#include <linux/reboot-mode.h>
#include <asm/sections.h>
#include <uncompress.h>
#include <globalvar.h>
#include <console_countdown.h>
#include <environment.h>
#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[],
__barebox_initcalls_end[];
@@ -58,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);
}
@@ -73,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;
@@ -148,14 +81,10 @@ 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");
+ pr_notice("No support for persistent environment. Using default environment\n");
}
nvvar_load();
@@ -235,7 +164,7 @@ void set_autoboot_state(enum autoboot_state autoboot)
*/
enum autoboot_state do_autoboot_countdown(void)
{
- enum autoboot_state autoboot_state;
+ static enum autoboot_state autoboot_state = AUTOBOOT_UNKNOWN;
unsigned flags = CONSOLE_COUNTDOWN_EXTERN;
int ret;
struct stat s;
@@ -243,6 +172,15 @@ enum autoboot_state do_autoboot_countdown(void)
char *abortkeys = NULL;
unsigned char outkey;
+ if (autoboot_state != AUTOBOOT_UNKNOWN)
+ return autoboot_state;
+
+ if (!console_get_first_active() &&
+ global_autoboot_state != AUTOBOOT_ABORT) {
+ printf("\nNon-interactive console, booting system\n");
+ return autoboot_state = AUTOBOOT_BOOT;
+ }
+
if (global_autoboot_state != AUTOBOOT_COUNTDOWN)
return global_autoboot_state;
@@ -268,8 +206,10 @@ enum autoboot_state do_autoboot_countdown(void)
break;
}
+ command_slice_release();
ret = console_countdown(global_autoboot_timeout, flags, abortkeys,
&outkey);
+ command_slice_acquire();
if (ret == 0)
autoboot_state = AUTOBOOT_BOOT;
@@ -281,19 +221,8 @@ enum autoboot_state do_autoboot_countdown(void)
return autoboot_state;
}
-static int run_init(void)
+static int register_autoboot_vars(void)
{
- DIR *dir;
- struct dirent *d;
- const char *initdir = "/env/init";
- bool env_bin_init_exists;
- enum autoboot_state autoboot;
- struct stat s;
-
- /*
- * Register autoboot variables here as they might be altered by
- * init scripts.
- */
globalvar_add_simple_enum("autoboot_abort_key",
&global_autoboot_abort_key,
global_autoboot_abort_keys,
@@ -305,7 +234,21 @@ static int run_init(void)
global_autoboot_states,
ARRAY_SIZE(global_autoboot_states));
+ return 0;
+}
+postcore_initcall(register_autoboot_vars);
+
+static int run_init(void)
+{
+ const char *bmode;
+ bool env_bin_init_exists;
+ enum autoboot_state autoboot;
+ struct stat s;
+ glob_t g;
+ int i, ret;
+
setenv("PATH", "/env/bin");
+ export("PATH");
/* Run legacy /env/bin/init if it exists */
env_bin_init_exists = stat(INITFILE, &s) == 0;
@@ -324,21 +267,40 @@ static int run_init(void)
}
/* Run scripts in /env/init/ */
- dir = opendir(initdir);
- if (dir) {
- char *scr;
+ ret = glob("/env/init/*", 0, NULL, &g);
+ if (!ret) {
+ for (i = 0; i < g.gl_pathc; i++) {
+ const char *path = g.gl_pathv[i];
+ char *scr;
+
+ ret = stat(path, &s);
+ if (ret)
+ continue;
- while ((d = readdir(dir))) {
- if (*d->d_name == '.')
+ if (!S_ISREG(s.st_mode))
continue;
- pr_debug("Executing '%s/%s'...\n", initdir, d->d_name);
- scr = basprintf("source %s/%s", initdir, d->d_name);
+ pr_debug("Executing '%s'...\n", path);
+ scr = basprintf("source %s", path);
run_command(scr);
free(scr);
}
- closedir(dir);
+ globfree(&g);
+ }
+
+ /* source matching script in /env/bmode/ */
+ bmode = reboot_mode_get();
+ if (bmode) {
+ char *scr, *path;
+
+ scr = xasprintf("source /env/bmode/%s", bmode);
+ path = &scr[strlen("source ")];
+ if (stat(path, &s) == 0) {
+ pr_info("Invoking '%s'...\n", path);
+ run_command(scr);
+ }
+ free(scr);
}
autoboot = do_autoboot_countdown();
@@ -348,15 +310,34 @@ 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);
+ if (autoboot == AUTOBOOT_ABORT && autoboot == global_autoboot_state)
+ watchdog_inhibit_all();
+
run_shell();
run_command(MENUFILE);
return 0;
}
+typedef void (*ctor_fn_t)(void);
+
+/* Call all constructor functions linked into the kernel. */
+static void do_ctors(void)
+{
+#ifdef CONFIG_CONSTRUCTORS
+ ctor_fn_t *fn = (ctor_fn_t *) __ctors_start;
+
+ for (; fn < (ctor_fn_t *) __ctors_end; fn++)
+ (*fn)();
+#endif
+}
+
int (*barebox_main)(void);
void __noreturn start_barebox(void)
@@ -367,6 +348,8 @@ void __noreturn start_barebox(void)
if (!IS_ENABLED(CONFIG_SHELL_NONE) && IS_ENABLED(CONFIG_COMMAND_SUPPORT))
barebox_main = run_init;
+ do_ctors();
+
for (initcall = __barebox_initcalls_start;
initcall < __barebox_initcalls_end; initcall++) {
pr_debug("initcall-> %pS\n", *initcall);
@@ -378,6 +361,9 @@ void __noreturn start_barebox(void)
pr_debug("initcalls done\n");
+ if (IS_ENABLED(CONFIG_SELFTEST_AUTORUN))
+ selftests_run();
+
if (barebox_main)
barebox_main();
@@ -409,14 +395,13 @@ void shutdown_barebox(void)
pr_debug("exitcall-> %pS\n", *exitcall);
(*exitcall)();
}
+
+ console_flush();
}
-BAREBOX_MAGICVAR_NAMED(autoboot_state,
- global.autoboot,
- "Autoboot state. Possible values: countdown (default), abort, menu, boot");
-BAREBOX_MAGICVAR_NAMED(global_autoboot_abort_key,
- global.autoboot_abort_key,
- "Which key allows to interrupt autoboot. Possible values: any, ctrl-c");
-BAREBOX_MAGICVAR_NAMED(global_autoboot_timeout,
- global.autoboot_timeout,
- "Timeout before autoboot starts in seconds");
+BAREBOX_MAGICVAR(global.autoboot,
+ "Autoboot state. Possible values: countdown (default), abort, menu, boot");
+BAREBOX_MAGICVAR(global.autoboot_abort_key,
+ "Which key allows to interrupt autoboot. Possible values: any, ctrl-c");
+BAREBOX_MAGICVAR(global.autoboot_timeout,
+ "Timeout before autoboot starts in seconds");
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 48f30db1f5..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
@@ -59,7 +59,7 @@ static int state_backend_format_dtb_verify(struct state_backend_format *format,
fdtb->root = NULL;
}
- root = of_unflatten_dtb(buf);
+ root = of_unflatten_dtb(buf, dtb_len);
if (IS_ERR(root)) {
dev_err(fdtb->dev, "Failed to unflatten dtb from buffer with length %zd, %ld\n",
len, PTR_ERR(root));
@@ -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 5a71149d34..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",
@@ -183,6 +180,7 @@ static int backend_format_raw_unpack(struct state_backend_format *format,
const struct backend_raw_header *header;
const void *data;
struct state_backend_format_raw *backend_raw = get_format_raw(format);
+ int ret = 0;
header = (const struct backend_raw_header *)buf;
data = buf + sizeof(*header);
@@ -191,12 +189,13 @@ static int backend_format_raw_unpack(struct state_backend_format *format,
if (sv->start + sv->size > header->data_len) {
dev_err(backend_raw->dev, "State variable ends behind valid data, %s\n",
sv->name);
+ ret = -ENOSPC;
continue;
}
memcpy(sv->raw, data + sv->start, sv->size);
}
- return 0;
+ return ret;
}
static int backend_format_raw_pack(struct state_backend_format *format,
@@ -300,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 d42920985d..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;
}
@@ -271,9 +273,8 @@ static int state_convert_node_variable(struct state *state,
if (conv == STATE_CONVERT_FROM_NODE_CREATE) {
sv = vtype->create(state, name, node, vtype);
if (IS_ERR(sv)) {
+ dev_err(&state->dev, "failed to create %s: %pe\n", name, sv);
ret = PTR_ERR(sv);
- dev_err(&state->dev, "failed to create %s: %s\n", name,
- strerror(-ret));
goto out_free;
}
@@ -334,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,
@@ -362,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);
}
@@ -557,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;
}
@@ -579,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
*
@@ -595,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);
}
@@ -615,11 +636,8 @@ struct state *state_new_from_node(struct device_node *node, bool readonly)
goto out_release_state;
}
-#ifdef __BAREBOX__
- 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",
@@ -627,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);
@@ -650,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;
@@ -701,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;
@@ -713,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/system-partitions.c b/common/system-partitions.c
new file mode 100644
index 0000000000..547e08a9f3
--- /dev/null
+++ b/common/system-partitions.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Ahmad Fatoum, Pengutronix
+ */
+
+#include <file-list.h>
+#include <param.h>
+#include <globalvar.h>
+#include <init.h>
+#include <magicvar.h>
+#include <system-partitions.h>
+
+static struct file_list *system_partitions;
+
+bool system_partitions_empty(void)
+{
+ return file_list_empty(system_partitions);
+}
+
+struct file_list *system_partitions_get(void)
+{
+ return file_list_dup(system_partitions);
+}
+
+void system_partitions_set(struct file_list *files)
+{
+ file_list_free(system_partitions);
+ system_partitions = files;
+}
+
+static int system_partitions_var_init(void)
+{
+ struct param_d *param;
+
+ system_partitions = file_list_parse("");
+ param = dev_add_param_file_list(&global_device, "system.partitions",
+ NULL, NULL, &system_partitions, NULL);
+
+ return PTR_ERR_OR_ZERO(param);
+}
+postcore_initcall(system_partitions_var_init);
+
+BAREBOX_MAGICVAR(global.system.partitions,
+ "board-specific list of updatable partitions");
diff --git a/common/tlsf.c b/common/tlsf.c
index ba68a5e688..ba2ed367c0 100644
--- a/common/tlsf.c
+++ b/common/tlsf.c
@@ -1,16 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <linux/stringify.h>
-
-#include "tlsf.h"
+#include <tlsf.h>
#include "tlsfbits.h"
+#include <linux/kasan.h>
-#ifdef DEBUG
-#define tlsf_assert(expr) \
- ((expr) ? (void)(0) : printf("%s\n", __stringify(expr)))
-#else
-#define tlsf_assert(expr) (void)(0)
+#define CHAR_BIT 8
+
+#ifndef CONFIG_KASAN
+#define __memcpy memcpy
#endif
/*
@@ -20,20 +20,18 @@
/* Public constants: may be modified. */
enum tlsf_public
{
- /* log2 of number of linear subdivisions of block sizes. */
+ /* log2 of number of linear subdivisions of block sizes. Larger
+ ** values require more memory in the control structure. Values of
+ ** 4 or 5 are typical.
+ */
SL_INDEX_COUNT_LOG2 = 5,
};
/* 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),
/*
@@ -87,7 +85,6 @@ enum tlsf_private
#define tlsf_static_assert(exp) \
typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1]
-#ifndef __BAREBOX__
/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */
tlsf_static_assert(sizeof(int) * CHAR_BIT == 32);
tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32);
@@ -98,7 +95,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);
-#endif
+
+tlsf_static_assert(ALIGN_SIZE >= CONFIG_MALLOC_ALIGNMENT);
/*
** Data structures and associated constants.
@@ -121,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;
@@ -133,31 +132,32 @@ 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 pool structure. */
-typedef struct pool_t
+/* The TLSF control structure. */
+typedef struct control_t
{
/* Empty lists point at this block to indicate they are free. */
block_header_t block_null;
@@ -165,10 +165,12 @@ typedef struct pool_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];
-} pool_t;
+} 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;
@@ -190,7 +192,7 @@ static void block_set_size(block_header_t* block, size_t size)
static int block_is_last(const block_header_t* block)
{
- return 0 == block_size(block);
+ return block_size(block) == 0;
}
static int block_is_free(const block_header_t* block)
@@ -244,6 +246,7 @@ static block_header_t* offset_to_block(const void* ptr, size_t size)
/* Return location of previous block. */
static block_header_t* block_prev(const block_header_t* block)
{
+ tlsf_assert(block_is_prev_free(block) && "previous block must be free");
return block->prev_phys_block;
}
@@ -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;
}
@@ -306,10 +309,15 @@ static void* align_ptr(const void* ptr, size_t align)
static size_t adjust_request_size(size_t size, size_t align)
{
size_t adjust = 0;
- if (size && size < block_size_max)
+ if (size)
{
const size_t aligned = align_up(size, align);
- adjust = tlsf_max(aligned, block_size_min);
+
+ /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */
+ if (aligned >= size && aligned < block_size_max)
+ {
+ adjust = tlsf_max(aligned, block_size_min);
+ }
}
return adjust;
}
@@ -341,7 +349,7 @@ static void mapping_insert(size_t size, int* fli, int* sli)
/* This version rounds up to the next block size (for allocations) */
static void mapping_search(size_t size, int* fli, int* sli)
{
- if (size >= (1 << SL_INDEX_COUNT_LOG2))
+ if (size >= SMALL_BLOCK_SIZE)
{
const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
size += round;
@@ -349,7 +357,7 @@ static void mapping_search(size_t size, int* fli, int* sli)
mapping_insert(size, fli, sli);
}
-static block_header_t* search_suitable_block(pool_t* pool, int* fli, int* sli)
+static block_header_t* search_suitable_block(control_t* control, int* fli, int* sli)
{
int fl = *fli;
int sl = *sli;
@@ -358,31 +366,31 @@ static block_header_t* search_suitable_block(pool_t* pool, int* fli, int* sli)
** First, search for a block in the list associated with the given
** fl/sl index.
*/
- unsigned int sl_map = pool->sl_bitmap[fl] & (~0 << sl);
+ unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl);
if (!sl_map)
{
/* No block exists. Search in the next largest first-level list. */
- const unsigned int fl_map = pool->fl_bitmap & (~0 << (fl + 1));
+ const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1));
if (!fl_map)
{
/* No free blocks available, memory has been exhausted. */
- return NULL;
+ return 0;
}
fl = tlsf_ffs(fl_map);
*fli = fl;
- sl_map = pool->sl_bitmap[fl];
+ sl_map = control->sl_bitmap[fl];
}
tlsf_assert(sl_map && "internal error - second level bitmap is null");
sl = tlsf_ffs(sl_map);
*sli = sl;
/* Return the first block in the free list. */
- return pool->blocks[fl][sl];
+ return control->blocks[fl][sl];
}
/* Remove a free block from the free list.*/
-static void remove_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+static void remove_free_block(control_t* control, block_header_t* block, int fl, int sl)
{
block_header_t* prev = block->prev_free;
block_header_t* next = block->next_free;
@@ -392,32 +400,32 @@ static void remove_free_block(pool_t* pool, block_header_t* block, int fl, int s
prev->next_free = next;
/* If this block is the head of the free list, set new head. */
- if (pool->blocks[fl][sl] == block)
+ if (control->blocks[fl][sl] == block)
{
- pool->blocks[fl][sl] = next;
+ control->blocks[fl][sl] = next;
/* If the new head is null, clear the bitmap. */
- if (next == &pool->block_null)
+ if (next == &control->block_null)
{
- pool->sl_bitmap[fl] &= ~(1 << sl);
+ control->sl_bitmap[fl] &= ~(1U << sl);
/* If the second bitmap is now empty, clear the fl bitmap. */
- if (!pool->sl_bitmap[fl])
+ if (!control->sl_bitmap[fl])
{
- pool->fl_bitmap &= ~(1 << fl);
+ control->fl_bitmap &= ~(1U << fl);
}
}
}
}
/* Insert a free block into the free block list. */
-static void insert_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+static void insert_free_block(control_t* control, block_header_t* block, int fl, int sl)
{
- block_header_t* current = pool->blocks[fl][sl];
+ block_header_t* current = control->blocks[fl][sl];
tlsf_assert(current && "free list cannot have a null entry");
tlsf_assert(block && "cannot insert a null entry into the free list");
block->next_free = current;
- block->prev_free = &pool->block_null;
+ block->prev_free = &control->block_null;
current->prev_free = block;
tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE)
@@ -426,25 +434,25 @@ static void insert_free_block(pool_t* pool, block_header_t* block, int fl, int s
** Insert the new block at the head of the list, and mark the first-
** and second-level bitmaps appropriately.
*/
- pool->blocks[fl][sl] = block;
- pool->fl_bitmap |= (1 << fl);
- pool->sl_bitmap[fl] |= (1 << sl);
+ control->blocks[fl][sl] = block;
+ control->fl_bitmap |= (1U << fl);
+ control->sl_bitmap[fl] |= (1U << sl);
}
/* Remove a given block from the free list. */
-static void block_remove(pool_t* pool, block_header_t* block)
+static void block_remove(control_t* control, block_header_t* block)
{
int fl, sl;
mapping_insert(block_size(block), &fl, &sl);
- remove_free_block(pool, block, fl, sl);
+ remove_free_block(control, block, fl, sl);
}
/* Insert a given block into the free list. */
-static void block_insert(pool_t* pool, block_header_t* block)
+static void block_insert(control_t* control, block_header_t* block)
{
int fl, sl;
mapping_insert(block_size(block), &fl, &sl);
- insert_free_block(pool, block, fl, sl);
+ insert_free_block(control, block, fl, sl);
}
static int block_can_split(block_header_t* block, size_t size)
@@ -457,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);
@@ -477,7 +485,7 @@ static block_header_t* block_split(block_header_t* block, size_t size)
/* Absorb a free block's storage into an adjacent previous free block. */
static block_header_t* block_absorb(block_header_t* prev, block_header_t* block)
{
- tlsf_assert(!block_is_last(prev) && "previous block can't be last!");
+ tlsf_assert(!block_is_last(prev) && "previous block can't be last");
/* Note: Leaves flags untouched. */
prev->size += block_size(block) + block_header_overhead;
block_link_next(prev);
@@ -485,14 +493,14 @@ static block_header_t* block_absorb(block_header_t* prev, block_header_t* block)
}
/* Merge a just-freed block with an adjacent previous free block. */
-static block_header_t* block_merge_prev(pool_t* pool, block_header_t* block)
+static block_header_t* block_merge_prev(control_t* control, block_header_t* block)
{
if (block_is_prev_free(block))
{
block_header_t* prev = block_prev(block);
tlsf_assert(prev && "prev physical block can't be null");
tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
- block_remove(pool, prev);
+ block_remove(control, prev);
block = block_absorb(prev, block);
}
@@ -500,15 +508,15 @@ static block_header_t* block_merge_prev(pool_t* pool, block_header_t* block)
}
/* Merge a just-freed block with an adjacent free block. */
-static block_header_t* block_merge_next(pool_t* pool, block_header_t* block)
+static block_header_t* block_merge_next(control_t* control, block_header_t* block)
{
block_header_t* next = block_next(block);
tlsf_assert(next && "next physical block can't be null");
if (block_is_free(next))
{
- tlsf_assert(!block_is_last(block) && "previous block can't be last!");
- block_remove(pool, next);
+ tlsf_assert(!block_is_last(block) && "previous block can't be last");
+ block_remove(control, next);
block = block_absorb(block, next);
}
@@ -516,7 +524,7 @@ static block_header_t* block_merge_next(pool_t* pool, block_header_t* block)
}
/* Trim any trailing block space off the end of a block, return to pool. */
-static void block_trim_free(pool_t* pool, block_header_t* block, size_t size)
+static void block_trim_free(control_t* control, block_header_t* block, size_t size)
{
tlsf_assert(block_is_free(block) && "block must be free");
if (block_can_split(block, size))
@@ -524,12 +532,12 @@ static void block_trim_free(pool_t* pool, block_header_t* block, size_t size)
block_header_t* remaining_block = block_split(block, size);
block_link_next(block);
block_set_prev_free(remaining_block);
- block_insert(pool, remaining_block);
+ block_insert(control, remaining_block);
}
}
/* Trim any trailing block space off the end of a used block, return to pool. */
-static void block_trim_used(pool_t* pool, block_header_t* block, size_t size)
+static void block_trim_used(control_t* control, block_header_t* block, size_t size, size_t used)
{
tlsf_assert(!block_is_free(block) && "block must be used");
if (block_can_split(block, size))
@@ -538,12 +546,16 @@ static void block_trim_used(pool_t* pool, block_header_t* block, size_t size)
block_header_t* remaining_block = block_split(block, size);
block_set_prev_used(remaining_block);
- remaining_block = block_merge_next(pool, remaining_block);
- block_insert(pool, remaining_block);
+ remaining_block = block_merge_next(control, remaining_block);
+ block_insert(control, remaining_block);
}
+
+ kasan_poison_shadow(&block->size, size + 2 * sizeof(size_t),
+ KASAN_KMALLOC_REDZONE);
+ kasan_unpoison_shadow(block_to_ptr(block), used);
}
-static block_header_t* block_trim_free_leading(pool_t* pool, block_header_t* block, size_t size)
+static block_header_t* block_trim_free_leading(control_t* control, block_header_t* block, size_t size)
{
block_header_t* remaining_block = block;
if (block_can_split(block, size))
@@ -553,59 +565,75 @@ static block_header_t* block_trim_free_leading(pool_t* pool, block_header_t* blo
block_set_prev_free(remaining_block);
block_link_next(block);
- block_insert(pool, block);
+ block_insert(control, block);
}
return remaining_block;
}
-static block_header_t* block_locate_free(pool_t* pool, size_t size)
+static block_header_t* block_locate_free(control_t* control, size_t size)
{
int fl = 0, sl = 0;
- block_header_t* block = NULL;
+ block_header_t* block = 0;
if (size)
{
mapping_search(size, &fl, &sl);
- block = search_suitable_block(pool, &fl, &sl);
+
+ /*
+ ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up
+ ** with indices that are off the end of the block array.
+ ** So, we protect against that here, since this is the only callsite of mapping_search.
+ ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range.
+ */
+ if (fl < FL_INDEX_COUNT)
+ {
+ block = search_suitable_block(control, &fl, &sl);
+ }
}
if (block)
{
tlsf_assert(block_size(block) >= size);
- remove_free_block(pool, block, fl, sl);
+ remove_free_block(control, block, fl, sl);
}
return block;
}
-static void* block_prepare_used(pool_t* pool, block_header_t* block, size_t size)
+static void* block_prepare_used(control_t* control, block_header_t* block,
+ size_t size, size_t used)
{
- void* p = NULL;
+ void* p = 0;
if (block)
{
- block_trim_free(pool, block, size);
+ tlsf_assert(size && "size must be non-zero");
+ block_trim_free(control, block, size);
block_mark_as_used(block);
p = block_to_ptr(block);
+
+ kasan_poison_shadow(&block->size, size + 2 * sizeof(size_t),
+ KASAN_KMALLOC_REDZONE);
+ kasan_unpoison_shadow(p, used);
}
return p;
}
/* Clear structure and point all empty lists at the null block. */
-static void pool_construct(pool_t* pool)
+static void control_construct(control_t* control)
{
int i, j;
- pool->block_null.next_free = &pool->block_null;
- pool->block_null.prev_free = &pool->block_null;
+ control->block_null.next_free = &control->block_null;
+ control->block_null.prev_free = &control->block_null;
- pool->fl_bitmap = 0;
+ control->fl_bitmap = 0;
for (i = 0; i < FL_INDEX_COUNT; ++i)
{
- pool->sl_bitmap[i] = 0;
+ control->sl_bitmap[i] = 0;
for (j = 0; j < SL_INDEX_COUNT; ++j)
{
- pool->blocks[i][j] = &pool->block_null;
+ control->blocks[i][j] = &control->block_null;
}
}
}
@@ -631,6 +659,7 @@ static void integrity_walker(void* ptr, size_t size, int used, void* user)
const size_t this_block_size = block_size(block);
int status = 0;
+ (void)used;
tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect");
tlsf_insist(size == this_block_size && "block size incorrect");
@@ -638,27 +667,22 @@ static void integrity_walker(void* ptr, size_t size, int used, void* user)
integ->status += status;
}
-int tlsf_check_heap(tlsf_pool tlsf)
+int tlsf_check(tlsf_t tlsf)
{
int i, j;
- pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ control_t* control = tlsf_cast(control_t*, tlsf);
int status = 0;
- /* Check that the blocks are physically correct. */
- integrity_t integ = { 0, 0 };
- tlsf_walk_heap(tlsf, integrity_walker, &integ);
- status = integ.status;
-
/* Check that the free lists and bitmaps are accurate. */
for (i = 0; i < FL_INDEX_COUNT; ++i)
{
for (j = 0; j < SL_INDEX_COUNT; ++j)
{
- const int fl_map = pool->fl_bitmap & (1 << i);
- const int sl_list = pool->sl_bitmap[i];
- const int sl_map = sl_list & (1 << j);
- const block_header_t* block = pool->blocks[i][j];
+ const int fl_map = control->fl_bitmap & (1U << i);
+ const int sl_list = control->sl_bitmap[i];
+ const int sl_map = sl_list & (1U << j);
+ const block_header_t* block = control->blocks[i][j];
/* Check that first- and second-level lists agree. */
if (!fl_map)
@@ -668,15 +692,15 @@ int tlsf_check_heap(tlsf_pool tlsf)
if (!sl_map)
{
- tlsf_insist(block == &pool->block_null && "block list must be null");
+ tlsf_insist(block == &control->block_null && "block list must be null");
continue;
}
/* Check that there is at least one free block. */
tlsf_insist(sl_list && "no free blocks in second-level map");
- tlsf_insist(block != &pool->block_null && "block should not be null");
+ tlsf_insist(block != &control->block_null && "block should not be null");
- while (block != &pool->block_null)
+ while (block != &control->block_null)
{
int fli, sli;
tlsf_insist(block_is_free(block) && "block should be free");
@@ -703,15 +727,15 @@ static void default_walker(void* ptr, size_t size, int used, void* user)
printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr));
}
-void tlsf_walk_heap(tlsf_pool pool, tlsf_walker walker, void* user)
+void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user)
{
- tlsf_walker heap_walker = walker ? walker : default_walker;
+ tlsf_walker pool_walker = walker ? walker : default_walker;
block_header_t* block =
- offset_to_block(pool, sizeof(pool_t) - block_header_overhead);
+ offset_to_block(pool, -(int)block_header_shift);
while (block && !block_is_last(block))
{
- heap_walker(
+ pool_walker(
block_to_ptr(block),
block_size(block),
!block_is_free(block),
@@ -731,31 +755,125 @@ size_t tlsf_block_size(void* ptr)
return size;
}
+int tlsf_check_pool(pool_t pool)
+{
+ /* Check that the blocks are physically correct. */
+ integrity_t integ = { 0, 0 };
+ tlsf_walk_pool(pool, integrity_walker, &integ);
+
+ return integ.status;
+}
+
/*
-** Overhead of the TLSF structures in a given memory block passed to
-** tlsf_create, equal to the size of a pool_t plus overhead of the initial
-** free block and the sentinel block.
+** Size of the TLSF structures in a given memory block passed to
+** tlsf_create, equal to aligned size of a control_t
*/
-size_t tlsf_overhead(void)
+size_t tlsf_size(void)
+{
+ return align_up(sizeof(control_t), ALIGN_SIZE);
+}
+
+size_t tlsf_align_size(void)
{
- const size_t pool_overhead = sizeof(pool_t) + 2 * block_header_overhead;
- return pool_overhead;
+ return ALIGN_SIZE;
+}
+
+size_t tlsf_block_size_min(void)
+{
+ return block_size_min;
+}
+
+size_t tlsf_block_size_max(void)
+{
+ return block_size_max;
}
/*
-** TLSF main interface. Right out of the white paper.
+** Overhead of the TLSF structures in a given memory block passed to
+** tlsf_add_pool, equal to the overhead of a free block and the
+** sentinel block.
*/
+size_t tlsf_pool_overhead(void)
+{
+ return 2 * block_header_overhead;
+}
+
+size_t tlsf_alloc_overhead(void)
+{
+ return block_header_overhead;
+}
-tlsf_pool tlsf_create(void* mem, size_t bytes)
+pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes)
{
block_header_t* block;
block_header_t* next;
- const size_t pool_overhead = tlsf_overhead();
+ const size_t pool_overhead = tlsf_pool_overhead();
const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
- pool_t* pool = tlsf_cast(pool_t*, mem);
-#ifdef DEBUG
+ if (((ptrdiff_t)mem % ALIGN_SIZE) != 0)
+ {
+ printf("tlsf_add_pool: Memory must be aligned by %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
+
+ if (pool_bytes < block_size_min || pool_bytes > block_size_max)
+ {
+#if defined (TLSF_64BIT)
+ printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)((pool_overhead + block_size_max) / 256));
+#else
+ printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)(pool_overhead + block_size_max));
+#endif
+ return 0;
+ }
+
+ /*
+ ** Create the main free block. Offset the start of the block slightly
+ ** 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_shift);
+ block_set_size(block, pool_bytes);
+ block_set_free(block);
+ block_set_prev_used(block);
+ block_insert(tlsf_cast(control_t*, tlsf), block);
+
+ /* Split the block to create a zero-size sentinel block. */
+ next = block_link_next(block);
+ block_set_size(next, 0);
+ block_set_used(next);
+ block_set_prev_free(next);
+
+ return mem;
+}
+
+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_shift);
+
+ int fl = 0, sl = 0;
+
+ tlsf_assert(block_is_free(block) && "block should be free");
+ tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free");
+ tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero");
+
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(control, block, fl, sl);
+}
+
+/*
+** TLSF main interface.
+*/
+
+#ifdef _DEBUG
+int test_ffs_fls()
+{
/* Verify ffs/fls work properly. */
int rv = 0;
rv += (tlsf_ffs(0) == -1) ? 0 : 0x1;
@@ -771,74 +889,79 @@ tlsf_pool tlsf_create(void* mem, size_t bytes)
rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100;
rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200;
rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400;
+#endif
+
if (rv)
{
- printf("tlsf_create: %x ffs/fls tests failed!\n", rv);
- return 0;
+ printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv);
}
-#endif
+ return rv;
+}
#endif
- if (pool_bytes < block_size_min || pool_bytes > block_size_max)
+tlsf_t tlsf_create(void* mem)
+{
+#ifdef _DEBUG
+ if (test_ffs_fls())
{
-#if defined (TLSF_64BIT)
- printf("tlsf_create: Pool size must be at least %d bytes.\n",
- (unsigned int)(pool_overhead + block_size_min));
-#else
- printf("tlsf_create: Pool size must be between %u and %u bytes.\n",
- (unsigned int)(pool_overhead + block_size_min),
- (unsigned int)(pool_overhead + block_size_max));
-#endif
- return NULL;
+ return 0;
}
+#endif
- /* Construct a valid pool object. */
- pool_construct(pool);
+ if (((tlsfptr_t)mem % ALIGN_SIZE) != 0)
+ {
+ printf("tlsf_create: Memory must be aligned to %u bytes.\n",
+ (unsigned int)ALIGN_SIZE);
+ return 0;
+ }
- /*
- ** Create the main free block. Offset the start of the block slightly
- ** so that the prev_phys_block field falls inside of the pool
- ** structure - it will never be used.
- */
- block = offset_to_block(
- tlsf_cast(void*, pool), sizeof(pool_t) - block_header_overhead);
- block_set_size(block, pool_bytes);
- block_set_free(block);
- block_set_prev_used(block);
- block_insert(pool, block);
+ control_construct(tlsf_cast(control_t*, mem));
- /* Split the block to create a zero-size pool sentinel block. */
- next = block_link_next(block);
- block_set_size(next, 0);
- block_set_used(next);
- block_set_prev_free(next);
+ return tlsf_cast(tlsf_t, mem);
+}
- return tlsf_cast(tlsf_pool, pool);
+tlsf_t tlsf_create_with_pool(void* mem, size_t bytes)
+{
+ tlsf_t tlsf = tlsf_create(mem);
+ tlsf_add_pool(tlsf, (char*)mem + tlsf_size(), bytes - tlsf_size());
+ kasan_poison_shadow(mem, bytes, KASAN_TAG_INVALID);
+ return tlsf;
}
-void tlsf_destroy(tlsf_pool pool)
+void tlsf_destroy(tlsf_t tlsf)
{
/* Nothing to do. */
- pool = pool;
+ (void)tlsf;
}
-void* tlsf_malloc(tlsf_pool tlsf, size_t size)
+pool_t tlsf_get_pool(tlsf_t tlsf)
{
- pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ return tlsf_cast(pool_t, (char*)tlsf + tlsf_size());
+}
+
+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(pool, adjust);
- return block_prepare_used(pool, block, adjust);
+ block_header_t* block;
+
+ if (!adjust)
+ return NULL;
+
+ block = block_locate_free(control, adjust);
+
+ return block_prepare_used(control, block, adjust, size);
}
-void* tlsf_memalign(tlsf_pool tlsf, size_t align, size_t size)
+void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size)
{
- pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ control_t* control = tlsf_cast(control_t*, tlsf);
const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
/*
** We must allocate an additional minimum block size bytes so that if
** our free block will leave an alignment gap which is smaller, we can
- ** trim a leading free block and release it back to the heap. We must
+ ** trim a leading free block and release it back to the pool. We must
** do this because the previous physical block is in use, therefore
** the prev_phys_block field is not valid, and we can't simply adjust
** the size of that block.
@@ -846,13 +969,21 @@ void* tlsf_memalign(tlsf_pool tlsf, size_t align, size_t size)
const size_t gap_minimum = sizeof(block_header_t);
const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
- /* If alignment is less than or equals base alignment, we're done. */
- const size_t aligned_size = (align <= ALIGN_SIZE) ? adjust : size_with_gap;
+ /*
+ ** If alignment is less than or equals base alignment, we're done.
+ ** If we requested 0 bytes, return null, as tlsf_malloc(0) does.
+ */
+ const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust;
+
+ block_header_t* block;
+
+ if (!adjust || !size_with_gap)
+ return NULL;
- block_header_t* block = block_locate_free(pool, aligned_size);
+ 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)
{
@@ -877,24 +1008,26 @@ void* tlsf_memalign(tlsf_pool tlsf, size_t align, size_t size)
if (gap)
{
tlsf_assert(gap >= gap_minimum && "gap size too small");
- block = block_trim_free_leading(pool, block, gap);
+ block = block_trim_free_leading(control, block, gap);
}
}
- return block_prepare_used(pool, block, adjust);
+ return block_prepare_used(control, block, adjust, size);
}
-void tlsf_free(tlsf_pool tlsf, void* ptr)
+void tlsf_free(tlsf_t tlsf, void* ptr)
{
/* Don't attempt to free a NULL pointer. */
if (ptr)
{
- pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ control_t* control = tlsf_cast(control_t*, tlsf);
block_header_t* block = block_from_ptr(ptr);
+ tlsf_assert(!block_is_free(block) && "block already marked as free");
+ kasan_poison_shadow(ptr, block_size(block), 0xff);
block_mark_as_free(block);
- block = block_merge_prev(pool, block);
- block = block_merge_next(pool, block);
- block_insert(pool, block);
+ block = block_merge_prev(control, block);
+ block = block_merge_next(control, block);
+ block_insert(control, block);
}
}
@@ -911,10 +1044,10 @@ void tlsf_free(tlsf_pool tlsf, void* ptr)
** - an extended buffer size will leave the newly-allocated area with
** contents undefined
*/
-void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
+void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size)
{
- pool_t* pool = tlsf_cast(pool_t*, tlsf);
- void* p = NULL;
+ control_t* control = tlsf_cast(control_t*, tlsf);
+ void* p = 0;
/* Zero-size requests are treated as free. */
if (ptr && size == 0)
@@ -935,6 +1068,11 @@ void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
const size_t combined = cursize + block_size(next) + block_header_overhead;
const size_t adjust = adjust_request_size(size, ALIGN_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.
@@ -945,7 +1083,7 @@ void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
if (p)
{
const size_t minsize = tlsf_min(cursize, size);
- memcpy(p, ptr, minsize);
+ __memcpy(p, ptr, minsize);
tlsf_free(tlsf, ptr);
}
}
@@ -954,12 +1092,12 @@ void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
/* Do we need to expand to the next block? */
if (adjust > cursize)
{
- block_merge_next(pool, block);
+ block_merge_next(control, block);
block_mark_as_used(block);
}
/* Trim the resulting block and return the original pointer. */
- block_trim_used(pool, block, adjust);
+ block_trim_used(control, block, adjust, size);
p = ptr;
}
}
diff --git a/common/tlsf_malloc.c b/common/tlsf_malloc.c
index 9cb9ede62c..981f09de41 100644
--- a/common/tlsf_malloc.c
+++ b/common/tlsf_malloc.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tlsf wrapper for barebox
*
* Copyright (C) 2011 Antony Pavlov <antonynpavlov@gmail.com>
- *
- * This file is part of barebox.
- *
- * 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 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.
- *
*/
#include <malloc.h>
@@ -23,7 +12,7 @@
#include <module.h>
#include <tlsf.h>
-extern tlsf_pool tlsf_mem_pool;
+extern tlsf_t tlsf_mem_pool;
void *malloc(size_t bytes)
{
@@ -91,7 +80,7 @@ void malloc_stats(void)
s.used = 0;
s.free = 0;
- tlsf_walk_heap(tlsf_mem_pool, malloc_walker, &s);
+ tlsf_walk_pool(tlsf_get_pool(tlsf_mem_pool), malloc_walker, &s);
printf("used: %zu\nfree: %zu\n", s.used, s.free);
}
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 cfaa68f164..d8399ad9d6 100644
--- a/common/ubiformat.c
+++ b/common/ubiformat.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2012 Wolfram Sang, Pengutronix
- *
- * 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.
*/
/*
@@ -453,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) {
@@ -754,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;
@@ -789,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 a84b8fddc4..cc9e5e510a 100644
--- a/common/uimage.c
+++ b/common/uimage.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* uimage.c - uimage handling code
*
* Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* partly based on U-Boot uImage code
- *
- * 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 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.
*/
#include <common.h>
#include <image.h>
@@ -27,6 +19,7 @@
#include <rtc.h>
#include <filetype.h>
#include <memory.h>
+#include <zero_page.h>
static inline int uimage_is_multi_image(struct uimage_handle *handle)
{
@@ -36,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));
@@ -105,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;
}
@@ -116,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;
}
@@ -223,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;
@@ -300,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)
@@ -341,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;
@@ -359,7 +354,10 @@ static int uimage_sdram_flush(void *buf, unsigned int len)
}
}
- memcpy(uimage_buf + uimage_size, buf, len);
+ if (zero_page_contains((unsigned long)uimage_buf + uimage_size))
+ zero_page_memcpy(uimage_buf + uimage_size, buf, len);
+ else
+ memcpy(uimage_buf + uimage_size, buf, len);
uimage_size += len;
@@ -388,7 +386,20 @@ struct resource *file_to_sdram(const char *filename, unsigned long adr)
goto out;
}
- now = read_full(fd, (void *)(res->start + ofs), BUFSIZ);
+ if (zero_page_contains(res->start + ofs)) {
+ void *tmp = malloc(BUFSIZ);
+ if (!tmp)
+ now = -ENOMEM;
+ else
+ now = read_full(fd, tmp, BUFSIZ);
+
+ if (now > 0)
+ zero_page_memcpy((void *)(res->start + ofs), tmp, now);
+ free(tmp);
+ } else {
+ now = read_full(fd, (void *)(res->start + ofs), BUFSIZ);
+ }
+
if (now < 0) {
release_sdram_region(res);
res = NULL;
diff --git a/common/usbgadget.c b/common/usbgadget.c
index a8f104cf1c..3713551163 100644
--- a/common/usbgadget.c
+++ b/common/usbgadget.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Oleksij Rempel <o.rempel@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 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.
- *
*/
#define pr_fmt(fmt) "usbgadget: " fmt
@@ -21,75 +12,77 @@
#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;
-static char *fastboot_function;
-static int fastboot_bbu;
-static struct file_list *parse(const char *files)
+static inline struct file_list *get_dfu_function(void)
{
- struct file_list *list = file_list_parse(files);
- if (IS_ERR(list)) {
- pr_err("Parsing file list \"%s\" failed: %s\n", files,
- strerrorp(list));
- return NULL;
- }
- return list;
+ if (dfu_function && *dfu_function)
+ return file_list_parse_null(dfu_function);
+ return system_partitions_get_null();
}
-int usbgadget_register(bool dfu, const char *dfu_opts,
- bool fastboot, const char *fastboot_opts,
- bool acm, bool export_bbu)
+int usbgadget_register(const struct usbgadget_funcs *funcs)
{
int ret;
- struct device_d *dev;
+ int flags = funcs->flags;
+ struct device *dev;
struct f_multi_opts *opts;
- if (dfu && !dfu_opts && dfu_function && *dfu_function)
- dfu_opts = dfu_function;
-
- if (fastboot && !fastboot_opts &&
- fastboot_function && *fastboot_function)
- fastboot_opts = fastboot_function;
+ opts = xzalloc(sizeof(*opts));
+ opts->release = usb_multi_opts_release;
- if (!dfu_opts && !fastboot_opts && !acm)
- return COMMAND_ERROR_USAGE;
+ if (flags & USBGADGET_DFU) {
+ opts->dfu_opts.files = file_list_parse_null(funcs->dfu_opts);
+ if (IS_ENABLED(CONFIG_USB_GADGET_DFU) && file_list_empty(opts->dfu_opts.files)) {
+ file_list_free(opts->dfu_opts.files);
+ opts->dfu_opts.files = get_dfu_function();
+ }
+ }
- /*
- * Creating a gadget with both DFU and Fastboot doesn't work.
- * Both client tools seem to assume that the device only has
- * a single configuration
- */
- if (fastboot_opts && dfu_opts) {
- pr_err("Only one of Fastboot and DFU allowed\n");
- return -EINVAL;
+ if (flags & USBGADGET_MASS_STORAGE) {
+ 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_null();
+ }
}
- opts = xzalloc(sizeof(*opts));
- opts->release = usb_multi_opts_release;
+ if (flags & USBGADGET_FASTBOOT) {
+ opts->fastboot_opts.files = file_list_parse_null(funcs->fastboot_opts);
+ if (IS_ENABLED(CONFIG_FASTBOOT_BASE) && file_list_empty(opts->fastboot_opts.files)) {
+ file_list_free(opts->fastboot_opts.files);
+ opts->fastboot_opts.files = get_fastboot_partitions();
+ }
- if (fastboot_opts) {
- opts->fastboot_opts.files = parse(fastboot_opts);
- opts->fastboot_opts.export_bbu = export_bbu;
+ opts->fastboot_opts.export_bbu = flags & USBGADGET_EXPORT_BBU;
}
- if (dfu_opts)
- opts->dfu_opts.files = parse(dfu_opts);
+ opts->create_acm = flags & USBGADGET_ACM;
- if (!opts->dfu_opts.files && !opts->fastboot_opts.files && !acm) {
+ if (usb_multi_count_functions(opts) == 0) {
pr_warn("No functions to register\n");
- free(opts);
- return 0;
+ ret = COMMAND_ERROR_USAGE;
+ goto err;
}
- opts->create_acm = acm;
+ /*
+ * Creating a gadget with both DFU and Fastboot may not work.
+ * fastboot 1:8.1.0+r23-5 can deal with it, but dfu-util 0.9
+ * seems to assume that the device only has a single configuration
+ * That's not our fault though. Emit a warning and continue
+ */
+ if (!file_list_empty(opts->fastboot_opts.files) && !file_list_empty(opts->dfu_opts.files))
+ pr_warn("Both DFU and Fastboot enabled. dfu-util may not like this!\n");
dev = get_device_by_name("otg");
if (dev)
@@ -97,48 +90,84 @@ int usbgadget_register(bool dfu, const char *dfu_opts,
ret = usb_multi_register(opts);
if (ret)
- usb_multi_opts_release(opts);
+ goto err;
+
+ return 0;
+err:
+ usb_multi_opts_release(opts);
return ret;
}
-static int usbgadget_autostart(void)
+static int usbgadget_do_autostart(void)
{
- if (!IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART) || !autostart)
+ struct usbgadget_funcs funcs = {};
+ static bool started;
+ int err;
+
+ 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;
- return usbgadget_register(true, NULL, true, NULL, acm, fastboot_bbu);
+ if (get_fastboot_bbu())
+ funcs.flags |= USBGADGET_EXPORT_BBU;
+ if (acm)
+ funcs.flags |= USBGADGET_ACM;
+
+ funcs.flags |= USBGADGET_DFU | USBGADGET_FASTBOOT | USBGADGET_MASS_STORAGE;
+
+ err = usbgadget_register(&funcs);
+ if (!err)
+ started = true;
+
+ 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();
}
-postenvironment_initcall(usbgadget_autostart);
static int usbgadget_globalvars_init(void)
{
- if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) {
- globalvar_add_simple_bool("usbgadget.autostart", &autostart);
- globalvar_add_simple_bool("usbgadget.acm", &acm);
- globalvar_add_simple_bool("usbgadget.fastboot_bbu",
- &fastboot_bbu);
- }
+ globalvar_add_simple_bool("usbgadget.acm", &acm);
globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function);
- globalvar_add_simple_string("usbgadget.fastboot_function",
- &fastboot_function);
+ if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART))
+ globalvar_add_bool("usbgadget.autostart", usbgadget_autostart_set,
+ &autostart, NULL);
+
+ return 0;
+}
+coredevice_initcall(usbgadget_globalvars_init);
+
+static int usbgadget_autostart_init(void)
+{
+ nv_loaded = true;
+
+ usbgadget_do_autostart();
return 0;
}
-device_initcall(usbgadget_globalvars_init);
-
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_autostart,
- global.usbgadget.autostart,
- "usbgadget: Automatically start usbgadget on boot");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_acm,
- global.usbgadget.acm,
- "usbgadget: Create CDC ACM function");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_dfu_function,
- global.usbgadget.dfu_function,
- "usbgadget: Create DFU function");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_function,
- global.usbgadget.fastboot_function,
- "usbgadget: Create Android Fastboot function");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_bbu,
- global.usbgadget.fastboot_bbu,
- "usbgadget: export barebox update handlers via fastboot");
+postenvironment_initcall(usbgadget_autostart_init);
+
+BAREBOX_MAGICVAR(global.usbgadget.autostart,
+ "usbgadget: Automatically start usbgadget on boot");
+BAREBOX_MAGICVAR(global.usbgadget.acm,
+ "usbgadget: Create CDC ACM function");
+BAREBOX_MAGICVAR(global.usbgadget.dfu_function,
+ "usbgadget: Create DFU function");
diff --git a/common/version.c b/common/version.c
index 8b1fd4dbe7..0cac5ee609 100644
--- a/common/version.c
+++ b/common/version.c
@@ -1,20 +1,28 @@
+// 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[] =
"barebox-" UTS_RELEASE;
EXPORT_SYMBOL(release_string);
+const char buildsystem_version_string[] =
+ BUILDSYSTEM_VERSION;
+EXPORT_SYMBOL(buildsystem_version_string);
+
#ifdef CONFIG_BANNER
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\n", buildsystem_version_string);
printf("\n\n");
pr_info("Board: %s\n", barebox_get_model());
}
diff --git a/common/workqueue.c b/common/workqueue.c
new file mode 100644
index 0000000000..dc6941dec1
--- /dev/null
+++ b/common/workqueue.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <common.h>
+#include <work.h>
+
+static void wq_do_pending_work(struct work_queue *wq)
+{
+ struct work_struct *work, *tmp;
+
+ list_for_each_entry_safe(work, tmp, &wq->work, list) {
+ if (work->delayed && get_time_ns() < work->timeout)
+ continue;
+
+ list_del(&work->list);
+ wq->fn(work);
+ }
+}
+
+void wq_cancel_work(struct work_queue *wq)
+{
+ struct work_struct *work, *tmp;
+
+ list_for_each_entry_safe(work, tmp, &wq->work, list) {
+ list_del(&work->list);
+ wq->cancel(work);
+ }
+}
+
+static LIST_HEAD(work_queues);
+
+/**
+ * wq_do_all_works - do all pending work
+ *
+ * This calls all pending work functions
+ */
+void wq_do_all_works(void)
+{
+ struct work_queue *wq;
+
+ list_for_each_entry(wq, &work_queues, list)
+ wq_do_pending_work(wq);
+}
+
+/**
+ * wq_register - register a new work queue
+ * @wq: The work queue
+ */
+void wq_register(struct work_queue *wq)
+{
+ INIT_LIST_HEAD(&wq->work);
+ list_add_tail(&wq->list, &work_queues);
+}
+
+/**
+ * wq_unregister - unregister a work queue
+ * @wq: The work queue
+ */
+void wq_unregister(struct work_queue *wq)
+{
+ wq_cancel_work(wq);
+ list_del(&wq->list);
+}