diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/common | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/common')
106 files changed, 51047 insertions, 0 deletions
diff --git a/roms/u-boot/common/Kconfig b/roms/u-boot/common/Kconfig new file mode 100644 index 000000000..26496f9a2 --- /dev/null +++ b/roms/u-boot/common/Kconfig @@ -0,0 +1,762 @@ +source "common/Kconfig.boot" + +menu "Console" + +config MENU + bool + help + This is the library functionality to provide a text-based menu of + choices for the user to make choices with. + +config CONSOLE_RECORD + bool "Console recording" + help + This provides a way to record console output (and provide console + input) through circular buffers. This is mostly useful for testing. + Console output is recorded even when the console is silent. + To enable console recording, call console_record_reset_enable() + from your code. + +config CONSOLE_RECORD_INIT_F + bool "Enable console recording during pre-relocation init" + depends on CONSOLE_RECORD && SYS_MALLOC_F + default y + help + This option enables console recording during pre-relocation init. + CONFIG_SYS_MALLOC_F must be enabled to use this feature. + +config CONSOLE_RECORD_OUT_SIZE + hex "Output buffer size" + depends on CONSOLE_RECORD + default 0x400 if CONSOLE_RECORD + help + Set the size of the console output buffer. When this fills up, no + more data will be recorded until some is removed. The buffer is + allocated immediately after the malloc() region is ready. + +config CONSOLE_RECORD_IN_SIZE + hex "Input buffer size" + depends on CONSOLE_RECORD + default 0x100 if CONSOLE_RECORD + help + Set the size of the console input buffer. When this contains data, + tstc() and getc() will use this in preference to real device input. + The buffer is allocated immediately after the malloc() region is + ready. + +config DISABLE_CONSOLE + bool "Add functionality to disable console completely" + help + Disable console (in & out). + +config IDENT_STRING + string "Board specific string to be added to uboot version string" + help + This options adds the board specific name to u-boot version. + +config LOGLEVEL + int "loglevel" + default 4 + range 0 10 + help + All Messages with a loglevel smaller than the console loglevel will + be compiled in. The loglevels are defined as follows: + + 0 - emergency + 1 - alert + 2 - critical + 3 - error + 4 - warning + 5 - note + 6 - info + 7 - debug + 8 - debug content + 9 - debug hardware I/O + +config SPL_LOGLEVEL + int + default LOGLEVEL + +config TPL_LOGLEVEL + int + default LOGLEVEL + +config SILENT_CONSOLE + bool "Support a silent console" + help + This option allows the console to be silenced, meaning that no + output will appear on the console devices. This is controlled by + setting the environment variable 'silent' to a non-empty value. + Note this also silences the console when booting Linux. + + When the console is set up, the variable is checked, and the + GD_FLG_SILENT flag is set. Changing the environment variable later + will update the flag. + +config SILENT_U_BOOT_ONLY + bool "Only silence the U-Boot console" + depends on SILENT_CONSOLE + help + Normally when the U-Boot console is silenced, Linux's console is + also silenced (assuming the board boots into Linux). This option + allows the linux console to operate normally, even if U-Boot's + is silenced. + +config SILENT_CONSOLE_UPDATE_ON_SET + bool "Changes to the 'silent' environment variable update immediately" + depends on SILENT_CONSOLE + default y if SILENT_CONSOLE + help + When the 'silent' environment variable is changed, update the + console silence flag immediately. This allows 'setenv' to be used + to silence or un-silence the console. + + The effect is that any change to the variable will affect the + GD_FLG_SILENT flag. + +config SILENT_CONSOLE_UPDATE_ON_RELOC + bool "Allow flags to take effect on relocation" + depends on SILENT_CONSOLE + help + In some cases the environment is not available until relocation + (e.g. NAND). This option makes the value of the 'silent' + environment variable take effect at relocation. + +config PRE_CONSOLE_BUFFER + bool "Buffer characters before the console is available" + help + Prior to the console being initialised (i.e. serial UART + initialised etc) all console output is silently discarded. + Defining CONFIG_PRE_CONSOLE_BUFFER will cause U-Boot to + buffer any console messages prior to the console being + initialised to a buffer. The buffer is a circular buffer, so + if it overflows, earlier output is discarded. + + Note that this is not currently supported in SPL. It would be + useful to be able to share the pre-console buffer with SPL. + +config PRE_CON_BUF_SZ + int "Sets the size of the pre-console buffer" + depends on PRE_CONSOLE_BUFFER + default 4096 + help + The size of the pre-console buffer affects how much console output + can be held before it overflows and starts discarding earlier + output. Normally there is very little output at this early stage, + unless debugging is enabled, so allow enough for ~10 lines of + text. + + This is a useful feature if you are using a video console and + want to see the full boot output on the console. Without this + option only the post-relocation output will be displayed. + +config PRE_CON_BUF_ADDR + hex "Address of the pre-console buffer" + depends on PRE_CONSOLE_BUFFER + default 0x2f000000 if ARCH_SUNXI && MACH_SUN9I + default 0x4f000000 if ARCH_SUNXI && !MACH_SUN9I + default 0x0f000000 if ROCKCHIP_RK3288 + default 0x0f200000 if ROCKCHIP_RK3399 + help + This sets the start address of the pre-console buffer. This must + be in available memory and is accessed before relocation and + possibly before DRAM is set up. Therefore choose an address + carefully. + + We should consider removing this option and allocating the memory + in board_init_f_init_reserve() instead. + +config CONSOLE_MUX + bool "Enable console multiplexing" + default y if DM_VIDEO || VIDEO || LCD + help + This allows multiple devices to be used for each console 'file'. + For example, stdout can be set to go to serial and video. + Similarly, stdin can be set to come from serial and keyboard. + Input can be provided from either source. Console multiplexing + adds a small amount of size to U-Boot. Changes to the environment + variables stdout, stdin and stderr will take effect immediately. + +config SYS_CONSOLE_IS_IN_ENV + bool "Select console devices from the environment" + default y if CONSOLE_MUX + help + This allows multiple input/output devices to be set at boot time. + For example, if stdout is set to "serial,video" then output will + be sent to both the serial and video devices on boot. The + environment variables can be updated after boot to change the + input/output devices. + +config SYS_CONSOLE_OVERWRITE_ROUTINE + bool "Allow board control over console overwriting" + help + If this is enabled, and the board-specific function + overwrite_console() returns 1, the stdin, stderr and stdout are + switched to the serial port, else the settings in the environment + are used. If this is not enabled, the console will not be switched + to serial. + +config SYS_CONSOLE_ENV_OVERWRITE + bool "Update environment variables during console init" + help + The console environment variables (stdout, stdin, stderr) can be + used to determine the correct console devices on start-up. This + option writes the console devices to these variables on console + start-up (after relocation). This causes the environment to be + updated to match the console devices actually chosen. + +config SYS_CONSOLE_INFO_QUIET + bool "Don't display the console devices on boot" + help + Normally U-Boot displays the current settings for stdout, stdin + and stderr on boot when the post-relocation console is set up. + Enable this option to suppress this output. It can be obtained by + calling stdio_print_current_devices() from board code. + +config SYS_STDIO_DEREGISTER + bool "Allow deregistering stdio devices" + default y if USB_KEYBOARD + help + Generally there is no need to deregister stdio devices since they + are never deactivated. But if a stdio device is used which can be + removed (for example a USB keyboard) then this option can be + enabled to ensure this is handled correctly. + +config SPL_SYS_STDIO_DEREGISTER + bool "Allow deregistering stdio devices in SPL" + help + Generally there is no need to deregister stdio devices since they + are never deactivated. But if a stdio device is used which can be + removed (for example a USB keyboard) then this option can be + enabled to ensure this is handled correctly. This is very rarely + needed in SPL. + +config SYS_DEVICE_NULLDEV + bool "Enable a null device for stdio" + default y if SPLASH_SCREEN || SYS_STDIO_DEREGISTER + help + Enable creation of a "nulldev" stdio device. This allows silent + operation of the console by setting stdout to "nulldev". Enable + this to use a serial console under board control. + +endmenu + +menu "Logging" + +config LOG + bool "Enable logging support" + depends on DM + help + This enables support for logging of status and debug messages. These + can be displayed on the console, recorded in a memory buffer, or + discarded if not needed. Logging supports various categories and + levels of severity. + +if LOG + +config LOG_MAX_LEVEL + int "Maximum log level to record" + default 6 + range 0 9 + help + This selects the maximum log level that will be recorded. Any value + higher than this will be ignored. If possible log statements below + this level will be discarded at build time. Levels: + + 0 - emergency + 1 - alert + 2 - critical + 3 - error + 4 - warning + 5 - note + 6 - info + 7 - debug + 8 - debug content + 9 - debug hardware I/O + +config LOG_DEFAULT_LEVEL + int "Default logging level to display" + default LOG_MAX_LEVEL + range 0 LOG_MAX_LEVEL + help + This is the default logging level set when U-Boot starts. It can + be adjusted later using the 'log level' command. Note that setting + this to a value above LOG_MAX_LEVEL will be ineffective, since the + higher levels are not compiled in to U-Boot. + + 0 - emergency + 1 - alert + 2 - critical + 3 - error + 4 - warning + 5 - note + 6 - info + 7 - debug + 8 - debug content + 9 - debug hardware I/O + +config LOG_CONSOLE + bool "Allow log output to the console" + default y + help + Enables a log driver which writes log records to the console. + Generally the console is the serial port or LCD display. Only the + log message is shown - other details like level, category, file and + line number are omitted. + +config LOGF_FILE + bool "Show source file name in log messages by default" + help + Show the source file name in log messages by default. This value + can be overridden using the 'log format' command. + +config LOGF_LINE + bool "Show source line number in log messages by default" + help + Show the source line number in log messages by default. This value + can be overridden using the 'log format' command. + +config LOGF_FUNC + bool "Show function name in log messages by default" + help + Show the function name in log messages by default. This value can + be overridden using the 'log format' command. + +config LOG_SYSLOG + bool "Log output to syslog server" + depends on NET + help + Enables a log driver which broadcasts log records via UDP port 514 + to syslog servers. + +config SPL_LOG + bool "Enable logging support in SPL" + depends on LOG + help + This enables support for logging of status and debug messages. These + can be displayed on the console, recorded in a memory buffer, or + discarded if not needed. Logging supports various categories and + levels of severity. + +if SPL_LOG + +config SPL_LOG_MAX_LEVEL + int "Maximum log level to record in SPL" + depends on SPL_LOG + default 3 + range 0 9 + help + This selects the maximum log level that will be recorded. Any value + higher than this will be ignored. If possible log statements below + this level will be discarded at build time. Levels: + + 0 - emergency + 1 - alert + 2 - critical + 3 - error + 4 - warning + 5 - note + 6 - info + 7 - debug + 8 - debug content + 9 - debug hardware I/O + +config SPL_LOG_CONSOLE + bool "Allow log output to the console in SPL" + default y + help + Enables a log driver which writes log records to the console. + Generally the console is the serial port or LCD display. Only the + log message is shown - other details like level, category, file and + line number are omitted. + +endif + +config TPL_LOG + bool "Enable logging support in TPL" + depends on LOG + help + This enables support for logging of status and debug messages. These + can be displayed on the console, recorded in a memory buffer, or + discarded if not needed. Logging supports various categories and + levels of severity. + +if TPL_LOG + +config TPL_LOG_MAX_LEVEL + int "Maximum log level to record in TPL" + depends on TPL_LOG + default 3 + range 0 9 + help + This selects the maximum log level that will be recorded. Any value + higher than this will be ignored. If possible log statements below + this level will be discarded at build time. Levels: + + 0 - emergency + 1 - alert + 2 - critical + 3 - error + 4 - warning + 5 - note + 6 - info + 7 - debug + 8 - debug content + 9 - debug hardware I/O + +config TPL_LOG_CONSOLE + bool "Allow log output to the console in TPL" + default y + help + Enables a log driver which writes log records to the console. + Generally the console is the serial port or LCD display. Only the + log message is shown - other details like level, category, file and + line number are omitted. + +endif + +config LOG_ERROR_RETURN + bool "Log all functions which return an error" + help + When an error is returned in U-Boot it is sometimes difficult to + figure out the root cause. For example, reading from SPI flash may + fail due to a problem in the SPI controller or due to the flash part + not returning the expected information. This option changes + log_ret() to log any errors it sees. With this option disabled, + log_ret() is a nop. + + You can add log_ret() to all functions which return an error code. + +config LOG_TEST + bool "Provide a test for logging" + depends on UNIT_TEST + default y if SANDBOX + help + This enables a 'log test' command to test logging. It is normally + executed from a pytest and simply outputs logging information + in various different ways to test that the logging system works + correctly with various settings. + +endif + +endmenu + +menu "Init options" + +config BOARD_TYPES + bool "Call get_board_type() to get and display the board type" + help + If this option is enabled, checkboard() will call get_board_type() + to get a string containing the board type and this will be + displayed immediately after the model is shown on the console + early in boot. + +config DISPLAY_CPUINFO + bool "Display information about the CPU during start up" + default y if ARC|| ARM || NIOS2 || X86 || XTENSA || M68K + help + Display information about the CPU that U-Boot is running on + when U-Boot starts up. The function print_cpuinfo() is called + to do this. + +config DISPLAY_BOARDINFO + bool "Display information about the board during early start up" + default y if ARC || ARM || M68K || MIPS || PPC || SANDBOX || XTENSA + help + Display information about the board that U-Boot is running on + when U-Boot starts up. The board function checkboard() is called + to do this. + +config DISPLAY_BOARDINFO_LATE + bool "Display information about the board during late start up" + help + Display information about the board that U-Boot is running on after + the relocation phase. The board function checkboard() is called to do + this. + +menu "Start-up hooks" + +config ARCH_EARLY_INIT_R + bool "Call arch-specific init soon after relocation" + help + With this option U-Boot will call arch_early_init_r() soon after + relocation. Driver model is running by this point, and the cache + is on. Note that board_early_init_r() is called first, if + enabled. This can be used to set up architecture-specific devices. + +config ARCH_MISC_INIT + bool "Call arch-specific init after relocation, when console is ready" + help + With this option U-Boot will call arch_misc_init() after + relocation to allow miscellaneous arch-dependent initialisation + to be performed. This function should be defined by the board + and will be called after the console is set up, after relocation. + +config BOARD_EARLY_INIT_F + bool "Call board-specific init before relocation" + help + Some boards need to perform initialisation as soon as possible + after boot. With this option, U-Boot calls board_early_init_f() + after driver model is ready in the pre-relocation init sequence. + Note that the normal serial console is not yet set up, but the + debug UART will be available if enabled. + +config BOARD_EARLY_INIT_R + bool "Call board-specific init after relocation" + help + Some boards need to perform initialisation as directly after + relocation. With this option, U-Boot calls board_early_init_r() + in the post-relocation init sequence. + +config BOARD_LATE_INIT + bool "Execute Board late init" + help + Sometimes board require some initialization code that might + require once the actual init done, example saving board specific env, + boot-modes etc. which eventually done at late. + + So this config enable the late init code with the help of board_late_init + function which should defined on respective boards. + +config LAST_STAGE_INIT + bool "Call board-specific as last setup step" + help + Some boards need to perform initialisation immediately before control + is passed to the command-line interpreter (e.g. for initializations + that depend on later phases in the init sequence). With this option, + U-Boot calls last_stage_init() before the command-line interpreter is + started. + +config MISC_INIT_F + bool "Execute pre-relocation misc init" + help + Enabling this option calls the 'misc_init_f' function in the init + sequence just before DRAM is inited. + +config MISC_INIT_R + bool "Execute Misc Init" + default y if ARCH_KEYSTONE || ARCH_SUNXI || MPC85xx + default y if ARCH_OMAP2PLUS && !AM33XX + help + Enabling this option calls 'misc_init_r' function + +config PCI_INIT_R + bool "Enumerate PCI buses during init" + depends on PCI + default y if !DM_PCI + help + With this option U-Boot will call pci_init() soon after relocation, + which will enumerate PCI buses. This is needed, for instance, in the + case of DM PCI-based Ethernet devices, which will not be detected + without having the enumeration performed earlier. + +endmenu + +endmenu # Init options + +menu "Security support" + +config HASH + bool # "Support hashing API (SHA1, SHA256, etc.)" + help + This provides a way to hash data in memory using various supported + algorithms (such as SHA1, MD5, CRC32). The API is defined in hash.h + and the algorithms it supports are defined in common/hash.c. See + also CMD_HASH for command-line access. + +config AVB_VERIFY + bool "Build Android Verified Boot operations" + depends on LIBAVB + depends on MMC + depends on PARTITION_UUIDS + help + This option enables compilation of bootloader-dependent operations, + used by Android Verified Boot 2.0 library (libavb). Includes: + * Helpers to process strings in order to build OS bootargs. + * Helpers to access MMC, similar to drivers/fastboot/fb_mmc.c. + * Helpers to alloc/init/free avb ops. + +if AVB_VERIFY + +config AVB_BUF_ADDR + hex "Define AVB buffer address" + default FASTBOOT_BUF_ADDR + help + AVB requires a buffer for memory transactions. This variable defines the + buffer address. + +config AVB_BUF_SIZE + hex "Define AVB buffer SIZE" + default FASTBOOT_BUF_SIZE + help + AVB requires a buffer for memory transactions. This variable defines the + buffer size. + +endif # AVB_VERIFY + +config SCP03 + bool "Build SCP03 - Secure Channel Protocol O3 - controls" + depends on OPTEE || SANDBOX + depends on TEE + help + This option allows U-Boot to enable and or provision SCP03 on an OPTEE + controlled Secured Element. + +config SPL_HASH + bool # "Support hashing API (SHA1, SHA256, etc.)" + help + This provides a way to hash data in memory using various supported + algorithms (such as SHA1, MD5, CRC32). The API is defined in hash.h + and the algorithms it supports are defined in common/hash.c. See + also CMD_HASH for command-line access. + +config TPL_HASH + bool # "Support hashing API (SHA1, SHA256, etc.)" + help + This provides a way to hash data in memory using various supported + algorithms (such as SHA1, MD5, CRC32). The API is defined in hash.h + and the algorithms it supports are defined in common/hash.c. See + also CMD_HASH for command-line access. + +config STACKPROTECTOR + bool "Stack Protector buffer overflow detection" + default n + help + Enable stack smash detection through compiler's stack-protector + canary logic + +config SPL_STACKPROTECTOR + bool "Stack Protector buffer overflow detection for SPL" + depends on STACKPROTECTOR && SPL + default n + +config TPL_STACKPROTECTOR + bool "Stack Protector buffer overflow detection for TPL" + depends on STACKPROTECTOR && TPL + default n + +endmenu + +menu "Update support" + +config UPDATE_COMMON + bool + default n + select DFU_WRITE_ALT + +config UPDATE_TFTP + bool "Auto-update using fitImage via TFTP" + depends on FIT + select UPDATE_COMMON + help + This option allows performing update of NOR with data in fitImage + sent via TFTP boot. + +config UPDATE_TFTP_CNT_MAX + int "The number of connection retries during auto-update" + default 0 + depends on UPDATE_TFTP + +config UPDATE_TFTP_MSEC_MAX + int "Delay in mSec to wait for the TFTP server during auto-update" + default 100 + depends on UPDATE_TFTP + +config UPDATE_FIT + bool "Firmware update using fitImage" + depends on FIT + depends on DFU + select UPDATE_COMMON + help + This option allows performing update of DFU-capable storage with + data in fitImage. + +config ANDROID_AB + bool "Android A/B updates" + default n + help + If enabled, adds support for the new Android A/B update model. This + allows the bootloader to select which slot to boot from based on the + information provided by userspace via the Android boot_ctrl HAL. This + allows a bootloader to try a new version of the system but roll back + to previous version if the new one didn't boot all the way. + +endmenu + +menu "Blob list" + +config BLOBLIST + bool "Support for a bloblist" + help + This enables support for a bloblist in U-Boot, which can be passed + from TPL to SPL to U-Boot proper (and potentially to Linux). The + blob list supports multiple binary blobs of data, each with a tag, + so that different U-Boot components can store data which can survive + through to the next stage of the boot. + +config SPL_BLOBLIST + bool "Support for a bloblist in SPL" + depends on BLOBLIST + default y if SPL + help + This enables a bloblist in SPL. If this is the first part of U-Boot + to run, then the bloblist is set up in SPL and passed to U-Boot + proper. If TPL also has a bloblist, then SPL uses the one from there. + +config TPL_BLOBLIST + bool "Support for a bloblist in TPL" + depends on BLOBLIST + default y if TPL + help + This enables a bloblist in TPL. The bloblist is set up in TPL and + passed to SPL and U-Boot proper. + +config BLOBLIST_SIZE + hex "Size of bloblist" + depends on BLOBLIST + default 0x400 + help + Sets the size of the bloblist in bytes. This must include all + overhead (alignment, bloblist header, record header). The bloblist + is set up in the first part of U-Boot to run (TPL, SPL or U-Boot + proper), and this sane bloblist is used for subsequent stages. + +config BLOBLIST_ADDR + hex "Address of bloblist" + depends on BLOBLIST + default 0xe000 if SANDBOX + help + Sets the address of the bloblist, set up by the first part of U-Boot + which runs. Subsequent U-Boot stages typically use the same address. + +config BLOBLIST_SIZE_RELOC + hex "Size of bloblist after relocation" + depends on BLOBLIST + default BLOBLIST_SIZE + help + Sets the size of the bloblist in bytes after relocation. Since U-Boot + has a lot more memory available then, it is possible to use a larger + size than the one set up by SPL. This bloblist is set up during the + relocation process. + +endmenu + +source "common/spl/Kconfig" + +config IMAGE_SIGN_INFO + bool + select SHA1 + select SHA256 + help + Enable image_sign_info helper functions. + +if IMAGE_SIGN_INFO + +config SPL_IMAGE_SIGN_INFO + bool + select SHA1 + select SHA256 + help + Enable image_sign_info helper functions in SPL. + +endif diff --git a/roms/u-boot/common/Kconfig.boot b/roms/u-boot/common/Kconfig.boot new file mode 100644 index 000000000..89a3161f1 --- /dev/null +++ b/roms/u-boot/common/Kconfig.boot @@ -0,0 +1,970 @@ +menu "Boot options" + +menu "Boot images" + +config ANDROID_BOOT_IMAGE + bool "Enable support for Android Boot Images" + default y if FASTBOOT + help + This enables support for booting images which use the Android + image format header. + +config FIT + bool "Support Flattened Image Tree" + select MD5 + select SHA1 + help + This option allows you to boot the new uImage structure, + Flattened Image Tree. FIT is formally a FDT, which can include + images of various types (kernel, FDT blob, ramdisk, etc.) + in a single blob. To boot this new uImage structure, + pass the address of the blob to the "bootm" command. + FIT is very flexible, supporting compression, multiple images, + multiple configurations, verification through hashing and also + verified boot (secure boot using RSA). + +if FIT + +config FIT_EXTERNAL_OFFSET + hex "FIT external data offset" + default 0x0 + help + This specifies a data offset in fit image. + The offset is from data payload offset to the beginning of + fit image header. When specifies a offset, specific data + could be put in the hole between data payload and fit image + header, such as CSF data on i.MX platform. + +config FIT_ENABLE_SHA256_SUPPORT + bool "Support SHA256 checksum of FIT image contents" + default y + select SHA256 + help + Enable this to support SHA256 checksum of FIT image contents. A + SHA256 checksum is a 256-bit (32-byte) hash value used to check that + the image contents have not been corrupted. + +config FIT_ENABLE_SHA384_SUPPORT + bool "Support SHA384 checksum of FIT image contents" + default n + select SHA384 + help + Enable this to support SHA384 checksum of FIT image contents. A + SHA384 checksum is a 384-bit (48-byte) hash value used to check that + the image contents have not been corrupted. Use this for the highest + security. + +config FIT_ENABLE_SHA512_SUPPORT + bool "Support SHA512 checksum of FIT image contents" + default n + select SHA512 + help + Enable this to support SHA512 checksum of FIT image contents. A + SHA512 checksum is a 512-bit (64-byte) hash value used to check that + the image contents have not been corrupted. + +config FIT_FULL_CHECK + bool "Do a full check of the FIT before using it" + default y + help + Enable this do a full check of the FIT to make sure it is valid. This + helps to protect against carefully crafted FITs which take advantage + of bugs or omissions in the code. This includes a bad structure, + multiple root nodes and the like. + +config FIT_SIGNATURE + bool "Enable signature verification of FIT uImages" + depends on DM + select HASH + select RSA + select RSA_VERIFY + select IMAGE_SIGN_INFO + select FIT_FULL_CHECK + help + This option enables signature verification of FIT uImages, + using a hash signed and verified using RSA. If + CONFIG_SHA_PROG_HW_ACCEL is defined, i.e support for progressive + hashing is available using hardware, then the RSA library will use + it. See doc/uImage.FIT/signature.txt for more details. + + WARNING: When relying on signed FIT images with a required signature + check the legacy image format is disabled by default, so that + unsigned images cannot be loaded. If a board needs the legacy image + format support in this case, enable it using + CONFIG_LEGACY_IMAGE_FORMAT. + +config FIT_SIGNATURE_MAX_SIZE + hex "Max size of signed FIT structures" + depends on FIT_SIGNATURE + default 0x10000000 + help + This option sets a max size in bytes for verified FIT uImages. + A sane value of 256MB protects corrupted DTB structures from overlapping + device memory. Assure this size does not extend past expected storage + space. + +config FIT_ENABLE_RSASSA_PSS_SUPPORT + bool "Support rsassa-pss signature scheme of FIT image contents" + depends on FIT_SIGNATURE + default n + help + Enable this to support the pss padding algorithm as described + in the rfc8017 (https://tools.ietf.org/html/rfc8017). + +config FIT_CIPHER + bool "Enable ciphering data in a FIT uImages" + depends on DM + select AES + help + Enable the feature of data ciphering/unciphering in the tool mkimage + and in the u-boot support of the FIT image. + +config FIT_VERBOSE + bool "Show verbose messages when FIT images fail" + help + Generally a system will have valid FIT images so debug messages + are a waste of code space. If you are debugging your images then + you can enable this option to get more verbose information about + failures. + +config FIT_BEST_MATCH + bool "Select the best match for the kernel device tree" + help + When no configuration is explicitly selected, default to the + one whose fdt's compatibility field best matches that of + U-Boot itself. A match is considered "best" if it matches the + most specific compatibility entry of U-Boot's fdt's root node. + The order of entries in the configuration's fdt is ignored. + +config FIT_IMAGE_POST_PROCESS + bool "Enable post-processing of FIT artifacts after loading by U-Boot" + depends on TI_SECURE_DEVICE || SOCFPGA_SECURE_VAB_AUTH + help + Allows doing any sort of manipulation to blobs after they got extracted + from FIT images like stripping off headers or modifying the size of the + blob, verification, authentication, decryption etc. in a platform or + board specific way. In order to use this feature a platform or board- + specific implementation of board_fit_image_post_process() must be + provided. Also, anything done during this post-processing step would + need to be comprehended in how the images were prepared before being + injected into the FIT creation (i.e. the blobs would have been pre- + processed before being added to the FIT image). + +config FIT_PRINT + bool "Support FIT printing" + default y + help + Support printing the content of the fitImage in a verbose manner. + +if SPL + +config SPL_FIT + bool "Support Flattened Image Tree within SPL" + depends on SPL + select SPL_OF_LIBFDT + +config SPL_FIT_PRINT + bool "Support FIT printing within SPL" + depends on SPL_FIT + help + Support printing the content of the fitImage in a verbose manner in SPL. + +config SPL_FIT_FULL_CHECK + bool "Do a full check of the FIT before using it" + help + Enable this do a full check of the FIT to make sure it is valid. This + helps to protect against carefully crafted FITs which take advantage + of bugs or omissions in the code. This includes a bad structure, + multiple root nodes and the like. + + +config SPL_FIT_SIGNATURE + bool "Enable signature verification of FIT firmware within SPL" + depends on SPL_DM + depends on SPL_LOAD_FIT || SPL_LOAD_FIT_FULL + select FIT_SIGNATURE + select SPL_FIT + select SPL_CRYPTO_SUPPORT + select SPL_HASH_SUPPORT + select SPL_RSA + select SPL_RSA_VERIFY + select SPL_IMAGE_SIGN_INFO + select SPL_FIT_FULL_CHECK + +config SPL_LOAD_FIT + bool "Enable SPL loading U-Boot as a FIT (basic fitImage features)" + select SPL_FIT + help + Normally with the SPL framework a legacy image is generated as part + of the build. This contains U-Boot along with information as to + where it should be loaded. This option instead enables generation + of a FIT (Flat Image Tree) which provides more flexibility. In + particular it can handle selecting from multiple device tree + and passing the correct one to U-Boot. + + This path has the following limitations: + + 1. "loadables" images, other than FDTs, which do not have a "load" + property will not be loaded. This limitation also applies to FPGA + images with the correct "compatible" string. + 2. For FPGA images, only the "compatible" = "u-boot,fpga-legacy" + loading method is supported. + 3. FDTs are only loaded for images with an "os" property of "u-boot". + "linux" images are also supported with Falcon boot mode. + +config SPL_LOAD_FIT_ADDRESS + hex "load address of fit image" + depends on SPL_LOAD_FIT + default 0x0 + help + Specify the load address of the fit image that will be loaded + by SPL. + +config SPL_LOAD_FIT_APPLY_OVERLAY + bool "Enable SPL applying DT overlays from FIT" + depends on SPL_LOAD_FIT + select OF_LIBFDT_OVERLAY + help + The device tree is loaded from the FIT image. Allow the SPL is to + also load device-tree overlays from the FIT image an apply them + over the device tree. + +config SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ + depends on SPL_LOAD_FIT_APPLY_OVERLAY + default 0x10000 + hex "size of temporary buffer used to load the overlays" + help + The size of the area where the overlays will be loaded and + uncompress. Must be at least as large as biggest overlay + (uncompressed) + +config SPL_LOAD_FIT_FULL + bool "Enable SPL loading U-Boot as a FIT (full fitImage features)" + select SPL_FIT + help + Normally with the SPL framework a legacy image is generated as part + of the build. This contains U-Boot along with information as to + where it should be loaded. This option instead enables generation + of a FIT (Flat Image Tree) which provides more flexibility. In + particular it can handle selecting from multiple device tree + and passing the correct one to U-Boot. + +config SPL_FIT_IMAGE_POST_PROCESS + bool "Enable post-processing of FIT artifacts after loading by the SPL" + depends on SPL_LOAD_FIT + help + Allows doing any sort of manipulation to blobs after they got extracted + from the U-Boot FIT image like stripping off headers or modifying the + size of the blob, verification, authentication, decryption etc. in a + platform or board specific way. In order to use this feature a platform + or board-specific implementation of board_fit_image_post_process() must + be provided. Also, anything done during this post-processing step would + need to be comprehended in how the images were prepared before being + injected into the FIT creation (i.e. the blobs would have been pre- + processed before being added to the FIT image). + +config SPL_FIT_SOURCE + string ".its source file for U-Boot FIT image" + depends on SPL_FIT + help + Specifies a (platform specific) FIT source file to generate the + U-Boot FIT image. This could specify further image to load and/or + execute. + +config USE_SPL_FIT_GENERATOR + bool "Use a script to generate the .its script" + default y if SPL_FIT && (!ARCH_SUNXI && !RISCV) + +config SPL_FIT_GENERATOR + string ".its file generator script for U-Boot FIT image" + depends on USE_SPL_FIT_GENERATOR + default "arch/arm/mach-rockchip/make_fit_atf.py" if SPL_LOAD_FIT && ARCH_ROCKCHIP + default "arch/arm/mach-zynqmp/mkimage_fit_atf.sh" if SPL_LOAD_FIT && ARCH_ZYNQMP + help + Specifies a (platform specific) script file to generate the FIT + source file used to build the U-Boot FIT image file. This gets + passed a list of supported device tree file stub names to + include in the generated image. + +endif # SPL + +endif # FIT + +config LEGACY_IMAGE_FORMAT + bool "Enable support for the legacy image format" + default y if !FIT_SIGNATURE + help + This option enables the legacy image format. It is enabled by + default for backward compatibility, unless FIT_SIGNATURE is + set where it is disabled so that unsigned images cannot be + loaded. If a board needs the legacy image format support in this + case, enable it here. + +config SUPPORT_RAW_INITRD + bool "Enable raw initrd images" + help + Note, defining the SUPPORT_RAW_INITRD allows user to supply + kernel with raw initrd images. The syntax is slightly different, the + address of the initrd must be augmented by it's size, in the following + format: "<initrd address>:<initrd size>". + +config OF_BOARD_SETUP + bool "Set up board-specific details in device tree before boot" + depends on OF_LIBFDT + help + This causes U-Boot to call ft_board_setup() before booting into + the Operating System. This function can set up various + board-specific information in the device tree for use by the OS. + The device tree is then passed to the OS. + +config OF_SYSTEM_SETUP + bool "Set up system-specific details in device tree before boot" + depends on OF_LIBFDT + help + This causes U-Boot to call ft_system_setup() before booting into + the Operating System. This function can set up various + system-specific information in the device tree for use by the OS. + The device tree is then passed to the OS. + +config OF_STDOUT_VIA_ALIAS + bool "Update the device-tree stdout alias from U-Boot" + depends on OF_LIBFDT + help + This uses U-Boot's serial alias from the aliases node to update + the device tree passed to the OS. The "linux,stdout-path" property + in the chosen node is set to point to the correct serial node. + This option currently references CONFIG_CONS_INDEX, which is + incorrect when used with device tree as this option does not + exist / should not be used. + +config SYS_EXTRA_OPTIONS + string "Extra Options (DEPRECATED)" + help + The old configuration infrastructure (= mkconfig + boards.cfg) + provided the extra options field. If you have something like + "HAS_BAR,BAZ=64", the optional options + #define CONFIG_HAS + #define CONFIG_BAZ 64 + will be defined in include/config.h. + This option was prepared for the smooth migration from the old + configuration to Kconfig. Since this option will be removed sometime, + new boards should not use this option. + +config HAVE_SYS_TEXT_BASE + bool + depends on !NIOS2 && !XTENSA + depends on !EFI_APP + default y + +config SYS_TEXT_BASE + depends on HAVE_SYS_TEXT_BASE + default 0x80800000 if ARCH_OMAP2PLUS || ARCH_K3 + default 0x4a000000 if ARCH_SUNXI && !MACH_SUN9I && !MACH_SUN8I_V3S + default 0x2a000000 if ARCH_SUNXI && MACH_SUN9I + default 0x42e00000 if ARCH_SUNXI && MACH_SUN8I_V3S + hex "Text Base" + help + The address in memory that U-Boot will be running from, initially. + +config SYS_CLK_FREQ + depends on ARC || ARCH_SUNXI || MPC83xx + int "CPU clock frequency" + help + TODO: Move CONFIG_SYS_CLK_FREQ for all the architecture + +config ARCH_FIXUP_FDT_MEMORY + bool "Enable arch_fixup_memory_banks() call" + default y + help + Enable FDT memory map syncup before OS boot. This feature can be + used for booting OS with different memory setup where the part of + the memory location should be used for different purpose. + +config CHROMEOS + bool "Support booting Chrome OS" + help + Chrome OS requires U-Boot to set up a table indicating the boot mode + (e.g. Developer mode) and a few other things. Enable this if you are + booting on a Chromebook to avoid getting an error about an invalid + firmware ID. + +config CHROMEOS_VBOOT + bool "Support Chrome OS verified boot" + help + This is intended to enable the full Chrome OS verified boot support + in U-Boot. It is not actually implemented in the U-Boot source code + at present, so this option is always set to 'n'. It allows + distinguishing between booting Chrome OS in a basic way (developer + mode) and a full boot. + +endmenu # Boot images + +menu "Boot timing" + +config BOOTSTAGE + bool "Boot timing and reporting" + help + Enable recording of boot time while booting. To use it, insert + calls to bootstage_mark() with a suitable BOOTSTAGE_ID from + bootstage.h. Only a single entry is recorded for each ID. You can + give the entry a name with bootstage_mark_name(). You can also + record elapsed time in a particular stage using bootstage_start() + before starting and bootstage_accum() when finished. Bootstage will + add up all the accumulated time and report it. + + Normally, IDs are defined in bootstage.h but a small number of + additional 'user' IDs can be used by passing BOOTSTAGE_ID_ALLOC + as the ID. + + Calls to show_boot_progress() will also result in log entries but + these will not have names. + +config SPL_BOOTSTAGE + bool "Boot timing and reported in SPL" + depends on BOOTSTAGE + help + Enable recording of boot time in SPL. To make this visible to U-Boot + proper, enable BOOTSTAGE_STASH as well. This will stash the timing + information when SPL finishes and load it when U-Boot proper starts + up. + +config TPL_BOOTSTAGE + bool "Boot timing and reported in TPL" + depends on BOOTSTAGE + help + Enable recording of boot time in SPL. To make this visible to U-Boot + proper, enable BOOTSTAGE_STASH as well. This will stash the timing + information when TPL finishes and load it when U-Boot proper starts + up. + +config BOOTSTAGE_REPORT + bool "Display a detailed boot timing report before booting the OS" + depends on BOOTSTAGE + help + Enable output of a boot time report just before the OS is booted. + This shows how long it took U-Boot to go through each stage of the + boot process. The report looks something like this: + + Timer summary in microseconds: + Mark Elapsed Stage + 0 0 reset + 3,575,678 3,575,678 board_init_f start + 3,575,695 17 arch_cpu_init A9 + 3,575,777 82 arch_cpu_init done + 3,659,598 83,821 board_init_r start + 3,910,375 250,777 main_loop + 29,916,167 26,005,792 bootm_start + 30,361,327 445,160 start_kernel + +config BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store" + depends on BOOTSTAGE + default 30 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config SPL_BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store for SPL" + depends on SPL_BOOTSTAGE + default 5 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config TPL_BOOTSTAGE_RECORD_COUNT + int "Number of boot stage records to store for TPL" + depends on TPL_BOOTSTAGE + default 5 + help + This is the size of the bootstage record list and is the maximum + number of bootstage records that can be recorded. + +config BOOTSTAGE_FDT + bool "Store boot timing information in the OS device tree" + depends on BOOTSTAGE + help + Stash the bootstage information in the FDT. A root 'bootstage' + node is created with each bootstage id as a child. Each child + has a 'name' property and either 'mark' containing the + mark time in microseconds, or 'accum' containing the + accumulated time for that bootstage id in microseconds. + For example: + + bootstage { + 154 { + name = "board_init_f"; + mark = <3575678>; + }; + 170 { + name = "lcd"; + accum = <33482>; + }; + }; + + Code in the Linux kernel can find this in /proc/devicetree. + +config BOOTSTAGE_STASH + bool "Stash the boot timing information in memory before booting OS" + depends on BOOTSTAGE + help + Some OSes do not support device tree. Bootstage can instead write + the boot timing information in a binary format at a given address. + This happens through a call to bootstage_stash(), typically in + the CPU's cleanup_before_linux() function. You can use the + 'bootstage stash' and 'bootstage unstash' commands to do this on + the command line. + +config BOOTSTAGE_STASH_ADDR + hex "Address to stash boot timing information" + default 0 + help + Provide an address which will not be overwritten by the OS when it + starts, so that it can read this information when ready. + +config BOOTSTAGE_STASH_SIZE + hex "Size of boot timing stash region" + default 0x1000 + help + This should be large enough to hold the bootstage stash. A value of + 4096 (4KiB) is normally plenty. + +config SHOW_BOOT_PROGRESS + bool "Show boot progress in a board-specific manner" + help + Defining this option allows to add some board-specific code (calling + a user-provided function show_boot_progress(int) that enables you to + show the system's boot progress on some display (for example, some + LEDs) on your board. At the moment, the following checkpoints are + implemented: + + Legacy uImage format: + + Arg Where When + 1 common/cmd_bootm.c before attempting to boot an image + -1 common/cmd_bootm.c Image header has bad magic number + 2 common/cmd_bootm.c Image header has correct magic number + -2 common/cmd_bootm.c Image header has bad checksum + 3 common/cmd_bootm.c Image header has correct checksum + -3 common/cmd_bootm.c Image data has bad checksum + 4 common/cmd_bootm.c Image data has correct checksum + -4 common/cmd_bootm.c Image is for unsupported architecture + 5 common/cmd_bootm.c Architecture check OK + -5 common/cmd_bootm.c Wrong Image Type (not kernel, multi) + 6 common/cmd_bootm.c Image Type check OK + -6 common/cmd_bootm.c gunzip uncompression error + -7 common/cmd_bootm.c Unimplemented compression type + 7 common/cmd_bootm.c Uncompression OK + 8 common/cmd_bootm.c No uncompress/copy overwrite error + -9 common/cmd_bootm.c Unsupported OS (not Linux, BSD, VxWorks, QNX) + + 9 common/image.c Start initial ramdisk verification + -10 common/image.c Ramdisk header has bad magic number + -11 common/image.c Ramdisk header has bad checksum + 10 common/image.c Ramdisk header is OK + -12 common/image.c Ramdisk data has bad checksum + 11 common/image.c Ramdisk data has correct checksum + 12 common/image.c Ramdisk verification complete, start loading + -13 common/image.c Wrong Image Type (not PPC Linux ramdisk) + 13 common/image.c Start multifile image verification + 14 common/image.c No initial ramdisk, no multifile, continue. + + 15 arch/<arch>/lib/bootm.c All preparation done, transferring control to OS + + -30 arch/powerpc/lib/board.c Fatal error, hang the system + -31 post/post.c POST test failed, detected by post_output_backlog() + -32 post/post.c POST test failed, detected by post_run_single() + + 34 common/cmd_doc.c before loading a Image from a DOC device + -35 common/cmd_doc.c Bad usage of "doc" command + 35 common/cmd_doc.c correct usage of "doc" command + -36 common/cmd_doc.c No boot device + 36 common/cmd_doc.c correct boot device + -37 common/cmd_doc.c Unknown Chip ID on boot device + 37 common/cmd_doc.c correct chip ID found, device available + -38 common/cmd_doc.c Read Error on boot device + 38 common/cmd_doc.c reading Image header from DOC device OK + -39 common/cmd_doc.c Image header has bad magic number + 39 common/cmd_doc.c Image header has correct magic number + -40 common/cmd_doc.c Error reading Image from DOC device + 40 common/cmd_doc.c Image header has correct magic number + 41 common/cmd_ide.c before loading a Image from a IDE device + -42 common/cmd_ide.c Bad usage of "ide" command + 42 common/cmd_ide.c correct usage of "ide" command + -43 common/cmd_ide.c No boot device + 43 common/cmd_ide.c boot device found + -44 common/cmd_ide.c Device not available + 44 common/cmd_ide.c Device available + -45 common/cmd_ide.c wrong partition selected + 45 common/cmd_ide.c partition selected + -46 common/cmd_ide.c Unknown partition table + 46 common/cmd_ide.c valid partition table found + -47 common/cmd_ide.c Invalid partition type + 47 common/cmd_ide.c correct partition type + -48 common/cmd_ide.c Error reading Image Header on boot device + 48 common/cmd_ide.c reading Image Header from IDE device OK + -49 common/cmd_ide.c Image header has bad magic number + 49 common/cmd_ide.c Image header has correct magic number + -50 common/cmd_ide.c Image header has bad checksum + 50 common/cmd_ide.c Image header has correct checksum + -51 common/cmd_ide.c Error reading Image from IDE device + 51 common/cmd_ide.c reading Image from IDE device OK + 52 common/cmd_nand.c before loading a Image from a NAND device + -53 common/cmd_nand.c Bad usage of "nand" command + 53 common/cmd_nand.c correct usage of "nand" command + -54 common/cmd_nand.c No boot device + 54 common/cmd_nand.c boot device found + -55 common/cmd_nand.c Unknown Chip ID on boot device + 55 common/cmd_nand.c correct chip ID found, device available + -56 common/cmd_nand.c Error reading Image Header on boot device + 56 common/cmd_nand.c reading Image Header from NAND device OK + -57 common/cmd_nand.c Image header has bad magic number + 57 common/cmd_nand.c Image header has correct magic number + -58 common/cmd_nand.c Error reading Image from NAND device + 58 common/cmd_nand.c reading Image from NAND device OK + + -60 common/env_common.c Environment has a bad CRC, using default + + 64 net/eth.c starting with Ethernet configuration. + -64 net/eth.c no Ethernet found. + 65 net/eth.c Ethernet found. + + -80 common/cmd_net.c usage wrong + 80 common/cmd_net.c before calling net_loop() + -81 common/cmd_net.c some error in net_loop() occurred + 81 common/cmd_net.c net_loop() back without error + -82 common/cmd_net.c size == 0 (File with size 0 loaded) + 82 common/cmd_net.c trying automatic boot + 83 common/cmd_net.c running "source" command + -83 common/cmd_net.c some error in automatic boot or "source" command + 84 common/cmd_net.c end without errors + + FIT uImage format: + + Arg Where When + 100 common/cmd_bootm.c Kernel FIT Image has correct format + -100 common/cmd_bootm.c Kernel FIT Image has incorrect format + 101 common/cmd_bootm.c No Kernel subimage unit name, using configuration + -101 common/cmd_bootm.c Can't get configuration for kernel subimage + 102 common/cmd_bootm.c Kernel unit name specified + -103 common/cmd_bootm.c Can't get kernel subimage node offset + 103 common/cmd_bootm.c Found configuration node + 104 common/cmd_bootm.c Got kernel subimage node offset + -104 common/cmd_bootm.c Kernel subimage hash verification failed + 105 common/cmd_bootm.c Kernel subimage hash verification OK + -105 common/cmd_bootm.c Kernel subimage is for unsupported architecture + 106 common/cmd_bootm.c Architecture check OK + -106 common/cmd_bootm.c Kernel subimage has wrong type + 107 common/cmd_bootm.c Kernel subimage type OK + -107 common/cmd_bootm.c Can't get kernel subimage data/size + 108 common/cmd_bootm.c Got kernel subimage data/size + -108 common/cmd_bootm.c Wrong image type (not legacy, FIT) + -109 common/cmd_bootm.c Can't get kernel subimage type + -110 common/cmd_bootm.c Can't get kernel subimage comp + -111 common/cmd_bootm.c Can't get kernel subimage os + -112 common/cmd_bootm.c Can't get kernel subimage load address + -113 common/cmd_bootm.c Image uncompress/copy overwrite error + + 120 common/image.c Start initial ramdisk verification + -120 common/image.c Ramdisk FIT image has incorrect format + 121 common/image.c Ramdisk FIT image has correct format + 122 common/image.c No ramdisk subimage unit name, using configuration + -122 common/image.c Can't get configuration for ramdisk subimage + 123 common/image.c Ramdisk unit name specified + -124 common/image.c Can't get ramdisk subimage node offset + 125 common/image.c Got ramdisk subimage node offset + -125 common/image.c Ramdisk subimage hash verification failed + 126 common/image.c Ramdisk subimage hash verification OK + -126 common/image.c Ramdisk subimage for unsupported architecture + 127 common/image.c Architecture check OK + -127 common/image.c Can't get ramdisk subimage data/size + 128 common/image.c Got ramdisk subimage data/size + 129 common/image.c Can't get ramdisk load address + -129 common/image.c Got ramdisk load address + + -130 common/cmd_doc.c Incorrect FIT image format + 131 common/cmd_doc.c FIT image format OK + + -140 common/cmd_ide.c Incorrect FIT image format + 141 common/cmd_ide.c FIT image format OK + + -150 common/cmd_nand.c Incorrect FIT image format + 151 common/cmd_nand.c FIT image format OK + +endmenu + +menu "Boot media" + +config NOR_BOOT + bool "Support for booting from NOR flash" + depends on NOR + help + Enabling this will make a U-Boot binary that is capable of being + booted via NOR. In this case we will enable certain pinmux early + as the ROM only partially sets up pinmux. We also default to using + NOR for environment. + +config NAND_BOOT + bool "Support for booting from NAND flash" + default n + imply MTD_RAW_NAND + help + Enabling this will make a U-Boot binary that is capable of being + booted via NAND flash. This is not a must, some SoCs need this, + some not. + +config ONENAND_BOOT + bool "Support for booting from ONENAND" + default n + imply MTD_RAW_NAND + help + Enabling this will make a U-Boot binary that is capable of being + booted via ONENAND. This is not a must, some SoCs need this, + some not. + +config QSPI_BOOT + bool "Support for booting from QSPI flash" + default n + help + Enabling this will make a U-Boot binary that is capable of being + booted via QSPI flash. This is not a must, some SoCs need this, + some not. + +config SATA_BOOT + bool "Support for booting from SATA" + default n + help + Enabling this will make a U-Boot binary that is capable of being + booted via SATA. This is not a must, some SoCs need this, + some not. + +config SD_BOOT + bool "Support for booting from SD/EMMC" + default n + help + Enabling this will make a U-Boot binary that is capable of being + booted via SD/EMMC. This is not a must, some SoCs need this, + some not. + +config SPI_BOOT + bool "Support for booting from SPI flash" + default n + help + Enabling this will make a U-Boot binary that is capable of being + booted via SPI flash. This is not a must, some SoCs need this, + some not. + +endmenu + +menu "Autoboot options" + +config AUTOBOOT + bool "Autoboot" + default y + help + This enables the autoboot. See doc/README.autoboot for detail. + +config BOOTDELAY + int "delay in seconds before automatically booting" + default 2 + depends on AUTOBOOT + help + Delay before automatically running bootcmd; + set to 0 to autoboot with no delay, but you can stop it by key input. + set to -1 to disable autoboot. + set to -2 to autoboot with no delay and not check for abort + + If this value is >= 0 then it is also used for the default delay + before starting the default entry in bootmenu. If it is < 0 then + a default value of 10s is used. + + See doc/README.autoboot for details. + +config AUTOBOOT_KEYED + bool "Stop autobooting via specific input key / string" + default n + help + This option enables stopping (aborting) of the automatic + boot feature only by issuing a specific input key or + string. If not enabled, any input key will abort the + U-Boot automatic booting process and bring the device + to the U-Boot prompt for user input. + +config AUTOBOOT_PROMPT + string "Autoboot stop prompt" + depends on AUTOBOOT_KEYED + default "Autoboot in %d seconds\\n" + help + This string is displayed before the boot delay selected by + CONFIG_BOOTDELAY starts. If it is not defined there is no + output indicating that autoboot is in progress. + + Note that this define is used as the (only) argument to a + printf() call, so it may contain '%' format specifications, + provided that it also includes, sepearated by commas exactly + like in a printf statement, the required arguments. It is + the responsibility of the user to select only such arguments + that are valid in the given context. + +config AUTOBOOT_ENCRYPTION + bool "Enable encryption in autoboot stopping" + depends on AUTOBOOT_KEYED + help + This option allows a string to be entered into U-Boot to stop the + autoboot. The string itself is hashed and compared against the hash + in the environment variable 'bootstopkeysha256'. If it matches then + boot stops and a command-line prompt is presented. + + This provides a way to ship a secure production device which can also + be accessed at the U-Boot command line. + +config AUTOBOOT_DELAY_STR + string "Delay autobooting via specific input key / string" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + help + This option delays the automatic boot feature by issuing + a specific input key or string. If CONFIG_AUTOBOOT_DELAY_STR + or the environment variable "bootdelaykey" is specified + and this string is received from console input before + autoboot starts booting, U-Boot gives a command prompt. The + U-Boot prompt will time out if CONFIG_BOOT_RETRY_TIME is + used, otherwise it never times out. + +config AUTOBOOT_STOP_STR + string "Stop autobooting via specific input key / string" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + help + This option enables stopping (aborting) of the automatic + boot feature only by issuing a specific input key or + string. If CONFIG_AUTOBOOT_STOP_STR or the environment + variable "bootstopkey" is specified and this string is + received from console input before autoboot starts booting, + U-Boot gives a command prompt. The U-Boot prompt never + times out, even if CONFIG_BOOT_RETRY_TIME is used. + +config AUTOBOOT_KEYED_CTRLC + bool "Enable Ctrl-C autoboot interruption" + depends on AUTOBOOT_KEYED && !AUTOBOOT_ENCRYPTION + default n + help + This option allows for the boot sequence to be interrupted + by ctrl-c, in addition to the "bootdelaykey" and "bootstopkey". + Setting this variable provides an escape sequence from the + limited "password" strings. + +config AUTOBOOT_STOP_STR_SHA256 + string "Stop autobooting via SHA256 encrypted password" + depends on AUTOBOOT_KEYED && AUTOBOOT_ENCRYPTION + help + This option adds the feature to only stop the autobooting, + and therefore boot into the U-Boot prompt, when the input + string / password matches a values that is encypted via + a SHA256 hash and saved in the environment variable + "bootstopkeysha256". If the value in that variable + includes a ":", the portion prior to the ":" will be treated + as a salt value. + +config AUTOBOOT_USE_MENUKEY + bool "Allow a specify key to run a menu from the environment" + depends on !AUTOBOOT_KEYED + help + If a specific key is pressed to stop autoboot, then the commands in + the environment variable 'menucmd' are executed before boot starts. + +config AUTOBOOT_MENUKEY + int "ASCII value of boot key to show a menu" + default 0 + depends on AUTOBOOT_USE_MENUKEY + help + If this key is pressed to stop autoboot, then the commands in the + environment variable 'menucmd' will be executed before boot starts. + For example, 33 means "!" in ASCII, so pressing ! at boot would take + this action. + +config AUTOBOOT_MENU_SHOW + bool "Show a menu on boot" + depends on CMD_BOOTMENU + help + This enables the boot menu, controlled by environment variables + defined by the board. The menu starts after running the 'preboot' + environmnent variable (if enabled) and before handling the boot delay. + See README.bootmenu for more details. + +endmenu + +config USE_BOOTARGS + bool "Enable boot arguments" + help + Provide boot arguments to bootm command. Boot arguments are specified + in CONFIG_BOOTARGS option. Enable this option to be able to specify + CONFIG_BOOTARGS string. If this option is disabled, CONFIG_BOOTARGS + will be undefined and won't take any space in U-Boot image. + +config BOOTARGS + string "Boot arguments" + depends on USE_BOOTARGS && !USE_DEFAULT_ENV_FILE + help + This can be used to pass arguments to the bootm command. The value of + CONFIG_BOOTARGS goes into the environment value "bootargs". Note that + this value will also override the "chosen" node in FDT blob. + +config BOOTARGS_SUBST + bool "Support substituting strings in boot arguments" + help + This allows substituting string values in the boot arguments. These + are applied after the commandline has been built. + + One use for this is to insert the root-disk UUID into the command + line where bootargs contains "root=${uuid}" + + setenv bootargs "console= root=${uuid}" + # Set the 'uuid' environment variable + part uuid mmc 2:2 uuid + + # Command-line substitution will put the real uuid into the + # kernel command line + bootm + +config USE_BOOTCOMMAND + bool "Enable a default value for bootcmd" + help + Provide a default value for the bootcmd entry in the environment. If + autoboot is enabled this is what will be run automatically. Enable + this option to be able to specify CONFIG_BOOTCOMMAND as a string. If + this option is disabled, CONFIG_BOOTCOMMAND will be undefined and + won't take any space in U-Boot image. + +config BOOTCOMMAND + string "bootcmd value" + depends on USE_BOOTCOMMAND && !USE_DEFAULT_ENV_FILE + default "run distro_bootcmd" if DISTRO_DEFAULTS + help + This is the string of commands that will be used as bootcmd and if + AUTOBOOT is set, automatically run. + +config USE_PREBOOT + bool "Enable preboot" + help + When this option is enabled, the existence of the environment + variable "preboot" will be checked immediately before starting the + CONFIG_BOOTDELAY countdown and/or running the auto-boot command resp. + entering interactive mode. + + This feature is especially useful when "preboot" is automatically + generated or modified. For example, the boot code can modify the + "preboot" when a user holds down a certain combination of keys. + +config PREBOOT + string "preboot default value" + depends on USE_PREBOOT && !USE_DEFAULT_ENV_FILE + default "usb start" if USB_KEYBOARD + default "" + help + This is the default of "preboot" environment variable. + +config DEFAULT_FDT_FILE + string "Default fdt file" + help + This option is used to set the default fdt file to boot OS. + +endmenu # Booting diff --git a/roms/u-boot/common/Makefile b/roms/u-boot/common/Makefile new file mode 100644 index 000000000..829ea5fb4 --- /dev/null +++ b/roms/u-boot/common/Makefile @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2004-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +# core +ifndef CONFIG_SPL_BUILD +obj-y += init/ +obj-y += main.o +obj-y += exports.o +obj-$(CONFIG_HASH) += hash.o +obj-$(CONFIG_HUSH_PARSER) += cli_hush.o +obj-$(CONFIG_AUTOBOOT) += autoboot.o + +# This option is not just y/n - it can have a numeric value +ifdef CONFIG_BOOT_RETRY_TIME +obj-y += bootretry.o +endif + +# # boards +obj-y += board_f.o +obj-y += board_r.o +obj-$(CONFIG_DISPLAY_BOARDINFO) += board_info.o +obj-$(CONFIG_DISPLAY_BOARDINFO_LATE) += board_info.o + +obj-$(CONFIG_CMD_BOOTM) += bootm.o bootm_os.o +obj-$(CONFIG_CMD_BOOTZ) += bootm.o bootm_os.o +obj-$(CONFIG_CMD_BOOTI) += bootm.o bootm_os.o + +obj-$(CONFIG_CMD_BEDBUG) += bedbug.o +obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += fdt_support.o +obj-$(CONFIG_MII) += miiphyutil.o +obj-$(CONFIG_CMD_MII) += miiphyutil.o +obj-$(CONFIG_PHYLIB) += miiphyutil.o + +ifdef CONFIG_USB +obj-y += usb.o usb_hub.o +obj-$(CONFIG_USB_STORAGE) += usb_storage.o +endif + +# others +obj-$(CONFIG_CONSOLE_MUX) += iomux.o +obj-$(CONFIG_MTD_NOR_FLASH) += flash.o +obj-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o +obj-$(CONFIG_I2C_EDID) += edid.o +obj-$(CONFIG_KALLSYMS) += kallsyms.o +obj-y += splash.o +obj-$(CONFIG_SPLASH_SOURCE) += splash_source.o +ifndef CONFIG_DM_VIDEO +obj-$(CONFIG_LCD) += lcd.o lcd_console.o +endif +obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o +obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o +obj-$(CONFIG_LYNXKDI) += lynxkdi.o +obj-$(CONFIG_MENU) += menu.o +obj-$(CONFIG_UPDATE_COMMON) += update.o +obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o +obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o + +endif # !CONFIG_SPL_BUILD + +obj-$(CONFIG_$(SPL_TPL_)BOOTSTAGE) += bootstage.o +obj-$(CONFIG_$(SPL_TPL_)BLOBLIST) += bloblist.o + +ifdef CONFIG_SPL_BUILD +ifdef CONFIG_SPL_DFU +obj-$(CONFIG_DFU_OVER_USB) += dfu.o +endif +obj-$(CONFIG_SPL_HASH_SUPPORT) += hash.o +obj-$(CONFIG_TPL_HASH_SUPPORT) += hash.o +obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o +obj-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o +obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += fdt_support.o + +ifdef CONFIG_SPL_USB_HOST_SUPPORT +obj-y += usb.o +obj-y += usb_hub.o +obj-$(CONFIG_SPL_USB_STORAGE) += usb_storage.o +else +obj-$(CONFIG_USB_MUSB_HOST) += usb.o +endif +endif # CONFIG_SPL_BUILD + +#others +obj-$(CONFIG_DDR_SPD) += ddr_spd.o +obj-$(CONFIG_SPD_EEPROM) += ddr_spd.o +obj-$(CONFIG_HWCONFIG) += hwconfig.o +obj-$(CONFIG_BOUNCE_BUFFER) += bouncebuf.o +ifdef CONFIG_SPL_BUILD +ifdef CONFIG_TPL_BUILD +obj-$(CONFIG_TPL_SERIAL_SUPPORT) += console.o +else +obj-$(CONFIG_SPL_SERIAL_SUPPORT) += console.o +endif +else +obj-y += console.o +endif # CONFIG_SPL_BUILD + +obj-$(CONFIG_CROS_EC) += cros_ec.o +obj-y += dlmalloc.o +ifdef CONFIG_SYS_MALLOC_F +ifneq ($(CONFIG_$(SPL_TPL_)SYS_MALLOC_F_LEN),0) +obj-y += malloc_simple.o +endif +endif + +obj-y += image.o +obj-$(CONFIG_ANDROID_AB) += android_ab.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o +obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o +obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o +obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o +obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o +obj-$(CONFIG_$(SPL_TPL_)IMAGE_SIGN_INFO) += image-sig.o +obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o +obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o +obj-$(CONFIG_IO_TRACE) += iotrace.o +obj-y += memsize.o +obj-y += stdio.o + +obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o + +ifdef CONFIG_CMD_EEPROM_LAYOUT +obj-y += eeprom/eeprom_field.o eeprom/eeprom_layout.o +endif + +obj-y += cli.o +obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o +obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += cli_simple.o cli_readline.o +obj-$(CONFIG_DFU_OVER_USB) += dfu.o +obj-y += command.o +obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o +obj-$(CONFIG_$(SPL_TPL_)LOG_CONSOLE) += log_console.o +obj-$(CONFIG_$(SPL_TPL_)LOG_SYSLOG) += log_syslog.o +obj-y += s_record.o +obj-$(CONFIG_CMD_LOADB) += xyzModem.o +obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o + +obj-$(CONFIG_AVB_VERIFY) += avb_verify.o +obj-$(CONFIG_$(SPL_TPL_)STACKPROTECTOR) += stackprot.o +obj-$(CONFIG_SCP03) += scp03.o + +obj-$(CONFIG_QFW) += qfw.o diff --git a/roms/u-boot/common/android_ab.c b/roms/u-boot/common/android_ab.c new file mode 100644 index 000000000..4943f26d5 --- /dev/null +++ b/roms/u-boot/common/android_ab.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2017 The Android Open Source Project + */ +#include <common.h> +#include <android_ab.h> +#include <android_bootloader_message.h> +#include <blk.h> +#include <log.h> +#include <malloc.h> +#include <part.h> +#include <memalign.h> +#include <linux/err.h> +#include <u-boot/crc.h> +#include <u-boot/crc.h> + +/** + * Compute the CRC-32 of the bootloader control struct. + * + * Only the bytes up to the crc32_le field are considered for the CRC-32 + * calculation. + * + * @param[in] abc bootloader control block + * + * @return crc32 sum + */ +static uint32_t ab_control_compute_crc(struct bootloader_control *abc) +{ + return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le)); +} + +/** + * Initialize bootloader_control to the default value. + * + * It allows us to boot all slots in order from the first one. This value + * should be used when the bootloader message is corrupted, but not when + * a valid message indicates that all slots are unbootable. + * + * @param[in] abc bootloader control block + * + * @return 0 on success and a negative on error + */ +static int ab_control_default(struct bootloader_control *abc) +{ + int i; + const struct slot_metadata metadata = { + .priority = 15, + .tries_remaining = 7, + .successful_boot = 0, + .verity_corrupted = 0, + .reserved = 0 + }; + + if (!abc) + return -EFAULT; + + memcpy(abc->slot_suffix, "a\0\0\0", 4); + abc->magic = BOOT_CTRL_MAGIC; + abc->version = BOOT_CTRL_VERSION; + abc->nb_slot = NUM_SLOTS; + memset(abc->reserved0, 0, sizeof(abc->reserved0)); + for (i = 0; i < abc->nb_slot; ++i) + abc->slot_info[i] = metadata; + + memset(abc->reserved1, 0, sizeof(abc->reserved1)); + abc->crc32_le = ab_control_compute_crc(abc); + + return 0; +} + +/** + * Load the boot_control struct from disk into newly allocated memory. + * + * This function allocates and returns an integer number of disk blocks, + * based on the block size of the passed device to help performing a + * read-modify-write operation on the boot_control struct. + * The boot_control struct offset (2 KiB) must be a multiple of the device + * block size, for simplicity. + * + * @param[in] dev_desc Device where to read the boot_control struct from + * @param[in] part_info Partition in 'dev_desc' where to read from, normally + * the "misc" partition should be used + * @param[out] pointer to pointer to bootloader_control data + * @return 0 on success and a negative on error + */ +static int ab_control_create_from_disk(struct blk_desc *dev_desc, + const struct disk_partition *part_info, + struct bootloader_control **abc) +{ + ulong abc_offset, abc_blocks, ret; + + abc_offset = offsetof(struct bootloader_message_ab, slot_suffix); + if (abc_offset % part_info->blksz) { + log_err("ANDROID: Boot control block not block aligned.\n"); + return -EINVAL; + } + abc_offset /= part_info->blksz; + + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + if (abc_offset + abc_blocks > part_info->size) { + log_err("ANDROID: boot control partition too small. Need at"); + log_err(" least %lu blocks but have %lu blocks.\n", + abc_offset + abc_blocks, part_info->size); + return -EINVAL; + } + *abc = malloc_cache_aligned(abc_blocks * part_info->blksz); + if (!*abc) + return -ENOMEM; + + ret = blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks, + *abc); + if (IS_ERR_VALUE(ret)) { + log_err("ANDROID: Could not read from boot ctrl partition\n"); + free(*abc); + return -EIO; + } + + log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks); + + return 0; +} + +/** + * Store the loaded boot_control block. + * + * Store back to the same location it was read from with + * ab_control_create_from_misc(). + * + * @param[in] dev_desc Device where we should write the boot_control struct + * @param[in] part_info Partition on the 'dev_desc' where to write + * @param[in] abc Pointer to the boot control struct and the extra bytes after + * it up to the nearest block boundary + * @return 0 on success and a negative on error + */ +static int ab_control_store(struct blk_desc *dev_desc, + const struct disk_partition *part_info, + struct bootloader_control *abc) +{ + ulong abc_offset, abc_blocks, ret; + + abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) / + part_info->blksz; + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + ret = blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks, + abc); + if (IS_ERR_VALUE(ret)) { + log_err("ANDROID: Could not write back the misc partition\n"); + return -EIO; + } + + return 0; +} + +/** + * Compare two slots. + * + * The function determines slot which is should we boot from among the two. + * + * @param[in] a The first bootable slot metadata + * @param[in] b The second bootable slot metadata + * @return Negative if the slot "a" is better, positive of the slot "b" is + * better or 0 if they are equally good. + */ +static int ab_compare_slots(const struct slot_metadata *a, + const struct slot_metadata *b) +{ + /* Higher priority is better */ + if (a->priority != b->priority) + return b->priority - a->priority; + + /* Higher successful_boot value is better, in case of same priority */ + if (a->successful_boot != b->successful_boot) + return b->successful_boot - a->successful_boot; + + /* Higher tries_remaining is better to ensure round-robin */ + if (a->tries_remaining != b->tries_remaining) + return b->tries_remaining - a->tries_remaining; + + return 0; +} + +int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info) +{ + struct bootloader_control *abc = NULL; + u32 crc32_le; + int slot, i, ret; + bool store_needed = false; + char slot_suffix[4]; + + ret = ab_control_create_from_disk(dev_desc, part_info, &abc); + if (ret < 0) { + /* + * This condition represents an actual problem with the code or + * the board setup, like an invalid partition information. + * Signal a repair mode and do not try to boot from either slot. + */ + return ret; + } + + crc32_le = ab_control_compute_crc(abc); + if (abc->crc32_le != crc32_le) { + log_err("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x),", + crc32_le, abc->crc32_le); + log_err("re-initializing A/B metadata.\n"); + + ret = ab_control_default(abc); + if (ret < 0) { + free(abc); + return -ENODATA; + } + store_needed = true; + } + + if (abc->magic != BOOT_CTRL_MAGIC) { + log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic); + free(abc); + return -ENODATA; + } + + if (abc->version > BOOT_CTRL_VERSION) { + log_err("ANDROID: Unsupported A/B metadata version: %.8x\n", + abc->version); + free(abc); + return -ENODATA; + } + + /* + * At this point a valid boot control metadata is stored in abc, + * followed by other reserved data in the same block. We select a with + * the higher priority slot that + * - is not marked as corrupted and + * - either has tries_remaining > 0 or successful_boot is true. + * If the selected slot has a false successful_boot, we also decrement + * the tries_remaining until it eventually becomes unbootable because + * tries_remaining reaches 0. This mechanism produces a bootloader + * induced rollback, typically right after a failed update. + */ + + /* Safety check: limit the number of slots. */ + if (abc->nb_slot > ARRAY_SIZE(abc->slot_info)) { + abc->nb_slot = ARRAY_SIZE(abc->slot_info); + store_needed = true; + } + + slot = -1; + for (i = 0; i < abc->nb_slot; ++i) { + if (abc->slot_info[i].verity_corrupted || + !abc->slot_info[i].tries_remaining) { + log_debug("ANDROID: unbootable slot %d tries: %d, ", + i, abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d\n", + abc->slot_info[i].verity_corrupted); + continue; + } + log_debug("ANDROID: bootable slot %d pri: %d, tries: %d, ", + i, abc->slot_info[i].priority, + abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d, successful: %d\n", + abc->slot_info[i].verity_corrupted, + abc->slot_info[i].successful_boot); + + if (slot < 0 || + ab_compare_slots(&abc->slot_info[i], + &abc->slot_info[slot]) < 0) { + slot = i; + } + } + + if (slot >= 0 && !abc->slot_info[slot].successful_boot) { + log_err("ANDROID: Attempting slot %c, tries remaining %d\n", + BOOT_SLOT_NAME(slot), + abc->slot_info[slot].tries_remaining); + abc->slot_info[slot].tries_remaining--; + store_needed = true; + } + + if (slot >= 0) { + /* + * Legacy user-space requires this field to be set in the BCB. + * Newer releases load this slot suffix from the command line + * or the device tree. + */ + memset(slot_suffix, 0, sizeof(slot_suffix)); + slot_suffix[0] = BOOT_SLOT_NAME(slot); + if (memcmp(abc->slot_suffix, slot_suffix, + sizeof(slot_suffix))) { + memcpy(abc->slot_suffix, slot_suffix, + sizeof(slot_suffix)); + store_needed = true; + } + } + + if (store_needed) { + abc->crc32_le = ab_control_compute_crc(abc); + ab_control_store(dev_desc, part_info, abc); + } + free(abc); + + if (slot < 0) + return -EINVAL; + + return slot; +} diff --git a/roms/u-boot/common/autoboot.c b/roms/u-boot/common/autoboot.c new file mode 100644 index 000000000..b42148c72 --- /dev/null +++ b/roms/u-boot/common/autoboot.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <autoboot.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <fdtdec.h> +#include <hash.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <menu.h> +#include <post.h> +#include <time.h> +#include <asm/global_data.h> +#include <linux/delay.h> +#include <u-boot/sha256.h> +#include <bootcount.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MAX_DELAY_STOP_STR 64 + +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 +#endif +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) + +/* Stored value of bootdelay, used by autoboot_command() */ +static int stored_bootdelay; +static int menukey; + +#ifdef CONFIG_AUTOBOOT_ENCRYPTION +#define AUTOBOOT_STOP_STR_SHA256 CONFIG_AUTOBOOT_STOP_STR_SHA256 +#else +#define AUTOBOOT_STOP_STR_SHA256 "" +#endif + +#ifdef CONFIG_AUTOBOOT_USE_MENUKEY +#define AUTOBOOT_MENUKEY CONFIG_AUTOBOOT_MENUKEY +#else +#define AUTOBOOT_MENUKEY 0 +#endif + +/* + * Use a "constant-length" time compare function for this + * hash compare: + * + * https://crackstation.net/hashing-security.htm + */ +static int slow_equals(u8 *a, u8 *b, int len) +{ + int diff = 0; + int i; + + for (i = 0; i < len; i++) + diff |= a[i] ^ b[i]; + + return diff == 0; +} + +/** + * passwd_abort_sha256() - check for a hashed key sequence to abort booting + * + * This checks for the user entering a SHA256 hash within a given time. + * + * @etime: Timeout value ticks (stop when get_ticks() reachs this) + * @return 0 if autoboot should continue, 1 if it should stop + */ +static int passwd_abort_sha256(uint64_t etime) +{ + const char *sha_env_str = env_get("bootstopkeysha256"); + u8 sha_env[SHA256_SUM_LEN]; + u8 *sha; + char *presskey; + char *c; + const char *algo_name = "sha256"; + u_int presskey_len = 0; + int abort = 0; + int size = sizeof(sha); + int ret; + + if (sha_env_str == NULL) + sha_env_str = AUTOBOOT_STOP_STR_SHA256; + + presskey = malloc_cache_aligned(MAX_DELAY_STOP_STR); + c = strstr(sha_env_str, ":"); + if (c && (c - sha_env_str < MAX_DELAY_STOP_STR)) { + /* preload presskey with salt */ + memcpy(presskey, sha_env_str, c - sha_env_str); + presskey_len = c - sha_env_str; + sha_env_str = c + 1; + } + /* + * Generate the binary value from the environment hash value + * so that we can compare this value with the computed hash + * from the user input + */ + ret = hash_parse_string(algo_name, sha_env_str, sha_env); + if (ret) { + printf("Hash %s not supported!\n", algo_name); + return 0; + } + + sha = malloc_cache_aligned(SHA256_SUM_LEN); + size = SHA256_SUM_LEN; + /* + * We don't know how long the stop-string is, so we need to + * generate the sha256 hash upon each input character and + * compare the value with the one saved in the environment + */ + do { + if (tstc()) { + /* Check for input string overflow */ + if (presskey_len >= MAX_DELAY_STOP_STR) { + free(presskey); + free(sha); + return 0; + } + + presskey[presskey_len++] = getchar(); + + /* Calculate sha256 upon each new char */ + hash_block(algo_name, (const void *)presskey, + presskey_len, sha, &size); + + /* And check if sha matches saved value in env */ + if (slow_equals(sha, sha_env, SHA256_SUM_LEN)) + abort = 1; + } + } while (!abort && get_ticks() <= etime); + + free(presskey); + free(sha); + return abort; +} + +/** + * passwd_abort_key() - check for a key sequence to aborted booting + * + * This checks for the user entering a string within a given time. + * + * @etime: Timeout value ticks (stop when get_ticks() reachs this) + * @return 0 if autoboot should continue, 1 if it should stop + */ +static int passwd_abort_key(uint64_t etime) +{ + int abort = 0; + struct { + char *str; + u_int len; + int retry; + } + delaykey[] = { + { .str = env_get("bootdelaykey"), .retry = 1 }, + { .str = env_get("bootstopkey"), .retry = 0 }, + }; + + char presskey[MAX_DELAY_STOP_STR]; + int presskey_len = 0; + int presskey_max = 0; + int i; + +# ifdef CONFIG_AUTOBOOT_DELAY_STR + if (delaykey[0].str == NULL) + delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR + if (delaykey[1].str == NULL) + delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR; +# endif + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) { + delaykey[i].len = delaykey[i].str == NULL ? + 0 : strlen(delaykey[i].str); + delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? + MAX_DELAY_STOP_STR : delaykey[i].len; + + presskey_max = presskey_max > delaykey[i].len ? + presskey_max : delaykey[i].len; + + debug_bootkeys("%s key:<%s>\n", + delaykey[i].retry ? "delay" : "stop", + delaykey[i].str ? delaykey[i].str : "NULL"); + } + + /* In order to keep up with incoming data, check timeout only + * when catch up. + */ + do { + if (tstc()) { + if (presskey_len < presskey_max) { + presskey[presskey_len++] = getchar(); + } else { + for (i = 0; i < presskey_max - 1; i++) + presskey[i] = presskey[i + 1]; + + presskey[i] = getchar(); + } + } + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) { + if (delaykey[i].len > 0 && + presskey_len >= delaykey[i].len && + memcmp(presskey + presskey_len - + delaykey[i].len, delaykey[i].str, + delaykey[i].len) == 0) { + debug_bootkeys("got %skey\n", + delaykey[i].retry ? "delay" : + "stop"); + + /* don't retry auto boot */ + if (!delaykey[i].retry) + bootretry_dont_retry(); + abort = 1; + } + } + } while (!abort && get_ticks() <= etime); + + return abort; +} + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 - no key string, allow autoboot 1 - got key string, abort + */ +static int abortboot_key_sequence(int bootdelay) +{ + int abort; + uint64_t etime = endtick(bootdelay); + +# ifdef CONFIG_AUTOBOOT_PROMPT + /* + * CONFIG_AUTOBOOT_PROMPT includes the %d for all boards. + * To print the bootdelay value upon bootup. + */ + printf(CONFIG_AUTOBOOT_PROMPT, bootdelay); +# endif + + if (IS_ENABLED(CONFIG_AUTOBOOT_ENCRYPTION)) + abort = passwd_abort_sha256(etime); + else + abort = passwd_abort_key(etime); + if (!abort) + debug_bootkeys("key timeout\n"); + + return abort; +} + +static int abortboot_single_key(int bootdelay) +{ + int abort = 0; + unsigned long ts; + + printf("Hit any key to stop autoboot: %2d ", bootdelay); + + /* + * Check if key already pressed + */ + if (tstc()) { /* we got a key press */ + getchar(); /* consume input */ + puts("\b\b\b 0"); + abort = 1; /* don't auto boot */ + } + + while ((bootdelay > 0) && (!abort)) { + --bootdelay; + /* delay 1000 ms */ + ts = get_timer(0); + do { + if (tstc()) { /* we got a key press */ + int key; + + abort = 1; /* don't auto boot */ + bootdelay = 0; /* no more delay */ + key = getchar();/* consume input */ + if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY)) + menukey = key; + break; + } + udelay(10000); + } while (!abort && get_timer(ts) < 1000); + + printf("\b\b\b%2d ", bootdelay); + } + + putc('\n'); + + return abort; +} + +static int abortboot(int bootdelay) +{ + int abort = 0; + + if (bootdelay >= 0) { + if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED)) + abort = abortboot_key_sequence(bootdelay); + else + abort = abortboot_single_key(bootdelay); + } + + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && abort) + gd->flags &= ~GD_FLG_SILENT; + + return abort; +} + +static void process_fdt_options(const void *blob) +{ +#ifdef CONFIG_SYS_TEXT_BASE + ulong addr; + + /* Add an env variable to point to a kernel payload, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); + if (addr) + env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); + + /* Add an env variable to point to a root disk, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); + if (addr) + env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); +#endif /* CONFIG_SYS_TEXT_BASE */ +} + +const char *bootdelay_process(void) +{ + char *s; + int bootdelay; + + bootcount_inc(); + + s = env_get("bootdelay"); + bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; + + if (IS_ENABLED(CONFIG_OF_CONTROL)) + bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", + bootdelay); + + debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); + + if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) + bootdelay = menu_show(bootdelay); + bootretry_init_cmd_timeout(); + +#ifdef CONFIG_POST + if (gd->flags & GD_FLG_POSTFAIL) { + s = env_get("failbootcmd"); + } else +#endif /* CONFIG_POST */ + if (bootcount_error()) + s = env_get("altbootcmd"); + else + s = env_get("bootcmd"); + + if (IS_ENABLED(CONFIG_OF_CONTROL)) + process_fdt_options(gd->fdt_blob); + stored_bootdelay = bootdelay; + + return s; +} + +void autoboot_command(const char *s) +{ + debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); + + if (s && (stored_bootdelay == -2 || + (stored_bootdelay != -1 && !abortboot(stored_bootdelay)))) { + bool lock; + int prev; + + lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) && + !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC); + if (lock) + prev = disable_ctrlc(1); /* disable Ctrl-C checking */ + + run_command_list(s, -1, 0); + + if (lock) + disable_ctrlc(prev); /* restore Ctrl-C checking */ + } + + if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY) && + menukey == AUTOBOOT_MENUKEY) { + s = env_get("menucmd"); + if (s) + run_command_list(s, -1, 0); + } +} diff --git a/roms/u-boot/common/avb_verify.c b/roms/u-boot/common/avb_verify.c new file mode 100644 index 000000000..0520a7145 --- /dev/null +++ b/roms/u-boot/common/avb_verify.c @@ -0,0 +1,1023 @@ +/* + * (C) Copyright 2018, Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <avb_verify.h> +#include <blk.h> +#include <cpu_func.h> +#include <image.h> +#include <malloc.h> +#include <part.h> +#include <tee.h> +#include <tee/optee_ta_avb.h> + +static const unsigned char avb_root_pub[1032] = { + 0x0, 0x0, 0x10, 0x0, 0x55, 0xd9, 0x4, 0xad, 0xd8, 0x4, + 0xaf, 0xe3, 0xd3, 0x84, 0x6c, 0x7e, 0xd, 0x89, 0x3d, 0xc2, + 0x8c, 0xd3, 0x12, 0x55, 0xe9, 0x62, 0xc9, 0xf1, 0xf, 0x5e, + 0xcc, 0x16, 0x72, 0xab, 0x44, 0x7c, 0x2c, 0x65, 0x4a, 0x94, + 0xb5, 0x16, 0x2b, 0x0, 0xbb, 0x6, 0xef, 0x13, 0x7, 0x53, + 0x4c, 0xf9, 0x64, 0xb9, 0x28, 0x7a, 0x1b, 0x84, 0x98, 0x88, + 0xd8, 0x67, 0xa4, 0x23, 0xf9, 0xa7, 0x4b, 0xdc, 0x4a, 0xf, + 0xf7, 0x3a, 0x18, 0xae, 0x54, 0xa8, 0x15, 0xfe, 0xb0, 0xad, + 0xac, 0x35, 0xda, 0x3b, 0xad, 0x27, 0xbc, 0xaf, 0xe8, 0xd3, + 0x2f, 0x37, 0x34, 0xd6, 0x51, 0x2b, 0x6c, 0x5a, 0x27, 0xd7, + 0x96, 0x6, 0xaf, 0x6b, 0xb8, 0x80, 0xca, 0xfa, 0x30, 0xb4, + 0xb1, 0x85, 0xb3, 0x4d, 0xaa, 0xaa, 0xc3, 0x16, 0x34, 0x1a, + 0xb8, 0xe7, 0xc7, 0xfa, 0xf9, 0x9, 0x77, 0xab, 0x97, 0x93, + 0xeb, 0x44, 0xae, 0xcf, 0x20, 0xbc, 0xf0, 0x80, 0x11, 0xdb, + 0x23, 0xc, 0x47, 0x71, 0xb9, 0x6d, 0xd6, 0x7b, 0x60, 0x47, + 0x87, 0x16, 0x56, 0x93, 0xb7, 0xc2, 0x2a, 0x9a, 0xb0, 0x4c, + 0x1, 0xc, 0x30, 0xd8, 0x93, 0x87, 0xf0, 0xed, 0x6e, 0x8b, + 0xbe, 0x30, 0x5b, 0xf6, 0xa6, 0xaf, 0xdd, 0x80, 0x7c, 0x45, + 0x5e, 0x8f, 0x91, 0x93, 0x5e, 0x44, 0xfe, 0xb8, 0x82, 0x7, + 0xee, 0x79, 0xca, 0xbf, 0x31, 0x73, 0x62, 0x58, 0xe3, 0xcd, + 0xc4, 0xbc, 0xc2, 0x11, 0x1d, 0xa1, 0x4a, 0xbf, 0xfe, 0x27, + 0x7d, 0xa1, 0xf6, 0x35, 0xa3, 0x5e, 0xca, 0xdc, 0x57, 0x2f, + 0x3e, 0xf0, 0xc9, 0x5d, 0x86, 0x6a, 0xf8, 0xaf, 0x66, 0xa7, + 0xed, 0xcd, 0xb8, 0xed, 0xa1, 0x5f, 0xba, 0x9b, 0x85, 0x1a, + 0xd5, 0x9, 0xae, 0x94, 0x4e, 0x3b, 0xcf, 0xcb, 0x5c, 0xc9, + 0x79, 0x80, 0xf7, 0xcc, 0xa6, 0x4a, 0xa8, 0x6a, 0xd8, 0xd3, + 0x31, 0x11, 0xf9, 0xf6, 0x2, 0x63, 0x2a, 0x1a, 0x2d, 0xd1, + 0x1a, 0x66, 0x1b, 0x16, 0x41, 0xbd, 0xbd, 0xf7, 0x4d, 0xc0, + 0x4a, 0xe5, 0x27, 0x49, 0x5f, 0x7f, 0x58, 0xe3, 0x27, 0x2d, + 0xe5, 0xc9, 0x66, 0xe, 0x52, 0x38, 0x16, 0x38, 0xfb, 0x16, + 0xeb, 0x53, 0x3f, 0xe6, 0xfd, 0xe9, 0xa2, 0x5e, 0x25, 0x59, + 0xd8, 0x79, 0x45, 0xff, 0x3, 0x4c, 0x26, 0xa2, 0x0, 0x5a, + 0x8e, 0xc2, 0x51, 0xa1, 0x15, 0xf9, 0x7b, 0xf4, 0x5c, 0x81, + 0x9b, 0x18, 0x47, 0x35, 0xd8, 0x2d, 0x5, 0xe9, 0xad, 0xf, + 0x35, 0x74, 0x15, 0xa3, 0x8e, 0x8b, 0xcc, 0x27, 0xda, 0x7c, + 0x5d, 0xe4, 0xfa, 0x4, 0xd3, 0x5, 0xb, 0xba, 0x3a, 0xb2, + 0x49, 0x45, 0x2f, 0x47, 0xc7, 0xd, 0x41, 0x3f, 0x97, 0x80, + 0x4d, 0x3f, 0xc1, 0xb5, 0xbb, 0x70, 0x5f, 0xa7, 0x37, 0xaf, + 0x48, 0x22, 0x12, 0x45, 0x2e, 0xf5, 0xf, 0x87, 0x92, 0xe2, + 0x84, 0x1, 0xf9, 0x12, 0xf, 0x14, 0x15, 0x24, 0xce, 0x89, + 0x99, 0xee, 0xb9, 0xc4, 0x17, 0x70, 0x70, 0x15, 0xea, 0xbe, + 0xc6, 0x6c, 0x1f, 0x62, 0xb3, 0xf4, 0x2d, 0x16, 0x87, 0xfb, + 0x56, 0x1e, 0x45, 0xab, 0xae, 0x32, 0xe4, 0x5e, 0x91, 0xed, + 0x53, 0x66, 0x5e, 0xbd, 0xed, 0xad, 0xe6, 0x12, 0x39, 0xd, + 0x83, 0xc9, 0xe8, 0x6b, 0x6c, 0x2d, 0xa5, 0xee, 0xc4, 0x5a, + 0x66, 0xae, 0x8c, 0x97, 0xd7, 0xd, 0x6c, 0x49, 0xc7, 0xf5, + 0xc4, 0x92, 0x31, 0x8b, 0x9, 0xee, 0x33, 0xda, 0xa9, 0x37, + 0xb6, 0x49, 0x18, 0xf8, 0xe, 0x60, 0x45, 0xc8, 0x33, 0x91, + 0xef, 0x20, 0x57, 0x10, 0xbe, 0x78, 0x2d, 0x83, 0x26, 0xd6, + 0xca, 0x61, 0xf9, 0x2f, 0xe0, 0xbf, 0x5, 0x30, 0x52, 0x5a, + 0x12, 0x1c, 0x0, 0xa7, 0x5d, 0xcc, 0x7c, 0x2e, 0xc5, 0x95, + 0x8b, 0xa3, 0x3b, 0xf0, 0x43, 0x2e, 0x5e, 0xdd, 0x0, 0xdb, + 0xd, 0xb3, 0x37, 0x99, 0xa9, 0xcd, 0x9c, 0xb7, 0x43, 0xf7, + 0x35, 0x44, 0x21, 0xc2, 0x82, 0x71, 0xab, 0x8d, 0xaa, 0xb4, + 0x41, 0x11, 0xec, 0x1e, 0x8d, 0xfc, 0x14, 0x82, 0x92, 0x4e, + 0x83, 0x6a, 0xa, 0x6b, 0x35, 0x5e, 0x5d, 0xe9, 0x5c, 0xcc, + 0x8c, 0xde, 0x39, 0xd1, 0x4a, 0x5b, 0x5f, 0x63, 0xa9, 0x64, + 0xe0, 0xa, 0xcb, 0xb, 0xb8, 0x5a, 0x7c, 0xc3, 0xb, 0xe6, + 0xbe, 0xfe, 0x8b, 0xf, 0x7d, 0x34, 0x8e, 0x2, 0x66, 0x74, + 0x1, 0x6c, 0xca, 0x76, 0xac, 0x7c, 0x67, 0x8, 0x2f, 0x3f, + 0x1a, 0xa6, 0x2c, 0x60, 0xb3, 0xff, 0xda, 0x8d, 0xb8, 0x12, + 0xc, 0x0, 0x7f, 0xcc, 0x50, 0xa1, 0x5c, 0x64, 0xa1, 0xe2, + 0x5f, 0x32, 0x65, 0xc9, 0x9c, 0xbe, 0xd6, 0xa, 0x13, 0x87, + 0x3c, 0x2a, 0x45, 0x47, 0xc, 0xca, 0x42, 0x82, 0xfa, 0x89, + 0x65, 0xe7, 0x89, 0xb4, 0x8f, 0xf7, 0x1e, 0xe6, 0x23, 0xa5, + 0xd0, 0x59, 0x37, 0x79, 0x92, 0xd7, 0xce, 0x3d, 0xfd, 0xe3, + 0xa1, 0xb, 0xcf, 0x6c, 0x85, 0xa0, 0x65, 0xf3, 0x5c, 0xc6, + 0x4a, 0x63, 0x5f, 0x6e, 0x3a, 0x3a, 0x2a, 0x8b, 0x6a, 0xb6, + 0x2f, 0xbb, 0xf8, 0xb2, 0x4b, 0x62, 0xbc, 0x1a, 0x91, 0x25, + 0x66, 0xe3, 0x69, 0xca, 0x60, 0x49, 0xb, 0xf6, 0x8a, 0xbe, + 0x3e, 0x76, 0x53, 0xc2, 0x7a, 0xa8, 0x4, 0x17, 0x75, 0xf1, + 0xf3, 0x3, 0x62, 0x1b, 0x85, 0xb2, 0xb0, 0xef, 0x80, 0x15, + 0xb6, 0xd4, 0x4e, 0xdf, 0x71, 0xac, 0xdb, 0x2a, 0x4, 0xd4, + 0xb4, 0x21, 0xba, 0x65, 0x56, 0x57, 0xe8, 0xfa, 0x84, 0xa2, + 0x7d, 0x13, 0xe, 0xaf, 0xd7, 0x9a, 0x58, 0x2a, 0xa3, 0x81, + 0x84, 0x8d, 0x9, 0xa0, 0x6a, 0xc1, 0xbb, 0xd9, 0xf5, 0x86, + 0xac, 0xbd, 0x75, 0x61, 0x9, 0xe6, 0x8c, 0x3d, 0x77, 0xb2, + 0xed, 0x30, 0x20, 0xe4, 0x0, 0x1d, 0x97, 0xe8, 0xbf, 0xc7, + 0x0, 0x1b, 0x21, 0xb1, 0x16, 0xe7, 0x41, 0x67, 0x2e, 0xec, + 0x38, 0xbc, 0xe5, 0x1b, 0xb4, 0x6, 0x23, 0x31, 0x71, 0x1c, + 0x49, 0xcd, 0x76, 0x4a, 0x76, 0x36, 0x8d, 0xa3, 0x89, 0x8b, + 0x4a, 0x7a, 0xf4, 0x87, 0xc8, 0x15, 0xf, 0x37, 0x39, 0xf6, + 0x6d, 0x80, 0x19, 0xef, 0x5c, 0xa8, 0x66, 0xce, 0x1b, 0x16, + 0x79, 0x21, 0xdf, 0xd7, 0x31, 0x30, 0xc4, 0x21, 0xdd, 0x34, + 0x5b, 0xd2, 0x1a, 0x2b, 0x3e, 0x5d, 0xf7, 0xea, 0xca, 0x5, + 0x8e, 0xb7, 0xcb, 0x49, 0x2e, 0xa0, 0xe3, 0xf4, 0xa7, 0x48, + 0x19, 0x10, 0x9c, 0x4, 0xa7, 0xf4, 0x28, 0x74, 0xc8, 0x6f, + 0x63, 0x20, 0x2b, 0x46, 0x24, 0x26, 0x19, 0x1d, 0xd1, 0x2c, + 0x31, 0x6d, 0x5a, 0x29, 0xa2, 0x6, 0xa6, 0xb2, 0x41, 0xcc, + 0xa, 0x27, 0x96, 0x9, 0x96, 0xac, 0x47, 0x65, 0x78, 0x68, + 0x51, 0x98, 0xd6, 0xd8, 0xa6, 0x2d, 0xa0, 0xcf, 0xec, 0xe2, + 0x74, 0xf2, 0x82, 0xe3, 0x97, 0xd9, 0x7e, 0xd4, 0xf8, 0xb, + 0x70, 0x43, 0x3d, 0xb1, 0x7b, 0x97, 0x80, 0xd6, 0xcb, 0xd7, + 0x19, 0xbc, 0x63, 0xb, 0xfd, 0x4d, 0x88, 0xfe, 0x67, 0xac, + 0xb8, 0xcc, 0x50, 0xb7, 0x68, 0xb3, 0x5b, 0xd6, 0x1e, 0x25, + 0xfc, 0x5f, 0x3c, 0x8d, 0xb1, 0x33, 0x7c, 0xb3, 0x49, 0x1, + 0x3f, 0x71, 0x55, 0xe, 0x51, 0xba, 0x61, 0x26, 0xfa, 0xea, + 0xe5, 0xb5, 0xe8, 0xaa, 0xcf, 0xcd, 0x96, 0x9f, 0xd6, 0xc1, + 0x5f, 0x53, 0x91, 0xad, 0x5, 0xde, 0x20, 0xe7, 0x51, 0xda, + 0x5b, 0x95, 0x67, 0xed, 0xf4, 0xee, 0x42, 0x65, 0x70, 0x13, + 0xb, 0x70, 0x14, 0x1c, 0xc9, 0xe0, 0x19, 0xca, 0x5f, 0xf5, + 0x1d, 0x70, 0x4b, 0x6c, 0x6, 0x74, 0xec, 0xb5, 0x2e, 0x77, + 0xe1, 0x74, 0xa1, 0xa3, 0x99, 0xa0, 0x85, 0x9e, 0xf1, 0xac, + 0xd8, 0x7e, +}; + +/** + * ============================================================================ + * Boot states support (GREEN, YELLOW, ORANGE, RED) and dm_verity + * ============================================================================ + */ +char *avb_set_state(AvbOps *ops, enum avb_boot_state boot_state) +{ + struct AvbOpsData *data; + char *cmdline = NULL; + + if (!ops) + return NULL; + + data = (struct AvbOpsData *)ops->user_data; + if (!data) + return NULL; + + data->boot_state = boot_state; + switch (boot_state) { + case AVB_GREEN: + cmdline = "androidboot.verifiedbootstate=green"; + break; + case AVB_YELLOW: + cmdline = "androidboot.verifiedbootstate=yellow"; + break; + case AVB_ORANGE: + cmdline = "androidboot.verifiedbootstate=orange"; + case AVB_RED: + break; + } + + return cmdline; +} + +char *append_cmd_line(char *cmdline_orig, char *cmdline_new) +{ + char *cmd_line; + + if (!cmdline_new) + return cmdline_orig; + + if (cmdline_orig) + cmd_line = cmdline_orig; + else + cmd_line = " "; + + cmd_line = avb_strdupv(cmd_line, " ", cmdline_new, NULL); + + return cmd_line; +} + +static int avb_find_dm_args(char **args, char *str) +{ + int i; + + if (!str) + return -1; + + for (i = 0; i < AVB_MAX_ARGS && args[i]; ++i) { + if (strstr(args[i], str)) + return i; + } + + return -1; +} + +static char *avb_set_enforce_option(const char *cmdline, const char *option) +{ + char *cmdarg[AVB_MAX_ARGS]; + char *newargs = NULL; + int i = 0; + int total_args; + + memset(cmdarg, 0, sizeof(cmdarg)); + cmdarg[i++] = strtok((char *)cmdline, " "); + + do { + cmdarg[i] = strtok(NULL, " "); + if (!cmdarg[i]) + break; + + if (++i >= AVB_MAX_ARGS) { + printf("%s: Can't handle more then %d args\n", + __func__, i); + return NULL; + } + } while (true); + + total_args = i; + i = avb_find_dm_args(&cmdarg[0], VERITY_TABLE_OPT_LOGGING); + if (i >= 0) { + cmdarg[i] = (char *)option; + } else { + i = avb_find_dm_args(&cmdarg[0], VERITY_TABLE_OPT_RESTART); + if (i < 0) { + printf("%s: No verity options found\n", __func__); + return NULL; + } + + cmdarg[i] = (char *)option; + } + + for (i = 0; i <= total_args; i++) + newargs = append_cmd_line(newargs, cmdarg[i]); + + return newargs; +} + +char *avb_set_ignore_corruption(const char *cmdline) +{ + char *newargs = NULL; + + newargs = avb_set_enforce_option(cmdline, VERITY_TABLE_OPT_LOGGING); + if (newargs) + newargs = append_cmd_line(newargs, + "androidboot.veritymode=eio"); + + return newargs; +} + +char *avb_set_enforce_verity(const char *cmdline) +{ + char *newargs; + + newargs = avb_set_enforce_option(cmdline, VERITY_TABLE_OPT_RESTART); + if (newargs) + newargs = append_cmd_line(newargs, + "androidboot.veritymode=enforcing"); + return newargs; +} + +/** + * ============================================================================ + * IO(mmc) auxiliary functions + * ============================================================================ + */ +static unsigned long mmc_read_and_flush(struct mmc_part *part, + lbaint_t start, + lbaint_t sectors, + void *buffer) +{ + unsigned long blks; + void *tmp_buf; + size_t buf_size; + bool unaligned = is_buf_unaligned(buffer); + + if (start < part->info.start) { + printf("%s: partition start out of bounds\n", __func__); + return 0; + } + if ((start + sectors) > (part->info.start + part->info.size)) { + sectors = part->info.start + part->info.size - start; + printf("%s: read sector aligned to partition bounds (%ld)\n", + __func__, sectors); + } + + /* + * Reading fails on unaligned buffers, so we have to + * use aligned temporary buffer and then copy to destination + */ + + if (unaligned) { + printf("Handling unaligned read buffer..\n"); + tmp_buf = get_sector_buf(); + buf_size = get_sector_buf_size(); + if (sectors > buf_size / part->info.blksz) + sectors = buf_size / part->info.blksz; + } else { + tmp_buf = buffer; + } + + blks = blk_dread(part->mmc_blk, + start, sectors, tmp_buf); + /* flush cache after read */ + flush_cache((ulong)tmp_buf, sectors * part->info.blksz); + + if (unaligned) + memcpy(buffer, tmp_buf, sectors * part->info.blksz); + + return blks; +} + +static unsigned long mmc_write(struct mmc_part *part, lbaint_t start, + lbaint_t sectors, void *buffer) +{ + void *tmp_buf; + size_t buf_size; + bool unaligned = is_buf_unaligned(buffer); + + if (start < part->info.start) { + printf("%s: partition start out of bounds\n", __func__); + return 0; + } + if ((start + sectors) > (part->info.start + part->info.size)) { + sectors = part->info.start + part->info.size - start; + printf("%s: sector aligned to partition bounds (%ld)\n", + __func__, sectors); + } + if (unaligned) { + tmp_buf = get_sector_buf(); + buf_size = get_sector_buf_size(); + printf("Handling unaligned wrire buffer..\n"); + if (sectors > buf_size / part->info.blksz) + sectors = buf_size / part->info.blksz; + + memcpy(tmp_buf, buffer, sectors * part->info.blksz); + } else { + tmp_buf = buffer; + } + + return blk_dwrite(part->mmc_blk, + start, sectors, tmp_buf); +} + +static struct mmc_part *get_partition(AvbOps *ops, const char *partition) +{ + int ret; + u8 dev_num; + int part_num = 0; + struct mmc_part *part; + struct blk_desc *mmc_blk; + + part = malloc(sizeof(struct mmc_part)); + if (!part) + return NULL; + + dev_num = get_boot_device(ops); + part->mmc = find_mmc_device(dev_num); + if (!part->mmc) { + printf("No MMC device at slot %x\n", dev_num); + goto err; + } + + if (mmc_init(part->mmc)) { + printf("MMC initialization failed\n"); + goto err; + } + + ret = mmc_switch_part(part->mmc, part_num); + if (ret) + goto err; + + mmc_blk = mmc_get_blk_desc(part->mmc); + if (!mmc_blk) { + printf("Error - failed to obtain block descriptor\n"); + goto err; + } + + ret = part_get_info_by_name(mmc_blk, partition, &part->info); + if (ret < 0) { + printf("Can't find partition '%s'\n", partition); + goto err; + } + + part->dev_num = dev_num; + part->mmc_blk = mmc_blk; + + return part; +err: + free(part); + return NULL; +} + +static AvbIOResult mmc_byte_io(AvbOps *ops, + const char *partition, + s64 offset, + size_t num_bytes, + void *buffer, + size_t *out_num_read, + enum mmc_io_type io_type) +{ + ulong ret; + struct mmc_part *part; + u64 start_offset, start_sector, sectors, residue; + u8 *tmp_buf; + size_t io_cnt = 0; + + if (!partition || !buffer || io_type > IO_WRITE) + return AVB_IO_RESULT_ERROR_IO; + + part = get_partition(ops, partition); + if (!part) + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + + if (!part->info.blksz) + return AVB_IO_RESULT_ERROR_IO; + + start_offset = calc_offset(part, offset); + while (num_bytes) { + start_sector = start_offset / part->info.blksz; + sectors = num_bytes / part->info.blksz; + /* handle non block-aligned reads */ + if (start_offset % part->info.blksz || + num_bytes < part->info.blksz) { + tmp_buf = get_sector_buf(); + if (start_offset % part->info.blksz) { + residue = part->info.blksz - + (start_offset % part->info.blksz); + if (residue > num_bytes) + residue = num_bytes; + } else { + residue = num_bytes; + } + + if (io_type == IO_READ) { + ret = mmc_read_and_flush(part, + part->info.start + + start_sector, + 1, tmp_buf); + + if (ret != 1) { + printf("%s: read error (%ld, %lld)\n", + __func__, ret, start_sector); + return AVB_IO_RESULT_ERROR_IO; + } + /* + * if this is not aligned at sector start, + * we have to adjust the tmp buffer + */ + tmp_buf += (start_offset % part->info.blksz); + memcpy(buffer, (void *)tmp_buf, residue); + } else { + ret = mmc_read_and_flush(part, + part->info.start + + start_sector, + 1, tmp_buf); + + if (ret != 1) { + printf("%s: read error (%ld, %lld)\n", + __func__, ret, start_sector); + return AVB_IO_RESULT_ERROR_IO; + } + memcpy((void *)tmp_buf + + start_offset % part->info.blksz, + buffer, residue); + + ret = mmc_write(part, part->info.start + + start_sector, 1, tmp_buf); + if (ret != 1) { + printf("%s: write error (%ld, %lld)\n", + __func__, ret, start_sector); + return AVB_IO_RESULT_ERROR_IO; + } + } + + io_cnt += residue; + buffer += residue; + start_offset += residue; + num_bytes -= residue; + continue; + } + + if (sectors) { + if (io_type == IO_READ) { + ret = mmc_read_and_flush(part, + part->info.start + + start_sector, + sectors, buffer); + } else { + ret = mmc_write(part, + part->info.start + + start_sector, + sectors, buffer); + } + + if (!ret) { + printf("%s: sector read error\n", __func__); + return AVB_IO_RESULT_ERROR_IO; + } + + io_cnt += ret * part->info.blksz; + buffer += ret * part->info.blksz; + start_offset += ret * part->info.blksz; + num_bytes -= ret * part->info.blksz; + } + } + + /* Set counter for read operation */ + if (io_type == IO_READ && out_num_read) + *out_num_read = io_cnt; + + return AVB_IO_RESULT_OK; +} + +/** + * ============================================================================ + * AVB 2.0 operations + * ============================================================================ + */ + +/** + * read_from_partition() - reads @num_bytes from @offset from partition + * identified by a string name + * + * @ops: contains AVB ops handlers + * @partition_name: partition name, NUL-terminated UTF-8 string + * @offset: offset from the beginning of partition + * @num_bytes: amount of bytes to read + * @buffer: destination buffer to store data + * @out_num_read: + * + * @return: + * AVB_IO_RESULT_OK, if partition was found and read operation succeed + * AVB_IO_RESULT_ERROR_IO, if i/o error occurred from the underlying i/o + * subsystem + * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, if there is no partition with + * the given name + */ +static AvbIOResult read_from_partition(AvbOps *ops, + const char *partition_name, + s64 offset_from_partition, + size_t num_bytes, + void *buffer, + size_t *out_num_read) +{ + return mmc_byte_io(ops, partition_name, offset_from_partition, + num_bytes, buffer, out_num_read, IO_READ); +} + +/** + * write_to_partition() - writes N bytes to a partition identified by a string + * name + * + * @ops: AvbOps, contains AVB ops handlers + * @partition_name: partition name + * @offset_from_partition: offset from the beginning of partition + * @num_bytes: amount of bytes to write + * @buf: data to write + * @out_num_read: + * + * @return: + * AVB_IO_RESULT_OK, if partition was found and read operation succeed + * AVB_IO_RESULT_ERROR_IO, if input/output error occurred + * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, if partition, specified in + * @partition_name was not found + */ +static AvbIOResult write_to_partition(AvbOps *ops, + const char *partition_name, + s64 offset_from_partition, + size_t num_bytes, + const void *buffer) +{ + return mmc_byte_io(ops, partition_name, offset_from_partition, + num_bytes, (void *)buffer, NULL, IO_WRITE); +} + +/** + * validate_vmbeta_public_key() - checks if the given public key used to sign + * the vbmeta partition is trusted + * + * @ops: AvbOps, contains AVB ops handlers + * @public_key_data: public key for verifying vbmeta partition signature + * @public_key_length: length of public key + * @public_key_metadata: + * @public_key_metadata_length: + * @out_key_is_trusted: + * + * @return: + * AVB_IO_RESULT_OK, if partition was found and read operation succeed + */ +static AvbIOResult validate_vbmeta_public_key(AvbOps *ops, + const u8 *public_key_data, + size_t public_key_length, + const u8 + *public_key_metadata, + size_t + public_key_metadata_length, + bool *out_key_is_trusted) +{ + if (!public_key_length || !public_key_data || !out_key_is_trusted) + return AVB_IO_RESULT_ERROR_IO; + + *out_key_is_trusted = false; + if (public_key_length != sizeof(avb_root_pub)) + return AVB_IO_RESULT_ERROR_IO; + + if (memcmp(avb_root_pub, public_key_data, public_key_length) == 0) + *out_key_is_trusted = true; + + return AVB_IO_RESULT_OK; +} + +#ifdef CONFIG_OPTEE_TA_AVB +static int get_open_session(struct AvbOpsData *ops_data) +{ + struct udevice *tee = NULL; + + while (!ops_data->tee) { + const struct tee_optee_ta_uuid uuid = TA_AVB_UUID; + struct tee_open_session_arg arg; + int rc; + + tee = tee_find_device(tee, NULL, NULL, NULL); + if (!tee) + return -ENODEV; + + memset(&arg, 0, sizeof(arg)); + tee_optee_ta_uuid_to_octets(arg.uuid, &uuid); + rc = tee_open_session(tee, &arg, 0, NULL); + if (!rc) { + ops_data->tee = tee; + ops_data->session = arg.session; + } + } + + return 0; +} + +static AvbIOResult invoke_func(struct AvbOpsData *ops_data, u32 func, + ulong num_param, struct tee_param *param) +{ + struct tee_invoke_arg arg; + + if (get_open_session(ops_data)) + return AVB_IO_RESULT_ERROR_IO; + + memset(&arg, 0, sizeof(arg)); + arg.func = func; + arg.session = ops_data->session; + + if (tee_invoke_func(ops_data->tee, &arg, num_param, param)) + return AVB_IO_RESULT_ERROR_IO; + switch (arg.ret) { + case TEE_SUCCESS: + return AVB_IO_RESULT_OK; + case TEE_ERROR_OUT_OF_MEMORY: + return AVB_IO_RESULT_ERROR_OOM; + case TEE_ERROR_STORAGE_NO_SPACE: + return AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE; + case TEE_ERROR_ITEM_NOT_FOUND: + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + case TEE_ERROR_TARGET_DEAD: + /* + * The TA has paniced, close the session to reload the TA + * for the next request. + */ + tee_close_session(ops_data->tee, ops_data->session); + ops_data->tee = NULL; + return AVB_IO_RESULT_ERROR_IO; + default: + return AVB_IO_RESULT_ERROR_IO; + } +} +#endif + +/** + * read_rollback_index() - gets the rollback index corresponding to the + * location of given by @out_rollback_index. + * + * @ops: contains AvbOps handlers + * @rollback_index_slot: + * @out_rollback_index: used to write a retrieved rollback index. + * + * @return + * AVB_IO_RESULT_OK, if the roolback index was retrieved + */ +static AvbIOResult read_rollback_index(AvbOps *ops, + size_t rollback_index_slot, + u64 *out_rollback_index) +{ +#ifndef CONFIG_OPTEE_TA_AVB + /* For now we always return 0 as the stored rollback index. */ + printf("%s not supported yet\n", __func__); + + if (out_rollback_index) + *out_rollback_index = 0; + + return AVB_IO_RESULT_OK; +#else + AvbIOResult rc; + struct tee_param param[2]; + + if (rollback_index_slot >= TA_AVB_MAX_ROLLBACK_LOCATIONS) + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = rollback_index_slot; + param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_READ_ROLLBACK_INDEX, + ARRAY_SIZE(param), param); + if (rc) + return rc; + + *out_rollback_index = (u64)param[1].u.value.a << 32 | + (u32)param[1].u.value.b; + return AVB_IO_RESULT_OK; +#endif +} + +/** + * write_rollback_index() - sets the rollback index corresponding to the + * location of given by @out_rollback_index. + * + * @ops: contains AvbOps handlers + * @rollback_index_slot: + * @rollback_index: rollback index to write. + * + * @return + * AVB_IO_RESULT_OK, if the roolback index was retrieved + */ +static AvbIOResult write_rollback_index(AvbOps *ops, + size_t rollback_index_slot, + u64 rollback_index) +{ +#ifndef CONFIG_OPTEE_TA_AVB + /* For now this is a no-op. */ + printf("%s not supported yet\n", __func__); + + return AVB_IO_RESULT_OK; +#else + struct tee_param param[2]; + + if (rollback_index_slot >= TA_AVB_MAX_ROLLBACK_LOCATIONS) + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = rollback_index_slot; + param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[1].u.value.a = (u32)(rollback_index >> 32); + param[1].u.value.b = (u32)rollback_index; + + return invoke_func(ops->user_data, TA_AVB_CMD_WRITE_ROLLBACK_INDEX, + ARRAY_SIZE(param), param); +#endif +} + +/** + * read_is_device_unlocked() - gets whether the device is unlocked + * + * @ops: contains AVB ops handlers + * @out_is_unlocked: device unlock state is stored here, true if unlocked, + * false otherwise + * + * @return: + * AVB_IO_RESULT_OK: state is retrieved successfully + * AVB_IO_RESULT_ERROR_IO: an error occurred + */ +static AvbIOResult read_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked) +{ +#ifndef CONFIG_OPTEE_TA_AVB + /* For now we always return that the device is unlocked. */ + + printf("%s not supported yet\n", __func__); + + *out_is_unlocked = true; + + return AVB_IO_RESULT_OK; +#else + AvbIOResult rc; + struct tee_param param = { .attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT }; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_READ_LOCK_STATE, 1, ¶m); + if (rc) + return rc; + *out_is_unlocked = !param.u.value.a; + return AVB_IO_RESULT_OK; +#endif +} + +/** + * get_unique_guid_for_partition() - gets the GUID for a partition identified + * by a string name + * + * @ops: contains AVB ops handlers + * @partition: partition name (NUL-terminated UTF-8 string) + * @guid_buf: buf, used to copy in GUID string. Example of value: + * 527c1c6d-6361-4593-8842-3c78fcd39219 + * @guid_buf_size: @guid_buf buffer size + * + * @return: + * AVB_IO_RESULT_OK, on success (GUID found) + * AVB_IO_RESULT_ERROR_IO, if incorrect buffer size (@guid_buf_size) was + * provided + * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, if partition was not found + */ +static AvbIOResult get_unique_guid_for_partition(AvbOps *ops, + const char *partition, + char *guid_buf, + size_t guid_buf_size) +{ + struct mmc_part *part; + size_t uuid_size; + + part = get_partition(ops, partition); + if (!part) + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + + uuid_size = sizeof(part->info.uuid); + if (uuid_size > guid_buf_size) + return AVB_IO_RESULT_ERROR_IO; + + memcpy(guid_buf, part->info.uuid, uuid_size); + guid_buf[uuid_size - 1] = 0; + + return AVB_IO_RESULT_OK; +} + +/** + * get_size_of_partition() - gets the size of a partition identified + * by a string name + * + * @ops: contains AVB ops handlers + * @partition: partition name (NUL-terminated UTF-8 string) + * @out_size_num_bytes: returns the value of a partition size + * + * @return: + * AVB_IO_RESULT_OK, on success (GUID found) + * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE, out_size_num_bytes is NULL + * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, if partition was not found + */ +static AvbIOResult get_size_of_partition(AvbOps *ops, + const char *partition, + u64 *out_size_num_bytes) +{ + struct mmc_part *part; + + if (!out_size_num_bytes) + return AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE; + + part = get_partition(ops, partition); + if (!part) + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + + *out_size_num_bytes = part->info.blksz * part->info.size; + + return AVB_IO_RESULT_OK; +} + +#ifdef CONFIG_OPTEE_TA_AVB +static AvbIOResult read_persistent_value(AvbOps *ops, + const char *name, + size_t buffer_size, + u8 *out_buffer, + size_t *out_num_bytes_read) +{ + AvbIOResult rc; + struct tee_shm *shm_name; + struct tee_shm *shm_buf; + struct tee_param param[2]; + struct udevice *tee; + size_t name_size = strlen(name) + 1; + + if (get_open_session(ops->user_data)) + return AVB_IO_RESULT_ERROR_IO; + + tee = ((struct AvbOpsData *)ops->user_data)->tee; + + rc = tee_shm_alloc(tee, name_size, + TEE_SHM_ALLOC, &shm_name); + if (rc) + return AVB_IO_RESULT_ERROR_OOM; + + rc = tee_shm_alloc(tee, buffer_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) { + rc = AVB_IO_RESULT_ERROR_OOM; + goto free_name; + } + + memcpy(shm_name->addr, name, name_size); + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = shm_name; + param[0].u.memref.size = name_size; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[1].u.memref.shm = shm_buf; + param[1].u.memref.size = buffer_size; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_READ_PERSIST_VALUE, + 2, param); + if (rc) + goto out; + + if (param[1].u.memref.size > buffer_size) { + rc = AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + goto out; + } + + *out_num_bytes_read = param[1].u.memref.size; + + memcpy(out_buffer, shm_buf->addr, *out_num_bytes_read); + +out: + tee_shm_free(shm_buf); +free_name: + tee_shm_free(shm_name); + + return rc; +} + +static AvbIOResult write_persistent_value(AvbOps *ops, + const char *name, + size_t value_size, + const u8 *value) +{ + AvbIOResult rc; + struct tee_shm *shm_name; + struct tee_shm *shm_buf; + struct tee_param param[2]; + struct udevice *tee; + size_t name_size = strlen(name) + 1; + + if (get_open_session(ops->user_data)) + return AVB_IO_RESULT_ERROR_IO; + + tee = ((struct AvbOpsData *)ops->user_data)->tee; + + if (!value_size) + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + + rc = tee_shm_alloc(tee, name_size, + TEE_SHM_ALLOC, &shm_name); + if (rc) + return AVB_IO_RESULT_ERROR_OOM; + + rc = tee_shm_alloc(tee, value_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) { + rc = AVB_IO_RESULT_ERROR_OOM; + goto free_name; + } + + memcpy(shm_name->addr, name, name_size); + memcpy(shm_buf->addr, value, value_size); + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = shm_name; + param[0].u.memref.size = name_size; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[1].u.memref.shm = shm_buf; + param[1].u.memref.size = value_size; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_WRITE_PERSIST_VALUE, + 2, param); + if (rc) + goto out; + +out: + tee_shm_free(shm_buf); +free_name: + tee_shm_free(shm_name); + + return rc; +} +#endif + +/** + * ============================================================================ + * AVB2.0 AvbOps alloc/initialisation/free + * ============================================================================ + */ +AvbOps *avb_ops_alloc(int boot_device) +{ + struct AvbOpsData *ops_data; + + ops_data = avb_calloc(sizeof(struct AvbOpsData)); + if (!ops_data) + return NULL; + + ops_data->ops.user_data = ops_data; + + ops_data->ops.read_from_partition = read_from_partition; + ops_data->ops.write_to_partition = write_to_partition; + ops_data->ops.validate_vbmeta_public_key = validate_vbmeta_public_key; + ops_data->ops.read_rollback_index = read_rollback_index; + ops_data->ops.write_rollback_index = write_rollback_index; + ops_data->ops.read_is_device_unlocked = read_is_device_unlocked; + ops_data->ops.get_unique_guid_for_partition = + get_unique_guid_for_partition; +#ifdef CONFIG_OPTEE_TA_AVB + ops_data->ops.write_persistent_value = write_persistent_value; + ops_data->ops.read_persistent_value = read_persistent_value; +#endif + ops_data->ops.get_size_of_partition = get_size_of_partition; + ops_data->mmc_dev = boot_device; + + return &ops_data->ops; +} + +void avb_ops_free(AvbOps *ops) +{ + struct AvbOpsData *ops_data; + + if (!ops) + return; + + ops_data = ops->user_data; + + if (ops_data) { +#ifdef CONFIG_OPTEE_TA_AVB + if (ops_data->tee) + tee_close_session(ops_data->tee, ops_data->session); +#endif + avb_free(ops_data); + } +} diff --git a/roms/u-boot/common/bedbug.c b/roms/u-boot/common/bedbug.c new file mode 100644 index 000000000..18a35ca23 --- /dev/null +++ b/roms/u-boot/common/bedbug.c @@ -0,0 +1,1254 @@ +/* $Id$ */ + +#include <common.h> +#include <asm/ptrace.h> + +#include <linux/ctype.h> +#include <bedbug/bedbug.h> +#include <bedbug/ppc.h> +#include <bedbug/regs.h> +#include <bedbug/tables.h> + +#define Elf32_Word unsigned long + +/* USE_SOURCE_CODE enables some symbolic debugging functions of this + code. This is only useful if the program will have access to the + source code for the binary being examined. +*/ + +/* #define USE_SOURCE_CODE 1 */ + +#ifdef USE_SOURCE_CODE +extern int line_info_from_addr __P ((Elf32_Word, char *, char *, int *)); +extern struct symreflist *symByAddr; +extern char *symbol_name_from_addr __P ((Elf32_Word, int, int *)); +#endif /* USE_SOURCE_CODE */ + +int print_operands __P ((struct ppc_ctx *)); +int get_operand_value __P ((struct opcode *, unsigned long, + enum OP_FIELD, unsigned long *)); +struct opcode *find_opcode __P ((unsigned long)); +struct opcode *find_opcode_by_name __P ((char *)); +char *spr_name __P ((int)); +int spr_value __P ((char *)); +char *tbr_name __P ((int)); +int tbr_value __P ((char *)); +int parse_operand __P ((unsigned long, struct opcode *, + struct operand *, char *, int *)); +int get_word __P ((char **, char *)); +long read_number __P ((char *)); +int downstring __P ((char *)); + + +/*====================================================================== + * Entry point for the PPC disassembler. + * + * Arguments: + * memaddr The address to start disassembling from. + * + * virtual If this value is non-zero, then this will be + * used as the base address for the output and + * symbol lookups. If this value is zero then + * memaddr is used as the absolute address. + * + * num_instr The number of instructions to disassemble. Since + * each instruction is 32 bits long, this can be + * computed if you know the total size of the region. + * + * pfunc The address of a function that is called to print + * each line of output. The function should take a + * single character pointer as its parameters a la puts. + * + * flags Sets options for the output. This is a + * bitwise-inclusive-OR of the following + * values. Note that only one of the radix + * options may be set. + * + * F_RADOCTAL - output radix is unsigned base 8. + * F_RADUDECIMAL - output radix is unsigned base 10. + * F_RADSDECIMAL - output radix is signed base 10. + * F_RADHEX - output radix is unsigned base 16. + * F_SIMPLE - use simplified mnemonics. + * F_SYMBOL - lookup symbols for addresses. + * F_INSTR - output raw instruction. + * F_LINENO - show line # info if available. + * + * Returns true if the area was successfully disassembled or false if + * a problem was encountered with accessing the memory. + */ + +int disppc (unsigned char *memaddr, unsigned char *virtual, int num_instr, + int (*pfunc) (const char *), unsigned long flags) +{ + int i; + struct ppc_ctx ctx; + +#ifdef USE_SOURCE_CODE + int line_no = 0; + int last_line_no = 0; + char funcname[128] = { 0 }; + char filename[256] = { 0 }; + char last_funcname[128] = { 0 }; + int symoffset; + char *symname; + char *cursym = (char *) 0; +#endif /* USE_SOURCE_CODE */ + /*------------------------------------------------------------*/ + + ctx.flags = flags; + ctx.virtual = virtual; + + /* Figure out the output radix before we go any further */ + + if (ctx.flags & F_RADOCTAL) { + /* Unsigned octal output */ + strcpy (ctx.radix_fmt, "O%o"); + } else if (ctx.flags & F_RADUDECIMAL) { + /* Unsigned decimal output */ + strcpy (ctx.radix_fmt, "%u"); + } else if (ctx.flags & F_RADSDECIMAL) { + /* Signed decimal output */ + strcpy (ctx.radix_fmt, "%d"); + } else { + /* Unsigned hex output */ + strcpy (ctx.radix_fmt, "0x%x"); + } + + if (ctx.virtual == 0) { + ctx.virtual = memaddr; + } +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_SYMBOL) { + if (symByAddr == 0) /* no symbols loaded */ + ctx.flags &= ~F_SYMBOL; + else { + cursym = (char *) 0; + symoffset = 0; + } + } +#endif /* USE_SOURCE_CODE */ + + /* format each line as "XXXXXXXX: <symbol> IIIIIIII disassembly" where, + XXXXXXXX is the memory address in hex, + <symbol> is the symbolic location if F_SYMBOL is set. + IIIIIIII is the raw machine code in hex if F_INSTR is set, + and disassembly is the disassembled machine code with numbers + formatted according to the 'radix' parameter */ + + for (i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4) { +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_LINENO) { + if ((line_info_from_addr ((Elf32_Word) ctx.virtual, + filename, funcname, &line_no) == true) && + ((line_no != last_line_no) || + (strcmp (last_funcname, funcname) != 0))) { + print_source_line (filename, funcname, line_no, pfunc); + } + last_line_no = line_no; + strcpy (last_funcname, funcname); + } +#endif /* USE_SOURCE_CODE */ + + sprintf (ctx.data, "%08lx: ", (unsigned long) ctx.virtual); + ctx.datalen = 10; + +#ifdef USE_SOURCE_CODE + if (ctx.flags & F_SYMBOL) { + if ((symname = + symbol_name_from_addr((Elf32_Word) ctx.virtual, + true, 0)) != 0) { + cursym = symname; + symoffset = 0; + } else { + if ((cursym == 0) && + ((symname = + symbol_name_from_addr((Elf32_Word) ctx.virtual, + false, &symoffset)) != 0)) { + cursym = symname; + } else { + symoffset += 4; + } + } + + if (cursym != 0) { + sprintf (&ctx.data[ctx.datalen], "<%s+", cursym); + ctx.datalen = strlen (ctx.data); + sprintf (&ctx.data[ctx.datalen], ctx.radix_fmt, symoffset); + strcat (ctx.data, ">"); + ctx.datalen = strlen (ctx.data); + } + } +#endif /* USE_SOURCE_CODE */ + + ctx.instr = INSTRUCTION (memaddr); + + if (ctx.flags & F_INSTR) { + /* Find the opcode structure for this opcode. If one is not found + then it must be an illegal instruction */ + sprintf (&ctx.data[ctx.datalen], + " %02lx %02lx %02lx %02lx ", + ((ctx.instr >> 24) & 0xff), + ((ctx.instr >> 16) & 0xff), ((ctx.instr >> 8) & 0xff), + (ctx.instr & 0xff)); + ctx.datalen += 18; + } else { + strcat (ctx.data, " "); + ctx.datalen += 3; + } + + if ((ctx.op = find_opcode (ctx.instr)) == 0) { + /* Illegal Opcode */ + sprintf (&ctx.data[ctx.datalen], " .long 0x%08lx", + ctx.instr); + ctx.datalen += 24; + (*pfunc) (ctx.data); + continue; + } + + if (((ctx.flags & F_SIMPLE) == 0) || + (ctx.op->hfunc == 0) || + ((*ctx.op->hfunc) (&ctx) == false)) { + sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name); + ctx.datalen += 8; + print_operands (&ctx); + } + + (*pfunc) (ctx.data); + } + + return true; +} /* disppc */ + + + +/*====================================================================== + * Called by the disassembler to print the operands for an instruction. + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * always returns 0. + */ + +int print_operands (struct ppc_ctx *ctx) +{ + int open_parens = 0; + int field; + unsigned long operand; + struct operand *opr; + +#ifdef USE_SOURCE_CODE + char *symname; + int offset; +#endif /* USE_SOURCE_CODE */ + /*------------------------------------------------------------*/ + + /* Walk through the operands and list each in order */ + for (field = 0; ctx->op->fields[field] != 0; ++field) { + if (ctx->op->fields[field] > n_operands) { + continue; /* bad operand ?! */ + } + + opr = &operands[ctx->op->fields[field] - 1]; + + if (opr->hint & OH_SILENT) { + continue; + } + + if ((field > 0) && !open_parens) { + strcat (ctx->data, ","); + ctx->datalen++; + } + + operand = (ctx->instr >> opr->shift) & ((1 << opr->bits) - 1); + + if (opr->hint & OH_ADDR) { + if ((operand & (1 << (opr->bits - 1))) != 0) { + operand = operand - (1 << opr->bits); + } + + if (ctx->op->hint & H_RELATIVE) + operand = (operand << 2) + (unsigned long) ctx->virtual; + else + operand = (operand << 2); + + + sprintf (&ctx->data[ctx->datalen], "0x%lx", operand); + ctx->datalen = strlen (ctx->data); + +#ifdef USE_SOURCE_CODE + if ((ctx->flags & F_SYMBOL) && + ((symname = + symbol_name_from_addr (operand, 0, &offset)) != 0)) { + sprintf (&ctx->data[ctx->datalen], " <%s", symname); + if (offset != 0) { + strcat (ctx->data, "+"); + ctx->datalen = strlen (ctx->data); + sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, + offset); + } + strcat (ctx->data, ">"); + } +#endif /* USE_SOURCE_CODE */ + } + + else if (opr->hint & OH_REG) { + if ((operand == 0) && + (opr->field == O_rA) && (ctx->op->hint & H_RA0_IS_0)) { + strcat (ctx->data, "0"); + } else { + sprintf (&ctx->data[ctx->datalen], "r%d", (short) operand); + } + + if (open_parens) { + strcat (ctx->data, ")"); + open_parens--; + } + } + + else if (opr->hint & OH_SPR) { + strcat (ctx->data, spr_name (operand)); + } + + else if (opr->hint & OH_TBR) { + strcat (ctx->data, tbr_name (operand)); + } + + else if (opr->hint & OH_LITERAL) { + switch (opr->field) { + case O_cr2: + strcat (ctx->data, "cr2"); + ctx->datalen += 3; + break; + + default: + break; + } + } + + else { + sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, + (unsigned short) operand); + + if (open_parens) { + strcat (ctx->data, ")"); + open_parens--; + } + + else if (opr->hint & OH_OFFSET) { + strcat (ctx->data, "("); + open_parens++; + } + } + + ctx->datalen = strlen (ctx->data); + } + + return 0; +} /* print_operands */ + + + +/*====================================================================== + * Called to get the value of an arbitrary operand with in an instruction. + * + * Arguments: + * op The pointer to the opcode structure to which + * the operands belong. + * + * instr The instruction (32 bits) containing the opcode + * and the operands to print. By the time that + * this routine is called the operand has already + * been added to the output. + * + * field The field (operand) to get the value of. + * + * value The address of an unsigned long to be filled in + * with the value of the operand if it is found. This + * will only be filled in if the function returns + * true. This may be passed as 0 if the value is + * not required. + * + * Returns true if the operand was found or false if it was not. + */ + +int get_operand_value (struct opcode *op, unsigned long instr, + enum OP_FIELD field, unsigned long *value) +{ + int i; + struct operand *opr; + + /*------------------------------------------------------------*/ + + if (field > n_operands) { + return false; /* bad operand ?! */ + } + + /* Walk through the operands and list each in order */ + for (i = 0; op->fields[i] != 0; ++i) { + if (op->fields[i] != field) { + continue; + } + + opr = &operands[op->fields[i] - 1]; + + if (value) { + *value = (instr >> opr->shift) & ((1 << opr->bits) - 1); + } + return true; + } + + return false; +} /* operand_value */ + + + +/*====================================================================== + * Called by the disassembler to match an opcode value to an opcode structure. + * + * Arguments: + * instr The instruction (32 bits) to match. This value + * may contain operand values as well as the opcode + * since they will be masked out anyway for this + * search. + * + * Returns the address of an opcode struct (from the opcode table) if the + * operand successfully matched an entry, or 0 if no match was found. + */ + +struct opcode *find_opcode (unsigned long instr) +{ + struct opcode *ptr; + int top = 0; + int bottom = n_opcodes - 1; + int idx; + + /*------------------------------------------------------------*/ + + while (top <= bottom) { + idx = (top + bottom) >> 1; + ptr = &opcodes[idx]; + + if ((instr & ptr->mask) < ptr->opcode) { + bottom = idx - 1; + } else if ((instr & ptr->mask) > ptr->opcode) { + top = idx + 1; + } else { + return ptr; + } + } + + return (struct opcode *) 0; +} /* find_opcode */ + + + +/*====================================================================== + * Called by the assembler to match an opcode name to an opcode structure. + * + * Arguments: + * name The text name of the opcode, e.g. "b", "mtspr", etc. + * + * The opcodes are sorted numerically by their instruction binary code + * so a search for the name cannot use the binary search used by the + * other find routine. + * + * Returns the address of an opcode struct (from the opcode table) if the + * name successfully matched an entry, or 0 if no match was found. + */ + +struct opcode *find_opcode_by_name (char *name) +{ + int idx; + + /*------------------------------------------------------------*/ + + downstring (name); + + for (idx = 0; idx < n_opcodes; ++idx) { + if (!strcmp (name, opcodes[idx].name)) + return &opcodes[idx]; + } + + return (struct opcode *) 0; +} /* find_opcode_by_name */ + + + +/*====================================================================== + * Convert the 'spr' operand from its numeric value to its symbolic name. + * + * Arguments: + * value The value of the 'spr' operand. This value should + * be unmodified from its encoding in the instruction. + * the split-field computations will be performed + * here before the switch. + * + * Returns the address of a character array containing the name of the + * special purpose register defined by the 'value' parameter, or the + * address of a character array containing "???" if no match was found. + */ + +char *spr_name (int value) +{ + unsigned short spr; + static char other[10]; + int i; + + /*------------------------------------------------------------*/ + + /* spr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + spr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); + + for (i = 0; i < n_sprs; ++i) { + if (spr == spr_map[i].spr_val) + return spr_map[i].spr_name; + } + + sprintf (other, "%d", spr); + return other; +} /* spr_name */ + + + +/*====================================================================== + * Convert the 'spr' operand from its symbolic name to its numeric value + * + * Arguments: + * name The symbolic name of the 'spr' operand. The + * split-field encoding will be done by this routine. + * NOTE: name can be a number. + * + * Returns the numeric value for the spr appropriate for encoding a machine + * instruction. Returns 0 if unable to find the SPR. + */ + +int spr_value (char *name) +{ + struct spr_info *sprp; + int spr; + int i; + + /*------------------------------------------------------------*/ + + if (!name || !*name) + return 0; + + if (isdigit ((int) name[0])) { + i = htonl (read_number (name)); + spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); + return spr; + } + + downstring (name); + + for (i = 0; i < n_sprs; ++i) { + sprp = &spr_map[i]; + + if (strcmp (name, sprp->spr_name) == 0) { + /* spr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + i = htonl (sprp->spr_val); + spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); + + return spr; + } + } + + return 0; +} /* spr_value */ + + + +/*====================================================================== + * Convert the 'tbr' operand from its numeric value to its symbolic name. + * + * Arguments: + * value The value of the 'tbr' operand. This value should + * be unmodified from its encoding in the instruction. + * the split-field computations will be performed + * here before the switch. + * + * Returns the address of a character array containing the name of the + * time base register defined by the 'value' parameter, or the address + * of a character array containing "???" if no match was found. + */ + +char *tbr_name (int value) +{ + unsigned short tbr; + + /*------------------------------------------------------------*/ + + /* tbr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + tbr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); + + if (tbr == 268) + return "TBL"; + + else if (tbr == 269) + return "TBU"; + + + return "???"; +} /* tbr_name */ + + + +/*====================================================================== + * Convert the 'tbr' operand from its symbolic name to its numeric value. + * + * Arguments: + * name The symbolic name of the 'tbr' operand. The + * split-field encoding will be done by this routine. + * + * Returns the numeric value for the spr appropriate for encoding a machine + * instruction. Returns 0 if unable to find the TBR. + */ + +int tbr_value (char *name) +{ + int tbr; + int val; + + /*------------------------------------------------------------*/ + + if (!name || !*name) + return 0; + + downstring (name); + + if (isdigit ((int) name[0])) { + val = read_number (name); + + if (val != 268 && val != 269) + return 0; + } else if (strcmp (name, "tbl") == 0) + val = 268; + else if (strcmp (name, "tbu") == 0) + val = 269; + else + return 0; + + /* tbr is a 10 bit field whose interpretation has the high and low + five-bit fields reversed from their encoding in the operand */ + + val = htonl (val); + tbr = ((val >> 5) & 0x1f) | ((val & 0x1f) << 5); + return tbr; +} /* tbr_name */ + + + +/*====================================================================== + * The next several functions (handle_xxx) are the routines that handle + * disassembling the opcodes with simplified mnemonics. + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * Returns true if the simpler form was printed or false if it was not. + */ + +int handle_bc (struct ppc_ctx *ctx) +{ + unsigned long bo; + unsigned long bi; + static struct opcode blt = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, + 0, "blt", H_RELATIVE + }; + static struct opcode bne = + { B_OPCODE (16, 0, 0), B_MASK, {O_cr2, O_BD, 0}, + 0, "bne", H_RELATIVE + }; + static struct opcode bdnz = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, + 0, "bdnz", H_RELATIVE + }; + + /*------------------------------------------------------------*/ + + if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false) + return false; + + if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false) + return false; + + if ((bo == 12) && (bi == 0)) { + ctx->op = &blt; + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } else if ((bo == 4) && (bi == 10)) { + ctx->op = =⃥ + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } else if ((bo == 16) && (bi == 0)) { + ctx->op = &bdnz; + sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); + ctx->datalen += 8; + print_operands (ctx); + return true; + } + + return false; +} /* handle_blt */ + + + +/*====================================================================== + * Outputs source line information for the disassembler. This should + * be modified in the future to lookup the actual line of source code + * from the file, but for now this will do. + * + * Arguments: + * filename The address of a character array containing the + * absolute path and file name of the source file. + * + * funcname The address of a character array containing the + * name of the function (not C++ demangled (yet)) + * to which this code belongs. + * + * line_no An integer specifying the source line number that + * generated this code. + * + * pfunc The address of a function to call to print the output. + * + * + * Returns true if it was able to output the line info, or false if it was + * not. + */ + +int print_source_line (char *filename, char *funcname, + int line_no, int (*pfunc) (const char *)) +{ + char out_buf[256]; + + /*------------------------------------------------------------*/ + + (*pfunc) (""); /* output a newline */ + sprintf (out_buf, "%s %s(): line %d", filename, funcname, line_no); + (*pfunc) (out_buf); + + return true; +} /* print_source_line */ + + + +/*====================================================================== + * Entry point for the PPC assembler. + * + * Arguments: + * asm_buf An array of characters containing the assembly opcode + * and operands to convert to a POWERPC machine + * instruction. + * + * Returns the machine instruction or zero. + */ + +unsigned long asmppc (unsigned long memaddr, char *asm_buf, int *err) +{ + struct opcode *opc; + struct operand *oper[MAX_OPERANDS]; + unsigned long instr; + unsigned long param; + char *ptr = asm_buf; + char scratch[20]; + int i; + int w_operands = 0; /* wanted # of operands */ + int n_operands = 0; /* # of operands read */ + int asm_debug = 0; + + /*------------------------------------------------------------*/ + + if (err) + *err = 0; + + if (get_word (&ptr, scratch) == 0) + return 0; + + /* Lookup the opcode structure based on the opcode name */ + if ((opc = find_opcode_by_name (scratch)) == (struct opcode *) 0) { + if (err) + *err = E_ASM_BAD_OPCODE; + return 0; + } + + if (asm_debug) { + printf ("asmppc: Opcode = \"%s\"\n", opc->name); + } + + for (i = 0; i < 8; ++i) { + if (opc->fields[i] == 0) + break; + ++w_operands; + } + + if (asm_debug) { + printf ("asmppc: Expecting %d operands\n", w_operands); + } + + instr = opc->opcode; + + /* read each operand */ + while (n_operands < w_operands) { + + oper[n_operands] = &operands[opc->fields[n_operands] - 1]; + + if (oper[n_operands]->hint & OH_SILENT) { + /* Skip silent operands, they are covered in opc->opcode */ + + if (asm_debug) { + printf ("asmppc: Operand %d \"%s\" SILENT\n", n_operands, + oper[n_operands]->name); + } + + ++n_operands; + continue; + } + + if (get_word (&ptr, scratch) == 0) + break; + + if (asm_debug) { + printf ("asmppc: Operand %d \"%s\" : \"%s\"\n", n_operands, + oper[n_operands]->name, scratch); + } + + if ((param = parse_operand (memaddr, opc, oper[n_operands], + scratch, err)) == -1) + return 0; + + instr |= param; + ++n_operands; + } + + if (n_operands < w_operands) { + if (err) + *err = E_ASM_NUM_OPERANDS; + return 0; + } + + if (asm_debug) { + printf ("asmppc: Instruction = 0x%08lx\n", instr); + } + + return instr; +} /* asmppc */ + + + +/*====================================================================== + * Called by the assembler to interpret a single operand + * + * Arguments: + * ctx A pointer to the disassembler context record. + * + * Returns 0 if the operand is ok, or -1 if it is bad. + */ + +int parse_operand (unsigned long memaddr, struct opcode *opc, + struct operand *oper, char *txt, int *err) +{ + long data; + long mask; + int is_neg = 0; + + /*------------------------------------------------------------*/ + + mask = (1 << oper->bits) - 1; + + if (oper->hint & OH_ADDR) { + data = read_number (txt); + + if (opc->hint & H_RELATIVE) + data = data - memaddr; + + if (data < 0) + is_neg = 1; + + data >>= 2; + data &= (mask >> 1); + + if (is_neg) + data |= 1 << (oper->bits - 1); + } + + else if (oper->hint & OH_REG) { + if (txt[0] == 'r' || txt[0] == 'R') + txt++; + else if (txt[0] == '%' && (txt[1] == 'r' || txt[1] == 'R')) + txt += 2; + + data = read_number (txt); + if (data > 31) { + if (err) + *err = E_ASM_BAD_REGISTER; + return -1; + } + + data = htonl (data); + } + + else if (oper->hint & OH_SPR) { + if ((data = spr_value (txt)) == 0) { + if (err) + *err = E_ASM_BAD_SPR; + return -1; + } + } + + else if (oper->hint & OH_TBR) { + if ((data = tbr_value (txt)) == 0) { + if (err) + *err = E_ASM_BAD_TBR; + return -1; + } + } + + else { + data = htonl (read_number (txt)); + } + + return (data & mask) << oper->shift; +} /* parse_operand */ + + +char *asm_error_str (int err) +{ + switch (err) { + case E_ASM_BAD_OPCODE: + return "Bad opcode"; + case E_ASM_NUM_OPERANDS: + return "Bad number of operands"; + case E_ASM_BAD_REGISTER: + return "Bad register number"; + case E_ASM_BAD_SPR: + return "Bad SPR name or number"; + case E_ASM_BAD_TBR: + return "Bad TBR name or number"; + } + + return ""; +} /* asm_error_str */ + + + +/*====================================================================== + * Copy a word from one buffer to another, ignores leading white spaces. + * + * Arguments: + * src The address of a character pointer to the + * source buffer. + * dest A pointer to a character buffer to write the word + * into. + * + * Returns the number of non-white space characters copied, or zero. + */ + +int get_word (char **src, char *dest) +{ + char *ptr = *src; + int nchars = 0; + + /*------------------------------------------------------------*/ + + /* Eat white spaces */ + while (*ptr && isblank (*ptr)) + ptr++; + + if (*ptr == 0) { + *src = ptr; + return 0; + } + + /* Find the text of the word */ + while (*ptr && !isblank (*ptr) && (*ptr != ',')) + dest[nchars++] = *ptr++; + ptr = (*ptr == ',') ? ptr + 1 : ptr; + dest[nchars] = 0; + + *src = ptr; + return nchars; +} /* get_word */ + + + +/*====================================================================== + * Convert a numeric string to a number, be aware of base notations. + * + * Arguments: + * txt The numeric string. + * + * Returns the converted numeric value. + */ + +long read_number (char *txt) +{ + long val; + int is_neg = 0; + + /*------------------------------------------------------------*/ + + if (txt == 0 || *txt == 0) + return 0; + + if (*txt == '-') { + is_neg = 1; + ++txt; + } + + if (txt[0] == '0' && (txt[1] == 'x' || txt[1] == 'X')) /* hex */ + val = simple_strtoul (&txt[2], NULL, 16); + else /* decimal */ + val = simple_strtoul (txt, NULL, 10); + + if (is_neg) + val = -val; + + return val; +} /* read_number */ + + +int downstring (char *s) +{ + if (!s || !*s) + return 0; + + while (*s) { + if (isupper (*s)) + *s = tolower (*s); + s++; + } + + return 0; +} /* downstring */ + + + +/*====================================================================== + * Examines the instruction at the current address and determines the + * next address to be executed. This will take into account branches + * of different types so that a "step" and "next" operations can be + * supported. + * + * Arguments: + * nextaddr The address (to be filled in) of the next + * instruction to execute. This will only be a valid + * address if true is returned. + * + * step_over A flag indicating how to compute addresses for + * branch statements: + * true = Step over the branch (next) + * false = step into the branch (step) + * + * Returns true if it was able to compute the address. Returns false if + * it has a problem reading the current instruction or one of the registers. + */ + +int find_next_address (unsigned char *nextaddr, int step_over, + struct pt_regs *regs) +{ + unsigned long pc; /* SRR0 register from PPC */ + unsigned long ctr; /* CTR register from PPC */ + unsigned long cr; /* CR register from PPC */ + unsigned long lr; /* LR register from PPC */ + unsigned long instr; /* instruction at SRR0 */ + unsigned long next; /* computed instruction for 'next' */ + unsigned long step; /* computed instruction for 'step' */ + unsigned long addr = 0; /* target address operand */ + unsigned long aa = 0; /* AA operand */ + unsigned long lk = 0; /* LK operand */ + unsigned long bo = 0; /* BO operand */ + unsigned long bi = 0; /* BI operand */ + struct opcode *op = 0; /* opcode structure for 'instr' */ + int ctr_ok = 0; + int cond_ok = 0; + int conditional = 0; + int branch = 0; + + /*------------------------------------------------------------*/ + + if (nextaddr == 0 || regs == 0) { + printf ("find_next_address: bad args"); + return false; + } + + pc = regs->nip & 0xfffffffc; + instr = INSTRUCTION (pc); + + if ((op = find_opcode (instr)) == (struct opcode *) 0) { + printf ("find_next_address: can't parse opcode 0x%lx", instr); + return false; + } + + ctr = regs->ctr; + cr = regs->ccr; + lr = regs->link; + + switch (op->opcode) { + case B_OPCODE (16, 0, 0): /* bc */ + case B_OPCODE (16, 0, 1): /* bcl */ + case B_OPCODE (16, 1, 0): /* bca */ + case B_OPCODE (16, 1, 1): /* bcla */ + if (!get_operand_value (op, instr, O_BD, &addr) || + !get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_AA, &aa) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + if ((addr & (1 << 13)) != 0) + addr = addr - (1 << 14); + addr <<= 2; + conditional = 1; + branch = 1; + break; + + case I_OPCODE (18, 0, 0): /* b */ + case I_OPCODE (18, 0, 1): /* bl */ + case I_OPCODE (18, 1, 0): /* ba */ + case I_OPCODE (18, 1, 1): /* bla */ + if (!get_operand_value (op, instr, O_LI, &addr) || + !get_operand_value (op, instr, O_AA, &aa) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + if ((addr & (1 << 23)) != 0) + addr = addr - (1 << 24); + addr <<= 2; + conditional = 0; + branch = 1; + break; + + case XL_OPCODE (19, 528, 0): /* bcctr */ + case XL_OPCODE (19, 528, 1): /* bcctrl */ + if (!get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + addr = ctr; + aa = 1; + conditional = 1; + branch = 1; + break; + + case XL_OPCODE (19, 16, 0): /* bclr */ + case XL_OPCODE (19, 16, 1): /* bclrl */ + if (!get_operand_value (op, instr, O_BO, &bo) || + !get_operand_value (op, instr, O_BI, &bi) || + !get_operand_value (op, instr, O_LK, &lk)) + return false; + + addr = lr; + aa = 1; + conditional = 1; + branch = 1; + break; + + default: + conditional = 0; + branch = 0; + break; + } + + if (conditional) { + switch ((bo & 0x1e) >> 1) { + case 0: /* 0000y */ + if (--ctr != 0) + ctr_ok = 1; + + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 1: /* 0001y */ + if (--ctr == 0) + ctr_ok = 1; + + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 2: /* 001zy */ + ctr_ok = 1; + cond_ok = !(cr & (1 << (31 - bi))); + break; + + case 4: /* 0100y */ + if (--ctr != 0) + ctr_ok = 1; + + cond_ok = cr & (1 << (31 - bi)); + break; + + case 5: /* 0101y */ + if (--ctr == 0) + ctr_ok = 1; + + cond_ok = cr & (1 << (31 - bi)); + break; + + case 6: /* 011zy */ + ctr_ok = 1; + cond_ok = cr & (1 << (31 - bi)); + break; + + case 8: /* 1z00y */ + if (--ctr != 0) + ctr_ok = cond_ok = 1; + break; + + case 9: /* 1z01y */ + if (--ctr == 0) + ctr_ok = cond_ok = 1; + break; + + case 10: /* 1z1zz */ + ctr_ok = cond_ok = 1; + break; + } + } + + if (branch && (!conditional || (ctr_ok && cond_ok))) { + if (aa) + step = addr; + else + step = addr + pc; + + if (lk) + next = pc + 4; + else + next = step; + } else { + step = next = pc + 4; + } + + if (step_over == true) + *(unsigned long *) nextaddr = next; + else + *(unsigned long *) nextaddr = step; + + return true; +} /* find_next_address */ + + +/* + * Copyright (c) 2000 William L. Pitts and W. Gerald Hicks + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ diff --git a/roms/u-boot/common/bloblist.c b/roms/u-boot/common/bloblist.c new file mode 100644 index 000000000..eab63e9ca --- /dev/null +++ b/roms/u-boot/common/bloblist.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <bloblist.h> +#include <log.h> +#include <mapmem.h> +#include <spl.h> +#include <asm/global_data.h> +#include <u-boot/crc.h> + +/* + * A bloblist is a single contiguous chunk of memory with a header + * (struct bloblist_hdr) and a number of blobs in it. + * + * Each blob starts on a BLOBLIST_ALIGN boundary relative to the start of the + * bloblist and consists of a struct bloblist_rec, some padding to the required + * alignment for the blog and then the actual data. The padding ensures that the + * start address of the data in each blob is aligned as required. Note that + * each blob's *data* is aligned to BLOBLIST_ALIGN regardless of the alignment + * of the bloblist itself or the blob header. + * + * So far, only BLOBLIST_ALIGN alignment is supported. + */ + +DECLARE_GLOBAL_DATA_PTR; + +static const char *const tag_name[] = { + [BLOBLISTT_NONE] = "(none)", + [BLOBLISTT_EC_HOSTEVENT] = "EC host event", + [BLOBLISTT_SPL_HANDOFF] = "SPL hand-off", + [BLOBLISTT_VBOOT_CTX] = "Chrome OS vboot context", + [BLOBLISTT_VBOOT_HANDOFF] = "Chrome OS vboot hand-off", + [BLOBLISTT_ACPI_GNVS] = "ACPI GNVS", + [BLOBLISTT_INTEL_VBT] = "Intel Video-BIOS table", + [BLOBLISTT_TPM2_TCG_LOG] = "TPM v2 log space", + [BLOBLISTT_TCPA_LOG] = "TPM log space", + [BLOBLISTT_ACPI_TABLES] = "ACPI tables for x86", + [BLOBLISTT_SMBIOS_TABLES] = "SMBIOS tables for x86", +}; + +const char *bloblist_tag_name(enum bloblist_tag_t tag) +{ + if (tag < 0 || tag >= BLOBLISTT_COUNT) + return "invalid"; + + return tag_name[tag]; +} + +static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr) +{ + if (hdr->alloced <= hdr->hdr_size) + return NULL; + return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size); +} + +static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, + struct bloblist_rec *rec) +{ + ulong offset; + + offset = (void *)rec - (void *)hdr; + offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN); + if (offset >= hdr->alloced) + return NULL; + return (struct bloblist_rec *)((void *)hdr + offset); +} + +#define foreach_rec(_rec, _hdr) \ + for (_rec = bloblist_first_blob(_hdr); \ + _rec; \ + _rec = bloblist_next_blob(_hdr, _rec)) + +static struct bloblist_rec *bloblist_findrec(uint tag) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + + if (!hdr) + return NULL; + + foreach_rec(rec, hdr) { + if (rec->tag == tag) + return rec; + } + + return NULL; +} + +static int bloblist_addrec(uint tag, int size, int align, + struct bloblist_rec **recp) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + int data_start, new_alloced; + + if (!align) + align = BLOBLIST_ALIGN; + + /* Figure out where the new data will start */ + data_start = map_to_sysmem(hdr) + hdr->alloced + sizeof(*rec); + + /* Align the address and then calculate the offset from ->alloced */ + data_start = ALIGN(data_start, align) - map_to_sysmem(hdr); + + /* Calculate the new allocated total */ + new_alloced = data_start + ALIGN(size, align); + + if (new_alloced >= hdr->size) { + log(LOGC_BLOBLIST, LOGL_ERR, + "Failed to allocate %x bytes size=%x, need size=%x\n", + size, hdr->size, new_alloced); + return log_msg_ret("bloblist add", -ENOSPC); + } + rec = (void *)hdr + hdr->alloced; + + rec->tag = tag; + rec->hdr_size = data_start - hdr->alloced; + rec->size = size; + rec->spare = 0; + + /* Zero the record data */ + memset((void *)rec + rec->hdr_size, '\0', rec->size); + + hdr->alloced = new_alloced; + *recp = rec; + + return 0; +} + +static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size, + int align) +{ + struct bloblist_rec *rec; + + rec = bloblist_findrec(tag); + if (rec) { + if (size && size != rec->size) { + *recp = rec; + return -ESPIPE; + } + } else { + int ret; + + ret = bloblist_addrec(tag, size, align, &rec); + if (ret) + return ret; + } + *recp = rec; + + return 0; +} + +void *bloblist_find(uint tag, int size) +{ + struct bloblist_rec *rec; + + rec = bloblist_findrec(tag); + if (!rec) + return NULL; + if (size && size != rec->size) + return NULL; + + return (void *)rec + rec->hdr_size; +} + +void *bloblist_add(uint tag, int size, int align) +{ + struct bloblist_rec *rec; + + if (bloblist_addrec(tag, size, align, &rec)) + return NULL; + + return (void *)rec + rec->hdr_size; +} + +int bloblist_ensure_size(uint tag, int size, int align, void **blobp) +{ + struct bloblist_rec *rec; + int ret; + + ret = bloblist_ensurerec(tag, &rec, size, align); + if (ret) + return ret; + *blobp = (void *)rec + rec->hdr_size; + + return 0; +} + +void *bloblist_ensure(uint tag, int size) +{ + struct bloblist_rec *rec; + + if (bloblist_ensurerec(tag, &rec, size, 0)) + return NULL; + + return (void *)rec + rec->hdr_size; +} + +int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp) +{ + struct bloblist_rec *rec; + int ret; + + ret = bloblist_ensurerec(tag, &rec, *sizep, 0); + if (ret == -ESPIPE) + *sizep = rec->size; + else if (ret) + return ret; + *blobp = (void *)rec + rec->hdr_size; + + return 0; +} + +static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr) +{ + struct bloblist_rec *rec; + u32 chksum; + + chksum = crc32(0, (unsigned char *)hdr, + offsetof(struct bloblist_hdr, chksum)); + foreach_rec(rec, hdr) { + chksum = crc32(chksum, (void *)rec, rec->hdr_size); + chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size); + } + + return chksum; +} + +int bloblist_new(ulong addr, uint size, uint flags) +{ + struct bloblist_hdr *hdr; + + if (size < sizeof(*hdr)) + return log_ret(-ENOSPC); + if (addr & (BLOBLIST_ALIGN - 1)) + return log_ret(-EFAULT); + hdr = map_sysmem(addr, size); + memset(hdr, '\0', sizeof(*hdr)); + hdr->version = BLOBLIST_VERSION; + hdr->hdr_size = sizeof(*hdr); + hdr->flags = flags; + hdr->magic = BLOBLIST_MAGIC; + hdr->size = size; + hdr->alloced = hdr->hdr_size; + hdr->chksum = 0; + gd->bloblist = hdr; + + return 0; +} + +int bloblist_check(ulong addr, uint size) +{ + struct bloblist_hdr *hdr; + u32 chksum; + + hdr = map_sysmem(addr, sizeof(*hdr)); + if (hdr->magic != BLOBLIST_MAGIC) + return log_msg_ret("Bad magic", -ENOENT); + if (hdr->version != BLOBLIST_VERSION) + return log_msg_ret("Bad version", -EPROTONOSUPPORT); + if (size && hdr->size != size) + return log_msg_ret("Bad size", -EFBIG); + chksum = bloblist_calc_chksum(hdr); + if (hdr->chksum != chksum) { + log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum, + chksum); + return log_msg_ret("Bad checksum", -EIO); + } + gd->bloblist = hdr; + + return 0; +} + +int bloblist_finish(void) +{ + struct bloblist_hdr *hdr = gd->bloblist; + + hdr->chksum = bloblist_calc_chksum(hdr); + + return 0; +} + +void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp) +{ + struct bloblist_hdr *hdr = gd->bloblist; + + *basep = map_to_sysmem(gd->bloblist); + *sizep = hdr->size; + *allocedp = hdr->alloced; +} + +static void show_value(const char *prompt, ulong value) +{ + printf("%s:%*s %-5lx ", prompt, 8 - (int)strlen(prompt), "", value); + print_size(value, "\n"); +} + +void bloblist_show_stats(void) +{ + ulong base, size, alloced; + + bloblist_get_stats(&base, &size, &alloced); + printf("base: %lx\n", base); + show_value("size", size); + show_value("alloced", alloced); + show_value("free", size - alloced); +} + +void bloblist_show_list(void) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + + printf("%-8s %8s Tag Name\n", "Address", "Size"); + for (rec = bloblist_first_blob(hdr); rec; + rec = bloblist_next_blob(hdr, rec)) { + printf("%08lx %8x %3d %s\n", + (ulong)map_to_sysmem((void *)rec + rec->hdr_size), + rec->size, rec->tag, bloblist_tag_name(rec->tag)); + } +} + +void bloblist_reloc(void *to, uint to_size, void *from, uint from_size) +{ + struct bloblist_hdr *hdr; + + memcpy(to, from, from_size); + hdr = to; + hdr->size = to_size; +} + +int bloblist_init(void) +{ + bool expected; + int ret = -ENOENT; + + /** + * Wed expect to find an existing bloblist in the first phase of U-Boot + * that runs + */ + expected = !u_boot_first_phase(); + if (spl_prev_phase() == PHASE_TPL && !IS_ENABLED(CONFIG_TPL_BLOBLIST)) + expected = false; + if (expected) + ret = bloblist_check(CONFIG_BLOBLIST_ADDR, + CONFIG_BLOBLIST_SIZE); + if (ret) { + log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG, + "Existing bloblist not found: creating new bloblist\n"); + ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE, + 0); + } else { + log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n"); + } + + return ret; +} diff --git a/roms/u-boot/common/board_f.c b/roms/u-boot/common/board_f.c new file mode 100644 index 000000000..203e96579 --- /dev/null +++ b/roms/u-boot/common/board_f.c @@ -0,0 +1,1016 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2002-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + */ + +#include <common.h> +#include <bloblist.h> +#include <bootstage.h> +#include <clock_legacy.h> +#include <console.h> +#include <cpu.h> +#include <cpu_func.h> +#include <dm.h> +#include <env.h> +#include <env_internal.h> +#include <fdtdec.h> +#include <fs.h> +#include <hang.h> +#include <i2c.h> +#include <init.h> +#include <initcall.h> +#include <lcd.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <os.h> +#include <post.h> +#include <relocate.h> +#include <serial.h> +#ifdef CONFIG_SPL +#include <spl.h> +#endif +#include <status_led.h> +#include <sysreset.h> +#include <timer.h> +#include <trace.h> +#include <video.h> +#include <watchdog.h> +#include <asm/cache.h> +#ifdef CONFIG_MACH_TYPE +#include <asm/mach-types.h> +#endif +#if defined(CONFIG_MP) && defined(CONFIG_PPC) +#include <asm/mp.h> +#endif +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/sections.h> +#include <dm/root.h> +#include <linux/errno.h> + +/* + * Pointer to initial global data area + * + * Here we initialize it if needed. + */ +#ifdef XTRN_DECLARE_GLOBAL_DATA_PTR +#undef XTRN_DECLARE_GLOBAL_DATA_PTR +#define XTRN_DECLARE_GLOBAL_DATA_PTR /* empty = allocate here */ +DECLARE_GLOBAL_DATA_PTR = (gd_t *)(CONFIG_SYS_INIT_GD_ADDR); +#else +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * TODO(sjg@chromium.org): IMO this code should be + * refactored to a single function, something like: + * + * void led_set_state(enum led_colour_t colour, int on); + */ +/************************************************************************ + * Coloured LED functionality + ************************************************************************ + * May be supplied by boards if desired + */ +__weak void coloured_LED_init(void) {} +__weak void red_led_on(void) {} +__weak void red_led_off(void) {} +__weak void green_led_on(void) {} +__weak void green_led_off(void) {} +__weak void yellow_led_on(void) {} +__weak void yellow_led_off(void) {} +__weak void blue_led_on(void) {} +__weak void blue_led_off(void) {} + +/* + * Why is gd allocated a register? Prior to reloc it might be better to + * just pass it around to each function in this file? + * + * After reloc one could argue that it is hardly used and doesn't need + * to be in a register. Or if it is it should perhaps hold pointers to all + * global data for all modules, so that post-reloc we can avoid the massive + * literal pool we get on ARM. Or perhaps just encourage each module to use + * a structure... + */ + +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) +static int init_func_watchdog_init(void) +{ +# if defined(CONFIG_HW_WATCHDOG) && \ + (defined(CONFIG_M68K) || defined(CONFIG_MICROBLAZE) || \ + defined(CONFIG_SH) || \ + defined(CONFIG_DESIGNWARE_WATCHDOG) || \ + defined(CONFIG_IMX_WATCHDOG)) + hw_watchdog_init(); + puts(" Watchdog enabled\n"); +# endif + WATCHDOG_RESET(); + + return 0; +} + +int init_func_watchdog_reset(void) +{ + WATCHDOG_RESET(); + + return 0; +} +#endif /* CONFIG_WATCHDOG */ + +__weak void board_add_ram_info(int use_default) +{ + /* please define platform specific board_add_ram_info() */ +} + +static int init_baud_rate(void) +{ + gd->baudrate = env_get_ulong("baudrate", 10, CONFIG_BAUDRATE); + return 0; +} + +static int display_text_info(void) +{ +#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_EFI_APP) + ulong bss_start, bss_end, text_base; + + bss_start = (ulong)&__bss_start; + bss_end = (ulong)&__bss_end; + +#ifdef CONFIG_SYS_TEXT_BASE + text_base = CONFIG_SYS_TEXT_BASE; +#else + text_base = CONFIG_SYS_MONITOR_BASE; +#endif + + debug("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n", + text_base, bss_start, bss_end); +#endif + + return 0; +} + +#ifdef CONFIG_SYSRESET +static int print_resetinfo(void) +{ + struct udevice *dev; + char status[256]; + int ret; + + ret = uclass_first_device_err(UCLASS_SYSRESET, &dev); + if (ret) { + debug("%s: No sysreset device found (error: %d)\n", + __func__, ret); + /* Not all boards have sysreset drivers available during early + * boot, so don't fail if one can't be found. + */ + return 0; + } + + if (!sysreset_get_status(dev, status, sizeof(status))) + printf("%s", status); + + return 0; +} +#endif + +#if defined(CONFIG_DISPLAY_CPUINFO) && CONFIG_IS_ENABLED(CPU) +static int print_cpuinfo(void) +{ + struct udevice *dev; + char desc[512]; + int ret; + + dev = cpu_get_current_dev(); + if (!dev) { + debug("%s: Could not get CPU device\n", + __func__); + return -ENODEV; + } + + ret = cpu_get_desc(dev, desc, sizeof(desc)); + if (ret) { + debug("%s: Could not get CPU description (err = %d)\n", + dev->name, ret); + return ret; + } + + printf("CPU: %s\n", desc); + + return 0; +} +#endif + +static int announce_dram_init(void) +{ + puts("DRAM: "); + return 0; +} + +static int show_dram_config(void) +{ + unsigned long long size; + int i; + + debug("\nRAM Configuration:\n"); + for (i = size = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + size += gd->bd->bi_dram[i].size; + debug("Bank #%d: %llx ", i, + (unsigned long long)(gd->bd->bi_dram[i].start)); +#ifdef DEBUG + print_size(gd->bd->bi_dram[i].size, "\n"); +#endif + } + debug("\nDRAM: "); + + print_size(size, ""); + board_add_ram_info(0); + putc('\n'); + + return 0; +} + +__weak int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = gd->ram_base; + gd->bd->bi_dram[0].size = get_effective_memsize(); + + return 0; +} + +#if defined(CONFIG_SYS_I2C) +static int init_func_i2c(void) +{ + puts("I2C: "); + i2c_init_all(); + puts("ready\n"); + return 0; +} +#endif + +#if defined(CONFIG_VID) +__weak int init_func_vid(void) +{ + return 0; +} +#endif + +static int setup_mon_len(void) +{ +#if defined(__ARM__) || defined(__MICROBLAZE__) + gd->mon_len = (ulong)&__bss_end - (ulong)_start; +#elif defined(CONFIG_SANDBOX) || defined(CONFIG_EFI_APP) + gd->mon_len = (ulong)&_end - (ulong)_init; +#elif defined(CONFIG_NIOS2) || defined(CONFIG_XTENSA) + gd->mon_len = CONFIG_SYS_MONITOR_LEN; +#elif defined(CONFIG_NDS32) || defined(CONFIG_SH) || defined(CONFIG_RISCV) + gd->mon_len = (ulong)(&__bss_end) - (ulong)(&_start); +#elif defined(CONFIG_SYS_MONITOR_BASE) + /* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */ + gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; +#endif + return 0; +} + +static int setup_spl_handoff(void) +{ +#if CONFIG_IS_ENABLED(HANDOFF) + gd->spl_handoff = bloblist_find(BLOBLISTT_SPL_HANDOFF, + sizeof(struct spl_handoff)); + debug("Found SPL hand-off info %p\n", gd->spl_handoff); +#endif + + return 0; +} + +__weak int arch_cpu_init(void) +{ + return 0; +} + +__weak int mach_cpu_init(void) +{ + return 0; +} + +/* Get the top of usable RAM */ +__weak ulong board_get_usable_ram_top(ulong total_size) +{ +#if defined(CONFIG_SYS_SDRAM_BASE) && CONFIG_SYS_SDRAM_BASE > 0 + /* + * Detect whether we have so much RAM that it goes past the end of our + * 32-bit address space. If so, clip the usable RAM so it doesn't. + */ + if (gd->ram_top < CONFIG_SYS_SDRAM_BASE) + /* + * Will wrap back to top of 32-bit space when reservations + * are made. + */ + return 0; +#endif + return gd->ram_top; +} + +static int setup_dest_addr(void) +{ + debug("Monitor len: %08lX\n", gd->mon_len); + /* + * Ram is setup, size stored in gd !! + */ + debug("Ram size: %08lX\n", (ulong)gd->ram_size); +#if defined(CONFIG_SYS_MEM_TOP_HIDE) + /* + * Subtract specified amount of memory to hide so that it won't + * get "touched" at all by U-Boot. By fixing up gd->ram_size + * the Linux kernel should now get passed the now "corrected" + * memory size and won't touch it either. This should work + * for arch/ppc and arch/powerpc. Only Linux board ports in + * arch/powerpc with bootwrapper support, that recalculate the + * memory size from the SDRAM controller setup will have to + * get fixed. + */ + gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE; +#endif +#ifdef CONFIG_SYS_SDRAM_BASE + gd->ram_base = CONFIG_SYS_SDRAM_BASE; +#endif + gd->ram_top = gd->ram_base + get_effective_memsize(); + gd->ram_top = board_get_usable_ram_top(gd->mon_len); + gd->relocaddr = gd->ram_top; + debug("Ram top: %08lX\n", (ulong)gd->ram_top); +#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500)) + /* + * We need to make sure the location we intend to put secondary core + * boot code is reserved and not used by any part of u-boot + */ + if (gd->relocaddr > determine_mp_bootpg(NULL)) { + gd->relocaddr = determine_mp_bootpg(NULL); + debug("Reserving MP boot page to %08lx\n", gd->relocaddr); + } +#endif + return 0; +} + +#ifdef CONFIG_PRAM +/* reserve protected RAM */ +static int reserve_pram(void) +{ + ulong reg; + + reg = env_get_ulong("pram", 10, CONFIG_PRAM); + gd->relocaddr -= (reg << 10); /* size is in kB */ + debug("Reserving %ldk for protected RAM at %08lx\n", reg, + gd->relocaddr); + return 0; +} +#endif /* CONFIG_PRAM */ + +/* Round memory pointer down to next 4 kB limit */ +static int reserve_round_4k(void) +{ + gd->relocaddr &= ~(4096 - 1); + return 0; +} + +__weak int arch_reserve_mmu(void) +{ + return 0; +} + +static int reserve_video(void) +{ +#ifdef CONFIG_DM_VIDEO + ulong addr; + int ret; + + addr = gd->relocaddr; + ret = video_reserve(&addr); + if (ret) + return ret; + debug("Reserving %luk for video at: %08lx\n", + ((unsigned long)gd->relocaddr - addr) >> 10, addr); + gd->relocaddr = addr; +#elif defined(CONFIG_LCD) +# ifdef CONFIG_FB_ADDR + gd->fb_base = CONFIG_FB_ADDR; +# else + /* reserve memory for LCD display (always full pages) */ + gd->relocaddr = lcd_setmem(gd->relocaddr); + gd->fb_base = gd->relocaddr; +# endif /* CONFIG_FB_ADDR */ +#endif + + return 0; +} + +static int reserve_trace(void) +{ +#ifdef CONFIG_TRACE + gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE; + gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE); + debug("Reserving %luk for trace data at: %08lx\n", + (unsigned long)CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr); +#endif + + return 0; +} + +static int reserve_uboot(void) +{ + if (!(gd->flags & GD_FLG_SKIP_RELOC)) { + /* + * reserve memory for U-Boot code, data & bss + * round down to next 4 kB limit + */ + gd->relocaddr -= gd->mon_len; + gd->relocaddr &= ~(4096 - 1); + #if defined(CONFIG_E500) || defined(CONFIG_MIPS) + /* round down to next 64 kB limit so that IVPR stays aligned */ + gd->relocaddr &= ~(65536 - 1); + #endif + + debug("Reserving %ldk for U-Boot at: %08lx\n", + gd->mon_len >> 10, gd->relocaddr); + } + + gd->start_addr_sp = gd->relocaddr; + + return 0; +} + +/* + * reserve after start_addr_sp the requested size and make the stack pointer + * 16-byte aligned, this alignment is needed for cast on the reserved memory + * ref = x86_64 ABI: https://reviews.llvm.org/D30049: 16 bytes + * = ARMv8 Instruction Set Overview: quad word, 16 bytes + */ +static unsigned long reserve_stack_aligned(size_t size) +{ + return ALIGN_DOWN(gd->start_addr_sp - size, 16); +} + +#ifdef CONFIG_SYS_NONCACHED_MEMORY +static int reserve_noncached(void) +{ + /* + * The value of gd->start_addr_sp must match the value of malloc_start + * calculated in boatrd_f.c:initr_malloc(), which is passed to + * board_r.c:mem_malloc_init() and then used by + * cache.c:noncached_init() + * + * These calculations must match the code in cache.c:noncached_init() + */ + gd->start_addr_sp = ALIGN(gd->start_addr_sp, MMU_SECTION_SIZE) - + MMU_SECTION_SIZE; + gd->start_addr_sp -= ALIGN(CONFIG_SYS_NONCACHED_MEMORY, + MMU_SECTION_SIZE); + debug("Reserving %dM for noncached_alloc() at: %08lx\n", + CONFIG_SYS_NONCACHED_MEMORY >> 20, gd->start_addr_sp); + + return 0; +} +#endif + +/* reserve memory for malloc() area */ +static int reserve_malloc(void) +{ + gd->start_addr_sp = reserve_stack_aligned(TOTAL_MALLOC_LEN); + debug("Reserving %dk for malloc() at: %08lx\n", + TOTAL_MALLOC_LEN >> 10, gd->start_addr_sp); +#ifdef CONFIG_SYS_NONCACHED_MEMORY + reserve_noncached(); +#endif + + return 0; +} + +/* (permanently) allocate a Board Info struct */ +static int reserve_board(void) +{ + if (!gd->bd) { + gd->start_addr_sp = reserve_stack_aligned(sizeof(struct bd_info)); + gd->bd = (struct bd_info *)map_sysmem(gd->start_addr_sp, + sizeof(struct bd_info)); + memset(gd->bd, '\0', sizeof(struct bd_info)); + debug("Reserving %zu Bytes for Board Info at: %08lx\n", + sizeof(struct bd_info), gd->start_addr_sp); + } + return 0; +} + +static int reserve_global_data(void) +{ + gd->start_addr_sp = reserve_stack_aligned(sizeof(gd_t)); + gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t)); + debug("Reserving %zu Bytes for Global Data at: %08lx\n", + sizeof(gd_t), gd->start_addr_sp); + return 0; +} + +static int reserve_fdt(void) +{ + if (!IS_ENABLED(CONFIG_OF_EMBED)) { + /* + * If the device tree is sitting immediately above our image + * then we must relocate it. If it is embedded in the data + * section, then it will be relocated with other data. + */ + if (gd->fdt_blob) { + gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob), 32); + + gd->start_addr_sp = reserve_stack_aligned(gd->fdt_size); + gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size); + debug("Reserving %lu Bytes for FDT at: %08lx\n", + gd->fdt_size, gd->start_addr_sp); + } + } + + return 0; +} + +static int reserve_bootstage(void) +{ +#ifdef CONFIG_BOOTSTAGE + int size = bootstage_get_size(); + + gd->start_addr_sp = reserve_stack_aligned(size); + gd->new_bootstage = map_sysmem(gd->start_addr_sp, size); + debug("Reserving %#x Bytes for bootstage at: %08lx\n", size, + gd->start_addr_sp); +#endif + + return 0; +} + +__weak int arch_reserve_stacks(void) +{ + return 0; +} + +static int reserve_stacks(void) +{ + /* make stack pointer 16-byte aligned */ + gd->start_addr_sp = reserve_stack_aligned(16); + + /* + * let the architecture-specific code tailor gd->start_addr_sp and + * gd->irq_sp + */ + return arch_reserve_stacks(); +} + +static int reserve_bloblist(void) +{ +#ifdef CONFIG_BLOBLIST + /* Align to a 4KB boundary for easier reading of addresses */ + gd->start_addr_sp = ALIGN_DOWN(gd->start_addr_sp - + CONFIG_BLOBLIST_SIZE_RELOC, 0x1000); + gd->new_bloblist = map_sysmem(gd->start_addr_sp, + CONFIG_BLOBLIST_SIZE_RELOC); +#endif + + return 0; +} + +static int display_new_sp(void) +{ + debug("New Stack Pointer is: %08lx\n", gd->start_addr_sp); + + return 0; +} + +__weak int arch_setup_bdinfo(void) +{ + return 0; +} + +int setup_bdinfo(void) +{ + struct bd_info *bd = gd->bd; + + if (IS_ENABLED(CONFIG_SYS_HAS_SRAM)) { + bd->bi_sramstart = CONFIG_SYS_SRAM_BASE; /* start of SRAM */ + bd->bi_sramsize = CONFIG_SYS_SRAM_SIZE; /* size of SRAM */ + } + +#ifdef CONFIG_MACH_TYPE + bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ +#endif + + return arch_setup_bdinfo(); +} + +#ifdef CONFIG_POST +static int init_post(void) +{ + post_bootmode_init(); + post_run(NULL, POST_ROM | post_bootmode_get(0)); + + return 0; +} +#endif + +static int reloc_fdt(void) +{ + if (!IS_ENABLED(CONFIG_OF_EMBED)) { + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + if (gd->new_fdt) { + memcpy(gd->new_fdt, gd->fdt_blob, + fdt_totalsize(gd->fdt_blob)); + gd->fdt_blob = gd->new_fdt; + } + } + + return 0; +} + +static int reloc_bootstage(void) +{ +#ifdef CONFIG_BOOTSTAGE + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + if (gd->new_bootstage) { + int size = bootstage_get_size(); + + debug("Copying bootstage from %p to %p, size %x\n", + gd->bootstage, gd->new_bootstage, size); + memcpy(gd->new_bootstage, gd->bootstage, size); + gd->bootstage = gd->new_bootstage; + bootstage_relocate(); + } +#endif + + return 0; +} + +static int reloc_bloblist(void) +{ +#ifdef CONFIG_BLOBLIST + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + if (gd->new_bloblist) { + int size = CONFIG_BLOBLIST_SIZE; + + debug("Copying bloblist from %p to %p, size %x\n", + gd->bloblist, gd->new_bloblist, size); + bloblist_reloc(gd->new_bloblist, CONFIG_BLOBLIST_SIZE_RELOC, + gd->bloblist, size); + gd->bloblist = gd->new_bloblist; + } +#endif + + return 0; +} + +static int setup_reloc(void) +{ + if (gd->flags & GD_FLG_SKIP_RELOC) { + debug("Skipping relocation due to flag\n"); + return 0; + } + +#ifdef CONFIG_SYS_TEXT_BASE +#ifdef ARM + gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start; +#elif defined(CONFIG_M68K) + /* + * On all ColdFire arch cpu, monitor code starts always + * just after the default vector table location, so at 0x400 + */ + gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400); +#elif !defined(CONFIG_SANDBOX) + gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; +#endif +#endif + memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); + + debug("Relocation Offset is: %08lx\n", gd->reloc_off); + debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n", + gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd), + gd->start_addr_sp); + + return 0; +} + +#ifdef CONFIG_OF_BOARD_FIXUP +static int fix_fdt(void) +{ + return board_fix_fdt((void *)gd->fdt_blob); +} +#endif + +/* ARM calls relocate_code from its crt0.S */ +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ + !CONFIG_IS_ENABLED(X86_64) + +static int jump_to_copy(void) +{ + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + /* + * x86 is special, but in a nice way. It uses a trampoline which + * enables the dcache if possible. + * + * For now, other archs use relocate_code(), which is implemented + * similarly for all archs. When we do generic relocation, hopefully + * we can make all archs enable the dcache prior to relocation. + */ +#if defined(CONFIG_X86) || defined(CONFIG_ARC) + /* + * SDRAM and console are now initialised. The final stack can now + * be setup in SDRAM. Code execution will continue in Flash, but + * with the stack in SDRAM and Global Data in temporary memory + * (CPU cache) + */ + arch_setup_gd(gd->new_gd); + board_init_f_r_trampoline(gd->start_addr_sp); +#else + relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr); +#endif + + return 0; +} +#endif + +/* Record the board_init_f() bootstage (after arch_cpu_init()) */ +static int initf_bootstage(void) +{ + bool from_spl = IS_ENABLED(CONFIG_SPL_BOOTSTAGE) && + IS_ENABLED(CONFIG_BOOTSTAGE_STASH); + int ret; + + ret = bootstage_init(!from_spl); + if (ret) + return ret; + if (from_spl) { + const void *stash = map_sysmem(CONFIG_BOOTSTAGE_STASH_ADDR, + CONFIG_BOOTSTAGE_STASH_SIZE); + + ret = bootstage_unstash(stash, CONFIG_BOOTSTAGE_STASH_SIZE); + if (ret && ret != -ENOENT) { + debug("Failed to unstash bootstage: err=%d\n", ret); + return ret; + } + } + + bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f"); + + return 0; +} + +static int initf_dm(void) +{ +#if defined(CONFIG_DM) && CONFIG_VAL(SYS_MALLOC_F_LEN) + int ret; + + bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f"); + ret = dm_init_and_scan(true); + bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_F); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_TIMER_EARLY)) { + ret = dm_timer_init(); + if (ret) + return ret; + } +#endif + + return 0; +} + +/* Architecture-specific memory reservation */ +__weak int reserve_arch(void) +{ + return 0; +} + +__weak int arch_cpu_init_dm(void) +{ + return 0; +} + +__weak int checkcpu(void) +{ + return 0; +} + +__weak int clear_bss(void) +{ + return 0; +} + +static const init_fnc_t init_sequence_f[] = { + setup_mon_len, +#ifdef CONFIG_OF_CONTROL + fdtdec_setup, +#endif +#ifdef CONFIG_TRACE_EARLY + trace_early_init, +#endif + initf_malloc, + log_init, + initf_bootstage, /* uses its own timer, so does not need DM */ +#ifdef CONFIG_BLOBLIST + bloblist_init, +#endif + setup_spl_handoff, +#if defined(CONFIG_CONSOLE_RECORD_INIT_F) + console_record_init, +#endif +#if defined(CONFIG_HAVE_FSP) + arch_fsp_init, +#endif + arch_cpu_init, /* basic arch cpu dependent setup */ + mach_cpu_init, /* SoC/machine dependent CPU setup */ + initf_dm, + arch_cpu_init_dm, +#if defined(CONFIG_BOARD_EARLY_INIT_F) + board_early_init_f, +#endif +#if defined(CONFIG_PPC) || defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K) + /* get CPU and bus clocks according to the environment variable */ + get_clocks, /* get CPU and bus clocks (etc.) */ +#endif +#if !defined(CONFIG_M68K) + timer_init, /* initialize timer */ +#endif +#if defined(CONFIG_BOARD_POSTCLK_INIT) + board_postclk_init, +#endif + env_init, /* initialize environment */ + init_baud_rate, /* initialze baudrate settings */ + serial_init, /* serial communications setup */ + console_init_f, /* stage 1 init of console */ + display_options, /* say that we are here */ + display_text_info, /* show debugging info if required */ + checkcpu, +#if defined(CONFIG_SYSRESET) + print_resetinfo, +#endif +#if defined(CONFIG_DISPLAY_CPUINFO) + print_cpuinfo, /* display cpu info (and speed) */ +#endif +#if defined(CONFIG_DTB_RESELECT) + embedded_dtb_select, +#endif +#if defined(CONFIG_DISPLAY_BOARDINFO) + show_board_info, +#endif + INIT_FUNC_WATCHDOG_INIT +#if defined(CONFIG_MISC_INIT_F) + misc_init_f, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_SYS_I2C) + init_func_i2c, +#endif +#if defined(CONFIG_VID) && !defined(CONFIG_SPL) + init_func_vid, +#endif + announce_dram_init, + dram_init, /* configure available RAM banks */ +#ifdef CONFIG_POST + post_init_f, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_SYS_DRAM_TEST) + testdram, +#endif /* CONFIG_SYS_DRAM_TEST */ + INIT_FUNC_WATCHDOG_RESET + +#ifdef CONFIG_POST + init_post, +#endif + INIT_FUNC_WATCHDOG_RESET + /* + * Now that we have DRAM mapped and working, we can + * relocate the code and continue running from DRAM. + * + * Reserve memory at end of RAM for (top down in that order): + * - area that won't get touched by U-Boot and Linux (optional) + * - kernel log buffer + * - protected RAM + * - LCD framebuffer + * - monitor code + * - board info struct + */ + setup_dest_addr, +#ifdef CONFIG_OF_BOARD_FIXUP + fix_fdt, +#endif +#ifdef CONFIG_PRAM + reserve_pram, +#endif + reserve_round_4k, + arch_reserve_mmu, + reserve_video, + reserve_trace, + reserve_uboot, + reserve_malloc, + reserve_board, + reserve_global_data, + reserve_fdt, + reserve_bootstage, + reserve_bloblist, + reserve_arch, + reserve_stacks, + dram_init_banksize, + show_dram_config, + INIT_FUNC_WATCHDOG_RESET + setup_bdinfo, + display_new_sp, + INIT_FUNC_WATCHDOG_RESET + reloc_fdt, + reloc_bootstage, + reloc_bloblist, + setup_reloc, +#if defined(CONFIG_X86) || defined(CONFIG_ARC) + copy_uboot_to_ram, + do_elf_reloc_fixups, +#endif + clear_bss, +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ + !CONFIG_IS_ENABLED(X86_64) + jump_to_copy, +#endif + NULL, +}; + +void board_init_f(ulong boot_flags) +{ + gd->flags = boot_flags; + gd->have_console = 0; + + if (initcall_run_list(init_sequence_f)) + hang(); + +#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ + !defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64) && \ + !defined(CONFIG_ARC) + /* NOTREACHED - jump_to_copy() does not return */ + hang(); +#endif +} + +#if defined(CONFIG_X86) || defined(CONFIG_ARC) +/* + * For now this code is only used on x86. + * + * init_sequence_f_r is the list of init functions which are run when + * U-Boot is executing from Flash with a semi-limited 'C' environment. + * The following limitations must be considered when implementing an + * '_f_r' function: + * - 'static' variables are read-only + * - Global Data (gd->xxx) is read/write + * + * The '_f_r' sequence must, as a minimum, copy U-Boot to RAM (if + * supported). It _should_, if possible, copy global data to RAM and + * initialise the CPU caches (to speed up the relocation process) + * + * NOTE: At present only x86 uses this route, but it is intended that + * all archs will move to this when generic relocation is implemented. + */ +static const init_fnc_t init_sequence_f_r[] = { +#if !CONFIG_IS_ENABLED(X86_64) + init_cache_f_r, +#endif + + NULL, +}; + +void board_init_f_r(void) +{ + if (initcall_run_list(init_sequence_f_r)) + hang(); + + /* + * The pre-relocation drivers may be using memory that has now gone + * away. Mark serial as unavailable - this will fall back to the debug + * UART if available. + * + * Do the same with log drivers since the memory may not be available. + */ + gd->flags &= ~(GD_FLG_SERIAL_READY | GD_FLG_LOG_READY); +#ifdef CONFIG_TIMER + gd->timer = NULL; +#endif + + /* + * U-Boot has been copied into SDRAM, the BSS has been cleared etc. + * Transfer execution from Flash to RAM by calculating the address + * of the in-RAM copy of board_init_r() and calling it + */ + (board_init_r + gd->reloc_off)((gd_t *)gd, gd->relocaddr); + + /* NOTREACHED - board_init_r() does not return */ + hang(); +} +#endif /* CONFIG_X86 */ diff --git a/roms/u-boot/common/board_info.c b/roms/u-boot/common/board_info.c new file mode 100644 index 000000000..1cfe34f70 --- /dev/null +++ b/roms/u-boot/common/board_info.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <dm.h> +#include <init.h> +#include <sysinfo.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +int __weak checkboard(void) +{ + return 0; +} + +/* + * Check sysinfo for board information. Failing that if the root node of the DTB + * has a "model" property, show it. + * + * Then call checkboard(). + */ +int __weak show_board_info(void) +{ + if (IS_ENABLED(CONFIG_OF_CONTROL)) { + struct udevice *dev; + const char *model; + char str[80]; + int ret = -ENOSYS; + + if (IS_ENABLED(CONFIG_SYSINFO)) { + /* This might provide more detail */ + ret = uclass_first_device_err(UCLASS_SYSINFO, &dev); + if (!ret) + ret = sysinfo_get_str(dev, + SYSINFO_ID_BOARD_MODEL, + sizeof(str), str); + } + + /* Fail back to the main 'model' if available */ + if (ret) + model = fdt_getprop(gd->fdt_blob, 0, "model", NULL); + else + model = str; + + if (model) + printf("Model: %s\n", model); + } + + return checkboard(); +} diff --git a/roms/u-boot/common/board_r.c b/roms/u-boot/common/board_r.c new file mode 100644 index 000000000..3f8240477 --- /dev/null +++ b/roms/u-boot/common/board_r.c @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2002-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + */ + +#include <common.h> +#include <api.h> +#include <bootstage.h> +#include <cpu_func.h> +#include <exports.h> +#include <flash.h> +#include <hang.h> +#include <image.h> +#include <irq_func.h> +#include <log.h> +#include <net.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <u-boot/crc.h> +/* TODO: can we just include all these headers whether needed or not? */ +#if defined(CONFIG_CMD_BEDBUG) +#include <bedbug/type.h> +#endif +#include <binman.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <env.h> +#include <env_internal.h> +#include <fdtdec.h> +#include <ide.h> +#include <init.h> +#include <initcall.h> +#if defined(CONFIG_CMD_KGDB) +#include <kgdb.h> +#endif +#include <irq_func.h> +#include <malloc.h> +#include <mapmem.h> +#ifdef CONFIG_BITBANGMII +#include <miiphy.h> +#endif +#include <mmc.h> +#include <mux.h> +#include <nand.h> +#include <of_live.h> +#include <onenand_uboot.h> +#include <pvblock.h> +#include <scsi.h> +#include <serial.h> +#include <status_led.h> +#include <stdio_dev.h> +#include <timer.h> +#include <trace.h> +#include <watchdog.h> +#ifdef CONFIG_XEN +#include <xen.h> +#endif +#ifdef CONFIG_ADDR_MAP +#include <asm/mmu.h> +#endif +#include <asm/sections.h> +#include <dm/root.h> +#include <linux/compiler.h> +#include <linux/err.h> +#include <efi_loader.h> +#include <wdt.h> +#if defined(CONFIG_GPIO_HOG) +#include <asm/gpio.h> +#endif +#ifdef CONFIG_EFI_SETUP_EARLY +#include <efi_loader.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +ulong monitor_flash_len; + +__weak int board_flash_wp_on(void) +{ + /* + * Most flashes can't be detected when write protection is enabled, + * so provide a way to let U-Boot gracefully ignore write protected + * devices. + */ + return 0; +} + +__weak int cpu_secondary_init_r(void) +{ + return 0; +} + +static int initr_trace(void) +{ +#ifdef CONFIG_TRACE + trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE); +#endif + + return 0; +} + +static int initr_reloc(void) +{ + /* tell others: relocation done */ + gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT; + + return 0; +} + +#ifdef CONFIG_ARM +/* + * Some of these functions are needed purely because the functions they + * call return void. If we change them to return 0, these stubs can go away. + */ +static int initr_caches(void) +{ + /* Enable caches */ + enable_caches(); + return 0; +} +#endif + +__weak int fixup_cpu(void) +{ + return 0; +} + +static int initr_reloc_global_data(void) +{ +#ifdef __ARM__ + monitor_flash_len = _end - __image_copy_start; +#elif defined(CONFIG_NDS32) || defined(CONFIG_RISCV) + monitor_flash_len = (ulong)&_end - (ulong)&_start; +#elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2) + monitor_flash_len = (ulong)&__init_end - gd->relocaddr; +#endif +#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) + /* + * The gd->cpu pointer is set to an address in flash before relocation. + * We need to update it to point to the same CPU entry in RAM. + * TODO: why not just add gd->reloc_ofs? + */ + gd->arch.cpu += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; + + /* + * If we didn't know the cpu mask & # cores, we can save them of + * now rather than 'computing' them constantly + */ + fixup_cpu(); +#endif +#ifdef CONFIG_SYS_RELOC_GD_ENV_ADDR + /* + * Relocate the early env_addr pointer unless we know it is not inside + * the binary. Some systems need this and for the rest, it doesn't hurt. + */ + gd->env_addr += gd->reloc_off; +#endif +#ifdef CONFIG_OF_EMBED + /* + * The fdt_blob needs to be moved to new relocation address + * incase of FDT blob is embedded with in image + */ + gd->fdt_blob += gd->reloc_off; +#endif +#ifdef CONFIG_EFI_LOADER + /* + * On the ARM architecture gd is mapped to a fixed register (r9 or x18). + * As this register may be overwritten by an EFI payload we save it here + * and restore it on every callback entered. + */ + efi_save_gd(); + + efi_runtime_relocate(gd->relocaddr, NULL); +#endif + + return 0; +} + +__weak int arch_initr_trap(void) +{ + return 0; +} + +#ifdef CONFIG_ADDR_MAP +static int initr_addr_map(void) +{ + init_addr_map(); + + return 0; +} +#endif + +#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500) +static int initr_unlock_ram_in_cache(void) +{ + unlock_ram_in_cache(); /* it's time to unlock D-cache in e500 */ + return 0; +} +#endif + +static int initr_barrier(void) +{ +#ifdef CONFIG_PPC + /* TODO: Can we not use dmb() macros for this? */ + asm("sync ; isync"); +#endif + return 0; +} + +static int initr_malloc(void) +{ + ulong malloc_start; + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr, + gd->malloc_ptr / 1024); +#endif + /* The malloc area is immediately below the monitor copy in DRAM */ + /* + * This value MUST match the value of gd->start_addr_sp in board_f.c: + * reserve_noncached(). + */ + malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; + mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN), + TOTAL_MALLOC_LEN); + return 0; +} + +static int initr_of_live(void) +{ + if (CONFIG_IS_ENABLED(OF_LIVE)) { + int ret; + + bootstage_start(BOOTSTAGE_ID_ACCUM_OF_LIVE, "of_live"); + ret = of_live_build(gd->fdt_blob, + (struct device_node **)gd_of_root_ptr()); + bootstage_accum(BOOTSTAGE_ID_ACCUM_OF_LIVE); + if (ret) + return ret; + } + + return 0; +} + +#ifdef CONFIG_DM +static int initr_dm(void) +{ + int ret; + + /* Save the pre-reloc driver model and start a new one */ + gd->dm_root_f = gd->dm_root; + gd->dm_root = NULL; +#ifdef CONFIG_TIMER + gd->timer = NULL; +#endif + bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r"); + ret = dm_init_and_scan(false); + bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R); + if (ret) + return ret; + + return 0; +} +#endif + +static int initr_dm_devices(void) +{ + int ret; + + if (IS_ENABLED(CONFIG_TIMER_EARLY)) { + ret = dm_timer_init(); + if (ret) + return ret; + } + + if (IS_ENABLED(CONFIG_MULTIPLEXER)) { + /* + * Initialize the multiplexer controls to their default state. + * This must be done early as other drivers may unknowingly + * rely on it. + */ + ret = dm_mux_init(); + if (ret) + return ret; + } + + return 0; +} + +static int initr_bootstage(void) +{ + bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); + + return 0; +} + +__weak int power_init_board(void) +{ + return 0; +} + +static int initr_announce(void) +{ + debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr); + return 0; +} + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +static int initr_manual_reloc_cmdtable(void) +{ + fixup_cmdtable(ll_entry_start(struct cmd_tbl, cmd), + ll_entry_count(struct cmd_tbl, cmd)); + return 0; +} +#endif + +static int initr_binman(void) +{ + if (!CONFIG_IS_ENABLED(BINMAN_FDT)) + return 0; + + return binman_init(); +} + +#if defined(CONFIG_MTD_NOR_FLASH) +__weak int is_flash_available(void) +{ + return 1; +} + +static int initr_flash(void) +{ + ulong flash_size = 0; + struct bd_info *bd = gd->bd; + + if (!is_flash_available()) + return 0; + + puts("Flash: "); + + if (board_flash_wp_on()) + printf("Uninitialized - Write Protect On\n"); + else + flash_size = flash_init(); + + print_size(flash_size, ""); +#ifdef CONFIG_SYS_FLASH_CHECKSUM + /* + * Compute and print flash CRC if flashchecksum is set to 'y' + * + * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX + */ + if (env_get_yesno("flashchecksum") == 1) { + const uchar *flash_base = (const uchar *)CONFIG_SYS_FLASH_BASE; + + printf(" CRC: %08X", crc32(0, + flash_base, + flash_size)); + } +#endif /* CONFIG_SYS_FLASH_CHECKSUM */ + putc('\n'); + + /* update start of FLASH memory */ +#ifdef CONFIG_SYS_FLASH_BASE + bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; +#endif + /* size of FLASH memory (final value) */ + bd->bi_flashsize = flash_size; + +#if defined(CONFIG_SYS_UPDATE_FLASH_SIZE) + /* Make a update of the Memctrl. */ + update_flash_size(flash_size); +#endif + +#if defined(CONFIG_OXC) || defined(CONFIG_RMU) + /* flash mapped at end of memory map */ + bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; +#elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE + bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */ +#endif + return 0; +} +#endif + +#ifdef CONFIG_CMD_NAND +/* go init the NAND */ +static int initr_nand(void) +{ + puts("NAND: "); + nand_init(); + printf("%lu MiB\n", nand_size() / 1024); + return 0; +} +#endif + +#if defined(CONFIG_CMD_ONENAND) +/* go init the NAND */ +static int initr_onenand(void) +{ + puts("NAND: "); + onenand_init(); + return 0; +} +#endif + +#ifdef CONFIG_MMC +static int initr_mmc(void) +{ + puts("MMC: "); + mmc_initialize(gd->bd); + return 0; +} +#endif + +#ifdef CONFIG_PVBLOCK +static int initr_pvblock(void) +{ + puts("PVBLOCK: "); + pvblock_init(); + return 0; +} +#endif + +/* + * Tell if it's OK to load the environment early in boot. + * + * If CONFIG_OF_CONTROL is defined, we'll check with the FDT to see + * if this is OK (defaulting to saying it's OK). + * + * NOTE: Loading the environment early can be a bad idea if security is + * important, since no verification is done on the environment. + * + * @return 0 if environment should not be loaded, !=0 if it is ok to load + */ +static int should_load_env(void) +{ + if (IS_ENABLED(CONFIG_OF_CONTROL)) + return fdtdec_get_config_int(gd->fdt_blob, + "load-environment", 1); + + if (IS_ENABLED(CONFIG_DELAY_ENVIRONMENT)) + return 0; + + return 1; +} + +static int initr_env(void) +{ + /* initialize environment */ + if (should_load_env()) + env_relocate(); + else + env_set_default(NULL, 0); + + env_import_fdt(); + + if (IS_ENABLED(CONFIG_OF_CONTROL)) + env_set_hex("fdtcontroladdr", + (unsigned long)map_to_sysmem(gd->fdt_blob)); + + /* Initialize from environment */ + image_load_addr = env_get_ulong("loadaddr", 16, image_load_addr); + + return 0; +} + +#ifdef CONFIG_SYS_BOOTPARAMS_LEN +static int initr_malloc_bootparams(void) +{ + gd->bd->bi_boot_params = (ulong)malloc(CONFIG_SYS_BOOTPARAMS_LEN); + if (!gd->bd->bi_boot_params) { + puts("WARNING: Cannot allocate space for boot parameters\n"); + return -ENOMEM; + } + return 0; +} +#endif + +#ifdef CONFIG_CMD_NET +static int initr_ethaddr(void) +{ + struct bd_info *bd = gd->bd; + + /* kept around for legacy kernels only ... ignore the next section */ + eth_env_get_enetaddr("ethaddr", bd->bi_enetaddr); + + return 0; +} +#endif /* CONFIG_CMD_NET */ + +#ifdef CONFIG_CMD_KGDB +static int initr_kgdb(void) +{ + puts("KGDB: "); + kgdb_init(); + return 0; +} +#endif + +#if defined(CONFIG_LED_STATUS) +static int initr_status_led(void) +{ +#if defined(CONFIG_LED_STATUS_BOOT) + status_led_set(CONFIG_LED_STATUS_BOOT, CONFIG_LED_STATUS_BLINKING); +#else + status_led_init(); +#endif + return 0; +} +#endif + +#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI) +static int initr_scsi(void) +{ + puts("SCSI: "); + scsi_init(); + puts("\n"); + + return 0; +} +#endif + +#ifdef CONFIG_CMD_NET +static int initr_net(void) +{ + puts("Net: "); + eth_initialize(); +#if defined(CONFIG_RESET_PHY_R) + debug("Reset Ethernet PHY\n"); + reset_phy(); +#endif + return 0; +} +#endif + +#ifdef CONFIG_POST +static int initr_post(void) +{ + post_run(NULL, POST_RAM | post_bootmode_get(0)); + return 0; +} +#endif + +#if defined(CONFIG_IDE) && !defined(CONFIG_BLK) +static int initr_ide(void) +{ + puts("IDE: "); +#if defined(CONFIG_START_IDE) + if (board_start_ide()) + ide_init(); +#else + ide_init(); +#endif + return 0; +} +#endif + +#if defined(CONFIG_PRAM) +/* + * Export available size of memory for Linux, taking into account the + * protected RAM at top of memory + */ +int initr_mem(void) +{ + ulong pram = 0; + char memsz[32]; + + pram = env_get_ulong("pram", 10, CONFIG_PRAM); + sprintf(memsz, "%ldk", (long int)((gd->ram_size / 1024) - pram)); + env_set("mem", memsz); + + return 0; +} +#endif + +static int run_main_loop(void) +{ +#ifdef CONFIG_SANDBOX + sandbox_main_loop_init(); +#endif + /* main_loop() can return to retry autoboot, if so just run it again */ + for (;;) + main_loop(); + return 0; +} + +/* + * We hope to remove most of the driver-related init and do it if/when + * the driver is later used. + * + * TODO: perhaps reset the watchdog in the initcall function after each call? + */ +static init_fnc_t init_sequence_r[] = { + initr_trace, + initr_reloc, + /* TODO: could x86/PPC have this also perhaps? */ +#ifdef CONFIG_ARM + initr_caches, + /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR. + * A temporary mapping of IFC high region is since removed, + * so environmental variables in NOR flash is not available + * until board_init() is called below to remap IFC to high + * region. + */ +#endif + initr_reloc_global_data, +#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500) + initr_unlock_ram_in_cache, +#endif + initr_barrier, + initr_malloc, + log_init, + initr_bootstage, /* Needs malloc() but has its own timer */ +#if defined(CONFIG_CONSOLE_RECORD) + console_record_init, +#endif +#ifdef CONFIG_SYS_NONCACHED_MEMORY + noncached_init, +#endif + initr_of_live, +#ifdef CONFIG_DM + initr_dm, +#endif +#ifdef CONFIG_ADDR_MAP + initr_addr_map, +#endif +#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) || \ + defined(CONFIG_SANDBOX) + board_init, /* Setup chipselects */ +#endif + /* + * TODO: printing of the clock inforamtion of the board is now + * implemented as part of bdinfo command. Currently only support for + * davinci SOC's is added. Remove this check once all the board + * implement this. + */ +#ifdef CONFIG_CLOCKS + set_cpu_clk_info, /* Setup clock information */ +#endif +#ifdef CONFIG_EFI_LOADER + efi_memory_init, +#endif + initr_binman, +#ifdef CONFIG_FSP_VERSION2 + arch_fsp_init_r, +#endif + initr_dm_devices, + stdio_init_tables, + serial_initialize, + initr_announce, +#if CONFIG_IS_ENABLED(WDT) + initr_watchdog, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_NEEDS_MANUAL_RELOC) && defined(CONFIG_BLOCK_CACHE) + blkcache_init, +#endif +#ifdef CONFIG_NEEDS_MANUAL_RELOC + initr_manual_reloc_cmdtable, +#endif + arch_initr_trap, +#if defined(CONFIG_BOARD_EARLY_INIT_R) + board_early_init_r, +#endif + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_POST + post_output_backlog, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_PCI_INIT_R) && defined(CONFIG_SYS_EARLY_PCI_INIT) + /* + * Do early PCI configuration _before_ the flash gets initialised, + * because PCU resources are crucial for flash access on some boards. + */ + pci_init, +#endif +#ifdef CONFIG_ARCH_EARLY_INIT_R + arch_early_init_r, +#endif + power_init_board, +#ifdef CONFIG_MTD_NOR_FLASH + initr_flash, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) + /* initialize higher level parts of CPU like time base and timers */ + cpu_init_r, +#endif +#ifdef CONFIG_CMD_NAND + initr_nand, +#endif +#ifdef CONFIG_CMD_ONENAND + initr_onenand, +#endif +#ifdef CONFIG_MMC + initr_mmc, +#endif +#ifdef CONFIG_XEN + xen_init, +#endif +#ifdef CONFIG_PVBLOCK + initr_pvblock, +#endif + initr_env, +#ifdef CONFIG_SYS_BOOTPARAMS_LEN + initr_malloc_bootparams, +#endif + INIT_FUNC_WATCHDOG_RESET + cpu_secondary_init_r, +#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) + mac_read_from_eeprom, +#endif + INIT_FUNC_WATCHDOG_RESET +#if defined(CONFIG_PCI_INIT_R) && !defined(CONFIG_SYS_EARLY_PCI_INIT) + /* + * Do pci configuration + */ + pci_init, +#endif + stdio_add_devices, + jumptable_init, +#ifdef CONFIG_API + api_init, +#endif + console_init_r, /* fully init console as a device */ +#ifdef CONFIG_DISPLAY_BOARDINFO_LATE + console_announce_r, + show_board_info, +#endif +#ifdef CONFIG_ARCH_MISC_INIT + arch_misc_init, /* miscellaneous arch-dependent init */ +#endif +#ifdef CONFIG_MISC_INIT_R + misc_init_r, /* miscellaneous platform-dependent init */ +#endif + INIT_FUNC_WATCHDOG_RESET +#ifdef CONFIG_CMD_KGDB + initr_kgdb, +#endif + interrupt_init, +#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_M68K) + timer_init, /* initialize timer */ +#endif +#if defined(CONFIG_LED_STATUS) + initr_status_led, +#endif + /* PPC has a udelay(20) here dating from 2002. Why? */ +#ifdef CONFIG_CMD_NET + initr_ethaddr, +#endif +#if defined(CONFIG_GPIO_HOG) + gpio_hog_probe_all, +#endif +#ifdef CONFIG_BOARD_LATE_INIT + board_late_init, +#endif +#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI) + INIT_FUNC_WATCHDOG_RESET + initr_scsi, +#endif +#ifdef CONFIG_BITBANGMII + bb_miiphy_init, +#endif +#ifdef CONFIG_PCI_ENDPOINT + pci_ep_init, +#endif +#ifdef CONFIG_CMD_NET + INIT_FUNC_WATCHDOG_RESET + initr_net, +#endif +#ifdef CONFIG_POST + initr_post, +#endif +#if defined(CONFIG_IDE) && !defined(CONFIG_BLK) + initr_ide, +#endif +#ifdef CONFIG_LAST_STAGE_INIT + INIT_FUNC_WATCHDOG_RESET + /* + * Some parts can be only initialized if all others (like + * Interrupts) are up and running (i.e. the PC-style ISA + * keyboard). + */ + last_stage_init, +#endif +#ifdef CONFIG_CMD_BEDBUG + INIT_FUNC_WATCHDOG_RESET + bedbug_init, +#endif +#if defined(CONFIG_PRAM) + initr_mem, +#endif +#ifdef CONFIG_EFI_SETUP_EARLY + (init_fnc_t)efi_init_obj_list, +#endif + run_main_loop, +}; + +void board_init_r(gd_t *new_gd, ulong dest_addr) +{ + /* + * Set up the new global data pointer. So far only x86 does this + * here. + * TODO(sjg@chromium.org): Consider doing this for all archs, or + * dropping the new_gd parameter. + */ +#if CONFIG_IS_ENABLED(X86_64) + arch_setup_gd(new_gd); +#endif + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + int i; +#endif + +#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + gd = new_gd; +#endif + gd->flags &= ~GD_FLG_LOG_READY; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++) + init_sequence_r[i] += gd->reloc_off; +#endif + + if (initcall_run_list(init_sequence_r)) + hang(); + + /* NOTREACHED - run_main_loop() does not return */ + hang(); +} diff --git a/roms/u-boot/common/boot_fit.c b/roms/u-boot/common/boot_fit.c new file mode 100644 index 000000000..dfc2a3117 --- /dev/null +++ b/roms/u-boot/common/boot_fit.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Texas Instruments, <www.ti.com> + * + * Franklin S Cooper Jr. <fcooper@ti.com> + */ + +#include <boot_fit.h> +#include <common.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <linux/libfdt.h> + +static int fdt_offset(const void *fit) +{ + int images, node, fdt_len, fdt_node, fdt_offset; + const char *fdt_name; + + node = fit_find_config_node(fit); + if (node < 0) + return node; + + images = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, images); + return -EINVAL; + } + + fdt_name = fdt_getprop(fit, node, FIT_FDT_PROP, &fdt_len); + if (!fdt_name) { + debug("%s: Cannot find fdt name property: %d\n", + __func__, fdt_len); + return -EINVAL; + } + + fdt_node = fdt_subnode_offset(fit, images, fdt_name); + if (fdt_node < 0) { + debug("%s: Cannot find fdt node '%s': %d\n", + __func__, fdt_name, fdt_node); + return -EINVAL; + } + + fdt_offset = fdt_getprop_u32(fit, fdt_node, "data-offset"); + + if (fdt_offset == FDT_ERROR) + return -ENOENT; + + fdt_len = fdt_getprop_u32(fit, fdt_node, "data-size"); + + if (fdt_len < 0) + return fdt_len; + + return fdt_offset; +} + +void *locate_dtb_in_fit(const void *fit) +{ + struct image_header *header; + int size; + int ret; + + size = fdt_totalsize(fit); + size = (size + 3) & ~3; + + header = (struct image_header *)fit; + + if (image_get_magic(header) != FDT_MAGIC) { + debug("No FIT image appended to U-boot\n"); + return NULL; + } + + ret = fdt_offset(fit); + + if (ret < 0) + return NULL; + else + return (void *)fit+size+ret; +} diff --git a/roms/u-boot/common/bootm.c b/roms/u-boot/common/bootm.c new file mode 100644 index 000000000..ea71522d0 --- /dev/null +++ b/roms/u-boot/common/bootm.c @@ -0,0 +1,1038 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <bootstage.h> +#include <cli.h> +#include <cpu_func.h> +#include <env.h> +#include <errno.h> +#include <fdt_support.h> +#include <irq_func.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <net.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/sizes.h> +#if defined(CONFIG_CMD_USB) +#include <usb.h> +#endif +#else +#include "mkimage.h" +#endif + +#include <command.h> +#include <bootm.h> +#include <image.h> + +#ifndef CONFIG_SYS_BOOTM_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN 0x800000 +#endif + +#define MAX_CMDLINE_SIZE SZ_4K + +#define IH_INITRD_ARCH IH_ARCH_DEFAULT + +#ifndef USE_HOSTCC + +DECLARE_GLOBAL_DATA_PTR; + +bootm_headers_t images; /* pointers to os/initrd/fdt images */ + +static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len); + +__weak void board_quiesce_devices(void) +{ +} + +#ifdef CONFIG_LMB +static void boot_start_lmb(bootm_headers_t *images) +{ + ulong mem_start; + phys_size_t mem_size; + + mem_start = env_get_bootm_low(); + mem_size = env_get_bootm_size(); + + lmb_init_and_reserve_range(&images->lmb, (phys_addr_t)mem_start, + mem_size, NULL); +} +#else +#define lmb_reserve(lmb, base, size) +static inline void boot_start_lmb(bootm_headers_t *images) { } +#endif + +static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + memset((void *)&images, 0, sizeof(images)); + images.verify = env_get_yesno("verify"); + + boot_start_lmb(&images); + + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); + images.state = BOOTM_STATE_START; + + return 0; +} + +static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const void *os_hdr; + bool ep_found = false; + int ret; + + /* get kernel image header, start address and length */ + os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, + &images, &images.os.image_start, &images.os.image_len); + if (images.os.image_len == 0) { + puts("ERROR: can't get kernel image!\n"); + return 1; + } + + /* get image parameters */ + switch (genimg_get_format(os_hdr)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + images.os.type = image_get_type(os_hdr); + images.os.comp = image_get_comp(os_hdr); + images.os.os = image_get_os(os_hdr); + + images.os.end = image_get_image_end(os_hdr); + images.os.load = image_get_load(os_hdr); + images.os.arch = image_get_arch(os_hdr); + break; +#endif +#if IMAGE_ENABLE_FIT + case IMAGE_FORMAT_FIT: + if (fit_image_get_type(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.type)) { + puts("Can't get image type!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_TYPE); + return 1; + } + + if (fit_image_get_comp(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.comp)) { + puts("Can't get image compression!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_COMPRESSION); + return 1; + } + + if (fit_image_get_os(images.fit_hdr_os, images.fit_noffset_os, + &images.os.os)) { + puts("Can't get image OS!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_OS); + return 1; + } + + if (fit_image_get_arch(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.arch)) { + puts("Can't get image ARCH!\n"); + return 1; + } + + images.os.end = fit_get_end(images.fit_hdr_os); + + if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os, + &images.os.load)) { + puts("Can't get image load address!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR); + return 1; + } + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = android_image_get_kcomp(os_hdr); + images.os.os = IH_OS_LINUX; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + images.ep = images.os.load; + ep_found = true; + break; +#endif + default: + puts("ERROR: unknown image format type!\n"); + return 1; + } + + /* If we have a valid setup.bin, we will use that for entry (x86) */ + if (images.os.arch == IH_ARCH_I386 || + images.os.arch == IH_ARCH_X86_64) { + ulong len; + + ret = boot_get_setup(&images, IH_ARCH_I386, &images.ep, &len); + if (ret < 0 && ret != -ENOENT) { + puts("Could not find a valid setup.bin for x86\n"); + return 1; + } + /* Kernel entry point is the setup.bin */ + } else if (images.legacy_hdr_valid) { + images.ep = image_get_ep(&images.legacy_hdr_os_copy); +#if IMAGE_ENABLE_FIT + } else if (images.fit_uname_os) { + int ret; + + ret = fit_image_get_entry(images.fit_hdr_os, + images.fit_noffset_os, &images.ep); + if (ret) { + puts("Can't get entry point property!\n"); + return 1; + } +#endif + } else if (!ep_found) { + puts("Could not find kernel entry point!\n"); + return 1; + } + + if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { + if (CONFIG_IS_ENABLED(CMD_BOOTI) && + images.os.arch == IH_ARCH_ARM64) { + ulong image_addr; + ulong image_size; + + ret = booti_setup(images.os.image_start, &image_addr, + &image_size, true); + if (ret != 0) + return 1; + + images.os.type = IH_TYPE_KERNEL; + images.os.load = image_addr; + images.ep = image_addr; + } else { + images.os.load = images.os.image_start; + images.ep += images.os.image_start; + } + } + + images.os.start = map_to_sysmem(os_hdr); + + return 0; +} + +/** + * bootm_find_images - wrapper to find and locate various images + * @flag: Ignored Argument + * @argc: command argument count + * @argv: command argument list + * @start: OS image start address + * @size: OS image size + * + * boot_find_images() will attempt to load an available ramdisk, + * flattened device tree, as well as specifically marked + * "loadable" images (loadables are FIT only) + * + * Note: bootm_find_images will skip an image if it is not found + * + * @return: + * 0, if all existing images were loaded correctly + * 1, if an image is found but corrupted, or invalid + */ +int bootm_find_images(int flag, int argc, char *const argv[], ulong start, + ulong size) +{ + int ret; + + /* find ramdisk */ + ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH, + &images.rd_start, &images.rd_end); + if (ret) { + puts("Ramdisk image is corrupt or invalid\n"); + return 1; + } + + /* check if ramdisk overlaps OS image */ + if (images.rd_start && (((ulong)images.rd_start >= start && + (ulong)images.rd_start < start + size) || + ((ulong)images.rd_end > start && + (ulong)images.rd_end <= start + size) || + ((ulong)images.rd_start < start && + (ulong)images.rd_end >= start + size))) { + printf("ERROR: RD image overlaps OS image (OS=0x%lx..0x%lx)\n", + start, start + size); + return 1; + } + +#if IMAGE_ENABLE_OF_LIBFDT + /* find flattened device tree */ + ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images, + &images.ft_addr, &images.ft_len); + if (ret) { + puts("Could not find a valid device tree\n"); + return 1; + } + + /* check if FDT overlaps OS image */ + if (images.ft_addr && + (((ulong)images.ft_addr >= start && + (ulong)images.ft_addr <= start + size) || + ((ulong)images.ft_addr + images.ft_len >= start && + (ulong)images.ft_addr + images.ft_len <= start + size))) { + printf("ERROR: FDT image overlaps OS image (OS=0x%lx..0x%lx)\n", + start, start + size); + return 1; + } + + if (CONFIG_IS_ENABLED(CMD_FDT)) + set_working_fdt_addr(map_to_sysmem(images.ft_addr)); +#endif + +#if IMAGE_ENABLE_FIT +#if defined(CONFIG_FPGA) + /* find bitstreams */ + ret = boot_get_fpga(argc, argv, &images, IH_ARCH_DEFAULT, + NULL, NULL); + if (ret) { + printf("FPGA image is corrupted or invalid\n"); + return 1; + } +#endif + + /* find all of the loadables */ + ret = boot_get_loadable(argc, argv, &images, IH_ARCH_DEFAULT, + NULL, NULL); + if (ret) { + printf("Loadable(s) is corrupt or invalid\n"); + return 1; + } +#endif + + return 0; +} + +static int bootm_find_other(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (((images.os.type == IH_TYPE_KERNEL) || + (images.os.type == IH_TYPE_KERNEL_NOLOAD) || + (images.os.type == IH_TYPE_MULTI)) && + (images.os.os == IH_OS_LINUX || + images.os.os == IH_OS_VXWORKS)) + return bootm_find_images(flag, argc, argv, 0, 0); + + return 0; +} +#endif /* USE_HOSTC */ + +#if !defined(USE_HOSTCC) || defined(CONFIG_FIT_SIGNATURE) +/** + * handle_decomp_error() - display a decompression error + * + * This function tries to produce a useful message. In the case where the + * uncompressed size is the same as the available space, we can assume that + * the image is too large for the buffer. + * + * @comp_type: Compression type being used (IH_COMP_...) + * @uncomp_size: Number of bytes uncompressed + * @ret: errno error code received from compression library + * @return Appropriate BOOTM_ERR_ error code + */ +static int handle_decomp_error(int comp_type, size_t uncomp_size, int ret) +{ + const char *name = genimg_get_comp_name(comp_type); + + /* ENOSYS means unimplemented compression type, don't reset. */ + if (ret == -ENOSYS) + return BOOTM_ERR_UNIMPLEMENTED; + + if (uncomp_size >= CONFIG_SYS_BOOTM_LEN) + printf("Image too large: increase CONFIG_SYS_BOOTM_LEN\n"); + else + printf("%s: uncompress error %d\n", name, ret); + + /* + * The decompression routines are now safe, so will not write beyond + * their bounds. Probably it is not necessary to reset, but maintain + * the current behaviour for now. + */ + printf("Must RESET board to recover\n"); +#ifndef USE_HOSTCC + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); +#endif + + return BOOTM_ERR_RESET; +} +#endif + +#ifndef USE_HOSTCC +static int bootm_load_os(bootm_headers_t *images, int boot_progress) +{ + image_info_t os = images->os; + ulong load = os.load; + ulong load_end; + ulong blob_start = os.start; + ulong blob_end = os.end; + ulong image_start = os.image_start; + ulong image_len = os.image_len; + ulong flush_start = ALIGN_DOWN(load, ARCH_DMA_MINALIGN); + bool no_overlap; + void *load_buf, *image_buf; + int err; + + load_buf = map_sysmem(load, 0); + image_buf = map_sysmem(os.image_start, image_len); + err = image_decomp(os.comp, load, os.image_start, os.type, + load_buf, image_buf, image_len, + CONFIG_SYS_BOOTM_LEN, &load_end); + if (err) { + err = handle_decomp_error(os.comp, load_end - load, err); + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return err; + } + /* We need the decompressed image size in the next steps */ + images->os.image_len = load_end - load; + + flush_cache(flush_start, ALIGN(load_end, ARCH_DMA_MINALIGN) - flush_start); + + debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, load_end); + bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); + + no_overlap = (os.comp == IH_COMP_NONE && load == image_start); + + if (!no_overlap && load < blob_end && load_end > blob_start) { + debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n", + blob_start, blob_end); + debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load, + load_end); + + /* Check what type of image this is. */ + if (images->legacy_hdr_valid) { + if (image_get_type(&images->legacy_hdr_os_copy) + == IH_TYPE_MULTI) + puts("WARNING: legacy format multi component image overwritten\n"); + return BOOTM_ERR_OVERLAP; + } else { + puts("ERROR: new format image overwritten - must RESET the board to recover\n"); + bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); + return BOOTM_ERR_RESET; + } + } + + lmb_reserve(&images->lmb, images->os.load, (load_end - + images->os.load)); + return 0; +} + +/** + * bootm_disable_interrupts() - Disable interrupts in preparation for load/boot + * + * @return interrupt flag (0 if interrupts were disabled, non-zero if they were + * enabled) + */ +ulong bootm_disable_interrupts(void) +{ + ulong iflag; + + /* + * We have reached the point of no return: we are going to + * overwrite all exception vector code, so we cannot easily + * recover from any failures any more... + */ + iflag = disable_interrupts(); +#ifdef CONFIG_NETCONSOLE + /* Stop the ethernet stack if NetConsole could have left it up */ + eth_halt(); +# ifndef CONFIG_DM_ETH + eth_unregister(eth_get_dev()); +# endif +#endif + +#if defined(CONFIG_CMD_USB) + /* + * turn off USB to prevent the host controller from writing to the + * SDRAM while Linux is booting. This could happen (at least for OHCI + * controller), because the HCCA (Host Controller Communication Area) + * lies within the SDRAM and the host controller writes continously to + * this area (as busmaster!). The HccaFrameNumber is for example + * updated every 1 ms within the HCCA structure in SDRAM! For more + * details see the OpenHCI specification. + */ + usb_stop(); +#endif + return iflag; +} + +#define CONSOLE_ARG "console=" +#define CONSOLE_ARG_SIZE sizeof(CONSOLE_ARG) + +/** + * fixup_silent_linux() - Handle silencing the linux boot if required + * + * This uses the silent_linux envvar to control whether to add/set a "console=" + * parameter to the command line + * + * @buf: Buffer containing the string to process + * @maxlen: Maximum length of buffer + * @return 0 if OK, -ENOSPC if @maxlen is too small + */ +static int fixup_silent_linux(char *buf, int maxlen) +{ + int want_silent; + char *cmdline; + int size; + + /* + * Move the input string to the end of buffer. The output string will be + * built up at the start. + */ + size = strlen(buf) + 1; + if (size * 2 > maxlen) + return -ENOSPC; + cmdline = buf + maxlen - size; + memmove(cmdline, buf, size); + /* + * Only fix cmdline when requested. The environment variable can be: + * + * no - we never fixup + * yes - we always fixup + * unset - we rely on the console silent flag + */ + want_silent = env_get_yesno("silent_linux"); + if (want_silent == 0) + return 0; + else if (want_silent == -1 && !(gd->flags & GD_FLG_SILENT)) + return 0; + + debug("before silent fix-up: %s\n", cmdline); + if (*cmdline) { + char *start = strstr(cmdline, CONSOLE_ARG); + + /* Check space for maximum possible new command line */ + if (size + CONSOLE_ARG_SIZE > maxlen) + return -ENOSPC; + + if (start) { + char *end = strchr(start, ' '); + int start_bytes; + + start_bytes = start - cmdline + CONSOLE_ARG_SIZE - 1; + strncpy(buf, cmdline, start_bytes); + if (end) + strcpy(buf + start_bytes, end); + else + buf[start_bytes] = '\0'; + } else { + sprintf(buf, "%s %s", cmdline, CONSOLE_ARG); + } + if (buf + strlen(buf) >= cmdline) + return -ENOSPC; + } else { + if (maxlen < sizeof(CONSOLE_ARG)) + return -ENOSPC; + strcpy(buf, CONSOLE_ARG); + } + debug("after silent fix-up: %s\n", buf); + + return 0; +} + +/** + * process_subst() - Handle substitution of ${...} fields in the environment + * + * Handle variable substitution in the provided buffer + * + * @buf: Buffer containing the string to process + * @maxlen: Maximum length of buffer + * @return 0 if OK, -ENOSPC if @maxlen is too small + */ +static int process_subst(char *buf, int maxlen) +{ + char *cmdline; + int size; + int ret; + + /* Move to end of buffer */ + size = strlen(buf) + 1; + cmdline = buf + maxlen - size; + if (buf + size > cmdline) + return -ENOSPC; + memmove(cmdline, buf, size); + + ret = cli_simple_process_macros(cmdline, buf, cmdline - buf); + + return ret; +} + +int bootm_process_cmdline(char *buf, int maxlen, int flags) +{ + int ret; + + /* Check config first to enable compiler to eliminate code */ + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && + !IS_ENABLED(CONFIG_SILENT_U_BOOT_ONLY) && + (flags & BOOTM_CL_SILENT)) { + ret = fixup_silent_linux(buf, maxlen); + if (ret) + return log_msg_ret("silent", ret); + } + if (IS_ENABLED(CONFIG_BOOTARGS_SUBST) && IS_ENABLED(CONFIG_CMDLINE) && + (flags & BOOTM_CL_SUBST)) { + ret = process_subst(buf, maxlen); + if (ret) + return log_msg_ret("subst", ret); + } + + return 0; +} + +int bootm_process_cmdline_env(int flags) +{ + const int maxlen = MAX_CMDLINE_SIZE; + bool do_silent; + const char *env; + char *buf; + int ret; + + /* First check if any action is needed */ + do_silent = IS_ENABLED(CONFIG_SILENT_CONSOLE) && + !IS_ENABLED(CONFIG_SILENT_U_BOOT_ONLY) && (flags & BOOTM_CL_SILENT); + if (!do_silent && !IS_ENABLED(CONFIG_BOOTARGS_SUBST)) + return 0; + + env = env_get("bootargs"); + if (env && strlen(env) >= maxlen) + return -E2BIG; + buf = malloc(maxlen); + if (!buf) + return -ENOMEM; + if (env) + strcpy(buf, env); + else + *buf = '\0'; + ret = bootm_process_cmdline(buf, maxlen, flags); + if (!ret) { + ret = env_set("bootargs", buf); + + /* + * If buf is "" and bootargs does not exist, this will produce + * an error trying to delete bootargs. Ignore it + */ + if (ret == -ENOENT) + ret = 0; + } + free(buf); + if (ret) + return log_msg_ret("env", ret); + + return 0; +} + +/** + * Execute selected states of the bootm command. + * + * Note the arguments to this state must be the first argument, Any 'bootm' + * or sub-command arguments must have already been taken. + * + * Note that if states contains more than one flag it MUST contain + * BOOTM_STATE_START, since this handles and consumes the command line args. + * + * Also note that aside from boot_os_fn functions and bootm_load_os no other + * functions we store the return value of in 'ret' may use a negative return + * value, without special handling. + * + * @param cmdtp Pointer to bootm command table entry + * @param flag Command flags (CMD_FLAG_...) + * @param argc Number of subcommand arguments (0 = no arguments) + * @param argv Arguments + * @param states Mask containing states to run (BOOTM_STATE_...) + * @param images Image header information + * @param boot_progress 1 to show boot progress, 0 to not do this + * @return 0 if ok, something else on error. Some errors will cause this + * function to perform a reboot! If states contains BOOTM_STATE_OS_GO + * then the intent is to boot an OS, so this function will not return + * unless the image type is standalone. + */ +int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int states, bootm_headers_t *images, + int boot_progress) +{ + boot_os_fn *boot_fn; + ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; + + /* + * Work through the states and see how far we get. We stop on + * any error. + */ + if (states & BOOTM_STATE_START) + ret = bootm_start(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOS)) + ret = bootm_find_os(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOTHER)) + ret = bootm_find_other(cmdtp, flag, argc, argv); + + /* Load the OS */ + if (!ret && (states & BOOTM_STATE_LOADOS)) { + iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, 0); + if (ret && ret != BOOTM_ERR_OVERLAP) + goto err; + else if (ret == BOOTM_ERR_OVERLAP) + ret = 0; + } + + /* Relocate the ramdisk */ +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + if (!ret && (states & BOOTM_STATE_RAMDISK)) { + ulong rd_len = images->rd_end - images->rd_start; + + ret = boot_ramdisk_high(&images->lmb, images->rd_start, + rd_len, &images->initrd_start, &images->initrd_end); + if (!ret) { + env_set_hex("initrd_start", images->initrd_start); + env_set_hex("initrd_end", images->initrd_end); + } + } +#endif +#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB) + if (!ret && (states & BOOTM_STATE_FDT)) { + boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); + ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, + &images->ft_len); + } +#endif + + /* From now on, we need the OS boot function */ + if (ret) + return ret; + boot_fn = bootm_os_get_boot_func(images->os.os); + need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { + if (iflag) + enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); + return 1; + } + + + /* Call various other states that are not generally used */ + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_BD_T)) + ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_PREP)) { + ret = bootm_process_cmdline_env(images->os.os == IH_OS_LINUX); + if (ret) { + printf("Cmdline setup failed (err=%d)\n", ret); + ret = CMD_RET_FAILURE; + goto err; + } + ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); + } + +#ifdef CONFIG_TRACE + /* Pretend to run the OS, then run a user command */ + if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + char *cmd_list = env_get("fakegocmd"); + + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, + images, boot_fn); + if (!ret && cmd_list) + ret = run_command_list(cmd_list, -1, flag); + } +#endif + + /* Check for unsupported subcommand. */ + if (ret) { + puts("subcommand not supported\n"); + return ret; + } + + /* Now run the OS! We hope this doesn't return */ + if (!ret && (states & BOOTM_STATE_OS_GO)) + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, + images, boot_fn); + + /* Deal with any fallout */ +err: + if (iflag) + enable_interrupts(); + + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + + return ret; +} + +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +/** + * image_get_kernel - verify legacy format kernel image + * @img_addr: in RAM address of the legacy format image to be verified + * @verify: data CRC verification flag + * + * image_get_kernel() verifies legacy image integrity and returns pointer to + * legacy image header if image verification was completed successfully. + * + * returns: + * pointer to a legacy image header if valid image was found + * otherwise return NULL + */ +static image_header_t *image_get_kernel(ulong img_addr, int verify) +{ + image_header_t *hdr = (image_header_t *)img_addr; + + if (!image_check_magic(hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC); + return NULL; + } + bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER); + + if (!image_check_hcrc(hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_HEADER); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM); + image_print_contents(hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH); + + if (!image_check_target_arch(hdr)) { + printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr)); + bootstage_error(BOOTSTAGE_ID_CHECK_ARCH); + return NULL; + } + return hdr; +} +#endif + +/** + * boot_get_kernel - find kernel image + * @os_data: pointer to a ulong variable, will hold os data start address + * @os_len: pointer to a ulong variable, will hold os data length + * + * boot_get_kernel() tries to find a kernel image, verifies its integrity + * and locates kernel data. + * + * returns: + * pointer to image header if valid image was found, plus kernel start + * address and length, otherwise NULL + */ +static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len) +{ +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + image_header_t *hdr; +#endif + ulong img_addr; + const void *buf; + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; +#if IMAGE_ENABLE_FIT + int os_noffset; +#endif + + img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], + &fit_uname_config, + &fit_uname_kernel); + + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); + + /* check image type, for FIT images get FIT kernel node */ + *os_data = *os_len = 0; + buf = map_sysmem(img_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + printf("## Booting kernel from Legacy Image at %08lx ...\n", + img_addr); + hdr = image_get_kernel(img_addr, images->verify); + if (!hdr) + return NULL; + bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE); + + /* get os_data and os_len */ + switch (image_get_type(hdr)) { + case IH_TYPE_KERNEL: + case IH_TYPE_KERNEL_NOLOAD: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + case IH_TYPE_MULTI: + image_multi_getimg(hdr, 0, os_data, os_len); + break; + case IH_TYPE_STANDALONE: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + default: + printf("Wrong Image Type for %s command\n", + cmdtp->name); + bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); + return NULL; + } + + /* + * copy image header to allow for image overwrites during + * kernel decompression. + */ + memmove(&images->legacy_hdr_os_copy, hdr, + sizeof(image_header_t)); + + /* save pointer to image header */ + images->legacy_hdr_os = hdr; + + images->legacy_hdr_valid = 1; + bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE); + break; +#endif +#if IMAGE_ENABLE_FIT + case IMAGE_FORMAT_FIT: + os_noffset = fit_image_load(images, img_addr, + &fit_uname_kernel, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_KERNEL, + BOOTSTAGE_ID_FIT_KERNEL_START, + FIT_LOAD_IGNORED, os_data, os_len); + if (os_noffset < 0) + return NULL; + + images->fit_hdr_os = map_sysmem(img_addr, 0); + images->fit_uname_os = fit_uname_kernel; + images->fit_uname_cfg = fit_uname_config; + images->fit_noffset_os = os_noffset; + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel(buf, images->verify, + os_data, os_len)) + return NULL; + break; +#endif + default: + printf("Wrong Image Format for %s command\n", cmdtp->name); + bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); + return NULL; + } + + debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", + *os_data, *os_len, *os_len); + + return buf; +} + +/** + * switch_to_non_secure_mode() - switch to non-secure mode + * + * This routine is overridden by architectures requiring this feature. + */ +void __weak switch_to_non_secure_mode(void) +{ +} + +#else /* USE_HOSTCC */ + +#if defined(CONFIG_FIT_SIGNATURE) +static int bootm_host_load_image(const void *fit, int req_image_type, + int cfg_noffset) +{ + const char *fit_uname_config = NULL; + ulong data, len; + bootm_headers_t images; + int noffset; + ulong load_end; + uint8_t image_type; + uint8_t imape_comp; + void *load_buf; + int ret; + + fit_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + memset(&images, '\0', sizeof(images)); + images.verify = 1; + noffset = fit_image_load(&images, (ulong)fit, + NULL, &fit_uname_config, + IH_ARCH_DEFAULT, req_image_type, -1, + FIT_LOAD_IGNORED, &data, &len); + if (noffset < 0) + return noffset; + if (fit_image_get_type(fit, noffset, &image_type)) { + puts("Can't get image type!\n"); + return -EINVAL; + } + + if (fit_image_get_comp(fit, noffset, &imape_comp)) { + puts("Can't get image compression!\n"); + return -EINVAL; + } + + /* Allow the image to expand by a factor of 4, should be safe */ + load_buf = malloc((1 << 20) + len * 4); + ret = image_decomp(imape_comp, 0, data, image_type, load_buf, + (void *)data, len, CONFIG_SYS_BOOTM_LEN, + &load_end); + free(load_buf); + + if (ret) { + ret = handle_decomp_error(imape_comp, load_end - 0, ret); + if (ret != BOOTM_ERR_UNIMPLEMENTED) + return ret; + } + + return 0; +} + +int bootm_host_load_images(const void *fit, int cfg_noffset) +{ + static uint8_t image_types[] = { + IH_TYPE_KERNEL, + IH_TYPE_FLATDT, + IH_TYPE_RAMDISK, + }; + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(image_types); i++) { + int ret; + + ret = bootm_host_load_image(fit, image_types[i], cfg_noffset); + if (!err && ret && ret != -ENOENT) + err = ret; + } + + /* Return the first error we found */ + return err; +} +#endif + +#endif /* ndef USE_HOSTCC */ diff --git a/roms/u-boot/common/bootm_os.c b/roms/u-boot/common/bootm_os.c new file mode 100644 index 000000000..0b6325db6 --- /dev/null +++ b/roms/u-boot/common/bootm_os.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <bootm.h> +#include <bootstage.h> +#include <cpu_func.h> +#include <efi_loader.h> +#include <env.h> +#include <fdt_support.h> +#include <image.h> +#include <lmb.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <mapmem.h> +#include <vxworks.h> +#include <tee/optee.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int do_bootm_standalone(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *s; + int (*appl)(int, char *const[]); + + /* Don't start if "autostart" is set to "no" */ + s = env_get("autostart"); + if ((s != NULL) && !strcmp(s, "no")) { + env_set_hex("filesize", images->os.image_len); + return 0; + } + appl = (int (*)(int, char * const []))images->ep; + appl(argc, argv); + return 0; +} + +/*******************************************************************/ +/* OS booting routines */ +/*******************************************************************/ + +#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) +static void copy_args(char *dest, int argc, char *const argv[], char delim) +{ + int i; + + for (i = 0; i < argc; i++) { + if (i > 0) + *dest++ = delim; + strcpy(dest, argv[i]); + dest += strlen(argv[i]); + } +} +#endif + +#ifdef CONFIG_BOOTM_NETBSD +static int do_bootm_netbsd(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*loader)(struct bd_info *, image_header_t *, char *, char *); + image_header_t *os_hdr, *hdr; + ulong kernel_data, kernel_len; + char *cmdline; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("NetBSD"); + return 1; + } +#endif + hdr = images->legacy_hdr_os; + + /* + * Booting a (NetBSD) kernel image + * + * This process is pretty similar to a standalone application: + * The (first part of an multi-) image must be a stage-2 loader, + * which in turn is responsible for loading & invoking the actual + * kernel. The only differences are the parameters being passed: + * besides the board info strucure, the loader expects a command + * line, the name of the console device, and (optionally) the + * address of the original image header. + */ + os_hdr = NULL; + if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { + image_multi_getimg(hdr, 1, &kernel_data, &kernel_len); + if (kernel_len) + os_hdr = hdr; + } + + if (argc > 0) { + ulong len; + int i; + + for (i = 0, len = 0; i < argc; i += 1) + len += strlen(argv[i]) + 1; + cmdline = malloc(len); + copy_args(cmdline, argc, argv, ' '); + } else { + cmdline = env_get("bootargs"); + if (cmdline == NULL) + cmdline = ""; + } + + loader = (void (*)(struct bd_info *, image_header_t *, char *, char *))images->ep; + + printf("## Transferring control to NetBSD stage-2 loader (at address %08lx) ...\n", + (ulong)loader); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * NetBSD Stage-2 Loader Parameters: + * arg[0]: pointer to board info data + * arg[1]: image load address + * arg[2]: char pointer to the console device to use + * arg[3]: char pointer to the boot arguments + */ + (*loader)(gd->bd, os_hdr, "", cmdline); + + return 1; +} +#endif /* CONFIG_BOOTM_NETBSD*/ + +#ifdef CONFIG_LYNXKDI +static int do_bootm_lynxkdi(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + image_header_t *hdr = &images->legacy_hdr_os_copy; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Lynx"); + return 1; + } +#endif + + lynxkdi_boot((image_header_t *)hdr); + + return 1; +} +#endif /* CONFIG_LYNXKDI */ + +#ifdef CONFIG_BOOTM_RTEMS +static int do_bootm_rtems(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(struct bd_info *); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("RTEMS"); + return 1; + } +#endif + + entry_point = (void (*)(struct bd_info *))images->ep; + + printf("## Transferring control to RTEMS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * RTEMS Parameters: + * r3: ptr to board info data + */ + (*entry_point)(gd->bd); + + return 1; +} +#endif /* CONFIG_BOOTM_RTEMS */ + +#if defined(CONFIG_BOOTM_OSE) +static int do_bootm_ose(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("OSE"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OSE (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OSE Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_OSE */ + +#if defined(CONFIG_BOOTM_PLAN9) +static int do_bootm_plan9(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + char *s; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Plan 9"); + return 1; + } +#endif + + /* See README.plan9 */ + s = env_get("confaddr"); + if (s != NULL) { + char *confaddr = (char *)simple_strtoul(s, NULL, 16); + + if (argc > 0) { + copy_args(confaddr, argc, argv, '\n'); + } else { + s = env_get("bootargs"); + if (s != NULL) + strcpy(confaddr, s); + } + } + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to Plan 9 (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * Plan 9 Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_PLAN9 */ + +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + +static void do_bootvx_fdt(bootm_headers_t *images) +{ +#if defined(CONFIG_OF_LIBFDT) + int ret; + char *bootline; + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + + if (*of_flat_tree) { + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return; + + /* Update ethernet nodes */ + fdt_fixup_ethernet(*of_flat_tree); + + ret = fdt_add_subnode(*of_flat_tree, 0, "chosen"); + if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) { + bootline = env_get("bootargs"); + if (bootline) { + ret = fdt_find_and_setprop(*of_flat_tree, + "/chosen", "bootargs", + bootline, + strlen(bootline) + 1, 1); + if (ret < 0) { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } + } else { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } +#endif + + boot_prep_vxworks(images); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) + printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n", + (ulong)images->ep, (ulong)*of_flat_tree); +#else + printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep); +#endif + + boot_jump_vxworks(images); + + puts("## vxWorks terminated\n"); +} + +static int do_bootm_vxworks_legacy(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("VxWorks"); + return 1; + } +#endif + + do_bootvx_fdt(images); + + return 1; +} + +int do_bootm_vxworks(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *bootargs; + int pos; + unsigned long vxflags; + bool std_dtb = false; + + /* get bootargs env */ + bootargs = env_get("bootargs"); + + if (bootargs != NULL) { + for (pos = 0; pos < strlen(bootargs); pos++) { + /* find f=0xnumber flag */ + if ((bootargs[pos] == '=') && (pos >= 1) && + (bootargs[pos - 1] == 'f')) { + vxflags = simple_strtoul(&bootargs[pos + 1], + NULL, 16); + if (vxflags & VXWORKS_SYSFLG_STD_DTB) + std_dtb = true; + } + } + } + + if (std_dtb) { + if (flag & BOOTM_STATE_OS_PREP) + printf(" Using standard DTB\n"); + return do_bootm_linux(flag, argc, argv, images); + } else { + if (flag & BOOTM_STATE_OS_PREP) + printf(" !!! WARNING !!! Using legacy DTB\n"); + return do_bootm_vxworks_legacy(flag, argc, argv, images); + } +} +#endif + +#if defined(CONFIG_CMD_ELF) +static int do_bootm_qnxelf(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + char *local_args[2]; + char str[16]; + int dcache; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("QNX"); + return 1; + } +#endif + + sprintf(str, "%lx", images->ep); /* write entry-point into string */ + local_args[0] = argv[0]; + local_args[1] = str; /* and provide it via the arguments */ + + /* + * QNX images require the data cache is disabled. + */ + dcache = dcache_status(); + if (dcache) + dcache_disable(); + + do_bootelf(NULL, 0, 2, local_args); + + if (dcache) + dcache_enable(); + + return 1; +} +#endif + +#ifdef CONFIG_INTEGRITY +static int do_bootm_integrity(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("INTEGRITY"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to INTEGRITY (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * INTEGRITY Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +#ifdef CONFIG_BOOTM_OPENRTOS +static int do_bootm_openrtos(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OpenRTOS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OpenRTOS Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +#ifdef CONFIG_BOOTM_OPTEE +static int do_bootm_tee(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + int ret; + + /* Verify OS type */ + if (images->os.os != IH_OS_TEE) { + return 1; + }; + + /* Validate OPTEE header */ + ret = optee_verify_bootm_image(images->os.image_start, + images->os.load, + images->os.image_len); + if (ret) + return ret; + + /* Locate FDT etc */ + ret = bootm_find_images(flag, argc, argv, 0, 0); + if (ret) + return ret; + + /* From here we can run the regular linux boot path */ + return do_bootm_linux(flag, argc, argv, images); +} +#endif + +#ifdef CONFIG_BOOTM_EFI +static int do_bootm_efi(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + int ret; + efi_status_t efi_ret; + void *image_buf; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + /* Locate FDT, if provided */ + ret = bootm_find_images(flag, argc, argv, 0, 0); + if (ret) + return ret; + + /* Initialize EFI drivers */ + efi_ret = efi_init_obj_list(); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to initialize UEFI sub-system: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Install device tree */ + efi_ret = efi_install_fdt(images->ft_len + ? images->ft_addr : EFI_FDT_USE_INTERNAL); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to install device tree: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Run EFI image */ + printf("## Transferring control to EFI (at address %08lx) ...\n", + images->ep); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* We expect to return */ + images->os.type = IH_TYPE_STANDALONE; + + image_buf = map_sysmem(images->ep, images->os.image_len); + + efi_ret = efi_run_image(image_buf, images->os.image_len); + if (efi_ret != EFI_SUCCESS) + return 1; + return 0; +} +#endif + +static boot_os_fn *boot_os[] = { + [IH_OS_U_BOOT] = do_bootm_standalone, +#ifdef CONFIG_BOOTM_LINUX + [IH_OS_LINUX] = do_bootm_linux, +#endif +#ifdef CONFIG_BOOTM_NETBSD + [IH_OS_NETBSD] = do_bootm_netbsd, +#endif +#ifdef CONFIG_LYNXKDI + [IH_OS_LYNXOS] = do_bootm_lynxkdi, +#endif +#ifdef CONFIG_BOOTM_RTEMS + [IH_OS_RTEMS] = do_bootm_rtems, +#endif +#if defined(CONFIG_BOOTM_OSE) + [IH_OS_OSE] = do_bootm_ose, +#endif +#if defined(CONFIG_BOOTM_PLAN9) + [IH_OS_PLAN9] = do_bootm_plan9, +#endif +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM) || defined(CONFIG_RISCV)) + [IH_OS_VXWORKS] = do_bootm_vxworks, +#endif +#if defined(CONFIG_CMD_ELF) + [IH_OS_QNX] = do_bootm_qnxelf, +#endif +#ifdef CONFIG_INTEGRITY + [IH_OS_INTEGRITY] = do_bootm_integrity, +#endif +#ifdef CONFIG_BOOTM_OPENRTOS + [IH_OS_OPENRTOS] = do_bootm_openrtos, +#endif +#ifdef CONFIG_BOOTM_OPTEE + [IH_OS_TEE] = do_bootm_tee, +#endif +#ifdef CONFIG_BOOTM_EFI + [IH_OS_EFI] = do_bootm_efi, +#endif +}; + +/* Allow for arch specific config before we boot */ +__weak void arch_preboot_os(void) +{ + /* please define platform specific arch_preboot_os() */ +} + +/* Allow for board specific config before we boot */ +__weak void board_preboot_os(void) +{ + /* please define board specific board_preboot_os() */ +} + +int boot_selected_os(int argc, char *const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) +{ + arch_preboot_os(); + board_preboot_os(); + boot_fn(state, argc, argv, images); + + /* Stand-alone may return when 'autostart' is 'no' */ + if (images->os.type == IH_TYPE_STANDALONE || + IS_ENABLED(CONFIG_SANDBOX) || + state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ + return 0; + bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); + debug("\n## Control returned to monitor - resetting...\n"); + + return BOOTM_ERR_RESET; +} + +boot_os_fn *bootm_os_get_boot_func(int os) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static bool relocated; + + if (!relocated) { + int i; + + /* relocate boot function table */ + for (i = 0; i < ARRAY_SIZE(boot_os); i++) + if (boot_os[i] != NULL) + boot_os[i] += gd->reloc_off; + + relocated = true; + } +#endif + return boot_os[os]; +} diff --git a/roms/u-boot/common/bootretry.c b/roms/u-boot/common/bootretry.c new file mode 100644 index 000000000..dac891fbc --- /dev/null +++ b/roms/u-boot/common/bootretry.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <env.h> +#include <errno.h> +#include <time.h> +#include <watchdog.h> + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +static uint64_t endtime; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ + +/*************************************************************************** + * initialize command line timeout + */ +void bootretry_init_cmd_timeout(void) +{ + char *s = env_get("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void bootretry_reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} + +int bootretry_tstc_timeout(void) +{ + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return -ETIMEDOUT; + WATCHDOG_RESET(); + } + + return 0; +} + +void bootretry_dont_retry(void) +{ + retry_time = -1; +} diff --git a/roms/u-boot/common/bootstage.c b/roms/u-boot/common/bootstage.c new file mode 100644 index 000000000..462110568 --- /dev/null +++ b/roms/u-boot/common/bootstage.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011, Google Inc. All rights reserved. + */ + + +/* + * This module records the progress of boot and arbitrary commands, and + * permits accurate timestamping of each. + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include <common.h> +#include <bootstage.h> +#include <hang.h> +#include <log.h> +#include <malloc.h> +#include <sort.h> +#include <spl.h> +#include <asm/global_data.h> +#include <linux/compiler.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + RECORD_COUNT = CONFIG_VAL(BOOTSTAGE_RECORD_COUNT), +}; + +struct bootstage_record { + ulong time_us; + uint32_t start_us; + const char *name; + int flags; /* see enum bootstage_flags */ + enum bootstage_id id; +}; + +struct bootstage_data { + uint rec_count; + uint next_id; + struct bootstage_record record[RECORD_COUNT]; +}; + +enum { + BOOTSTAGE_VERSION = 0, + BOOTSTAGE_MAGIC = 0xb00757a3, + BOOTSTAGE_DIGITS = 9, +}; + +struct bootstage_hdr { + u32 version; /* BOOTSTAGE_VERSION */ + u32 count; /* Number of records */ + u32 size; /* Total data size (non-zero if valid) */ + u32 magic; /* Magic number */ + u32 next_id; /* Next ID to use for bootstage */ +}; + +int bootstage_relocate(void) +{ + struct bootstage_data *data = gd->bootstage; + int i; + char *ptr; + + /* Figure out where to relocate the strings to */ + ptr = (char *)(data + 1); + + /* + * Duplicate all strings. They may point to an old location in the + * program .text section that can eventually get trashed. + */ + debug("Relocating %d records\n", data->rec_count); + for (i = 0; i < data->rec_count; i++) { + const char *from = data->record[i].name; + + strcpy(ptr, from); + data->record[i].name = ptr; + ptr += strlen(ptr) + 1; + } + + return 0; +} + +struct bootstage_record *find_id(struct bootstage_data *data, + enum bootstage_id id) +{ + struct bootstage_record *rec; + struct bootstage_record *end; + + for (rec = data->record, end = rec + data->rec_count; rec < end; + rec++) { + if (rec->id == id) + return rec; + } + + return NULL; +} + +struct bootstage_record *ensure_id(struct bootstage_data *data, + enum bootstage_id id) +{ + struct bootstage_record *rec; + + rec = find_id(data, id); + if (!rec && data->rec_count < RECORD_COUNT) { + rec = &data->record[data->rec_count++]; + rec->id = id; + return rec; + } + + return rec; +} + +ulong bootstage_add_record(enum bootstage_id id, const char *name, + int flags, ulong mark) +{ + struct bootstage_data *data = gd->bootstage; + struct bootstage_record *rec; + + /* + * initf_bootstage() is called very early during boot but since hang() + * calls bootstage_error() we can be called before bootstage is set up. + * Add a check to avoid this. + */ + if (!data) + return mark; + if (flags & BOOTSTAGEF_ALLOC) + id = data->next_id++; + + /* Only record the first event for each */ + rec = find_id(data, id); + if (!rec) { + if (data->rec_count < RECORD_COUNT) { + rec = &data->record[data->rec_count++]; + rec->time_us = mark; + rec->name = name; + rec->flags = flags; + rec->id = id; + } else { + log_warning("Bootstage space exhasuted\n"); + } + } + + /* Tell the board about this progress */ + show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id); + + return mark; +} + + +ulong bootstage_mark(enum bootstage_id id) +{ + return bootstage_add_record(id, NULL, 0, timer_get_boot_us()); +} + +ulong bootstage_error(enum bootstage_id id) +{ + return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR, + timer_get_boot_us()); +} + +ulong bootstage_mark_name(enum bootstage_id id, const char *name) +{ + int flags = 0; + + if (id == BOOTSTAGE_ID_ALLOC) + flags = BOOTSTAGEF_ALLOC; + + return bootstage_add_record(id, name, flags, timer_get_boot_us()); +} + +ulong bootstage_mark_code(const char *file, const char *func, int linenum) +{ + char *str, *p; + __maybe_unused char *end; + int len = 0; + + /* First work out the length we need to allocate */ + if (linenum != -1) + len = 11; + if (func) + len += strlen(func); + if (file) + len += strlen(file); + + str = malloc(len + 1); + p = str; + end = p + len; + if (file) + p += snprintf(p, end - p, "%s,", file); + if (linenum != -1) + p += snprintf(p, end - p, "%d", linenum); + if (func) + p += snprintf(p, end - p, ": %s", func); + + return bootstage_mark_name(BOOTSTAGE_ID_ALLOC, str); +} + +uint32_t bootstage_start(enum bootstage_id id, const char *name) +{ + struct bootstage_data *data = gd->bootstage; + struct bootstage_record *rec = ensure_id(data, id); + ulong start_us = timer_get_boot_us(); + + if (rec) { + rec->start_us = start_us; + rec->name = name; + } + + return start_us; +} + +uint32_t bootstage_accum(enum bootstage_id id) +{ + struct bootstage_data *data = gd->bootstage; + struct bootstage_record *rec = ensure_id(data, id); + uint32_t duration; + + if (!rec) + return 0; + duration = (uint32_t)timer_get_boot_us() - rec->start_us; + rec->time_us += duration; + + return duration; +} + +/** + * Get a record name as a printable string + * + * @param buf Buffer to put name if needed + * @param len Length of buffer + * @param rec Boot stage record to get the name from + * @return pointer to name, either from the record or pointing to buf. + */ +static const char *get_record_name(char *buf, int len, + const struct bootstage_record *rec) +{ + if (rec->name) + return rec->name; + else if (rec->id >= BOOTSTAGE_ID_USER) + snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER); + else + snprintf(buf, len, "id=%d", rec->id); + + return buf; +} + +static uint32_t print_time_record(struct bootstage_record *rec, uint32_t prev) +{ + char buf[20]; + + if (prev == -1U) { + printf("%11s", ""); + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); + } else { + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); + print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS); + } + printf(" %s\n", get_record_name(buf, sizeof(buf), rec)); + + return rec->time_us; +} + +static int h_compare_record(const void *r1, const void *r2) +{ + const struct bootstage_record *rec1 = r1, *rec2 = r2; + + return rec1->time_us > rec2->time_us ? 1 : -1; +} + +#ifdef CONFIG_OF_LIBFDT +/** + * Add all bootstage timings to a device tree. + * + * @param blob Device tree blob + * @return 0 on success, != 0 on failure. + */ +static int add_bootstages_devicetree(struct fdt_header *blob) +{ + struct bootstage_data *data = gd->bootstage; + int bootstage; + char buf[20]; + int recnum; + int i; + + if (!blob) + return 0; + + /* + * Create the node for bootstage. + * The address of flat device tree is set up by the command bootm. + */ + bootstage = fdt_add_subnode(blob, 0, "bootstage"); + if (bootstage < 0) + return -EINVAL; + + /* + * Insert the timings to the device tree in the reverse order so + * that they can be printed in the Linux kernel in the right order. + */ + for (recnum = data->rec_count - 1, i = 0; recnum >= 0; recnum--, i++) { + struct bootstage_record *rec = &data->record[recnum]; + int node; + + if (rec->id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0) + continue; + + node = fdt_add_subnode(blob, bootstage, simple_itoa(i)); + if (node < 0) + break; + + /* add properties to the node. */ + if (fdt_setprop_string(blob, node, "name", + get_record_name(buf, sizeof(buf), rec))) + return -EINVAL; + + /* Check if this is a 'mark' or 'accum' record */ + if (fdt_setprop_cell(blob, node, + rec->start_us ? "accum" : "mark", + rec->time_us)) + return -EINVAL; + } + + return 0; +} + +int bootstage_fdt_add_report(void) +{ + if (add_bootstages_devicetree(working_fdt)) + puts("bootstage: Failed to add to device tree\n"); + + return 0; +} +#endif + +void bootstage_report(void) +{ + struct bootstage_data *data = gd->bootstage; + struct bootstage_record *rec = data->record; + uint32_t prev; + int i; + + printf("Timer summary in microseconds (%d records):\n", + data->rec_count); + printf("%11s%11s %s\n", "Mark", "Elapsed", "Stage"); + + prev = print_time_record(rec, 0); + + /* Sort records by increasing time */ + qsort(data->record, data->rec_count, sizeof(*rec), h_compare_record); + + for (i = 1, rec++; i < data->rec_count; i++, rec++) { + if (rec->id && !rec->start_us) + prev = print_time_record(rec, prev); + } + if (data->rec_count > RECORD_COUNT) + printf("Overflowed internal boot id table by %d entries\n" + "Please increase CONFIG_(SPL_TPL_)BOOTSTAGE_RECORD_COUNT\n", + data->rec_count - RECORD_COUNT); + + puts("\nAccumulated time:\n"); + for (i = 0, rec = data->record; i < data->rec_count; i++, rec++) { + if (rec->start_us) + prev = print_time_record(rec, -1); + } +} + +/** + * Append data to a memory buffer + * + * Write data to the buffer if there is space. Whether there is space or not, + * the buffer pointer is incremented. + * + * @param ptrp Pointer to buffer, updated by this function + * @param end Pointer to end of buffer + * @param data Data to write to buffer + * @param size Size of data + */ +static void append_data(char **ptrp, char *end, const void *data, int size) +{ + char *ptr = *ptrp; + + *ptrp += size; + if (*ptrp > end) + return; + + memcpy(ptr, data, size); +} + +int bootstage_stash(void *base, int size) +{ + const struct bootstage_data *data = gd->bootstage; + struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + const struct bootstage_record *rec; + char buf[20]; + char *ptr = base, *end = ptr + size; + int i; + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -ENOSPC; + } + + /* Write an arbitrary version number */ + hdr->version = BOOTSTAGE_VERSION; + + hdr->count = data->rec_count; + hdr->size = 0; + hdr->magic = BOOTSTAGE_MAGIC; + hdr->next_id = data->next_id; + ptr += sizeof(*hdr); + + /* Write the records, silently stopping when we run out of space */ + for (rec = data->record, i = 0; i < data->rec_count; i++, rec++) + append_data(&ptr, end, rec, sizeof(*rec)); + + /* Write the name strings */ + for (rec = data->record, i = 0; i < data->rec_count; i++, rec++) { + const char *name; + + name = get_record_name(buf, sizeof(buf), rec); + append_data(&ptr, end, name, strlen(name) + 1); + } + + /* Check for buffer overflow */ + if (ptr > end) { + debug("%s: Not enough space for bootstage stash\n", __func__); + return -ENOSPC; + } + + /* Update total data size */ + hdr->size = ptr - (char *)base; + debug("Stashed %d records\n", hdr->count); + + return 0; +} + +int bootstage_unstash(const void *base, int size) +{ + const struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + struct bootstage_data *data = gd->bootstage; + const char *ptr = base, *end = ptr + size; + struct bootstage_record *rec; + uint rec_size; + int i; + + if (size == -1) + end = (char *)(~(uintptr_t)0); + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -EPERM; + } + + if (hdr->magic != BOOTSTAGE_MAGIC) { + debug("%s: Invalid bootstage magic\n", __func__); + return -ENOENT; + } + + if (ptr + hdr->size > end) { + debug("%s: Bootstage data runs past buffer end\n", __func__); + return -ENOSPC; + } + + if (hdr->count * sizeof(*rec) > hdr->size) { + debug("%s: Bootstage has %d records needing %lu bytes, but " + "only %d bytes is available\n", __func__, hdr->count, + (ulong)hdr->count * sizeof(*rec), hdr->size); + return -ENOSPC; + } + + if (hdr->version != BOOTSTAGE_VERSION) { + debug("%s: Bootstage data version %#0x unrecognised\n", + __func__, hdr->version); + return -EINVAL; + } + + if (data->rec_count + hdr->count > RECORD_COUNT) { + debug("%s: Bootstage has %d records, we have space for %d\n" + "Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT\n", + __func__, hdr->count, RECORD_COUNT - data->rec_count); + return -ENOSPC; + } + + ptr += sizeof(*hdr); + + /* Read the records */ + rec_size = hdr->count * sizeof(*data->record); + memcpy(data->record + data->rec_count, ptr, rec_size); + + /* Read the name strings */ + ptr += rec_size; + for (rec = data->record + data->next_id, i = 0; i < hdr->count; + i++, rec++) { + rec->name = ptr; + if (spl_phase() == PHASE_SPL) + rec->name = strdup(ptr); + + /* Assume no data corruption here */ + ptr += strlen(ptr) + 1; + } + + /* Mark the records as read */ + data->rec_count += hdr->count; + data->next_id = hdr->next_id; + debug("Unstashed %d records\n", hdr->count); + + return 0; +} + +int bootstage_get_size(void) +{ + struct bootstage_data *data = gd->bootstage; + struct bootstage_record *rec; + int size; + int i; + + size = sizeof(struct bootstage_data); + for (rec = data->record, i = 0; i < data->rec_count; + i++, rec++) + size += strlen(rec->name) + 1; + + return size; +} + +int bootstage_init(bool first) +{ + struct bootstage_data *data; + int size = sizeof(struct bootstage_data); + + gd->bootstage = (struct bootstage_data *)malloc(size); + if (!gd->bootstage) + return -ENOMEM; + data = gd->bootstage; + memset(data, '\0', size); + if (first) { + data->next_id = BOOTSTAGE_ID_USER; + bootstage_add_record(BOOTSTAGE_ID_AWAKE, "reset", 0, 0); + } + + return 0; +} diff --git a/roms/u-boot/common/bouncebuf.c b/roms/u-boot/common/bouncebuf.c new file mode 100644 index 000000000..6d98920de --- /dev/null +++ b/roms/u-boot/common/bouncebuf.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic bounce buffer implementation + * + * Copyright (C) 2012 Marek Vasut <marex@denx.de> + */ + +#include <common.h> +#include <cpu_func.h> +#include <log.h> +#include <malloc.h> +#include <errno.h> +#include <bouncebuf.h> +#include <asm/cache.h> + +static int addr_aligned(struct bounce_buffer *state) +{ + const ulong align_mask = ARCH_DMA_MINALIGN - 1; + + /* Check if start is aligned */ + if ((ulong)state->user_buffer & align_mask) { + debug("Unaligned buffer address %p\n", state->user_buffer); + return 0; + } + + /* Check if length is aligned */ + if (state->len != state->len_aligned) { + debug("Unaligned buffer length %zu\n", state->len); + return 0; + } + + /* Aligned */ + return 1; +} + +int bounce_buffer_start_extalign(struct bounce_buffer *state, void *data, + size_t len, unsigned int flags, + size_t alignment, + int (*addr_is_aligned)(struct bounce_buffer *state)) +{ + state->user_buffer = data; + state->bounce_buffer = data; + state->len = len; + state->len_aligned = roundup(len, alignment); + state->flags = flags; + + if (!addr_is_aligned(state)) { + state->bounce_buffer = memalign(alignment, + state->len_aligned); + if (!state->bounce_buffer) + return -ENOMEM; + + if (state->flags & GEN_BB_READ) + memcpy(state->bounce_buffer, state->user_buffer, + state->len); + } + + /* + * Flush data to RAM so DMA reads can pick it up, + * and any CPU writebacks don't race with DMA writes + */ + flush_dcache_range((unsigned long)state->bounce_buffer, + (unsigned long)(state->bounce_buffer) + + state->len_aligned); + + return 0; +} + +int bounce_buffer_start(struct bounce_buffer *state, void *data, + size_t len, unsigned int flags) +{ + return bounce_buffer_start_extalign(state, data, len, flags, + ARCH_DMA_MINALIGN, + addr_aligned); +} + +int bounce_buffer_stop(struct bounce_buffer *state) +{ + if (state->flags & GEN_BB_WRITE) { + /* Invalidate cache so that CPU can see any newly DMA'd data */ + invalidate_dcache_range((unsigned long)state->bounce_buffer, + (unsigned long)(state->bounce_buffer) + + state->len_aligned); + } + + if (state->bounce_buffer == state->user_buffer) + return 0; + + if (state->flags & GEN_BB_WRITE) + memcpy(state->user_buffer, state->bounce_buffer, state->len); + + free(state->bounce_buffer); + + return 0; +} diff --git a/roms/u-boot/common/cli.c b/roms/u-boot/common/cli.c new file mode 100644 index 000000000..048eacb9e --- /dev/null +++ b/roms/u-boot/common/cli.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> + */ + +#include <common.h> +#include <bootstage.h> +#include <cli.h> +#include <cli_hush.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <fdtdec.h> +#include <hang.h> +#include <malloc.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_CMDLINE +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ +#if !CONFIG_IS_ENABLED(HUSH_PARSER) + /* + * cli_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (cli_simple_run_command(cmd, flag) == -1) + return 1; + + return 0; +#else + int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP; + + if (flag & CMD_FLAG_ENV) + hush_flags |= FLAG_CONT_ON_NEWLINE; + return parse_string_outer(cmd, hush_flags); +#endif +} + +/* + * Run a command using the selected parser, and check if it is repeatable. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error. + */ +int run_command_repeatable(const char *cmd, int flag) +{ +#ifndef CONFIG_HUSH_PARSER + return cli_simple_run_command(cmd, flag); +#else + /* + * parse_string_outer() returns 1 for failure, so clean up + * its result. + */ + if (parse_string_outer(cmd, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP)) + return -1; + + return 0; +#endif +} +#else +__weak int board_run_command(const char *cmdline) +{ + printf("## Commands are disabled. Please enable CONFIG_CMDLINE.\n"); + + return 1; +} +#endif /* CONFIG_CMDLINE */ + +int run_command_list(const char *cmd, int len, int flag) +{ + int need_buff = 1; + char *buff = (char *)cmd; /* cast away const */ + int rcode = 0; + + if (len == -1) { + len = strlen(cmd); +#ifdef CONFIG_HUSH_PARSER + /* hush will never change our string */ + need_buff = 0; +#else + /* the built-in parser will change our string if it sees \n */ + need_buff = strchr(cmd, '\n') != NULL; +#endif + } + if (need_buff) { + buff = malloc(len + 1); + if (!buff) + return 1; + memcpy(buff, cmd, len); + buff[len] = '\0'; + } +#ifdef CONFIG_HUSH_PARSER + rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); +#else + /* + * This function will overwrite any \n it sees with a \0, which + * is why it can't work with a const char *. Here we are making + * using of internal knowledge of this function, to avoid always + * doing a malloc() which is actually required only in a case that + * is pretty rare. + */ +#ifdef CONFIG_CMDLINE + rcode = cli_simple_run_command_list(buff, flag); +#else + rcode = board_run_command(buff); +#endif +#endif + if (need_buff) + free(buff); + + return rcode; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_RUN) +int do_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + for (i = 1; i < argc; ++i) { + char *arg; + + arg = env_get(argv[i]); + if (arg == NULL) { + printf("## Error: \"%s\" not defined\n", argv[i]); + return 1; + } + + if (run_command(arg, flag | CMD_FLAG_ENV) != 0) + return 1; + } + return 0; +} +#endif + +#if CONFIG_IS_ENABLED(OF_CONTROL) +bool cli_process_fdt(const char **cmdp) +{ + /* Allow the fdt to override the boot command */ + char *env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); + if (env) + *cmdp = env; + /* + * If the bootsecure option was chosen, use secure_boot_cmd(). + * Always use 'env' in this case, since bootsecure requres that the + * bootcmd was specified in the FDT too. + */ + return fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0) != 0; +} + +/* + * Runs the given boot command securely. Specifically: + * - Doesn't run the command with the shell (run_command or parse_string_outer), + * since that's a lot of code surface that an attacker might exploit. + * Because of this, we don't do any argument parsing--the secure boot command + * has to be a full-fledged u-boot command. + * - Doesn't check for keypresses before booting, since that could be a + * security hole; also disables Ctrl-C. + * - Doesn't allow the command to return. + * + * Upon any failures, this function will drop into an infinite loop after + * printing the error message to console. + */ +void cli_secure_boot_cmd(const char *cmd) +{ +#ifdef CONFIG_CMDLINE + struct cmd_tbl *cmdtp; +#endif + int rc; + + if (!cmd) { + printf("## Error: Secure boot command not specified\n"); + goto err; + } + + /* Disable Ctrl-C just in case some command is used that checks it. */ + disable_ctrlc(1); + + /* Find the command directly. */ +#ifdef CONFIG_CMDLINE + cmdtp = find_cmd(cmd); + if (!cmdtp) { + printf("## Error: \"%s\" not defined\n", cmd); + goto err; + } + + /* Run the command, forcing no flags and faking argc and argv. */ + rc = (cmdtp->cmd)(cmdtp, 0, 1, (char **)&cmd); + +#else + rc = board_run_command(cmd); +#endif + + /* Shouldn't ever return from boot command. */ + printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); + +err: + /* + * Not a whole lot to do here. Rebooting won't help much, since we'll + * just end up right back here. Just loop. + */ + hang(); +} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ + +void cli_loop(void) +{ + bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP); +#ifdef CONFIG_HUSH_PARSER + parse_file_outer(); + /* This point is never reached */ + for (;;); +#elif defined(CONFIG_CMDLINE) + cli_simple_loop(); +#else + printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n"); +#endif /*CONFIG_HUSH_PARSER*/ +} + +void cli_init(void) +{ +#ifdef CONFIG_HUSH_PARSER + u_boot_hush_start(); +#endif + +#if defined(CONFIG_HUSH_INIT_VAR) + hush_init_var(); +#endif +} diff --git a/roms/u-boot/common/cli_hush.c b/roms/u-boot/common/cli_hush.c new file mode 100644 index 000000000..1467ff81b --- /dev/null +++ b/roms/u-boot/common/cli_hush.c @@ -0,0 +1,3705 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sh.c -- a prototype Bourne shell grammar parser + * Intended to follow the original Thompson and Ritchie + * "small and simple is beautiful" philosophy, which + * incidentally is a good match to today's BusyBox. + * + * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> + * + * Credits: + * The parser routines proper are all original material, first + * written Dec 2000 and Jan 2001 by Larry Doolittle. + * The execution engine, the builtins, and much of the underlying + * support has been adapted from busybox-0.49pre's lash, + * which is Copyright (C) 2000 by Lineo, Inc., and + * written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>. + * That, in turn, is based in part on ladsh.c, by Michael K. Johnson and + * Erik W. Troan, which they placed in the public domain. I don't know + * how much of the Johnson/Troan code has survived the repeated rewrites. + * Other credits: + * b_addchr() derived from similar w_addchar function in glibc-2.2 + * setup_redirect(), redirect_opt_num(), and big chunks of main() + * and many builtins derived from contributions by Erik Andersen + * miscellaneous bugfixes from Matt Kraai + * + * There are two big (and related) architecture differences between + * this parser and the lash parser. One is that this version is + * actually designed from the ground up to understand nearly all + * of the Bourne grammar. The second, consequential change is that + * the parser and input reader have been turned inside out. Now, + * the parser is in control, and asks for input as needed. The old + * way had the input reader in control, and it asked for parsing to + * take place as needed. The new way makes it much easier to properly + * handle the recursion implicit in the various substitutions, especially + * across continuation lines. + * + * Bash grammar not implemented: (how many of these were in original sh?) + * $@ (those sure look like weird quoting rules) + * $_ + * ! negation operator for pipes + * &> and >& redirection of stdout+stderr + * Brace Expansion + * Tilde Expansion + * fancy forms of Parameter Expansion + * aliases + * Arithmetic Expansion + * <(list) and >(list) Process Substitution + * reserved words: case, esac, select, function + * Here Documents ( << word ) + * Functions + * Major bugs: + * job handling woefully incomplete and buggy + * reserved word execution woefully incomplete and buggy + * to-do: + * port selected bugfixes from post-0.49 busybox lash - done? + * finish implementing reserved words: for, while, until, do, done + * change { and } from special chars to reserved words + * builtins: break, continue, eval, return, set, trap, ulimit + * test magic exec + * handle children going into background + * clean up recognition of null pipes + * check setting of global_argc and global_argv + * control-C handling, probably with longjmp + * follow IFS rules more precisely, including update semantics + * figure out what to do with backslash-newline + * explain why we use signal instead of sigaction + * propagate syntax errors, die on resource errors? + * continuation lines, both explicit and implicit - done? + * memory leak finding and plugging - done? + * more testing, especially quoting rules and redirection + * document how quoting rules not precisely followed for variable assignments + * maybe change map[] to use 2-bit entries + * (eventually) remove all the printf's + */ + +#define __U_BOOT__ +#ifdef __U_BOOT__ +#include <common.h> /* readline */ +#include <env.h> +#include <malloc.h> /* malloc, free, realloc*/ +#include <linux/ctype.h> /* isalpha, isdigit */ +#include <console.h> +#include <bootretry.h> +#include <cli.h> +#include <cli_hush.h> +#include <command.h> /* find_cmd */ +#include <asm/global_data.h> +#endif +#ifndef __U_BOOT__ +#include <ctype.h> /* isalpha, isdigit */ +#include <unistd.h> /* getpid */ +#include <stdlib.h> /* getenv, atoi */ +#include <string.h> /* strchr */ +#include <stdio.h> /* popen etc. */ +#include <glob.h> /* glob, of course */ +#include <stdarg.h> /* va_list */ +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> /* should be pretty obvious */ + +#include <sys/stat.h> /* ulimit */ +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + +/* #include <dmalloc.h> */ + +#if 1 +#include "busybox.h" +#include "cmdedit.h" +#else +#define applet_name "hush" +#include "standalone.h" +#define hush_main main +#undef CONFIG_FEATURE_SH_FANCY_PROMPT +#define BB_BANNER +#endif +#endif +#define SPECIAL_VAR_SYMBOL 03 +#define SUBSTED_VAR_SYMBOL 04 +#ifndef __U_BOOT__ +#define FLAG_EXIT_FROM_LOOP 1 +#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ +#define FLAG_REPARSING (1 << 2) /* >= 2nd pass */ + +#endif + +#ifdef __U_BOOT__ +DECLARE_GLOBAL_DATA_PTR; + +#define EXIT_SUCCESS 0 +#define EOF -1 +#define syntax() syntax_err() +#define xstrdup strdup +#define error_msg printf +#else +typedef enum { + REDIRECT_INPUT = 1, + REDIRECT_OVERWRITE = 2, + REDIRECT_APPEND = 3, + REDIRECT_HEREIS = 4, + REDIRECT_IO = 5 +} redir_type; + +/* The descrip member of this structure is only used to make debugging + * output pretty */ +struct {int mode; int default_fd; char *descrip;} redir_table[] = { + { 0, 0, "()" }, + { O_RDONLY, 0, "<" }, + { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, + { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, + { O_RDONLY, -1, "<<" }, + { O_RDWR, 1, "<>" } +}; +#endif + +typedef enum { + PIPE_SEQ = 1, + PIPE_AND = 2, + PIPE_OR = 3, + PIPE_BG = 4, +} pipe_style; + +/* might eventually control execution */ +typedef enum { + RES_NONE = 0, + RES_IF = 1, + RES_THEN = 2, + RES_ELIF = 3, + RES_ELSE = 4, + RES_FI = 5, + RES_FOR = 6, + RES_WHILE = 7, + RES_UNTIL = 8, + RES_DO = 9, + RES_DONE = 10, + RES_XXXX = 11, + RES_IN = 12, + RES_SNTX = 13 +} reserved_style; +#define FLAG_END (1<<RES_NONE) +#define FLAG_IF (1<<RES_IF) +#define FLAG_THEN (1<<RES_THEN) +#define FLAG_ELIF (1<<RES_ELIF) +#define FLAG_ELSE (1<<RES_ELSE) +#define FLAG_FI (1<<RES_FI) +#define FLAG_FOR (1<<RES_FOR) +#define FLAG_WHILE (1<<RES_WHILE) +#define FLAG_UNTIL (1<<RES_UNTIL) +#define FLAG_DO (1<<RES_DO) +#define FLAG_DONE (1<<RES_DONE) +#define FLAG_IN (1<<RES_IN) +#define FLAG_START (1<<RES_XXXX) + +/* This holds pointers to the various results of parsing */ +struct p_context { + struct child_prog *child; + struct pipe *list_head; + struct pipe *pipe; +#ifndef __U_BOOT__ + struct redir_struct *pending_redirect; +#endif + reserved_style w; + int old_flag; /* for figuring out valid reserved words */ + struct p_context *stack; + int type; /* define type of parser : ";$" common or special symbol */ + /* How about quoting status? */ +}; + +#ifndef __U_BOOT__ +struct redir_struct { + redir_type type; /* type of redirection */ + int fd; /* file descriptor being redirected */ + int dup; /* -1, or file descriptor being duplicated */ + struct redir_struct *next; /* pointer to the next redirect in the list */ + glob_t word; /* *word.gl_pathv is the filename */ +}; +#endif + +struct child_prog { +#ifndef __U_BOOT__ + pid_t pid; /* 0 if exited */ +#endif + char **argv; /* program name and arguments */ + /* was quoted when parsed; copy of struct o_string.nonnull field */ + int *argv_nonnull; +#ifdef __U_BOOT__ + int argc; /* number of program arguments */ +#endif + struct pipe *group; /* if non-NULL, first in group or subshell */ +#ifndef __U_BOOT__ + int subshell; /* flag, non-zero if group must be forked */ + struct redir_struct *redirects; /* I/O redirections */ + glob_t glob_result; /* result of parameter globbing */ + int is_stopped; /* is the program currently running? */ + struct pipe *family; /* pointer back to the child's parent pipe */ +#endif + int sp; /* number of SPECIAL_VAR_SYMBOL */ + int type; +}; + +struct pipe { +#ifndef __U_BOOT__ + int jobid; /* job number */ +#endif + int num_progs; /* total number of programs in job */ +#ifndef __U_BOOT__ + int running_progs; /* number of programs running */ + char *text; /* name of job */ + char *cmdbuf; /* buffer various argv's point into */ + pid_t pgrp; /* process group ID for the job */ +#endif + struct child_prog *progs; /* array of commands in pipe */ + struct pipe *next; /* to track background commands */ +#ifndef __U_BOOT__ + int stopped_progs; /* number of programs alive, but stopped */ + int job_context; /* bitmask defining current context */ +#endif + pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ + reserved_style r_mode; /* supports if, for, while, until */ +}; + +#ifndef __U_BOOT__ +struct close_me { + int fd; + struct close_me *next; +}; +#endif + +struct variables { + char *name; + char *value; + int flg_export; + int flg_read_only; + struct variables *next; +}; + +/* globals, connect us to the outside world + * the first three support $?, $#, and $1 */ +#ifndef __U_BOOT__ +char **global_argv; +unsigned int global_argc; +#endif +static unsigned int last_return_code; +#ifndef __U_BOOT__ +extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ +#endif + +/* "globals" within this file */ +static uchar *ifs; +static char map[256]; +#ifndef __U_BOOT__ +static int fake_mode; +static int interactive; +static struct close_me *close_me_head; +static const char *cwd; +static struct pipe *job_list; +static unsigned int last_bg_pid; +static unsigned int last_jobid; +static unsigned int shell_terminal; +static char *PS1; +static char *PS2; +struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; +struct variables *top_vars = &shell_ver; +#else +static int flag_repeat = 0; +static int do_repeat = 0; +static struct variables *top_vars = NULL ; +#endif /*__U_BOOT__ */ + +#define B_CHUNK (100) +#define B_NOSPAC 1 + +typedef struct { + char *data; + int length; + int maxlen; + int quote; + int nonnull; +} o_string; +#define NULL_O_STRING {NULL,0,0,0,0} +/* used for initialization: + o_string foo = NULL_O_STRING; */ + +/* I can almost use ordinary FILE *. Is open_memstream() universally + * available? Where is it documented? */ +struct in_str { + const char *p; +#ifndef __U_BOOT__ + char peek_buf[2]; +#endif + int __promptme; + int promptmode; +#ifndef __U_BOOT__ + FILE *file; +#endif + int (*get) (struct in_str *); + int (*peek) (struct in_str *); +}; +#define b_getch(input) ((input)->get(input)) +#define b_peek(input) ((input)->peek(input)) + +#ifndef __U_BOOT__ +#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" + +struct built_in_command { + char *cmd; /* name */ + char *descr; /* description */ + int (*function) (struct child_prog *); /* function ptr */ +}; +#endif + +/* define DEBUG_SHELL for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG_SHELL +#endif + +/* This should be in utility.c */ +#ifdef DEBUG_SHELL +#ifndef __U_BOOT__ +static void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} +#else +#define debug_printf(fmt,args...) printf (fmt ,##args) +#endif +#else +static inline void debug_printf(const char *format, ...) { } +#endif +#define final_printf debug_printf + +#ifdef __U_BOOT__ +static void syntax_err(void) { + printf("syntax error\n"); +} +#else +static void __syntax(char *file, int line) { + error_msg("syntax error %s:%d", file, line); +} +#define syntax() __syntax(__FILE__, __LINE__) +#endif + +#ifdef __U_BOOT__ +static void *xmalloc(size_t size); +static void *xrealloc(void *ptr, size_t size); +#else +/* Index of subroutines: */ +/* function prototypes for builtins */ +static int builtin_cd(struct child_prog *child); +static int builtin_env(struct child_prog *child); +static int builtin_eval(struct child_prog *child); +static int builtin_exec(struct child_prog *child); +static int builtin_exit(struct child_prog *child); +static int builtin_export(struct child_prog *child); +static int builtin_fg_bg(struct child_prog *child); +static int builtin_help(struct child_prog *child); +static int builtin_jobs(struct child_prog *child); +static int builtin_pwd(struct child_prog *child); +static int builtin_read(struct child_prog *child); +static int builtin_set(struct child_prog *child); +static int builtin_shift(struct child_prog *child); +static int builtin_source(struct child_prog *child); +static int builtin_umask(struct child_prog *child); +static int builtin_unset(struct child_prog *child); +static int builtin_not_written(struct child_prog *child); +#endif +/* o_string manipulation: */ +static int b_check_space(o_string *o, int len); +static int b_addchr(o_string *o, int ch); +static void b_reset(o_string *o); +static int b_addqchr(o_string *o, int ch, int quote); +#ifndef __U_BOOT__ +static int b_adduint(o_string *o, unsigned int i); +#endif +/* in_str manipulations: */ +static int static_get(struct in_str *i); +static int static_peek(struct in_str *i); +static int file_get(struct in_str *i); +static int file_peek(struct in_str *i); +#ifndef __U_BOOT__ +static void setup_file_in_str(struct in_str *i, FILE *f); +#else +static void setup_file_in_str(struct in_str *i); +#endif +static void setup_string_in_str(struct in_str *i, const char *s); +#ifndef __U_BOOT__ +/* close_me manipulations: */ +static void mark_open(int fd); +static void mark_closed(int fd); +static void close_all(void); +#endif +/* "run" the final data structures: */ +static char *indenter(int i); +static int free_pipe_list(struct pipe *head, int indent); +static int free_pipe(struct pipe *pi, int indent); +/* really run the final data structures: */ +#ifndef __U_BOOT__ +static int setup_redirects(struct child_prog *prog, int squirrel[]); +#endif +static int run_list_real(struct pipe *pi); +#ifndef __U_BOOT__ +static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn)); +#endif +static int run_pipe_real(struct pipe *pi); +/* extended glob support: */ +#ifndef __U_BOOT__ +static int globhack(const char *src, int flags, glob_t *pglob); +static int glob_needed(const char *s); +static int xglob(o_string *dest, int flags, glob_t *pglob); +#endif +/* variable assignment: */ +static int is_assignment(const char *s); +/* data structure manipulation: */ +#ifndef __U_BOOT__ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); +#endif +static void initialize_context(struct p_context *ctx); +static int done_word(o_string *dest, struct p_context *ctx); +static int done_command(struct p_context *ctx); +static int done_pipe(struct p_context *ctx, pipe_style type); +/* primary string parsing: */ +#ifndef __U_BOOT__ +static int redirect_dup_num(struct in_str *input); +static int redirect_opt_num(o_string *o); +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end); +static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); +#endif +static char *lookup_param(char *src); +static char *make_string(char **inp, int *nonnull); +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); +#ifndef __U_BOOT__ +static int parse_string(o_string *dest, struct p_context *ctx, const char *src); +#endif +static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger); +/* setup: */ +static int parse_stream_outer(struct in_str *inp, int flag); +#ifndef __U_BOOT__ +static int parse_string_outer(const char *s, int flag); +static int parse_file_outer(FILE *f); +#endif +#ifndef __U_BOOT__ +/* job management: */ +static int checkjobs(struct pipe* fg_pipe); +static void insert_bg_job(struct pipe *pi); +static void remove_bg_job(struct pipe *pi); +#endif +/* local variable support */ +static char **make_list_in(char **inp, char *name); +static char *insert_var_value(char *inp); +static char *insert_var_value_sub(char *inp, int tag_subst); + +#ifndef __U_BOOT__ +/* Table of built-in functions. They can be forked or not, depending on + * context: within pipes, they fork. As simple commands, they do not. + * When used in non-forking context, they can change global variables + * in the parent shell process. If forked, of course they can not. + * For example, 'unset foo | whatever' will parse and run, but foo will + * still be set at the end. */ +static struct built_in_command bltins[] = { + {"bg", "Resume a job in the background", builtin_fg_bg}, + {"break", "Exit for, while or until loop", builtin_not_written}, + {"cd", "Change working directory", builtin_cd}, + {"continue", "Continue for, while or until loop", builtin_not_written}, + {"env", "Print all environment variables", builtin_env}, + {"eval", "Construct and run shell command", builtin_eval}, + {"exec", "Exec command, replacing this shell with the exec'd process", + builtin_exec}, + {"exit", "Exit from shell()", builtin_exit}, + {"export", "Set environment variable", builtin_export}, + {"fg", "Bring job into the foreground", builtin_fg_bg}, + {"jobs", "Lists the active jobs", builtin_jobs}, + {"pwd", "Print current directory", builtin_pwd}, + {"read", "Input environment variable", builtin_read}, + {"return", "Return from a function", builtin_not_written}, + {"set", "Set/unset shell local variables", builtin_set}, + {"shift", "Shift positional parameters", builtin_shift}, + {"trap", "Trap signals", builtin_not_written}, + {"ulimit","Controls resource limits", builtin_not_written}, + {"umask","Sets file creation mask", builtin_umask}, + {"unset", "Unset environment variable", builtin_unset}, + {".", "Source-in and run commands in a file", builtin_source}, + {"help", "List shell built-in commands", builtin_help}, + {NULL, NULL, NULL} +}; + +static const char *set_cwd(void) +{ + if(cwd==unknown) + cwd = NULL; /* xgetcwd(arg) called free(arg) */ + cwd = xgetcwd((char *)cwd); + if (!cwd) + cwd = unknown; + return cwd; +} + +/* built-in 'eval' handler */ +static int builtin_eval(struct child_prog *child) +{ + char *str = NULL; + int rcode = EXIT_SUCCESS; + + if (child->argv[1]) { + str = make_string(child->argv + 1); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | + FLAG_PARSE_SEMICOLON); + free(str); + rcode = last_return_code; + } + return rcode; +} + +/* built-in 'cd <path>' handler */ +static int builtin_cd(struct child_prog *child) +{ + char *newdir; + if (child->argv[1] == NULL) + newdir = env_get("HOME"); + else + newdir = child->argv[1]; + if (chdir(newdir)) { + printf("cd: %s: %s\n", newdir, strerror(errno)); + return EXIT_FAILURE; + } + set_cwd(); + return EXIT_SUCCESS; +} + +/* built-in 'env' handler */ +static int builtin_env(struct child_prog *dummy) +{ + char **e = environ; + if (e == NULL) return EXIT_FAILURE; + for (; *e; e++) { + puts(*e); + } + return EXIT_SUCCESS; +} + +/* built-in 'exec' handler */ +static int builtin_exec(struct child_prog *child) +{ + if (child->argv[1] == NULL) + return EXIT_SUCCESS; /* Really? */ + child->argv++; + pseudo_exec(child); + /* never returns */ +} + +/* built-in 'exit' handler */ +static int builtin_exit(struct child_prog *child) +{ + if (child->argv[1] == NULL) + exit(last_return_code); + exit (atoi(child->argv[1])); +} + +/* built-in 'export VAR=value' handler */ +static int builtin_export(struct child_prog *child) +{ + int res = 0; + char *name = child->argv[1]; + + if (name == NULL) { + return (builtin_env(child)); + } + + name = strdup(name); + + if(name) { + char *value = strchr(name, '='); + + if (!value) { + char *tmp; + /* They are exporting something without an =VALUE */ + + value = get_local_var(name); + if (value) { + size_t ln = strlen(name); + + tmp = realloc(name, ln+strlen(value)+2); + if(tmp==NULL) + res = -1; + else { + sprintf(tmp+ln, "=%s", value); + name = tmp; + } + } else { + /* bash does not return an error when trying to export + * an undefined variable. Do likewise. */ + res = 1; + } + } + } + if (res<0) + perror_msg("export"); + else if(res==0) + res = set_local_var(name, 1); + else + res = 0; + free(name); + return res; +} + +/* built-in 'fg' and 'bg' handler */ +static int builtin_fg_bg(struct child_prog *child) +{ + int i, jobnum; + struct pipe *pi=NULL; + + if (!interactive) + return EXIT_FAILURE; + /* If they gave us no args, assume they want the last backgrounded task */ + if (!child->argv[1]) { + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == last_jobid) { + break; + } + } + if (!pi) { + error_msg("%s: no current job", child->argv[0]); + return EXIT_FAILURE; + } + } else { + if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { + error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + return EXIT_FAILURE; + } + for (pi = job_list; pi; pi = pi->next) { + if (pi->jobid == jobnum) { + break; + } + } + if (!pi) { + error_msg("%s: %d: no such job", child->argv[0], jobnum); + return EXIT_FAILURE; + } + } + + if (*child->argv[0] == 'f') { + /* Put the job into the foreground. */ + tcsetpgrp(shell_terminal, pi->pgrp); + } + + /* Restart the processes in the job */ + for (i = 0; i < pi->num_progs; i++) + pi->progs[i].is_stopped = 0; + + if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) { + if (i == ESRCH) { + remove_bg_job(pi); + } else { + perror_msg("kill (SIGCONT)"); + } + } + + pi->stopped_progs = 0; + return EXIT_SUCCESS; +} + +/* built-in 'help' handler */ +static int builtin_help(struct child_prog *dummy) +{ + struct built_in_command *x; + + printf("\nBuilt-in commands:\n"); + printf("-------------------\n"); + for (x = bltins; x->cmd; x++) { + if (x->descr==NULL) + continue; + printf("%s\t%s\n", x->cmd, x->descr); + } + printf("\n\n"); + return EXIT_SUCCESS; +} + +/* built-in 'jobs' handler */ +static int builtin_jobs(struct child_prog *child) +{ + struct pipe *job; + char *status_string; + + for (job = job_list; job; job = job->next) { + if (job->running_progs == job->stopped_progs) + status_string = "Stopped"; + else + status_string = "Running"; + + printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); + } + return EXIT_SUCCESS; +} + + +/* built-in 'pwd' handler */ +static int builtin_pwd(struct child_prog *dummy) +{ + puts(set_cwd()); + return EXIT_SUCCESS; +} + +/* built-in 'read VAR' handler */ +static int builtin_read(struct child_prog *child) +{ + int res; + + if (child->argv[1]) { + char string[BUFSIZ]; + char *var = 0; + + string[0] = 0; /* In case stdin has only EOF */ + /* read string */ + fgets(string, sizeof(string), stdin); + chomp(string); + var = malloc(strlen(child->argv[1])+strlen(string)+2); + if(var) { + sprintf(var, "%s=%s", child->argv[1], string); + res = set_local_var(var, 0); + } else + res = -1; + if (res) + fprintf(stderr, "read: %m\n"); + free(var); /* So not move up to avoid breaking errno */ + return res; + } else { + do res=getchar(); while(res!='\n' && res!=EOF); + return 0; + } +} + +/* built-in 'set VAR=value' handler */ +static int builtin_set(struct child_prog *child) +{ + char *temp = child->argv[1]; + struct variables *e; + + if (temp == NULL) + for(e = top_vars; e; e=e->next) + printf("%s=%s\n", e->name, e->value); + else + set_local_var(temp, 0); + + return EXIT_SUCCESS; +} + + +/* Built-in 'shift' handler */ +static int builtin_shift(struct child_prog *child) +{ + int n=1; + if (child->argv[1]) { + n=atoi(child->argv[1]); + } + if (n>=0 && n<global_argc) { + /* XXX This probably breaks $0 */ + global_argc -= n; + global_argv += n; + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} + +/* Built-in '.' handler (read-in and execute commands from file) */ +static int builtin_source(struct child_prog *child) +{ + FILE *input; + int status; + + if (child->argv[1] == NULL) + return EXIT_FAILURE; + + /* XXX search through $PATH is missing */ + input = fopen(child->argv[1], "r"); + if (!input) { + error_msg("Couldn't open file '%s'", child->argv[1]); + return EXIT_FAILURE; + } + + /* Now run the file */ + /* XXX argv and argc are broken; need to save old global_argv + * (pointer only is OK!) on this stack frame, + * set global_argv=child->argv+1, recurse, and restore. */ + mark_open(fileno(input)); + status = parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + return (status); +} + +static int builtin_umask(struct child_prog *child) +{ + mode_t new_umask; + const char *arg = child->argv[1]; + char *end; + if (arg) { + new_umask=strtoul(arg, &end, 8); + if (*end!='\0' || end == arg) { + return EXIT_FAILURE; + } + } else { + printf("%.3o\n", (unsigned int) (new_umask=umask(0))); + } + umask(new_umask); + return EXIT_SUCCESS; +} + +/* built-in 'unset VAR' handler */ +static int builtin_unset(struct child_prog *child) +{ + /* bash returned already true */ + unset_local_var(child->argv[1]); + return EXIT_SUCCESS; +} + +static int builtin_not_written(struct child_prog *child) +{ + printf("builtin_%s not written\n",child->argv[0]); + return EXIT_FAILURE; +} +#endif + +static int b_check_space(o_string *o, int len) +{ + /* It would be easy to drop a more restrictive policy + * in here, such as setting a maximum string length */ + if (o->length + len > o->maxlen) { + char *old_data = o->data; + /* assert (data == NULL || o->maxlen != 0); */ + o->maxlen += max(2*len, B_CHUNK); + o->data = realloc(o->data, 1 + o->maxlen); + if (o->data == NULL) { + free(old_data); + } + } + return o->data == NULL; +} + +static int b_addchr(o_string *o, int ch) +{ + debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); + if (b_check_space(o, 1)) return B_NOSPAC; + o->data[o->length] = ch; + o->length++; + o->data[o->length] = '\0'; + return 0; +} + +static void b_reset(o_string *o) +{ + o->length = 0; + o->nonnull = 0; + if (o->data != NULL) *o->data = '\0'; +} + +static void b_free(o_string *o) +{ + b_reset(o); + free(o->data); + o->data = NULL; + o->maxlen = 0; +} + +/* My analysis of quoting semantics tells me that state information + * is associated with a destination, not a source. + */ +static int b_addqchr(o_string *o, int ch, int quote) +{ + if (quote && strchr("*?[\\",ch)) { + int rc; + rc = b_addchr(o, '\\'); + if (rc) return rc; + } + return b_addchr(o, ch); +} + +#ifndef __U_BOOT__ +static int b_adduint(o_string *o, unsigned int i) +{ + int r; + char *p = simple_itoa(i); + /* no escape checking necessary */ + do r=b_addchr(o, *p++); while (r==0 && *p); + return r; +} +#endif + +static int static_get(struct in_str *i) +{ + int ch = *i->p++; + if (ch=='\0') return EOF; + return ch; +} + +static int static_peek(struct in_str *i) +{ + return *i->p; +} + +#ifndef __U_BOOT__ +static inline void cmdedit_set_initial_prompt(void) +{ +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + PS1 = NULL; +#else + PS1 = env_get("PS1"); + if(PS1==0) + PS1 = "\\w \\$ "; +#endif +} + +static inline void setup_prompt_string(int promptmode, char **prompt_str) +{ + debug_printf("setup_prompt_string %d ",promptmode); +#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT + /* Set up the prompt */ + if (promptmode == 1) { + free(PS1); + PS1=xmalloc(strlen(cwd)+4); + sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); + *prompt_str = PS1; + } else { + *prompt_str = PS2; + } +#else + *prompt_str = (promptmode==1)? PS1 : PS2; +#endif + debug_printf("result %s\n",*prompt_str); +} +#endif + +#ifdef __U_BOOT__ +static int uboot_cli_readline(struct in_str *i) +{ + char *prompt; + char __maybe_unused *ps_prompt = NULL; + + if (i->promptmode == 1) + prompt = CONFIG_SYS_PROMPT; + else + prompt = CONFIG_SYS_PROMPT_HUSH_PS2; + +#ifdef CONFIG_CMDLINE_PS_SUPPORT + if (i->promptmode == 1) + ps_prompt = env_get("PS1"); + else + ps_prompt = env_get("PS2"); + if (ps_prompt) + prompt = ps_prompt; +#endif + + return cli_readline(prompt); +} +#endif + +static void get_user_input(struct in_str *i) +{ +#ifndef __U_BOOT__ + char *prompt_str; + static char the_command[BUFSIZ]; + + setup_prompt_string(i->promptmode, &prompt_str); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_read_input(prompt_str, the_command); +#else + fputs(prompt_str, stdout); + fflush(stdout); + the_command[0]=fgetc(i->file); + the_command[1]='\0'; +#endif + fflush(stdout); + i->p = the_command; +#else + int n; + static char the_command[CONFIG_SYS_CBSIZE + 1]; + + bootretry_reset_cmd_timeout(); + i->__promptme = 1; + n = uboot_cli_readline(i); + +#ifdef CONFIG_BOOT_RETRY_TIME + if (n == -2) { + puts("\nTimeout waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + do_reset(NULL, 0, 0, NULL); +# else +# error "This currently only works with CONFIG_RESET_TO_RETRY enabled" +# endif + } +#endif + if (n == -1 ) { + flag_repeat = 0; + i->__promptme = 0; + } + n = strlen(console_buffer); + console_buffer[n] = '\n'; + console_buffer[n+1]= '\0'; + if (had_ctrlc()) flag_repeat = 0; + clear_ctrlc(); + do_repeat = 0; + if (i->promptmode == 1) { + if (console_buffer[0] == '\n'&& flag_repeat == 0) { + strcpy(the_command,console_buffer); + } + else { + if (console_buffer[0] != '\n') { + strcpy(the_command,console_buffer); + flag_repeat = 1; + } + else { + do_repeat = 1; + } + } + i->p = the_command; + } + else { + if (console_buffer[0] != '\n') { + if (strlen(the_command) + strlen(console_buffer) + < CONFIG_SYS_CBSIZE) { + n = strlen(the_command); + the_command[n-1] = ' '; + strcpy(&the_command[n],console_buffer); + } + else { + the_command[0] = '\n'; + the_command[1] = '\0'; + flag_repeat = 0; + } + } + if (i->__promptme == 0) { + the_command[0] = '\n'; + the_command[1] = '\0'; + } + i->p = console_buffer; + } +#endif +} + +/* This is the magic location that prints prompts + * and gets data back from the user */ +static int file_get(struct in_str *i) +{ + int ch; + + ch = 0; + /* If there is data waiting, eat it up */ + if (i->p && *i->p) { + ch = *i->p++; + } else { + /* need to double check i->file because we might be doing something + * more complicated by now, like sourcing or substituting. */ +#ifndef __U_BOOT__ + if (i->__promptme && interactive && i->file == stdin) { + while(! i->p || (interactive && strlen(i->p)==0) ) { +#else + while(! i->p || strlen(i->p)==0 ) { +#endif + get_user_input(i); + } + i->promptmode=2; +#ifndef __U_BOOT__ + i->__promptme = 0; +#endif + if (i->p && *i->p) { + ch = *i->p++; + } +#ifndef __U_BOOT__ + } else { + ch = fgetc(i->file); + } + +#endif + debug_printf("b_getch: got a %d\n", ch); + } +#ifndef __U_BOOT__ + if (ch == '\n') i->__promptme=1; +#endif + return ch; +} + +/* All the callers guarantee this routine will never be + * used right after a newline, so prompting is not needed. + */ +static int file_peek(struct in_str *i) +{ +#ifndef __U_BOOT__ + if (i->p && *i->p) { +#endif + return *i->p; +#ifndef __U_BOOT__ + } else { + i->peek_buf[0] = fgetc(i->file); + i->peek_buf[1] = '\0'; + i->p = i->peek_buf; + debug_printf("b_peek: got a %d\n", *i->p); + return *i->p; + } +#endif +} + +#ifndef __U_BOOT__ +static void setup_file_in_str(struct in_str *i, FILE *f) +#else +static void setup_file_in_str(struct in_str *i) +#endif +{ + i->peek = file_peek; + i->get = file_get; + i->__promptme=1; + i->promptmode=1; +#ifndef __U_BOOT__ + i->file = f; +#endif + i->p = NULL; +} + +static void setup_string_in_str(struct in_str *i, const char *s) +{ + i->peek = static_peek; + i->get = static_get; + i->__promptme=1; + i->promptmode=1; + i->p = s; +} + +#ifndef __U_BOOT__ +static void mark_open(int fd) +{ + struct close_me *new = xmalloc(sizeof(struct close_me)); + new->fd = fd; + new->next = close_me_head; + close_me_head = new; +} + +static void mark_closed(int fd) +{ + struct close_me *tmp; + if (close_me_head == NULL || close_me_head->fd != fd) + error_msg_and_die("corrupt close_me"); + tmp = close_me_head; + close_me_head = close_me_head->next; + free(tmp); +} + +static void close_all(void) +{ + struct close_me *c; + for (c=close_me_head; c; c=c->next) { + close(c->fd); + } + close_me_head = NULL; +} + +/* squirrel != NULL means we squirrel away copies of stdin, stdout, + * and stderr if they are redirected. */ +static int setup_redirects(struct child_prog *prog, int squirrel[]) +{ + int openfd, mode; + struct redir_struct *redir; + + for (redir=prog->redirects; redir; redir=redir->next) { + if (redir->dup == -1 && redir->word.gl_pathv == NULL) { + /* something went wrong in the parse. Pretend it didn't happen */ + continue; + } + if (redir->dup == -1) { + mode=redir_table[redir->type].mode; + openfd = open(redir->word.gl_pathv[0], mode, 0666); + if (openfd < 0) { + /* this could get lost if stderr has been redirected, but + bash and ash both lose it as well (though zsh doesn't!) */ + perror_msg("error opening %s", redir->word.gl_pathv[0]); + return 1; + } + } else { + openfd = redir->dup; + } + + if (openfd != redir->fd) { + if (squirrel && redir->fd < 3) { + squirrel[redir->fd] = dup(redir->fd); + } + if (openfd == -3) { + close(openfd); + } else { + dup2(openfd, redir->fd); + if (redir->dup == -1) + close (openfd); + } + } + } + return 0; +} + +static void restore_redirects(int squirrel[]) +{ + int i, fd; + for (i=0; i<3; i++) { + fd = squirrel[i]; + if (fd != -1) { + /* No error checking. I sure wouldn't know what + * to do with an error if I found one! */ + dup2(fd, i); + close(fd); + } + } +} + +/* never returns */ +/* XXX no exit() here. If you don't exec, use _exit instead. + * The at_exit handlers apparently confuse the calling process, + * in particular stdin handling. Not sure why? */ +static void pseudo_exec(struct child_prog *child) +{ + int i, rcode; + char *p; + struct built_in_command *x; + if (child->argv) { + for (i=0; is_assignment(child->argv[i]); i++) { + debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]); + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) free(p); + } + child->argv+=i; /* XXX this hack isn't so horrible, since we are about + to exit, and therefore don't need to keep data + structures consistent for free() use. */ + /* If a variable is assigned in a forest, and nobody listens, + * was it ever really set? + */ + if (child->argv[0] == NULL) { + _exit(EXIT_SUCCESS); + } + + /* + * Check if the command matches any of the builtins. + * Depending on context, this might be redundant. But it's + * easier to waste a few CPU cycles than it is to figure out + * if this is one of those cases. + */ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[0], x->cmd) == 0 ) { + debug_printf("builtin exec %s\n", child->argv[0]); + rcode = x->function(child); + fflush(stdout); + _exit(rcode); + } + } + + /* Check if the command matches any busybox internal commands + * ("applets") here. + * FIXME: This feature is not 100% safe, since + * BusyBox is not fully reentrant, so we have no guarantee the things + * from the .bss are still zeroed, or that things from .data are still + * at their defaults. We could exec ourself from /proc/self/exe, but I + * really dislike relying on /proc for things. We could exec ourself + * from global_argv[0], but if we are in a chroot, we may not be able + * to find ourself... */ +#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + { + int argc_l; + char** argv_l=child->argv; + char *name = child->argv[0]; + +#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN + /* Following discussions from November 2000 on the busybox mailing + * list, the default configuration, (without + * get_last_path_component()) lets the user force use of an + * external command by specifying the full (with slashes) filename. + * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN then applets + * _aways_ override external commands, so if you want to run + * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the + * filesystem and is _not_ busybox. Some systems may want this, + * most do not. */ + name = get_last_path_component(name); +#endif + /* Count argc for use in a second... */ + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); + optind = 1; + debug_printf("running applet %s\n", name); + run_applet_by_name(name, argc_l, child->argv); + } +#endif + debug_printf("exec of %s\n",child->argv[0]); + execvp(child->argv[0],child->argv); + perror_msg("couldn't exec: %s",child->argv[0]); + _exit(1); + } else if (child->group) { + debug_printf("runtime nesting to group\n"); + interactive=0; /* crucial!!!! */ + rcode = run_list_real(child->group); + /* OK to leak memory by not calling free_pipe_list, + * since this process is about to exit */ + _exit(rcode); + } else { + /* Can happen. See what bash does with ">foo" by itself. */ + debug_printf("trying to pseudo_exec null command\n"); + _exit(EXIT_SUCCESS); + } +} + +static void insert_bg_job(struct pipe *pi) +{ + struct pipe *thejob; + + /* Linear search for the ID of the job to use */ + pi->jobid = 1; + for (thejob = job_list; thejob; thejob = thejob->next) + if (thejob->jobid >= pi->jobid) + pi->jobid = thejob->jobid + 1; + + /* add thejob to the list of running jobs */ + if (!job_list) { + thejob = job_list = xmalloc(sizeof(*thejob)); + } else { + for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; + thejob->next = xmalloc(sizeof(*thejob)); + thejob = thejob->next; + } + + /* physically copy the struct job */ + memcpy(thejob, pi, sizeof(struct pipe)); + thejob->next = NULL; + thejob->running_progs = thejob->num_progs; + thejob->stopped_progs = 0; + thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ + + /*if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) */ + { + char *bar=thejob->text; + char **foo=pi->progs[0].argv; + while(foo && *foo) { + bar += sprintf(bar, "%s ", *foo++); + } + } + + /* we don't wait for background thejobs to return -- append it + to the list of backgrounded thejobs and leave it alone */ + printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); + last_bg_pid = thejob->progs[0].pid; + last_jobid = thejob->jobid; +} + +/* remove a backgrounded job */ +static void remove_bg_job(struct pipe *pi) +{ + struct pipe *prev_pipe; + + if (pi == job_list) { + job_list = pi->next; + } else { + prev_pipe = job_list; + while (prev_pipe->next != pi) + prev_pipe = prev_pipe->next; + prev_pipe->next = pi->next; + } + if (job_list) + last_jobid = job_list->jobid; + else + last_jobid = 0; + + pi->stopped_progs = 0; + free_pipe(pi, 0); + free(pi); +} + +/* Checks to see if any processes have exited -- if they + have, figure out why and see if a job has completed */ +static int checkjobs(struct pipe* fg_pipe) +{ + int attributes; + int status; + int prognum = 0; + struct pipe *pi; + pid_t childpid; + + attributes = WUNTRACED; + if (fg_pipe==NULL) { + attributes |= WNOHANG; + } + + while ((childpid = waitpid(-1, &status, attributes)) > 0) { + if (fg_pipe) { + int i, rcode = 0; + for (i=0; i < fg_pipe->num_progs; i++) { + if (fg_pipe->progs[i].pid == childpid) { + if (i==fg_pipe->num_progs-1) + rcode=WEXITSTATUS(status); + (fg_pipe->num_progs)--; + return(rcode); + } + } + } + + for (pi = job_list; pi; pi = pi->next) { + prognum = 0; + while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) { + prognum++; + } + if (prognum < pi->num_progs) + break; + } + + if(pi==NULL) { + debug_printf("checkjobs: pid %d was not in our list!\n", childpid); + continue; + } + + if (WIFEXITED(status) || WIFSIGNALED(status)) { + /* child exited */ + pi->running_progs--; + pi->progs[prognum].pid = 0; + + if (!pi->running_progs) { + printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text); + remove_bg_job(pi); + } + } else { + /* child stopped */ + pi->stopped_progs++; + pi->progs[prognum].is_stopped = 1; + +#if 0 + /* Printing this stuff is a pain, since it tends to + * overwrite the prompt an inconveinient moments. So + * don't do that. */ + if (pi->stopped_progs == pi->num_progs) { + printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text); + } +#endif + } + } + + if (childpid == -1 && errno != ECHILD) + perror_msg("waitpid"); + + /* move the shell to the foreground */ + /*if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) */ + /* perror_msg("tcsetpgrp-2"); */ + return -1; +} + +/* Figure out our controlling tty, checking in order stderr, + * stdin, and stdout. If check_pgrp is set, also check that + * we belong to the foreground process group associated with + * that tty. The value of shell_terminal is needed in order to call + * tcsetpgrp(shell_terminal, ...); */ +void controlling_tty(int check_pgrp) +{ + pid_t curpgrp; + + if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0 + && (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0) + goto shell_terminal_error; + + if (check_pgrp && curpgrp != getpgid(0)) + goto shell_terminal_error; + + return; + +shell_terminal_error: + shell_terminal = -1; + return; +} +#endif + +/* run_pipe_real() starts all the jobs, but doesn't wait for anything + * to finish. See checkjobs(). + * + * return code is normally -1, when the caller has to wait for children + * to finish to determine the exit status of the pipe. If the pipe + * is a simple builtin command, however, the action is done by the + * time run_pipe_real returns, and the exit code is provided as the + * return value. + * + * The input of the pipe is always stdin, the output is always + * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, + * because it tries to avoid running the command substitution in + * subshell, when that is in fact necessary. The subshell process + * now has its stdout directed to the input of the appropriate pipe, + * so this routine is noticeably simpler. + */ +static int run_pipe_real(struct pipe *pi) +{ + int i; +#ifndef __U_BOOT__ + int nextin, nextout; + int pipefds[2]; /* pipefds[0] is for reading */ + struct child_prog *child; + struct built_in_command *x; + char *p; +# if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &i; + (void) &nextin; + (void) &nextout; + (void) &child; +# endif +#else + int nextin; + int flag = do_repeat ? CMD_FLAG_REPEAT : 0; + struct child_prog *child; + char *p; +# if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &i; + (void) &nextin; + (void) &child; +# endif +#endif /* __U_BOOT__ */ + + nextin = 0; +#ifndef __U_BOOT__ + pi->pgrp = -1; +#endif + + /* Check if this is a simple builtin (not part of a pipe). + * Builtins within pipes have to fork anyway, and are handled in + * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. + */ + if (pi->num_progs == 1) child = & (pi->progs[0]); +#ifndef __U_BOOT__ + if (pi->num_progs == 1 && child->group && child->subshell == 0) { + int squirrel[] = {-1, -1, -1}; + int rcode; + debug_printf("non-subshell grouping\n"); + setup_redirects(child, squirrel); + /* XXX could we merge code with following builtin case, + * by creating a pseudo builtin that calls run_list_real? */ + rcode = run_list_real(child->group); + restore_redirects(squirrel); +#else + if (pi->num_progs == 1 && child->group) { + int rcode; + debug_printf("non-subshell grouping\n"); + rcode = run_list_real(child->group); +#endif + return rcode; + } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) { + for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ } + if (i!=0 && child->argv[i]==NULL) { + /* assignments, but no command: set the local environment */ + for (i=0; child->argv[i]!=NULL; i++) { + + /* Ok, this case is tricky. We have to decide if this is a + * local variable, or an already exported variable. If it is + * already exported, we have to export the new value. If it is + * not exported, we need only set this as a local variable. + * 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]); + debug_printf("Local environment set: %s\n", name); + value = strchr(name, '='); + if (value) + *value=0; +#ifndef __U_BOOT__ + if ( get_local_var(name)) { + export_me=1; + } +#endif + free(name); + p = insert_var_value(child->argv[i]); + set_local_var(p, export_me); + if (p != child->argv[i]) free(p); + } + return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + } + for (i = 0; is_assignment(child->argv[i]); i++) { + p = insert_var_value(child->argv[i]); +#ifndef __U_BOOT__ + putenv(strdup(p)); +#else + set_local_var(p, 0); +#endif + if (p != child->argv[i]) { + child->sp--; + free(p); + } + } + if (child->sp) { + char * str = NULL; + + str = make_string(child->argv + i, + child->argv_nonnull + i); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); + free(str); + return last_return_code; + } +#ifndef __U_BOOT__ + for (x = bltins; x->cmd; x++) { + if (strcmp(child->argv[i], x->cmd) == 0 ) { + int squirrel[] = {-1, -1, -1}; + int rcode; + if (x->function == builtin_exec && child->argv[i+1]==NULL) { + debug_printf("magic exec\n"); + setup_redirects(child,NULL); + return EXIT_SUCCESS; + } + debug_printf("builtin inline %s\n", child->argv[0]); + /* XXX setup_redirects acts on file descriptors, not FILEs. + * This is perfect for work that comes after exec(). + * Is it really safe for inline use? Experimentally, + * things seem to work with glibc. */ + setup_redirects(child, squirrel); + + child->argv += i; /* XXX horrible hack */ + rcode = x->function(child); + /* XXX restore hack so free() can work right */ + child->argv -= i; + restore_redirects(squirrel); + } + return rcode; + } +#else + /* check ";", because ,example , argv consist from + * "help;flinfo" must not execute + */ + if (strchr(child->argv[i], ';')) { + printf("Unknown command '%s' - try 'help' or use " + "'run' command\n", child->argv[i]); + return -1; + } + /* Process the command */ + return cmd_process(flag, child->argc - i, child->argv + i, + &flag_repeat, NULL); +#endif + } +#ifndef __U_BOOT__ + + for (i = 0; i < pi->num_progs; i++) { + child = & (pi->progs[i]); + + /* pipes are inserted between pairs of commands */ + if ((i + 1) < pi->num_progs) { + if (pipe(pipefds)<0) perror_msg_and_die("pipe"); + nextout = pipefds[1]; + } else { + nextout=1; + pipefds[0] = -1; + } + + /* XXX test for failed fork()? */ + if (!(child->pid = fork())) { + /* Set the handling for job control signals back to the default. */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + close_all(); + + if (nextin != 0) { + dup2(nextin, 0); + close(nextin); + } + if (nextout != 1) { + dup2(nextout, 1); + close(nextout); + } + if (pipefds[0]!=-1) { + close(pipefds[0]); /* opposite end of our output pipe */ + } + + /* Like bash, explicit redirects override pipes, + * and the pipe fd is available for dup'ing. */ + setup_redirects(child,NULL); + + if (interactive && pi->followup!=PIPE_BG) { + /* If we (the child) win the race, put ourselves in the process + * group whose leader is the first process in this pipe. */ + if (pi->pgrp < 0) { + pi->pgrp = getpid(); + } + if (setpgid(0, pi->pgrp) == 0) { + tcsetpgrp(2, pi->pgrp); + } + } + + pseudo_exec(child); + } + + + /* put our child in the process group whose leader is the + first process in this pipe */ + if (pi->pgrp < 0) { + pi->pgrp = child->pid; + } + /* Don't check for errors. The child may be dead already, + * in which case setpgid returns error code EACCES. */ + setpgid(child->pid, pi->pgrp); + + if (nextin != 0) + close(nextin); + if (nextout != 1) + close(nextout); + + /* If there isn't another process, nextin is garbage + but it doesn't matter */ + nextin = pipefds[0]; + } +#endif + return -1; +} + +static int run_list_real(struct pipe *pi) +{ + char *save_name = NULL; + char **list = NULL; + char **save_list = NULL; + struct pipe *rpipe; + int flag_rep = 0; +#ifndef __U_BOOT__ + int save_num_progs; +#endif + int rcode=0, flag_skip=1; + int flag_restore = 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; + /* check syntax for "for" */ + for (rpipe = pi; rpipe; rpipe = rpipe->next) { + if ((rpipe->r_mode == RES_IN || + rpipe->r_mode == RES_FOR) && + (rpipe->next == NULL)) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + return 1; + } + if ((rpipe->r_mode == RES_IN && + (rpipe->next->r_mode == RES_IN && + rpipe->next->progs->argv != NULL))|| + (rpipe->r_mode == RES_FOR && + rpipe->next->r_mode != RES_IN)) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + return 1; + } + } + for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { + if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || + pi->r_mode == RES_FOR) { +#ifdef __U_BOOT__ + /* check Ctrl-C */ + ctrlc(); + if ((had_ctrlc())) { + return 1; + } +#endif + flag_restore = 0; + if (!rpipe) { + flag_rep = 0; + rpipe = pi; + } + } + rmode = pi->r_mode; + debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); + if (rmode == skip_more_in_this_rmode && flag_skip) { + if (pi->followup == PIPE_SEQ) flag_skip=0; + continue; + } + flag_skip = 1; + skip_more_in_this_rmode = RES_XXXX; + if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; + if (rmode == RES_THEN && if_code) continue; + if (rmode == RES_ELSE && !if_code) continue; + if (rmode == RES_ELIF && !if_code) break; + if (rmode == RES_FOR && pi->num_progs) { + if (!list) { + /* if no variable values after "in" we skip "for" */ + if (!pi->next->progs->argv) continue; + /* create list of variable values */ + list = make_list_in(pi->next->progs->argv, + pi->progs->argv[0]); + save_list = list; + save_name = pi->progs->argv[0]; + pi->progs->argv[0] = NULL; + flag_rep = 1; + } + if (!(*list)) { + free(pi->progs->argv[0]); + free(save_list); + list = NULL; + flag_rep = 0; + pi->progs->argv[0] = save_name; +#ifndef __U_BOOT__ + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; +#endif + continue; + } else { + /* insert new value from list for variable */ + free(pi->progs->argv[0]); + pi->progs->argv[0] = *list++; +#ifndef __U_BOOT__ + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; +#endif + } + } + if (rmode == RES_IN) continue; + if (rmode == RES_DO) { + if (!flag_rep) continue; + } + if (rmode == RES_DONE) { + if (flag_rep) { + flag_restore = 1; + } else { + rpipe = NULL; + } + } + if (pi->num_progs == 0) continue; +#ifndef __U_BOOT__ + save_num_progs = pi->num_progs; /* save number of programs */ +#endif + rcode = run_pipe_real(pi); + debug_printf("run_pipe_real returned %d\n",rcode); +#ifndef __U_BOOT__ + if (rcode!=-1) { + /* We only ran a builtin: rcode was set by the return value + * of run_pipe_real(), and we don't need to wait for anything. */ + } else if (pi->followup==PIPE_BG) { + /* XXX check bash's behavior with nontrivial pipes */ + /* XXX compute jobid */ + /* XXX what does bash do with attempts to background builtins? */ + insert_bg_job(pi); + rcode = EXIT_SUCCESS; + } else { + if (interactive) { + /* move the new process group into the foreground */ + if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) + perror_msg("tcsetpgrp-3"); + rcode = checkjobs(pi); + /* move the shell to the foreground */ + if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) + perror_msg("tcsetpgrp-4"); + } else { + rcode = checkjobs(pi); + } + debug_printf("checkjobs returned %d\n",rcode); + } + last_return_code=rcode; +#else + if (rcode < -1) { + last_return_code = -rcode - 2; + return -2; /* exit */ + } + last_return_code=(rcode == 0) ? 0 : 1; +#endif +#ifndef __U_BOOT__ + pi->num_progs = save_num_progs; /* restore number of programs */ +#endif + if ( rmode == RES_IF || rmode == RES_ELIF ) + next_if_code=rcode; /* can be overwritten a number of times */ + if (rmode == RES_WHILE) + flag_rep = !last_return_code; + if (rmode == RES_UNTIL) + flag_rep = last_return_code; + if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || + (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) + skip_more_in_this_rmode=rmode; +#ifndef __U_BOOT__ + checkjobs(NULL); +#endif + } + return rcode; +} + +/* broken, of course, but OK for testing */ +static char *indenter(int i) +{ + static char blanks[]=" "; + return &blanks[sizeof(blanks)-i-1]; +} + +/* return code is the exit status of the pipe */ +static int free_pipe(struct pipe *pi, int indent) +{ + char **p; + struct child_prog *child; +#ifndef __U_BOOT__ + struct redir_struct *r, *rnext; +#endif + int a, i, ret_code=0; + char *ind = indenter(indent); + +#ifndef __U_BOOT__ + if (pi->stopped_progs > 0) + return ret_code; + final_printf("%s run pipe: (pid %d)\n",ind,getpid()); +#endif + for (i=0; i<pi->num_progs; i++) { + child = &pi->progs[i]; + final_printf("%s command %d:\n",ind,i); + if (child->argv) { + for (a=0,p=child->argv; *p; a++,p++) { + final_printf("%s argv[%d] = %s\n",ind,a,*p); + } +#ifndef __U_BOOT__ + globfree(&child->glob_result); +#else + for (a = 0; a < child->argc; a++) { + free(child->argv[a]); + } + free(child->argv); + free(child->argv_nonnull); + child->argc = 0; +#endif + child->argv=NULL; + } else if (child->group) { +#ifndef __U_BOOT__ + final_printf("%s begin group (subshell:%d)\n",ind, child->subshell); +#endif + ret_code = free_pipe_list(child->group,indent+3); + final_printf("%s end group\n",ind); + } else { + final_printf("%s (nil)\n",ind); + } +#ifndef __U_BOOT__ + for (r=child->redirects; r; r=rnext) { + final_printf("%s redirect %d%s", ind, r->fd, redir_table[r->type].descrip); + if (r->dup == -1) { + /* guard against the case >$FOO, where foo is unset or blank */ + if (r->word.gl_pathv) { + final_printf(" %s\n", *r->word.gl_pathv); + globfree(&r->word); + } + } else { + final_printf("&%d\n", r->dup); + } + rnext=r->next; + free(r); + } + child->redirects=NULL; +#endif + } + free(pi->progs); /* children are an array, they get freed all at once */ + pi->progs=NULL; + return ret_code; +} + +static int free_pipe_list(struct pipe *head, int indent) +{ + int rcode=0; /* if list has no members */ + struct pipe *pi, *next; + char *ind = indenter(indent); + for (pi=head; pi; pi=next) { + final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode); + rcode = free_pipe(pi, indent); + final_printf("%s pipe followup code %d\n", ind, pi->followup); + next=pi->next; + pi->next=NULL; + free(pi); + } + return rcode; +} + +/* Select which version we will use */ +static int run_list(struct pipe *pi) +{ + int rcode=0; +#ifndef __U_BOOT__ + if (fake_mode==0) { +#endif + rcode = run_list_real(pi); +#ifndef __U_BOOT__ + } +#endif + /* free_pipe_list has the side effect of clearing memory + * In the long run that function can be merged with run_list_real, + * but doing that now would hobble the debugging effort. */ + free_pipe_list(pi,0); + return rcode; +} + +/* The API for glob is arguably broken. This routine pushes a non-matching + * string into the output structure, removing non-backslashed backslashes. + * If someone can prove me wrong, by performing this function within the + * original glob(3) api, feel free to rewrite this routine into oblivion. + * Return code (0 vs. GLOB_NOSPACE) matches glob(3). + * XXX broken if the last character is '\\', check that before calling. + */ +#ifndef __U_BOOT__ +static int globhack(const char *src, int flags, glob_t *pglob) +{ + int cnt=0, pathc; + const char *s; + char *dest; + for (cnt=1, s=src; s && *s; s++) { + if (*s == '\\') s++; + cnt++; + } + dest = malloc(cnt); + if (!dest) return GLOB_NOSPACE; + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathv=NULL; + pglob->gl_pathc=0; + pglob->gl_offs=0; + pglob->gl_offs=0; + } + pathc = ++pglob->gl_pathc; + pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv)); + if (pglob->gl_pathv == NULL) return GLOB_NOSPACE; + pglob->gl_pathv[pathc-1]=dest; + pglob->gl_pathv[pathc]=NULL; + for (s=src; s && *s; s++, dest++) { + if (*s == '\\') s++; + *dest = *s; + } + *dest='\0'; + return 0; +} + +/* XXX broken if the last character is '\\', check that before calling */ +static int glob_needed(const char *s) +{ + for (; *s; s++) { + if (*s == '\\') s++; + if (strchr("*[?",*s)) return 1; + } + return 0; +} + +#if 0 +static void globprint(glob_t *pglob) +{ + int i; + debug_printf("glob_t at %p:\n", pglob); + debug_printf(" gl_pathc=%d gl_pathv=%p gl_offs=%d gl_flags=%d\n", + pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags); + for (i=0; i<pglob->gl_pathc; i++) + debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i, + pglob->gl_pathv[i], pglob->gl_pathv[i]); +} +#endif + +static int xglob(o_string *dest, int flags, glob_t *pglob) +{ + int gr; + + /* short-circuit for null word */ + /* we can code this better when the debug_printf's are gone */ + if (dest->length == 0) { + if (dest->nonnull) { + /* bash man page calls this an "explicit" null */ + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } else { + return 0; + } + } else if (glob_needed(dest->data)) { + gr = glob(dest->data, flags, NULL, pglob); + debug_printf("glob returned %d\n",gr); + if (gr == GLOB_NOMATCH) { + /* quote removal, or more accurately, backslash removal */ + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } + } else { + gr = globhack(dest->data, flags, pglob); + debug_printf("globhack returned %d\n",gr); + } + if (gr == GLOB_NOSPACE) + error_msg_and_die("out of memory during glob"); + if (gr != 0) { /* GLOB_ABORTED ? */ + error_msg("glob(3) error %d",gr); + } + /* globprint(glob_target); */ + return gr; +} +#endif + +#ifdef __U_BOOT__ +static char *get_dollar_var(char ch); +#endif + +/* This is used to get/check local shell variables */ +char *get_local_var(const char *s) +{ + struct variables *cur; + + if (!s) + return NULL; + +#ifdef __U_BOOT__ + if (*s == '$') + return get_dollar_var(s[1]); +#endif + + for (cur = top_vars; cur; cur=cur->next) + if(strcmp(cur->name, s)==0) + return cur->value; + return NULL; +} + +/* This is used to set local shell variables + flg_export==0 if only local (not exporting) variable + flg_export==1 if "new" exporting environ + flg_export>1 if current startup environ (not call putenv()) */ +int set_local_var(const char *s, int flg_export) +{ + char *name, *value; + int result=0; + struct variables *cur; + +#ifdef __U_BOOT__ + /* might be possible! */ + if (!isalpha(*s)) + return -1; +#endif + + name=strdup(s); + + /* 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, '='); + if (value == NULL || *(value + 1) == 0) { + free(name); + return -1; + } + *value++ = 0; + + for(cur = top_vars; cur; cur = cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + + if(cur) { + if(strcmp(cur->value, value)==0) { + if(flg_export>0 && cur->flg_export==0) + cur->flg_export=flg_export; + else + result++; + } else { + if(cur->flg_read_only) { + error_msg("%s: readonly variable", name); + result = -1; + } else { + if(flg_export>0 || cur->flg_export>1) + cur->flg_export=1; + free(cur->value); + + cur->value = strdup(value); + } + } + } else { + cur = malloc(sizeof(struct variables)); + if(!cur) { + result = -1; + } else { + cur->name = strdup(name); + if (cur->name == NULL) { + free(cur); + result = -1; + } else { + struct variables *bottom = top_vars; + cur->value = strdup(value); + cur->next = NULL; + cur->flg_export = flg_export; + cur->flg_read_only = 0; + while(bottom->next) bottom=bottom->next; + bottom->next = cur; + } + } + } + +#ifndef __U_BOOT__ + if(result==0 && cur->flg_export==1) { + *(value-1) = '='; + result = putenv(name); + } else { +#endif + free(name); +#ifndef __U_BOOT__ + if(result>0) /* equivalent to previous set */ + result = 0; + } +#endif + return result; +} + +void unset_local_var(const char *name) +{ + struct variables *cur; + + if (name) { + for (cur = top_vars; cur; cur=cur->next) { + if(strcmp(cur->name, name)==0) + break; + } + if (cur != NULL) { + struct variables *next = top_vars; + if(cur->flg_read_only) { + error_msg("%s: readonly variable", name); + return; + } else { +#ifndef __U_BOOT__ + if(cur->flg_export) + unenv_set(cur->name); +#endif + free(cur->name); + free(cur->value); + while (next->next != cur) + next = next->next; + next->next = cur->next; + } + free(cur); + } + } +} + +static int is_assignment(const char *s) +{ + if (s == NULL) + return 0; + + if (!isalpha(*s)) return 0; + ++s; + while(isalnum(*s) || *s=='_') ++s; + return *s=='='; +} + +#ifndef __U_BOOT__ +/* the src parameter allows us to peek forward to a possible &n syntax + * for file descriptor duplication, e.g., "2>&1". + * Return code is 0 normally, 1 if a syntax error is detected in src. + * Resource errors (in xmalloc) cause the process to exit */ +static int setup_redirect(struct p_context *ctx, int fd, redir_type style, + struct in_str *input) +{ + struct child_prog *child=ctx->child; + struct redir_struct *redir = child->redirects; + struct redir_struct *last_redir=NULL; + + /* Create a new redir_struct and drop it onto the end of the linked list */ + while(redir) { + last_redir=redir; + redir=redir->next; + } + redir = xmalloc(sizeof(struct redir_struct)); + redir->next=NULL; + redir->word.gl_pathv=NULL; + if (last_redir) { + last_redir->next=redir; + } else { + child->redirects=redir; + } + + redir->type=style; + redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ; + + debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); + + /* Check for a '2>&1' type redirect */ + redir->dup = redirect_dup_num(input); + if (redir->dup == -2) return 1; /* syntax error */ + if (redir->dup != -1) { + /* Erik had a check here that the file descriptor in question + * is legit; I postpone that to "run time" + * A "-" representation of "close me" shows up as a -3 here */ + debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); + } else { + /* We do _not_ try to open the file that src points to, + * since we need to return and let src be expanded first. + * Set ctx->pending_redirect, so we know what to do at the + * end of the next parsed word. + */ + ctx->pending_redirect = redir; + } + return 0; +} +#endif + +static struct pipe *new_pipe(void) +{ + struct pipe *pi; + pi = xmalloc(sizeof(struct pipe)); + pi->num_progs = 0; + pi->progs = NULL; + pi->next = NULL; + pi->followup = 0; /* invalid */ + pi->r_mode = RES_NONE; + return pi; +} + +static void initialize_context(struct p_context *ctx) +{ + ctx->pipe=NULL; +#ifndef __U_BOOT__ + ctx->pending_redirect=NULL; +#endif + ctx->child=NULL; + ctx->list_head=new_pipe(); + ctx->pipe=ctx->list_head; + ctx->w=RES_NONE; + ctx->stack=NULL; +#ifdef __U_BOOT__ + ctx->old_flag=0; +#endif + done_command(ctx); /* creates the memory for working child */ +} + +/* normal return is 0 + * if a reserved word is found, and processed, return 1 + * should handle if, then, elif, else, fi, for, while, until, do, done. + * case, function, and select are obnoxious, save those for later. + */ +struct reserved_combo { + char *literal; + int code; + long flag; +}; +/* Mostly a list of accepted follow-up reserved words. + * FLAG_END means we are done with the sequence, and are ready + * to turn the compound list into a command. + * FLAG_START means the word must start a new compound list. + */ +static struct reserved_combo reserved_list[] = { + { "if", RES_IF, FLAG_THEN | FLAG_START }, + { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, + { "elif", RES_ELIF, FLAG_THEN }, + { "else", RES_ELSE, FLAG_FI }, + { "fi", RES_FI, FLAG_END }, + { "for", RES_FOR, FLAG_IN | FLAG_START }, + { "while", RES_WHILE, FLAG_DO | FLAG_START }, + { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "in", RES_IN, FLAG_DO }, + { "do", RES_DO, FLAG_DONE }, + { "done", RES_DONE, FLAG_END } +}; +#define NRES (sizeof(reserved_list)/sizeof(struct reserved_combo)) + +static int reserved_word(o_string *dest, struct p_context *ctx) +{ + struct reserved_combo *r; + for (r=reserved_list; + r<reserved_list+NRES; r++) { + if (strcmp(dest->data, r->literal) == 0) { + debug_printf("found reserved word %s, code %d\n",r->literal,r->code); + if (r->flag & FLAG_START) { + struct p_context *new = xmalloc(sizeof(struct p_context)); + debug_printf("push stack\n"); + if (ctx->w == RES_IN || ctx->w == RES_FOR) { + syntax(); + free(new); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + *new = *ctx; /* physical copy */ + initialize_context(ctx); + ctx->stack=new; + } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) { + syntax(); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } + ctx->w=r->code; + ctx->old_flag = r->flag; + if (ctx->old_flag & FLAG_END) { + struct p_context *old; + debug_printf("pop stack\n"); + done_pipe(ctx,PIPE_SEQ); + old = ctx->stack; + old->child->group = ctx->list_head; +#ifndef __U_BOOT__ + old->child->subshell = 0; +#endif + *ctx = *old; /* physical copy */ + free(old); + } + b_reset (dest); + return 1; + } + } + return 0; +} + +/* normal return is 0. + * Syntax or xglob errors return 1. */ +static int done_word(o_string *dest, struct p_context *ctx) +{ + struct child_prog *child=ctx->child; +#ifndef __U_BOOT__ + glob_t *glob_target; + int gr, flags = 0; +#else + char *str, *s; + int argc, cnt; +#endif + + debug_printf("done_word: %s %p\n", dest->data, child); + if (dest->length == 0 && !dest->nonnull) { + debug_printf(" true null, ignored\n"); + return 0; + } +#ifndef __U_BOOT__ + if (ctx->pending_redirect) { + glob_target = &ctx->pending_redirect->word; + } else { +#endif + if (child->group) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) { + debug_printf("checking %s for reserved-ness\n",dest->data); + if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; + } +#ifndef __U_BOOT__ + glob_target = &child->glob_result; + if (child->argv) flags |= GLOB_APPEND; +#else + for (cnt = 1, s = dest->data; s && *s; s++) { + if (*s == '\\') s++; + cnt++; + } + str = malloc(cnt); + if (!str) return 1; + if ( child->argv == NULL) { + child->argc=0; + } + argc = ++child->argc; + child->argv = realloc(child->argv, (argc+1)*sizeof(*child->argv)); + if (child->argv == NULL) { + free(str); + return 1; + } + child->argv_nonnull = realloc(child->argv_nonnull, + (argc+1)*sizeof(*child->argv_nonnull)); + if (child->argv_nonnull == NULL) { + free(str); + return 1; + } + child->argv[argc-1]=str; + child->argv_nonnull[argc-1] = dest->nonnull; + child->argv[argc]=NULL; + child->argv_nonnull[argc] = 0; + for (s = dest->data; s && *s; s++,str++) { + if (*s == '\\') s++; + *str = *s; + } + *str = '\0'; +#endif +#ifndef __U_BOOT__ + } + gr = xglob(dest, flags, glob_target); + if (gr != 0) return 1; +#endif + + b_reset(dest); +#ifndef __U_BOOT__ + if (ctx->pending_redirect) { + ctx->pending_redirect=NULL; + if (glob_target->gl_pathc != 1) { + error_msg("ambiguous redirect"); + return 1; + } + } else { + child->argv = glob_target->gl_pathv; + } +#endif + if (ctx->w == RES_FOR) { + done_word(dest,ctx); + done_pipe(ctx,PIPE_SEQ); + } + return 0; +} + +/* The only possible error here is out of memory, in which case + * xmalloc exits. */ +static int done_command(struct p_context *ctx) +{ + /* The child is really already in the pipe structure, so + * advance the pipe counter and make a new, null child. + * Only real trickiness here is that the uncommitted + * child structure, to which ctx->child points, is not + * counted in pi->num_progs. */ + struct pipe *pi=ctx->pipe; + struct child_prog *prog=ctx->child; + + if (prog && prog->group == NULL + && prog->argv == NULL +#ifndef __U_BOOT__ + && prog->redirects == NULL) { +#else + ) { +#endif + debug_printf("done_command: skipping null command\n"); + return 0; + } else if (prog) { + pi->num_progs++; + debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs); + } else { + debug_printf("done_command: initializing\n"); + } + pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); + + prog = pi->progs + pi->num_progs; +#ifndef __U_BOOT__ + prog->redirects = NULL; +#endif + prog->argv = NULL; + prog->argv_nonnull = NULL; +#ifndef __U_BOOT__ + prog->is_stopped = 0; +#endif + prog->group = NULL; +#ifndef __U_BOOT__ + prog->glob_result.gl_pathv = NULL; + prog->family = pi; +#endif + prog->sp = 0; + ctx->child = prog; + prog->type = ctx->type; + + /* but ctx->pipe and ctx->list_head remain unchanged */ + return 0; +} + +static int done_pipe(struct p_context *ctx, pipe_style type) +{ + struct pipe *new_p; + done_command(ctx); /* implicit closure of previous command */ + debug_printf("done_pipe, type %d\n", type); + ctx->pipe->followup = type; + ctx->pipe->r_mode = ctx->w; + new_p=new_pipe(); + ctx->pipe->next = new_p; + ctx->pipe = new_p; + ctx->child = NULL; + done_command(ctx); /* set up new pipe to accept commands */ + return 0; +} + +#ifndef __U_BOOT__ +/* peek ahead in the in_str to find out if we have a "&n" construct, + * as in "2>&1", that represents duplicating a file descriptor. + * returns either -2 (syntax error), -1 (no &), or the number found. + */ +static int redirect_dup_num(struct in_str *input) +{ + int ch, d=0, ok=0; + ch = b_peek(input); + if (ch != '&') return -1; + + b_getch(input); /* get the & */ + ch=b_peek(input); + if (ch == '-') { + b_getch(input); + return -3; /* "-" represents "close me" */ + } + while (isdigit(ch)) { + d = d*10+(ch-'0'); + ok=1; + b_getch(input); + ch = b_peek(input); + } + if (ok) return d; + + error_msg("ambiguous redirect"); + return -2; +} + +/* If a redirect is immediately preceded by a number, that number is + * supposed to tell which file descriptor to redirect. This routine + * looks for such preceding numbers. In an ideal world this routine + * needs to handle all the following classes of redirects... + * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo + * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo + * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo + * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo + * A -1 output from this program means no valid number was found, so the + * caller should use the appropriate default for this redirection. + */ +static int redirect_opt_num(o_string *o) +{ + int num; + + if (o->length==0) return -1; + for(num=0; num<o->length; num++) { + if (!isdigit(*(o->data+num))) { + return -1; + } + } + /* reuse num (and save an int) */ + num=atoi(o->data); + b_reset(o); + return num; +} + +FILE *generate_stream_from_list(struct pipe *head) +{ + FILE *pf; +#if 1 + int pid, channel[2]; + if (pipe(channel)<0) perror_msg_and_die("pipe"); + pid=fork(); + if (pid<0) { + perror_msg_and_die("fork"); + } else if (pid==0) { + close(channel[0]); + if (channel[1] != 1) { + dup2(channel[1],1); + close(channel[1]); + } +#if 0 +#define SURROGATE "surrogate response" + write(1,SURROGATE,sizeof(SURROGATE)); + _exit(run_list(head)); +#else + _exit(run_list_real(head)); /* leaks memory */ +#endif + } + debug_printf("forked child %d\n",pid); + close(channel[1]); + pf = fdopen(channel[0],"r"); + debug_printf("pipe on FILE *%p\n",pf); +#else + free_pipe_list(head,0); + pf=popen("echo surrogate response","r"); + debug_printf("started fake pipe on FILE *%p\n",pf); +#endif + return pf; +} + +/* this version hacked for testing purposes */ +/* return code is exit status of the process that is run. */ +static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end) +{ + int retcode; + o_string result=NULL_O_STRING; + struct p_context inner; + FILE *p; + struct in_str pipe_str; + initialize_context(&inner); + + /* recursion to generate command */ + retcode = parse_stream(&result, &inner, input, subst_end); + if (retcode != 0) return retcode; /* syntax error or EOF */ + done_word(&result, &inner); + done_pipe(&inner, PIPE_SEQ); + b_free(&result); + + p=generate_stream_from_list(inner.list_head); + if (p==NULL) return 1; + mark_open(fileno(p)); + setup_file_in_str(&pipe_str, p); + + /* now send results of command back into original context */ + retcode = parse_stream(dest, ctx, &pipe_str, '\0'); + /* XXX In case of a syntax error, should we try to kill the child? + * That would be tough to do right, so just read until EOF. */ + if (retcode == 1) { + while (b_getch(&pipe_str)!=EOF) { /* discard */ }; + } + + debug_printf("done reading from pipe, pclose()ing\n"); + /* This is the step that wait()s for the child. Should be pretty + * safe, since we just read an EOF from its stdout. We could try + * to better, by using wait(), and keeping track of background jobs + * at the same time. That would be a lot of work, and contrary + * to the KISS philosophy of this program. */ + mark_closed(fileno(p)); + retcode=pclose(p); + free_pipe_list(inner.list_head,0); + debug_printf("pclosed, retcode=%d\n",retcode); + /* XXX this process fails to trim a single trailing newline */ + return retcode; +} + +static int parse_group(o_string *dest, struct p_context *ctx, + struct in_str *input, int ch) +{ + int rcode, endch=0; + struct p_context sub; + struct child_prog *child = ctx->child; + if (child->argv) { + syntax(); + return 1; /* syntax error, groups and arglists don't mix */ + } + initialize_context(&sub); + switch(ch) { + case '(': endch=')'; child->subshell=1; break; + case '{': endch='}'; break; + default: syntax(); /* really logic error */ + } + rcode=parse_stream(dest,&sub,input,endch); + done_word(dest,&sub); /* finish off the final word in the subcontext */ + done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ + child->group = sub.list_head; + return rcode; + /* child remains "open", available for possible redirects */ +} +#endif + +/* basically useful version until someone wants to get fancier, + * see the bash man page under "Parameter Expansion" */ +static char *lookup_param(char *src) +{ + char *p; + char *sep; + char *default_val = NULL; + int assign = 0; + int expand_empty = 0; + + if (!src) + return NULL; + + sep = strchr(src, ':'); + + if (sep) { + *sep = '\0'; + if (*(sep + 1) == '-') + default_val = sep+2; + if (*(sep + 1) == '=') { + default_val = sep+2; + assign = 1; + } + if (*(sep + 1) == '+') { + default_val = sep+2; + expand_empty = 1; + } + } + + p = env_get(src); + if (!p) + p = get_local_var(src); + + if (!p || strlen(p) == 0) { + p = default_val; + if (assign) { + char *var = malloc(strlen(src)+strlen(default_val)+2); + if (var) { + sprintf(var, "%s=%s", src, default_val); + set_local_var(var, 0); + } + free(var); + } + } else if (expand_empty) { + p += strlen(p); + } + + if (sep) + *sep = ':'; + + return p; +} + +#ifdef __U_BOOT__ +static char *get_dollar_var(char ch) +{ + static char buf[40]; + + buf[0] = '\0'; + switch (ch) { + case '?': + sprintf(buf, "%u", (unsigned int)last_return_code); + break; + default: + return NULL; + } + return buf; +} +#endif + +/* return code: 0 for OK, 1 for syntax error */ +static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) +{ +#ifndef __U_BOOT__ + int i, advance=0; +#else + int advance=0; +#endif +#ifndef __U_BOOT__ + char sep[]=" "; +#endif + int ch = input->peek(input); /* first character after the $ */ + debug_printf("handle_dollar: ch=%c\n",ch); + if (isalpha(ch)) { + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + while(ch=b_peek(input),isalnum(ch) || ch=='_') { + b_getch(input); + b_addchr(dest,ch); + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); +#ifndef __U_BOOT__ + } else if (isdigit(ch)) { + i = ch-'0'; /* XXX is $0 special? */ + if (i<global_argc) { + parse_string(dest, ctx, global_argv[i]); /* recursion */ + } + advance = 1; +#endif + } else switch (ch) { +#ifndef __U_BOOT__ + case '$': + b_adduint(dest,getpid()); + advance = 1; + break; + case '!': + if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); + advance = 1; + break; +#endif + case '?': +#ifndef __U_BOOT__ + b_adduint(dest,last_return_code); +#else + ctx->child->sp++; + b_addchr(dest, SPECIAL_VAR_SYMBOL); + b_addchr(dest, '$'); + b_addchr(dest, '?'); + b_addchr(dest, SPECIAL_VAR_SYMBOL); +#endif + advance = 1; + break; +#ifndef __U_BOOT__ + case '#': + b_adduint(dest,global_argc ? global_argc-1 : 0); + advance = 1; + break; +#endif + case '{': + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; + b_getch(input); + /* XXX maybe someone will try to escape the '}' */ + while(ch=b_getch(input),ch!=EOF && ch!='}') { + b_addchr(dest,ch); + } + if (ch != '}') { + syntax(); + return 1; + } + b_addchr(dest, SPECIAL_VAR_SYMBOL); + break; +#ifndef __U_BOOT__ + case '(': + b_getch(input); + process_command_subs(dest, ctx, input, ')'); + break; + case '*': + sep[0]=ifs[0]; + for (i=1; i<global_argc; i++) { + parse_string(dest, ctx, global_argv[i]); + if (i+1 < global_argc) parse_string(dest, ctx, sep); + } + break; + case '@': + case '-': + case '_': + /* still unhandled, but should be eventually */ + error_msg("unhandled syntax: $%c",ch); + return 1; + break; +#endif + default: + b_addqchr(dest,'$',dest->quote); + } + /* Eat the character if the flag was set. If the compiler + * is smart enough, we could substitute "b_getch(input);" + * for all the "advance = 1;" above, and also end up with + * a nice size-optimized program. Hah! That'll be the day. + */ + if (advance) b_getch(input); + return 0; +} + +#ifndef __U_BOOT__ +int parse_string(o_string *dest, struct p_context *ctx, const char *src) +{ + struct in_str foo; + setup_string_in_str(&foo, src); + return parse_stream(dest, ctx, &foo, '\0'); +} +#endif + +/* return code is 0 for normal exit, 1 for syntax error */ +static int parse_stream(o_string *dest, struct p_context *ctx, + struct in_str *input, int end_trigger) +{ + unsigned int ch, m; +#ifndef __U_BOOT__ + int redir_fd; + redir_type redir_style; +#endif + int next; + + /* Only double-quote state is handled in the state variable dest->quote. + * A single-quote triggers a bypass of the main loop until its mate is + * found. When recursing, quote state is passed in via dest->quote. */ + + debug_printf("parse_stream, end_trigger=%d\n",end_trigger); + while ((ch=b_getch(input))!=EOF) { + m = map[ch]; +#ifdef __U_BOOT__ + if (input->__promptme == 0) return 1; +#endif + next = (ch == '\n') ? 0 : b_peek(input); + + debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n", + ch >= ' ' ? ch : '.', ch, m, + dest->quote, ctx->stack == NULL ? '*' : '.'); + + if (m==0 || ((m==1 || m==2) && dest->quote)) { + b_addqchr(dest, ch, dest->quote); + } else { + if (m==2) { /* unquoted IFS */ + if (done_word(dest, ctx)) { + return 1; + } + /* If we aren't performing a substitution, treat a newline as a + * command separator. */ + if (end_trigger != '\0' && ch=='\n') + done_pipe(ctx,PIPE_SEQ); + } + if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) { + debug_printf("leaving parse_stream (triggered)\n"); + return 0; + } +#if 0 + if (ch=='\n') { + /* Yahoo! Time to run with it! */ + done_pipe(ctx,PIPE_SEQ); + run_list(ctx->list_head); + initialize_context(ctx); + } +#endif + if (m!=2) switch (ch) { + case '#': + if (dest->length == 0 && !dest->quote) { + while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); } + } else { + b_addqchr(dest, ch, dest->quote); + } + break; + case '\\': + if (next == EOF) { + syntax(); + return 1; + } + b_addqchr(dest, '\\', dest->quote); + b_addqchr(dest, b_getch(input), dest->quote); + break; + case '$': + if (handle_dollar(dest, ctx, input)!=0) return 1; + break; + case '\'': + dest->nonnull = 1; + while(ch=b_getch(input),ch!=EOF && ch!='\'') { +#ifdef __U_BOOT__ + if(input->__promptme == 0) return 1; +#endif + b_addchr(dest,ch); + } + if (ch==EOF) { + syntax(); + return 1; + } + break; + case '"': + dest->nonnull = 1; + dest->quote = !dest->quote; + break; +#ifndef __U_BOOT__ + case '`': + process_command_subs(dest, ctx, input, '`'); + break; + case '>': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_OVERWRITE; + if (next == '>') { + redir_style=REDIRECT_APPEND; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support >(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; + case '<': + redir_fd = redirect_opt_num(dest); + done_word(dest, ctx); + redir_style=REDIRECT_INPUT; + if (next == '<') { + redir_style=REDIRECT_HEREIS; + b_getch(input); + } else if (next == '>') { + redir_style=REDIRECT_IO; + b_getch(input); + } else if (next == '(') { + syntax(); /* until we support <(list) Process Substitution */ + return 1; + } + setup_redirect(ctx, redir_fd, redir_style, input); + break; +#endif + case ';': + done_word(dest, ctx); + done_pipe(ctx,PIPE_SEQ); + break; + case '&': + done_word(dest, ctx); + if (next=='&') { + b_getch(input); + done_pipe(ctx,PIPE_AND); + } else { +#ifndef __U_BOOT__ + done_pipe(ctx,PIPE_BG); +#else + syntax_err(); + return 1; +#endif + } + break; + case '|': + done_word(dest, ctx); + if (next=='|') { + b_getch(input); + done_pipe(ctx,PIPE_OR); + } else { + /* we could pick up a file descriptor choice here + * with redirect_opt_num(), but bash doesn't do it. + * "echo foo 2| cat" yields "foo 2". */ +#ifndef __U_BOOT__ + done_command(ctx); +#else + syntax_err(); + return 1; +#endif + } + break; +#ifndef __U_BOOT__ + case '(': + case '{': + if (parse_group(dest, ctx, input, ch)!=0) return 1; + break; + case ')': + case '}': + syntax(); /* Proper use of this character caught by end_trigger */ + return 1; + break; +#endif + case SUBSTED_VAR_SYMBOL: + dest->nonnull = 1; + while (ch = b_getch(input), ch != EOF && + ch != SUBSTED_VAR_SYMBOL) { + debug_printf("subst, pass=%d\n", ch); + if (input->__promptme == 0) + return 1; + b_addchr(dest, ch); + } + debug_printf("subst, term=%d\n", ch); + if (ch == EOF) { + syntax(); + return 1; + } + break; + default: + syntax(); /* this is really an internal logic error */ + return 1; + } + } + } + /* complain if quote? No, maybe we just finished a command substitution + * that was quoted. Example: + * $ echo "`cat foo` plus more" + * and we just got the EOF generated by the subshell that ran "cat foo" + * The only real complaint is if we got an EOF when end_trigger != '\0', + * that is, we were really supposed to get end_trigger, and never got + * one before the EOF. Can't use the standard "syntax error" return code, + * so that parse_stream_outer can distinguish the EOF and exit smoothly. */ + debug_printf("leaving parse_stream (EOF)\n"); + if (end_trigger != '\0') return -1; + return 0; +} + +static void mapset(const unsigned char *set, int code) +{ + const unsigned char *s; + for (s=set; *s; s++) map[*s] = code; +} + +static void update_ifs_map(void) +{ + /* char *ifs and char map[256] are both globals. */ + ifs = (uchar *)env_get("IFS"); + if (ifs == NULL) ifs=(uchar *)" \t\n"; + /* Precompute a list of 'flow through' behavior so it can be treated + * quickly up front. Computation is necessary because of IFS. + * Special case handling of IFS == " \t\n" is not implemented. + * The map[] array only really needs two bits each, and on most machines + * that would be faster because of the reduced L1 cache footprint. + */ + memset(map,0,sizeof(map)); /* most characters flow through always */ +#ifndef __U_BOOT__ + mapset((uchar *)"\\$'\"`", 3); /* never flow through */ + mapset((uchar *)"<>;&|(){}#", 1); /* flow through if quoted */ +#else + { + uchar subst[2] = {SUBSTED_VAR_SYMBOL, 0}; + mapset(subst, 3); /* never flow through */ + } + mapset((uchar *)"\\$'\"", 3); /* never flow through */ + mapset((uchar *)";&|#", 1); /* flow through if quoted */ +#endif + mapset(ifs, 2); /* also flow through if quoted */ +} + +/* most recursion does not come through here, the exeception is + * from builtin_source() */ +static int parse_stream_outer(struct in_str *inp, int flag) +{ + + struct p_context ctx; + o_string temp=NULL_O_STRING; + int rcode; +#ifdef __U_BOOT__ + int code = 1; +#endif + do { + ctx.type = flag; + initialize_context(&ctx); + update_ifs_map(); + if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0); + inp->promptmode=1; + rcode = parse_stream(&temp, &ctx, inp, + flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n'); +#ifdef __U_BOOT__ + if (rcode == 1) flag_repeat = 0; +#endif + if (rcode != 1 && ctx.old_flag != 0) { + syntax(); +#ifdef __U_BOOT__ + flag_repeat = 0; +#endif + } + if (rcode != 1 && ctx.old_flag == 0) { + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); +#ifndef __U_BOOT__ + run_list(ctx.list_head); +#else + code = run_list(ctx.list_head); + if (code == -2) { /* exit */ + b_free(&temp); + code = 0; + /* XXX hackish way to not allow exit from main loop */ + if (inp->peek == file_peek) { + printf("exit not allowed from main input shell.\n"); + continue; + } + break; + } + if (code == -1) + flag_repeat = 0; +#endif + } else { + if (ctx.old_flag != 0) { + free(ctx.stack); + b_reset(&temp); + } +#ifdef __U_BOOT__ + if (inp->__promptme == 0) printf("<INTERRUPT>\n"); + inp->__promptme = 1; +#endif + temp.nonnull = 0; + temp.quote = 0; + inp->p = NULL; + free_pipe_list(ctx.list_head,0); + } + b_free(&temp); + /* loop on syntax errors, return on EOF */ + } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) && + (inp->peek != static_peek || b_peek(inp))); +#ifndef __U_BOOT__ + return 0; +#else + return (code != 0) ? 1 : 0; +#endif /* __U_BOOT__ */ +} + +#ifndef __U_BOOT__ +static int parse_string_outer(const char *s, int flag) +#else +int parse_string_outer(const char *s, int flag) +#endif /* __U_BOOT__ */ +{ + struct in_str input; +#ifdef __U_BOOT__ + char *p = NULL; + int rcode; + if (!s) + return 1; + if (!*s) + return 0; + if (!(p = strchr(s, '\n')) || *++p) { + p = xmalloc(strlen(s) + 2); + strcpy(p, s); + strcat(p, "\n"); + setup_string_in_str(&input, p); + rcode = parse_stream_outer(&input, flag); + free(p); + return rcode; + } else { +#endif + setup_string_in_str(&input, s); + return parse_stream_outer(&input, flag); +#ifdef __U_BOOT__ + } +#endif +} + +#ifndef __U_BOOT__ +static int parse_file_outer(FILE *f) +#else +int parse_file_outer(void) +#endif +{ + int rcode; + struct in_str input; +#ifndef __U_BOOT__ + setup_file_in_str(&input, f); +#else + setup_file_in_str(&input); +#endif + rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); + return rcode; +} + +#ifdef __U_BOOT__ +#ifdef CONFIG_NEEDS_MANUAL_RELOC +static void u_boot_hush_reloc(void) +{ + unsigned long addr; + struct reserved_combo *r; + + for (r=reserved_list; r<reserved_list+NRES; r++) { + addr = (ulong) (r->literal) + gd->reloc_off; + r->literal = (char *)addr; + } +} +#endif + +int u_boot_hush_start(void) +{ + if (top_vars == NULL) { + top_vars = malloc(sizeof(struct variables)); + top_vars->name = "HUSH_VERSION"; + top_vars->value = "0.01"; + top_vars->next = NULL; + top_vars->flg_export = 0; + top_vars->flg_read_only = 1; +#ifdef CONFIG_NEEDS_MANUAL_RELOC + u_boot_hush_reloc(); +#endif + } + return 0; +} + +static void *xmalloc(size_t size) +{ + void *p = NULL; + + if (!(p = malloc(size))) { + printf("ERROR : xmalloc failed\n"); + for(;;); + } + return p; +} + +static void *xrealloc(void *ptr, size_t size) +{ + void *p = NULL; + + if (!(p = realloc(ptr, size))) { + printf("ERROR : xrealloc failed\n"); + for(;;); + } + return p; +} +#endif /* __U_BOOT__ */ + +#ifndef __U_BOOT__ +/* Make sure we have a controlling tty. If we get started under a job + * aware app (like bash for example), make sure we are now in charge so + * we don't fight over who gets the foreground */ +static void setup_job_control(void) +{ + static pid_t shell_pgrp; + /* Loop until we are in the foreground. */ + while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ())) + kill (- shell_pgrp, SIGTTIN); + + /* Ignore interactive and job-control signals. */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + + /* Put ourselves in our own process group. */ + setsid(); + shell_pgrp = getpid (); + setpgid (shell_pgrp, shell_pgrp); + + /* Grab control of the terminal. */ + tcsetpgrp(shell_terminal, shell_pgrp); +} + +int hush_main(int argc, char * const *argv) +{ + int opt; + FILE *input; + char **e = environ; + + /* XXX what should these be while sourcing /etc/profile? */ + global_argc = argc; + global_argv = argv; + + /* (re?) initialize globals. Sometimes hush_main() ends up calling + * hush_main(), therefore we cannot rely on the BSS to zero out this + * stuff. Reset these to 0 every time. */ + ifs = NULL; + /* map[] is taken care of with call to update_ifs_map() */ + fake_mode = 0; + interactive = 0; + close_me_head = NULL; + last_bg_pid = 0; + job_list = NULL; + last_jobid = 0; + + /* Initialize some more globals to non-zero values */ + set_cwd(); +#ifdef CONFIG_FEATURE_COMMAND_EDITING + cmdedit_set_initial_prompt(); +#else + PS1 = NULL; +#endif + PS2 = "> "; + + /* initialize our shell local variables with the values + * currently living in the environment */ + if (e) { + for (; *e; e++) + set_local_var(*e, 2); /* without call putenv() */ + } + + last_return_code=EXIT_SUCCESS; + + + if (argv[0] && argv[0][0] == '-') { + debug_printf("\nsourcing /etc/profile\n"); + if ((input = fopen("/etc/profile", "r")) != NULL) { + mark_open(fileno(input)); + parse_file_outer(input); + mark_closed(fileno(input)); + fclose(input); + } + } + input=stdin; + + while ((opt = getopt(argc, argv, "c:xif")) > 0) { + switch (opt) { + case 'c': + { + global_argv = argv+optind; + global_argc = argc-optind; + opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); + goto final_return; + } + break; + case 'i': + interactive++; + break; + case 'f': + fake_mode++; + break; + default: +#ifndef BB_VER + fprintf(stderr, "Usage: sh [FILE]...\n" + " or: sh -c command [args]...\n\n"); + exit(EXIT_FAILURE); +#else + show_usage(); +#endif + } + } + /* A shell is interactive if the `-i' flag was given, or if all of + * the following conditions are met: + * no -c command + * no arguments remaining or the -s flag given + * standard input is a terminal + * standard output is a terminal + * Refer to Posix.2, the description of the `sh' utility. */ + if (argv[optind]==NULL && input==stdin && + isatty(fileno(stdin)) && isatty(fileno(stdout))) { + interactive++; + } + + debug_printf("\ninteractive=%d\n", interactive); + if (interactive) { + /* Looks like they want an interactive shell */ +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET + printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); +#endif + setup_job_control(); + } + + if (argv[optind]==NULL) { + opt=parse_file_outer(stdin); + goto final_return; + } + + debug_printf("\nrunning script '%s'\n", argv[optind]); + global_argv = argv+optind; + global_argc = argc-optind; + input = xfopen(argv[optind], "r"); + opt = parse_file_outer(input); + +#ifdef CONFIG_FEATURE_CLEAN_UP + fclose(input); + if (cwd && cwd != unknown) + free((char*)cwd); + { + struct variables *cur, *tmp; + for(cur = top_vars; cur; cur = tmp) { + tmp = cur->next; + if (!cur->flg_read_only) { + free(cur->name); + free(cur->value); + free(cur); + } + } + } +#endif + +final_return: + return(opt?opt:last_return_code); +} +#endif + +static char *insert_var_value(char *inp) +{ + return insert_var_value_sub(inp, 0); +} + +static char *insert_var_value_sub(char *inp, int tag_subst) +{ + int res_str_len = 0; + int len; + int done = 0; + char *p, *p1, *res_str = NULL; + + while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { + /* check the beginning of the string for normal characters */ + if (p != inp) { + /* copy any characters to the result string */ + len = p - inp; + res_str = xrealloc(res_str, (res_str_len + len)); + strncpy((res_str + res_str_len), inp, len); + res_str_len += len; + } + inp = ++p; + /* find the ending marker */ + p = strchr(inp, SPECIAL_VAR_SYMBOL); + *p = '\0'; + /* look up the value to substitute */ + if ((p1 = lookup_param(inp))) { + if (tag_subst) + len = res_str_len + strlen(p1) + 2; + else + len = res_str_len + strlen(p1); + res_str = xrealloc(res_str, (1 + len)); + if (tag_subst) { + /* + * copy the variable value to the result + * string + */ + strcpy((res_str + res_str_len + 1), p1); + + /* + * mark the replaced text to be accepted as + * is + */ + res_str[res_str_len] = SUBSTED_VAR_SYMBOL; + res_str[res_str_len + 1 + strlen(p1)] = + SUBSTED_VAR_SYMBOL; + } else + /* + * copy the variable value to the result + * string + */ + strcpy((res_str + res_str_len), p1); + + res_str_len = len; + } + *p = SPECIAL_VAR_SYMBOL; + inp = ++p; + done = 1; + } + if (done) { + res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp))); + strcpy((res_str + res_str_len), inp); + while ((p = strchr(res_str, '\n'))) { + *p = ' '; + } + } + return (res_str == NULL) ? inp : res_str; +} + +static char **make_list_in(char **inp, char *name) +{ + int len, i; + int name_len = strlen(name); + int n = 0; + char **list; + char *p1, *p2, *p3; + + /* create list of variable values */ + list = xmalloc(sizeof(*list)); + for (i = 0; inp[i]; i++) { + p3 = insert_var_value(inp[i]); + p1 = p3; + while (*p1) { + if (*p1 == ' ') { + p1++; + continue; + } + if ((p2 = strchr(p1, ' '))) { + len = p2 - p1; + } else { + len = strlen(p1); + p2 = p1 + len; + } + /* we use n + 2 in realloc for list,because we add + * new element and then we will add NULL element */ + list = xrealloc(list, sizeof(*list) * (n + 2)); + list[n] = xmalloc(2 + name_len + len); + strcpy(list[n], name); + strcat(list[n], "="); + strncat(list[n], p1, len); + list[n++][name_len + len + 1] = '\0'; + p1 = p2; + } + if (p3 != inp[i]) free(p3); + } + list[n] = NULL; + return list; +} + +/* + * Make new string for parser + * inp - array of argument strings to flatten + * nonnull - indicates argument was quoted when originally parsed + */ +static char *make_string(char **inp, int *nonnull) +{ + char *p; + char *str = NULL; + int n; + int len = 2; + char *noeval_str; + int noeval = 0; + + noeval_str = get_local_var("HUSH_NO_EVAL"); + if (noeval_str != NULL && *noeval_str != '0' && *noeval_str != '\0') + noeval = 1; + for (n = 0; inp[n]; n++) { + p = insert_var_value_sub(inp[n], noeval); + str = xrealloc(str, (len + strlen(p) + (2 * nonnull[n]))); + if (n) { + strcat(str, " "); + } else { + *str = '\0'; + } + if (nonnull[n]) + strcat(str, "'"); + strcat(str, p); + if (nonnull[n]) + strcat(str, "'"); + len = strlen(str) + 3; + if (p != inp[n]) free(p); + } + len = strlen(str); + *(str + len) = '\n'; + *(str + len + 1) = '\0'; + return str; +} + +#ifdef __U_BOOT__ +static int do_showvar(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i, k; + int rcode = 0; + struct variables *cur; + + if (argc == 1) { /* Print all env variables */ + for (cur = top_vars; cur; cur = cur->next) { + printf ("%s=%s\n", cur->name, cur->value); + if (ctrlc ()) { + puts ("\n ** Abort\n"); + return 1; + } + } + return 0; + } + for (i = 1; i < argc; ++i) { /* print single env variables */ + char *name = argv[i]; + + k = -1; + for (cur = top_vars; cur; cur = cur->next) { + if(strcmp (cur->name, name) == 0) { + k = 0; + printf ("%s=%s\n", cur->name, cur->value); + } + if (ctrlc ()) { + puts ("\n ** Abort\n"); + return 1; + } + } + if (k < 0) { + printf ("## Error: \"%s\" not defined\n", name); + rcode ++; + } + } + return rcode; +} + +U_BOOT_CMD( + showvar, CONFIG_SYS_MAXARGS, 1, do_showvar, + "print local hushshell variables", + "\n - print values of all hushshell variables\n" + "showvar name ...\n" + " - print value of hushshell variable 'name'" +); + +#endif +/****************************************************************************/ diff --git a/roms/u-boot/common/cli_readline.c b/roms/u-boot/common/cli_readline.c new file mode 100644 index 000000000..c7614a4c9 --- /dev/null +++ b/roms/u-boot/common/cli_readline.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <time.h> +#include <watchdog.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char erase_seq[] = "\b \b"; /* erase sequence */ +static const char tab_seq[] = " "; /* used to expand TABs */ + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) + return p; + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + puts(erase_seq); + (*colp)--; + } + for (s = buffer; s < p; ++s) { + if (*s == '\t') { + puts(tab_seq + ((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putc(*s); + } + } + } else { + puts(erase_seq); + (*colp)--; + } + (*np)--; + + return p; +} + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu <nandy@mizi.com> + */ + +#define putnstr(str, n) printf("%.*s", (int)n, str) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putc(ch) +#define getcmd_getch() getchar() +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 20 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ + +#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char *hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = hist_list[hist_cur]; + } + + return ret; +} + +static char *hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = hist_list[hist_cur]; + + return ret; +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned long n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf("%s\n", hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf("%*s", (int)(eol_num - num), ""); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) + memmove(&buf[*num+1], &buf[*num], wlen-1); + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) + getcmd_putch(CTL_BACKSPACE); + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, + unsigned long *num, unsigned long *eol_num, + char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + char ichar; + int insert = 1; + int esc_len = 0; + char esc_save[8]; + int init_len = strlen(buf); + int first = 1; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + + while (1) { + if (bootretry_tstc_timeout()) + return -2; /* timed out */ + if (first && timeout) { + uint64_t etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } + first = 0; + } + + ichar = getcmd_getch(); + + /* ichar=0x0 when error occurs in U-Boot getc */ + if (!ichar) + continue; + + if ((ichar == '\n') || (ichar == '\r')) { + putc('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + enum { ESC_REJECT, ESC_SAVE, ESC_CONVERTED } act = ESC_REJECT; + + if (esc_len == 1) { + if (ichar == '[' || ichar == 'O') + act = ESC_SAVE; + } else if (esc_len == 2) { + switch (ichar) { + case 'D': /* <- key */ + ichar = CTL_CH('b'); + act = ESC_CONVERTED; + break; /* pass off to ^B handler */ + case 'C': /* -> key */ + ichar = CTL_CH('f'); + act = ESC_CONVERTED; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + act = ESC_CONVERTED; + break; /* pass off to ^A handler */ + case 'F': /* End key */ + ichar = CTL_CH('e'); + act = ESC_CONVERTED; + break; /* pass off to ^E handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + act = ESC_CONVERTED; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + act = ESC_CONVERTED; + break; /* pass off to ^N handler */ + case '1': + case '3': + case '4': + case '7': + case '8': + if (esc_save[1] == '[') { + /* see if next character is ~ */ + act = ESC_SAVE; + } + break; + } + } else if (esc_len == 3) { + if (ichar == '~') { + switch (esc_save[2]) { + case '3': /* Delete key */ + ichar = CTL_CH('d'); + act = ESC_CONVERTED; + break; /* pass to ^D handler */ + case '1': /* Home key */ + case '7': + ichar = CTL_CH('a'); + act = ESC_CONVERTED; + break; /* pass to ^A handler */ + case '4': /* End key */ + case '8': + ichar = CTL_CH('e'); + act = ESC_CONVERTED; + break; /* pass to ^E handler */ + } + } + } + + switch (act) { + case ESC_SAVE: + esc_save[esc_len++] = ichar; + continue; + case ESC_REJECT: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + case ESC_CONVERTED: + esc_len = 0; + break; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + puts("impossible condition #876\n"); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + if (ichar >= ' ' && ichar <= '~') { + cread_add_char(ichar, insert, &num, &eol_num, + buf, *len); + } + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +int cli_readline(const char *const prompt) +{ + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return cli_readline_into_buffer(prompt, console_buffer, 0); +} + + +int cli_readline_into_buffer(const char *const prompt, char *buffer, + int timeout) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + /* + * History uses a global array which is not + * writable until after relocation to RAM. + * Revert to non-history version if still + * running from flash. + */ + if (gd->flags & GD_FLG_RELOC) { + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + puts(prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { +#endif /* CONFIG_CMDLINE_EDITING */ + char *p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen(prompt); + puts(prompt); + } + col = plen; + + for (;;) { + if (bootretry_tstc_timeout()) + return -2; /* timed out */ + WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + + c = getchar(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + puts("\r\n"); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + puts(erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p = delete_char(p_buf, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* + * if auto completion triggered just + * continue + */ + *p = '\0'; + if (cmd_auto_complete(prompt, + console_buffer, + &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + puts(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char __maybe_unused buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putc('\a'); + } + } + } +#ifdef CONFIG_CMDLINE_EDITING + } +#endif +} diff --git a/roms/u-boot/common/cli_simple.c b/roms/u-boot/common/cli_simple.c new file mode 100644 index 000000000..e80ba488a --- /dev/null +++ b/roms/u-boot/common/cli_simple.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <log.h> +#include <linux/ctype.h> + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + + +int cli_simple_parse_line(char *line, char *argv[]) +{ + int nargs = 0; + + debug_parser("%s: \"%s\"\n", __func__, line); + while (nargs < CONFIG_SYS_MAXARGS) { + /* skip any white space */ + while (isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("%s: nargs=%d\n", __func__, nargs); + return nargs; + } + + argv[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && !isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; + } + + *line++ = '\0'; /* terminate current arg */ + } + + printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); + + debug_parser("%s: nargs=%d\n", __func__, nargs); + return nargs; +} + +int cli_simple_process_macros(const char *input, char *output, int max_size) +{ + char c, prev; + const char *varname_start = NULL; + int inputcnt = strlen(input); + int outputcnt = max_size; + int state = 0; /* 0 = waiting for '$' */ + int ret; + + /* 1 = waiting for '(' or '{' */ + /* 2 = waiting for ')' or '}' */ + /* 3 = waiting for ''' */ + char __maybe_unused *output_start = output; + + debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), + input); + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(' || c == '{') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')' || c == '}') { + int i; + char envname[CONFIG_SYS_CBSIZE], *envval; + /* Varname # of chars */ + int envcnt = input - varname_start - 1; + + /* Get the varname */ + for (i = 0; i < envcnt; i++) + envname[i] = varname_start[i]; + envname[i] = 0; + + /* Get its value */ + envval = env_get(envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) { + state = 0; + } else { + *(output++) = c; + outputcnt--; + } + break; + } + prev = c; + } + + ret = inputcnt ? -ENOSPC : 0; + if (outputcnt) { + *output = 0; + } else { + *(output - 1) = 0; + ret = -ENOSPC; + } + + debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", + strlen(output_start), output_start); + + return ret; +} + + /* + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from env_get(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ +int cli_simple_run_command(const char *cmd, int flag) +{ + char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char finaltoken[CONFIG_SYS_CBSIZE]; + char *str = cmdbuf; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc, inquotes; + int repeatable = 1; + int rc = 0; + + debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); + if (DEBUG_PARSER) { + /* use puts - string may be loooong */ + puts(cmd ? cmd : "NULL"); + puts("\"\n"); + } + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) + return -1; /* empty command */ + + if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { + puts("## Command too long!\n"); + return -1; + } + + strcpy(cmdbuf, cmd); + + /* Process separators and check for invalid + * repeatable commands + */ + + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); + while (*str) { + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep == '\'') && + (*(sep - 1) != '\\')) + inquotes = !inquotes; + + if (!inquotes && + (*sep == ';') && /* separator */ + (sep != str) && /* past string start */ + (*(sep - 1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } else { + str = sep; /* no more commands for next pass */ + } + debug_parser("token: \"%s\"\n", token); + + /* find macros in this token and replace them */ + cli_simple_process_macros(token, finaltoken, + sizeof(finaltoken)); + + /* Extract arguments */ + argc = cli_simple_parse_line(finaltoken, argv); + if (argc == 0) { + rc = -1; /* no command at all */ + continue; + } + + if (cmd_process(flag, argc, argv, &repeatable, NULL)) + rc = -1; + + /* Did the user stop this? */ + if (had_ctrlc()) + return -1; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} + +void cli_simple_loop(void) +{ + static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, }; + + int len; + int flag; + int rc = 1; + + for (;;) { + if (rc >= 0) { + /* Saw enough of a valid command to + * restart the timeout. + */ + bootretry_reset_cmd_timeout(); + } + len = cli_readline(CONFIG_SYS_PROMPT); + + flag = 0; /* assume no special flags for now */ + if (len > 0) + strlcpy(lastcommand, console_buffer, + CONFIG_SYS_CBSIZE + 1); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; +#ifdef CONFIG_BOOT_RETRY_TIME + else if (len == -2) { + /* -2 means timed out, retry autoboot + */ + puts("\nTimed out waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + /* Reinit board to run initialization code again */ + do_reset(NULL, 0, 0, NULL); +# else + return; /* retry autoboot */ +# endif + } +#endif + + if (len == -1) + puts("<INTERRUPT>\n"); + else + rc = run_command_repeatable(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } +} + +int cli_simple_run_command_list(char *cmd, int flag) +{ + char *line, *next; + int rcode = 0; + + /* + * Break into individual lines, and execute each line; terminate on + * error. + */ + next = cmd; + line = cmd; + while (*next) { + if (*next == '\n') { + *next = '\0'; + /* run only non-empty commands */ + if (*line) { + debug("** exec: \"%s\"\n", line); + if (cli_simple_run_command(line, 0) < 0) { + rcode = 1; + break; + } + } + line = next + 1; + } + ++next; + } + if (rcode == 0 && *line) + rcode = (cli_simple_run_command(line, 0) < 0); + + return rcode; +} diff --git a/roms/u-boot/common/command.c b/roms/u-boot/common/command.c new file mode 100644 index 000000000..95af73f17 --- /dev/null +++ b/roms/u-boot/common/command.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Command Processor Table + */ + +#include <common.h> +#include <compiler.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/ctype.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Use puts() instead of printf() to avoid printf buffer overflow + * for long help messages + */ + +int _do_help(struct cmd_tbl *cmd_start, int cmd_items, struct cmd_tbl *cmdtp, + int flag, int argc, char *const argv[]) +{ + int i; + int rcode = 0; + + if (argc == 1) { /* show list of commands */ + struct cmd_tbl *cmd_array[cmd_items]; + int i, j, swaps; + + /* Make array of commands from .uboot_cmd section */ + cmdtp = cmd_start; + for (i = 0; i < cmd_items; i++) { + cmd_array[i] = cmdtp++; + } + + /* Sort command list (trivial bubble sort) */ + for (i = cmd_items - 1; i > 0; --i) { + swaps = 0; + for (j = 0; j < i; ++j) { + if (strcmp(cmd_array[j]->name, + cmd_array[j + 1]->name) > 0) { + struct cmd_tbl *tmp; + tmp = cmd_array[j]; + cmd_array[j] = cmd_array[j + 1]; + cmd_array[j + 1] = tmp; + ++swaps; + } + } + if (!swaps) + break; + } + + /* print short help (usage) */ + for (i = 0; i < cmd_items; i++) { + const char *usage = cmd_array[i]->usage; + + /* allow user abort */ + if (ctrlc()) + return 1; + if (usage == NULL) + continue; + printf("%-*s- %s\n", CONFIG_SYS_HELP_CMD_WIDTH, + cmd_array[i]->name, usage); + } + return 0; + } + /* + * command help (long version) + */ + for (i = 1; i < argc; ++i) { + cmdtp = find_cmd_tbl(argv[i], cmd_start, cmd_items); + if (cmdtp != NULL) { + rcode |= cmd_usage(cmdtp); + } else { + printf("Unknown command '%s' - try 'help' without arguments for list of all known commands\n\n", + argv[i]); + rcode = 1; + } + } + return rcode; +} + +/* find command table entry for a command */ +struct cmd_tbl *find_cmd_tbl(const char *cmd, struct cmd_tbl *table, + int table_len) +{ +#ifdef CONFIG_CMDLINE + struct cmd_tbl *cmdtp; + struct cmd_tbl *cmdtp_temp = table; /* Init value */ + const char *p; + int len; + int n_found = 0; + + if (!cmd) + return NULL; + /* + * Some commands allow length modifiers (like "cp.b"); + * compare command name only until first dot. + */ + len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd); + + for (cmdtp = table; cmdtp != table + table_len; cmdtp++) { + if (strncmp(cmd, cmdtp->name, len) == 0) { + if (len == strlen(cmdtp->name)) + return cmdtp; /* full match */ + + cmdtp_temp = cmdtp; /* abbreviated command ? */ + n_found++; + } + } + if (n_found == 1) { /* exactly one match */ + return cmdtp_temp; + } +#endif /* CONFIG_CMDLINE */ + + return NULL; /* not found or ambiguous command */ +} + +struct cmd_tbl *find_cmd(const char *cmd) +{ + struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd); + const int len = ll_entry_count(struct cmd_tbl, cmd); + return find_cmd_tbl(cmd, start, len); +} + +int cmd_usage(const struct cmd_tbl *cmdtp) +{ + printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); + +#ifdef CONFIG_SYS_LONGHELP + printf("Usage:\n%s ", cmdtp->name); + + if (!cmdtp->help) { + puts ("- No additional help available.\n"); + return 1; + } + + puts(cmdtp->help); + putc('\n'); +#endif /* CONFIG_SYS_LONGHELP */ + return 1; +} + +#ifdef CONFIG_AUTO_COMPLETE +static char env_complete_buf[512]; + +int var_complete(int argc, char *const argv[], char last_char, int maxv, + char *cmdv[]) +{ + int space; + + space = last_char == '\0' || isblank(last_char); + + if (space && argc == 1) + return env_complete("", maxv, cmdv, sizeof(env_complete_buf), + env_complete_buf, false); + + if (!space && argc == 2) + return env_complete(argv[1], maxv, cmdv, + sizeof(env_complete_buf), + env_complete_buf, false); + + return 0; +} + +static int dollar_complete(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]) +{ + /* Make sure the last argument starts with a $. */ + if (argc < 1 || argv[argc - 1][0] != '$' || + last_char == '\0' || isblank(last_char)) + return 0; + + return env_complete(argv[argc - 1], maxv, cmdv, sizeof(env_complete_buf), + env_complete_buf, true); +} + +/*************************************************************************************/ + +int complete_subcmdv(struct cmd_tbl *cmdtp, int count, int argc, + char *const argv[], char last_char, + int maxv, char *cmdv[]) +{ +#ifdef CONFIG_CMDLINE + const struct cmd_tbl *cmdend = cmdtp + count; + const char *p; + int len, clen; + int n_found = 0; + const char *cmd; + + /* sanity? */ + if (maxv < 2) + return -2; + + cmdv[0] = NULL; + + if (argc == 0) { + /* output full list of commands */ + for (; cmdtp != cmdend; cmdtp++) { + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + cmdv[n_found++] = cmdtp->name; + } + cmdv[n_found] = NULL; + return n_found; + } + + /* more than one arg or one but the start of the next */ + if (argc > 1 || last_char == '\0' || isblank(last_char)) { + cmdtp = find_cmd_tbl(argv[0], cmdtp, count); + if (cmdtp == NULL || cmdtp->complete == NULL) { + cmdv[0] = NULL; + return 0; + } + return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); + } + + cmd = argv[0]; + /* + * Some commands allow length modifiers (like "cp.b"); + * compare command name only until first dot. + */ + p = strchr(cmd, '.'); + if (p == NULL) + len = strlen(cmd); + else + len = p - cmd; + + /* return the partial matches */ + for (; cmdtp != cmdend; cmdtp++) { + + clen = strlen(cmdtp->name); + if (clen < len) + continue; + + if (memcmp(cmd, cmdtp->name, len) != 0) + continue; + + /* too many! */ + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + + cmdv[n_found++] = cmdtp->name; + } + + cmdv[n_found] = NULL; + return n_found; +#else + return 0; +#endif +} + +static int complete_cmdv(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]) +{ +#ifdef CONFIG_CMDLINE + return complete_subcmdv(ll_entry_start(struct cmd_tbl, cmd), + ll_entry_count(struct cmd_tbl, cmd), argc, argv, + last_char, maxv, cmdv); +#else + return 0; +#endif +} + +static int make_argv(char *s, int argvsz, char *argv[]) +{ + int argc = 0; + + /* split into argv */ + while (argc < argvsz - 1) { + + /* skip any white space */ + while (isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + argv[argc++] = s; /* begin of argument string */ + + /* find end of string */ + while (*s && !isblank(*s)) + ++s; + + if (*s == '\0') /* end of s, no more args */ + break; + + *s++ = '\0'; /* terminate current arg */ + } + argv[argc] = NULL; + + return argc; +} + +static void print_argv(const char *banner, const char *leader, const char *sep, + int linemax, char *const argv[]) +{ + int ll = leader != NULL ? strlen(leader) : 0; + int sl = sep != NULL ? strlen(sep) : 0; + int len, i; + + if (banner) { + puts("\n"); + puts(banner); + } + + i = linemax; /* force leader and newline */ + while (*argv != NULL) { + len = strlen(*argv) + sl; + if (i + len >= linemax) { + puts("\n"); + if (leader) + puts(leader); + i = ll - sl; + } else if (sep) + puts(sep); + puts(*argv++); + i += len; + } + printf("\n"); +} + +static int find_common_prefix(char *const argv[]) +{ + int i, len; + char *anchor, *s, *t; + + if (*argv == NULL) + return 0; + + /* begin with max */ + anchor = *argv++; + len = strlen(anchor); + while ((t = *argv++) != NULL) { + s = anchor; + for (i = 0; i < len; i++, t++, s++) { + if (*t != *s) + break; + } + len = s - anchor; + } + return len; +} + +static char tmp_buf[CONFIG_SYS_CBSIZE + 1]; /* copy of console I/O buffer */ + +int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp) +{ + int n = *np, col = *colp; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + char *cmdv[20]; + char *s, *t; + const char *sep; + int i, j, k, len, seplen, argc; + int cnt; + char last_char; +#ifdef CONFIG_CMDLINE_PS_SUPPORT + const char *ps_prompt = env_get("PS1"); +#else + const char *ps_prompt = CONFIG_SYS_PROMPT; +#endif + + if (strcmp(prompt, ps_prompt) != 0) + return 0; /* not in normal console */ + + cnt = strlen(buf); + if (cnt >= 1) + last_char = buf[cnt - 1]; + else + last_char = '\0'; + + /* copy to secondary buffer which will be affected */ + strcpy(tmp_buf, buf); + + /* separate into argv */ + argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); + + /* first try a $ completion */ + i = dollar_complete(argc, argv, last_char, + sizeof(cmdv) / sizeof(cmdv[0]), cmdv); + if (!i) { + /* do the completion and return the possible completions */ + i = complete_cmdv(argc, argv, last_char, + sizeof(cmdv) / sizeof(cmdv[0]), cmdv); + } + + /* no match; bell and out */ + if (i == 0) { + if (argc > 1) /* allow tab for non command */ + return 0; + putc('\a'); + return 1; + } + + s = NULL; + len = 0; + sep = NULL; + seplen = 0; + if (i == 1) { /* one match; perfect */ + if (last_char != '\0' && !isblank(last_char)) + k = strlen(argv[argc - 1]); + else + k = 0; + + s = cmdv[0] + k; + len = strlen(s); + sep = " "; + seplen = 1; + } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ + if (last_char != '\0' && !isblank(last_char)) + k = strlen(argv[argc - 1]); + else + k = 0; + + j -= k; + if (j > 0) { + s = cmdv[0] + k; + len = j; + } + } + + if (s != NULL) { + k = len + seplen; + /* make sure it fits */ + if (n + k >= CONFIG_SYS_CBSIZE - 2) { + putc('\a'); + return 1; + } + + t = buf + cnt; + for (i = 0; i < len; i++) + *t++ = *s++; + if (sep != NULL) + for (i = 0; i < seplen; i++) + *t++ = sep[i]; + *t = '\0'; + n += k; + col += k; + puts(t - k); + if (sep == NULL) + putc('\a'); + *np = n; + *colp = col; + } else { + print_argv(NULL, " ", " ", 78, cmdv); + + puts(prompt); + puts(buf); + } + return 1; +} + +#endif + +#ifdef CMD_DATA_SIZE +int cmd_get_data_size(char* arg, int default_size) +{ + /* Check for a size specification .b, .w or .l. + */ + int len = strlen(arg); + if (len > 2 && arg[len-2] == '.') { + switch (arg[len-1]) { + case 'b': + return 1; + case 'w': + return 2; + case 'l': + return 4; + case 's': + return CMD_DATA_SIZE_STR; + case 'q': + if (MEM_SUPPORT_64BIT_DATA) + return 8; + /* no break */ + default: + return CMD_DATA_SIZE_ERR; + } + } + return default_size; +} +#endif + +void fixup_cmdtable(struct cmd_tbl *cmdtp, int size) +{ + int i; + + if (gd->reloc_off == 0) + return; + + for (i = 0; i < size; i++) { + ulong addr; + + addr = (ulong)(cmdtp->cmd_rep) + gd->reloc_off; + cmdtp->cmd_rep = + (int (*)(struct cmd_tbl *, int, int, + char * const [], int *))addr; + + addr = (ulong)(cmdtp->cmd) + gd->reloc_off; +#ifdef DEBUG_COMMANDS + printf("Command \"%s\": 0x%08lx => 0x%08lx\n", + cmdtp->name, (ulong)(cmdtp->cmd), addr); +#endif + cmdtp->cmd = (int (*)(struct cmd_tbl *, int, int, + char *const []))addr; + addr = (ulong)(cmdtp->name) + gd->reloc_off; + cmdtp->name = (char *)addr; + if (cmdtp->usage) { + addr = (ulong)(cmdtp->usage) + gd->reloc_off; + cmdtp->usage = (char *)addr; + } +#ifdef CONFIG_SYS_LONGHELP + if (cmdtp->help) { + addr = (ulong)(cmdtp->help) + gd->reloc_off; + cmdtp->help = (char *)addr; + } +#endif +#ifdef CONFIG_AUTO_COMPLETE + if (cmdtp->complete) { + addr = (ulong)(cmdtp->complete) + gd->reloc_off; + cmdtp->complete = + (int (*)(int, char * const [], char, int, char * []))addr; + } +#endif + cmdtp++; + } +} + +int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int *repeatable) +{ + *repeatable = 1; + + return cmdtp->cmd(cmdtp, flag, argc, argv); +} + +int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int *repeatable) +{ + *repeatable = 0; + + return cmdtp->cmd(cmdtp, flag, argc, argv); +} + +int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int repeatable; + + return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable); +} + +/** + * Call a command function. This should be the only route in U-Boot to call + * a command, so that we can track whether we are waiting for input or + * executing a command. + * + * @param cmdtp Pointer to the command to execute + * @param flag Some flags normally 0 (see CMD_FLAG_.. above) + * @param argc Number of arguments (arg 0 must be the command text) + * @param argv Arguments + * @param repeatable Can the command be repeated + * @return 0 if command succeeded, else non-zero (CMD_RET_...) + */ +static int cmd_call(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int *repeatable) +{ + int result; + + result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable); + if (result) + debug("Command failed, result=%d\n", result); + return result; +} + +enum command_ret_t cmd_process(int flag, int argc, char *const argv[], + int *repeatable, ulong *ticks) +{ + enum command_ret_t rc = CMD_RET_SUCCESS; + struct cmd_tbl *cmdtp; + +#if defined(CONFIG_SYS_XTRACE) + char *xtrace; + + xtrace = env_get("xtrace"); + if (xtrace) { + puts("+"); + for (int i = 0; i < argc; i++) { + puts(" "); + puts(argv[i]); + } + puts("\n"); + } +#endif + + /* Look up command in command table */ + cmdtp = find_cmd(argv[0]); + if (cmdtp == NULL) { + printf("Unknown command '%s' - try 'help'\n", argv[0]); + return 1; + } + + /* found - check max args */ + if (argc > cmdtp->maxargs) + rc = CMD_RET_USAGE; + +#if defined(CONFIG_CMD_BOOTD) + /* avoid "bootd" recursion */ + else if (cmdtp->cmd == do_bootd) { + if (flag & CMD_FLAG_BOOTD) { + puts("'bootd' recursion detected\n"); + rc = CMD_RET_FAILURE; + } else { + flag |= CMD_FLAG_BOOTD; + } + } +#endif + + /* If OK so far, then do the command */ + if (!rc) { + int newrep; + + if (ticks) + *ticks = get_timer(0); + rc = cmd_call(cmdtp, flag, argc, argv, &newrep); + if (ticks) + *ticks = get_timer(*ticks); + *repeatable &= newrep; + } + if (rc == CMD_RET_USAGE) + rc = cmd_usage(cmdtp); + return rc; +} + +int cmd_process_error(struct cmd_tbl *cmdtp, int err) +{ + if (err == CMD_RET_USAGE) + return CMD_RET_USAGE; + + if (err) { + printf("Command '%s' failed: Error %d\n", cmdtp->name, err); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} diff --git a/roms/u-boot/common/common_fit.c b/roms/u-boot/common/common_fit.c new file mode 100644 index 000000000..cde2dc45e --- /dev/null +++ b/roms/u-boot/common/common_fit.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <linux/libfdt.h> + +ulong fdt_getprop_u32(const void *fdt, int node, const char *prop) +{ + const u32 *cell; + int len; + + cell = fdt_getprop(fdt, node, prop, &len); + if (!cell || len != sizeof(*cell)) + return FDT_ERROR; + + return fdt32_to_cpu(*cell); +} + +__weak int board_fit_config_name_match(const char *name) +{ + return -EINVAL; +} + +/* + * Iterate over all /configurations subnodes and call a platform specific + * function to find the matching configuration. + * Returns the node offset or a negative error number. + */ +int fit_find_config_node(const void *fdt) +{ + const char *name; + int conf, node, len; + const char *dflt_conf_name; + const char *dflt_conf_desc = NULL; + int dflt_conf_node = -ENOENT; + + conf = fdt_path_offset(fdt, FIT_CONFS_PATH); + if (conf < 0) { + debug("%s: Cannot find /configurations node: %d\n", __func__, + conf); + return -EINVAL; + } + + dflt_conf_name = fdt_getprop(fdt, conf, "default", &len); + + for (node = fdt_first_subnode(fdt, conf); + node >= 0; + node = fdt_next_subnode(fdt, node)) { + name = fdt_getprop(fdt, node, "description", &len); + if (!name) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: Missing FDT description in DTB\n", + __func__); +#endif + return -EINVAL; + } + + if (dflt_conf_name) { + const char *node_name = fdt_get_name(fdt, node, NULL); + if (strcmp(dflt_conf_name, node_name) == 0) { + dflt_conf_node = node; + dflt_conf_desc = name; + } + } + + if (board_fit_config_name_match(name)) + continue; + + debug("Selecting config '%s'\n", name); + + return node; + } + + if (dflt_conf_node != -ENOENT) { + debug("Selecting default config '%s'\n", dflt_conf_desc); + return dflt_conf_node; + } + + return -ENOENT; +} diff --git a/roms/u-boot/common/console.c b/roms/u-boot/common/console.c new file mode 100644 index 000000000..561cdf36a --- /dev/null +++ b/roms/u-boot/common/console.c @@ -0,0 +1,1097 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + */ + +#include <common.h> +#include <console.h> +#include <debug_uart.h> +#include <dm.h> +#include <env.h> +#include <stdarg.h> +#include <iomux.h> +#include <malloc.h> +#include <mapmem.h> +#include <os.h> +#include <serial.h> +#include <stdio_dev.h> +#include <exports.h> +#include <env_internal.h> +#include <watchdog.h> +#include <asm/global_data.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int on_console(const char *name, const char *value, enum env_op op, + int flags) +{ + int console = -1; + + /* Check for console redirection */ + if (strcmp(name, "stdin") == 0) + console = stdin; + else if (strcmp(name, "stdout") == 0) + console = stdout; + else if (strcmp(name, "stderr") == 0) + console = stderr; + + /* if not actually setting a console variable, we don't care */ + if (console == -1 || (gd->flags & GD_FLG_DEVINIT) == 0) + return 0; + + switch (op) { + case env_op_create: + case env_op_overwrite: + + if (CONFIG_IS_ENABLED(CONSOLE_MUX)) { + if (iomux_doenv(console, value)) + return 1; + } else { + /* Try assigning specified device */ + if (console_assign(console, value) < 0) + return 1; + } + + return 0; + + case env_op_delete: + if ((flags & H_FORCE) == 0) + printf("Can't delete \"%s\"\n", name); + return 1; + + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(console, on_console); + +#ifdef CONFIG_SILENT_CONSOLE +static int on_silent(const char *name, const char *value, enum env_op op, + int flags) +{ + if (!CONFIG_IS_ENABLED(SILENT_CONSOLE_UPDATE_ON_SET)) + if (flags & H_INTERACTIVE) + return 0; + + if (!CONFIG_IS_ENABLED(SILENT_CONSOLE_UPDATE_ON_RELOC)) + if ((flags & H_INTERACTIVE) == 0) + return 0; + + if (value != NULL) + gd->flags |= GD_FLG_SILENT; + else + gd->flags &= ~GD_FLG_SILENT; + + return 0; +} +U_BOOT_ENV_CALLBACK(silent, on_silent); +#endif + +#ifdef CONFIG_CONSOLE_RECORD +/* helper function: access to gd->console_out and gd->console_in */ +static void console_record_putc(const char c) +{ + if (!(gd->flags & GD_FLG_RECORD)) + return; + if (gd->console_out.start) + membuff_putbyte((struct membuff *)&gd->console_out, c); +} + +static void console_record_puts(const char *s) +{ + if (!(gd->flags & GD_FLG_RECORD)) + return; + if (gd->console_out.start) + membuff_put((struct membuff *)&gd->console_out, s, strlen(s)); +} + +static int console_record_getc(void) +{ + if (!(gd->flags & GD_FLG_RECORD)) + return -1; + if (!gd->console_in.start) + return -1; + + return membuff_getbyte((struct membuff *)&gd->console_in); +} + +static int console_record_tstc(void) +{ + if (!(gd->flags & GD_FLG_RECORD)) + return 0; + if (gd->console_in.start) { + if (membuff_peekbyte((struct membuff *)&gd->console_in) != -1) + return 1; + } + return 0; +} +#else +static void console_record_putc(char c) +{ +} + +static void console_record_puts(const char *s) +{ +} + +static int console_record_getc(void) +{ + return -1; +} + +static int console_record_tstc(void) +{ + return 0; +} +#endif + +#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) +/* + * if overwrite_console returns 1, the stdin, stderr and stdout + * are switched to the serial port, else the settings in the + * environment are used + */ +#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console(void); +#define OVERWRITE_CONSOLE overwrite_console() +#else +#define OVERWRITE_CONSOLE 0 +#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */ + +#endif /* CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */ + +static int console_setfile(int file, struct stdio_dev * dev) +{ + int error = 0; + + if (dev == NULL) + return -1; + + switch (file) { + case stdin: + case stdout: + case stderr: + error = console_start(file, dev); + if (error) + break; + + /* Assign the new device (leaving the existing one started) */ + stdio_devices[file] = dev; + + /* + * Update monitor functions + * (to use the console stuff by other applications) + */ + switch (file) { + case stdin: + gd->jt->getc = getchar; + gd->jt->tstc = tstc; + break; + case stdout: + gd->jt->putc = putc; + gd->jt->puts = puts; + gd->jt->printf = printf; + break; + } + break; + + default: /* Invalid file ID */ + error = -1; + } + return error; +} + +/** + * console_dev_is_serial() - Check if a stdio device is a serial device + * + * @sdev: Device to check + * @return true if this device is in the serial uclass (or for pre-driver-model, + * whether it is called "serial". + */ +static bool console_dev_is_serial(struct stdio_dev *sdev) +{ + bool is_serial; + + if (IS_ENABLED(CONFIG_DM_SERIAL) && (sdev->flags & DEV_FLAGS_DM)) { + struct udevice *dev = sdev->priv; + + is_serial = device_get_uclass_id(dev) == UCLASS_SERIAL; + } else { + is_serial = !strcmp(sdev->name, "serial"); + } + + return is_serial; +} + +#if CONFIG_IS_ENABLED(CONSOLE_MUX) +/** Console I/O multiplexing *******************************************/ + +/* tstcdev: save the last stdio device with pending characters, with tstc != 0 */ +static struct stdio_dev *tstcdev; +struct stdio_dev **console_devices[MAX_FILES]; +int cd_count[MAX_FILES]; + +static void console_devices_set(int file, struct stdio_dev *dev) +{ + console_devices[file][0] = dev; + cd_count[file] = 1; +} + +/** + * console_needs_start_stop() - check if we need to start or stop the STDIO device + * @file: STDIO file + * @sdev: STDIO device in question + * + * This function checks if we need to start or stop the stdio device used for + * a console. For IOMUX case it simply enforces one time start and one time + * stop of the device independently of how many STDIO files are using it. In + * other words, we start console once before first STDIO device wants it and + * stop after the last is gone. + */ +static bool console_needs_start_stop(int file, struct stdio_dev *sdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cd_count); i++) { + if (i == file) + continue; + + if (iomux_match_device(console_devices[i], cd_count[i], sdev) >= 0) + return false; + } + return true; +} + +/* + * This depends on tstc() always being called before getchar(). + * This is guaranteed to be true because this routine is called + * only from fgetc() which assures it. + * No attempt is made to demultiplex multiple input sources. + */ +static int console_getc(int file) +{ + unsigned char ret; + + /* This is never called with testcdev == NULL */ + ret = tstcdev->getc(tstcdev); + tstcdev = NULL; + return ret; +} + +/* Upper layer may have already called tstc(): check the saved result */ +static bool console_has_tstc(void) +{ + return !!tstcdev; +} + +static int console_tstc(int file) +{ + int i, ret; + struct stdio_dev *dev; + int prev; + + prev = disable_ctrlc(1); + for_each_console_dev(i, file, dev) { + if (dev->tstc != NULL) { + ret = dev->tstc(dev); + if (ret > 0) { + tstcdev = dev; + disable_ctrlc(prev); + return ret; + } + } + } + disable_ctrlc(prev); + + return 0; +} + +static void console_putc(int file, const char c) +{ + int i; + struct stdio_dev *dev; + + for_each_console_dev(i, file, dev) { + if (dev->putc != NULL) + dev->putc(dev, c); + } +} + +/** + * console_puts_select() - Output a string to all console devices + * + * @file: File number to output to (e,g, stdout, see stdio.h) + * @serial_only: true to output only to serial, false to output to everything + * else + * @s: String to output + */ +static void console_puts_select(int file, bool serial_only, const char *s) +{ + int i; + struct stdio_dev *dev; + + for_each_console_dev(i, file, dev) { + bool is_serial = console_dev_is_serial(dev); + + if (dev->puts && serial_only == is_serial) + dev->puts(dev, s); + } +} + +void console_puts_select_stderr(bool serial_only, const char *s) +{ + console_puts_select(stderr, serial_only, s); +} + +static void console_puts(int file, const char *s) +{ + int i; + struct stdio_dev *dev; + + for_each_console_dev(i, file, dev) { + if (dev->puts != NULL) + dev->puts(dev, s); + } +} + +#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) +static inline void console_doenv(int file, struct stdio_dev *dev) +{ + iomux_doenv(file, dev->name); +} +#endif +#else + +static void console_devices_set(int file, struct stdio_dev *dev) +{ +} + +static inline bool console_needs_start_stop(int file, struct stdio_dev *sdev) +{ + return true; +} + +static inline int console_getc(int file) +{ + return stdio_devices[file]->getc(stdio_devices[file]); +} + +static bool console_has_tstc(void) +{ + return false; +} + +static inline int console_tstc(int file) +{ + return stdio_devices[file]->tstc(stdio_devices[file]); +} + +static inline void console_putc(int file, const char c) +{ + stdio_devices[file]->putc(stdio_devices[file], c); +} + +void console_puts_select(int file, bool serial_only, const char *s) +{ + if (serial_only == console_dev_is_serial(stdio_devices[file])) + stdio_devices[file]->puts(stdio_devices[file], s); +} + +static inline void console_puts(int file, const char *s) +{ + stdio_devices[file]->puts(stdio_devices[file], s); +} + +#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) +static inline void console_doenv(int file, struct stdio_dev *dev) +{ + console_setfile(file, dev); +} +#endif +#endif /* CONIFIG_IS_ENABLED(CONSOLE_MUX) */ + +static void __maybe_unused console_setfile_and_devices(int file, struct stdio_dev *dev) +{ + console_setfile(file, dev); + console_devices_set(file, dev); +} + +int console_start(int file, struct stdio_dev *sdev) +{ + int error; + + if (!console_needs_start_stop(file, sdev)) + return 0; + + /* Start new device */ + if (sdev->start) { + error = sdev->start(sdev); + /* If it's not started don't use it */ + if (error < 0) + return error; + } + return 0; +} + +void console_stop(int file, struct stdio_dev *sdev) +{ + if (!console_needs_start_stop(file, sdev)) + return; + + if (sdev->stop) + sdev->stop(sdev); +} + +/** U-Boot INITIAL CONSOLE-NOT COMPATIBLE FUNCTIONS *************************/ + +int serial_printf(const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + serial_puts(printbuffer); + return i; +} + +int fgetc(int file) +{ + if (file < MAX_FILES) { + /* + * Effectively poll for input wherever it may be available. + */ + for (;;) { + WATCHDOG_RESET(); + if (CONFIG_IS_ENABLED(CONSOLE_MUX)) { + /* + * Upper layer may have already called tstc() so + * check for that first. + */ + if (console_has_tstc()) + return console_getc(file); + console_tstc(file); + } else { + if (console_tstc(file)) + return console_getc(file); + } + + /* + * If the watchdog must be rate-limited then it should + * already be handled in board-specific code. + */ + if (IS_ENABLED(CONFIG_WATCHDOG)) + udelay(1); + } + } + + return -1; +} + +int ftstc(int file) +{ + if (file < MAX_FILES) + return console_tstc(file); + + return -1; +} + +void fputc(int file, const char c) +{ + if (file < MAX_FILES) + console_putc(file, c); +} + +void fputs(int file, const char *s) +{ + if (file < MAX_FILES) + console_puts(file, s); +} + +int fprintf(int file, const char *fmt, ...) +{ + va_list args; + uint i; + char printbuffer[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); + va_end(args); + + /* Send to desired file */ + fputs(file, printbuffer); + return i; +} + +/** U-Boot INITIAL CONSOLE-COMPATIBLE FUNCTION *****************************/ + +int getchar(void) +{ + int ch; + + if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE)) + return 0; + + if (!gd->have_console) + return 0; + + ch = console_record_getc(); + if (ch != -1) + return ch; + + if (gd->flags & GD_FLG_DEVINIT) { + /* Get from the standard input */ + return fgetc(stdin); + } + + /* Send directly to the handler */ + return serial_getc(); +} + +int tstc(void) +{ + if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE)) + return 0; + + if (!gd->have_console) + return 0; + + if (console_record_tstc()) + return 1; + + if (gd->flags & GD_FLG_DEVINIT) { + /* Test the standard input */ + return ftstc(stdin); + } + + /* Send directly to the handler */ + return serial_tstc(); +} + +#define PRE_CONSOLE_FLUSHPOINT1_SERIAL 0 +#define PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL 1 + +#if CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER) +#define CIRC_BUF_IDX(idx) ((idx) % (unsigned long)CONFIG_PRE_CON_BUF_SZ) + +static void pre_console_putc(const char c) +{ + char *buffer; + + buffer = map_sysmem(CONFIG_PRE_CON_BUF_ADDR, CONFIG_PRE_CON_BUF_SZ); + + buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c; + + unmap_sysmem(buffer); +} + +static void pre_console_puts(const char *s) +{ + while (*s) + pre_console_putc(*s++); +} + +static void print_pre_console_buffer(int flushpoint) +{ + unsigned long in = 0, out = 0; + char buf_out[CONFIG_PRE_CON_BUF_SZ + 1]; + char *buf_in; + + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT)) + return; + + buf_in = map_sysmem(CONFIG_PRE_CON_BUF_ADDR, CONFIG_PRE_CON_BUF_SZ); + if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ) + in = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ; + + while (in < gd->precon_buf_idx) + buf_out[out++] = buf_in[CIRC_BUF_IDX(in++)]; + unmap_sysmem(buf_in); + + buf_out[out] = 0; + + switch (flushpoint) { + case PRE_CONSOLE_FLUSHPOINT1_SERIAL: + puts(buf_out); + break; + case PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL: + console_puts_select(stdout, false, buf_out); + break; + } +} +#else +static inline void pre_console_putc(const char c) {} +static inline void pre_console_puts(const char *s) {} +static inline void print_pre_console_buffer(int flushpoint) {} +#endif + +void putc(const char c) +{ + if (!gd) + return; + + console_record_putc(c); + + /* sandbox can send characters to stdout before it has a console */ + if (IS_ENABLED(CONFIG_SANDBOX) && !(gd->flags & GD_FLG_SERIAL_READY)) { + os_putc(c); + return; + } + + /* if we don't have a console yet, use the debug UART */ + if (IS_ENABLED(CONFIG_DEBUG_UART) && !(gd->flags & GD_FLG_SERIAL_READY)) { + printch(c); + return; + } + + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT)) { + if (!(gd->flags & GD_FLG_DEVINIT)) + pre_console_putc(c); + return; + } + + if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE)) + return; + + if (!gd->have_console) + return pre_console_putc(c); + + if (gd->flags & GD_FLG_DEVINIT) { + /* Send to the standard output */ + fputc(stdout, c); + } else { + /* Send directly to the handler */ + pre_console_putc(c); + serial_putc(c); + } +} + +void puts(const char *s) +{ + if (!gd) + return; + + console_record_puts(s); + + /* sandbox can send characters to stdout before it has a console */ + if (IS_ENABLED(CONFIG_SANDBOX) && !(gd->flags & GD_FLG_SERIAL_READY)) { + os_puts(s); + return; + } + + if (IS_ENABLED(CONFIG_DEBUG_UART) && !(gd->flags & GD_FLG_SERIAL_READY)) { + while (*s) { + int ch = *s++; + + printch(ch); + } + return; + } + + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT)) { + if (!(gd->flags & GD_FLG_DEVINIT)) + pre_console_puts(s); + return; + } + + if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE)) + return; + + if (!gd->have_console) + return pre_console_puts(s); + + if (gd->flags & GD_FLG_DEVINIT) { + /* Send to the standard output */ + fputs(stdout, s); + } else { + /* Send directly to the handler */ + pre_console_puts(s); + serial_puts(s); + } +} + +#ifdef CONFIG_CONSOLE_RECORD +int console_record_init(void) +{ + int ret; + + ret = membuff_new((struct membuff *)&gd->console_out, + CONFIG_CONSOLE_RECORD_OUT_SIZE); + if (ret) + return ret; + ret = membuff_new((struct membuff *)&gd->console_in, + CONFIG_CONSOLE_RECORD_IN_SIZE); + + return ret; +} + +void console_record_reset(void) +{ + membuff_purge((struct membuff *)&gd->console_out); + membuff_purge((struct membuff *)&gd->console_in); +} + +int console_record_reset_enable(void) +{ + console_record_reset(); + gd->flags |= GD_FLG_RECORD; + + return 0; +} + +int console_record_readline(char *str, int maxlen) +{ + return membuff_readline((struct membuff *)&gd->console_out, str, + maxlen, ' '); +} + +int console_record_avail(void) +{ + return membuff_avail((struct membuff *)&gd->console_out); +} + +#endif + +/* test if ctrl-c was pressed */ +static int ctrlc_disabled = 0; /* see disable_ctrl() */ +static int ctrlc_was_pressed = 0; +int ctrlc(void) +{ + if (!ctrlc_disabled && gd->have_console) { + if (tstc()) { + switch (getchar()) { + case 0x03: /* ^C - Control C */ + ctrlc_was_pressed = 1; + return 1; + default: + break; + } + } + } + + return 0; +} +/* Reads user's confirmation. + Returns 1 if user's input is "y", "Y", "yes" or "YES" +*/ +int confirm_yesno(void) +{ + int i; + char str_input[5]; + + /* Flush input */ + while (tstc()) + getchar(); + i = 0; + while (i < sizeof(str_input)) { + str_input[i] = getchar(); + putc(str_input[i]); + if (str_input[i] == '\r') + break; + i++; + } + putc('\n'); + if (strncmp(str_input, "y\r", 2) == 0 || + strncmp(str_input, "Y\r", 2) == 0 || + strncmp(str_input, "yes\r", 4) == 0 || + strncmp(str_input, "YES\r", 4) == 0) + return 1; + return 0; +} +/* pass 1 to disable ctrlc() checking, 0 to enable. + * returns previous state + */ +int disable_ctrlc(int disable) +{ + int prev = ctrlc_disabled; /* save previous state */ + + ctrlc_disabled = disable; + return prev; +} + +int had_ctrlc (void) +{ + return ctrlc_was_pressed; +} + +void clear_ctrlc(void) +{ + ctrlc_was_pressed = 0; +} + +/** U-Boot INIT FUNCTIONS *************************************************/ + +struct stdio_dev *console_search_dev(int flags, const char *name) +{ + struct stdio_dev *dev; + + dev = stdio_get_by_name(name); +#ifdef CONFIG_VIDCONSOLE_AS_LCD + if (!dev && !strcmp(name, CONFIG_VIDCONSOLE_AS_NAME)) + dev = stdio_get_by_name("vidconsole"); +#endif + + if (dev && (dev->flags & flags)) + return dev; + + return NULL; +} + +int console_assign(int file, const char *devname) +{ + int flag; + struct stdio_dev *dev; + + /* Check for valid file */ + flag = stdio_file_to_flags(file); + if (flag < 0) + return flag; + + /* Check for valid device name */ + + dev = console_search_dev(flag, devname); + + if (dev) + return console_setfile(file, dev); + + return -1; +} + +/* return true if the 'silent' flag is removed */ +static bool console_update_silent(void) +{ + unsigned long flags = gd->flags; + + if (!IS_ENABLED(CONFIG_SILENT_CONSOLE)) + return false; + + if (env_get("silent")) { + gd->flags |= GD_FLG_SILENT; + return false; + } + + gd->flags &= ~GD_FLG_SILENT; + + return !!(flags & GD_FLG_SILENT); +} + +int console_announce_r(void) +{ +#if !CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER) + char buf[DISPLAY_OPTIONS_BANNER_LENGTH]; + + display_options_get_banner(false, buf, sizeof(buf)); + + console_puts_select(stdout, false, buf); +#endif + + return 0; +} + +/* Called before relocation - use serial functions */ +int console_init_f(void) +{ + gd->have_console = 1; + + console_update_silent(); + + print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL); + + return 0; +} + +void stdio_print_current_devices(void) +{ + /* Print information */ + puts("In: "); + if (stdio_devices[stdin] == NULL) { + puts("No input devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stdin]->name); + } + + puts("Out: "); + if (stdio_devices[stdout] == NULL) { + puts("No output devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stdout]->name); + } + + puts("Err: "); + if (stdio_devices[stderr] == NULL) { + puts("No error devices available!\n"); + } else { + printf ("%s\n", stdio_devices[stderr]->name); + } +} + +#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) +/* Called after the relocation - use desired console functions */ +int console_init_r(void) +{ + char *stdinname, *stdoutname, *stderrname; + struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL; + int i; + int iomux_err = 0; + int flushpoint; + + /* update silent for env loaded from flash (initr_env) */ + if (console_update_silent()) + flushpoint = PRE_CONSOLE_FLUSHPOINT1_SERIAL; + else + flushpoint = PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL; + + /* set default handlers at first */ + gd->jt->getc = serial_getc; + gd->jt->tstc = serial_tstc; + gd->jt->putc = serial_putc; + gd->jt->puts = serial_puts; + gd->jt->printf = serial_printf; + + /* stdin stdout and stderr are in environment */ + /* scan for it */ + stdinname = env_get("stdin"); + stdoutname = env_get("stdout"); + stderrname = env_get("stderr"); + + if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ + inputdev = console_search_dev(DEV_FLAGS_INPUT, stdinname); + outputdev = console_search_dev(DEV_FLAGS_OUTPUT, stdoutname); + errdev = console_search_dev(DEV_FLAGS_OUTPUT, stderrname); + if (CONFIG_IS_ENABLED(CONSOLE_MUX)) { + iomux_err = iomux_doenv(stdin, stdinname); + iomux_err += iomux_doenv(stdout, stdoutname); + iomux_err += iomux_doenv(stderr, stderrname); + if (!iomux_err) + /* Successful, so skip all the code below. */ + goto done; + } + } + /* if the devices are overwritten or not found, use default device */ + if (inputdev == NULL) { + inputdev = console_search_dev(DEV_FLAGS_INPUT, "serial"); + } + if (outputdev == NULL) { + outputdev = console_search_dev(DEV_FLAGS_OUTPUT, "serial"); + } + if (errdev == NULL) { + errdev = console_search_dev(DEV_FLAGS_OUTPUT, "serial"); + } + /* Initializes output console first */ + if (outputdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stdout, outputdev); + } + if (errdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stderr, errdev); + } + if (inputdev != NULL) { + /* need to set a console if not done above. */ + console_doenv(stdin, inputdev); + } + +done: + + if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET)) + stdio_print_current_devices(); + +#ifdef CONFIG_VIDCONSOLE_AS_LCD + if (strstr(stdoutname, CONFIG_VIDCONSOLE_AS_NAME)) + printf("Warning: Please change '%s' to 'vidconsole' in stdout/stderr environment vars\n", + CONFIG_VIDCONSOLE_AS_NAME); +#endif + + if (IS_ENABLED(CONFIG_SYS_CONSOLE_ENV_OVERWRITE)) { + /* set the environment variables (will overwrite previous env settings) */ + for (i = 0; i < MAX_FILES; i++) + env_set(stdio_names[i], stdio_devices[i]->name); + } + + gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ + + print_pre_console_buffer(flushpoint); + return 0; +} + +#else /* !CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */ + +/* Called after the relocation - use desired console functions */ +int console_init_r(void) +{ + struct stdio_dev *inputdev = NULL, *outputdev = NULL; + int i; + struct list_head *list = stdio_get_list(); + struct list_head *pos; + struct stdio_dev *dev; + int flushpoint; + + /* update silent for env loaded from flash (initr_env) */ + if (console_update_silent()) + flushpoint = PRE_CONSOLE_FLUSHPOINT1_SERIAL; + else + flushpoint = PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL; + + /* + * suppress all output if splash screen is enabled and we have + * a bmp to display. We redirect the output from frame buffer + * console to serial console in this case or suppress it if + * "silent" mode was requested. + */ + if (IS_ENABLED(CONFIG_SPLASH_SCREEN) && env_get("splashimage")) { + if (!(gd->flags & GD_FLG_SILENT)) + outputdev = console_search_dev (DEV_FLAGS_OUTPUT, "serial"); + } + + /* Scan devices looking for input and output devices */ + list_for_each(pos, list) { + dev = list_entry(pos, struct stdio_dev, list); + + if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) { + inputdev = dev; + } + if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) { + outputdev = dev; + } + if(inputdev && outputdev) + break; + } + + /* Initializes output console first */ + if (outputdev != NULL) { + console_setfile_and_devices(stdout, outputdev); + console_setfile_and_devices(stderr, outputdev); + } + + /* Initializes input console */ + if (inputdev != NULL) + console_setfile_and_devices(stdin, inputdev); + + if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET)) + stdio_print_current_devices(); + + /* Setting environment variables */ + for (i = 0; i < MAX_FILES; i++) { + env_set(stdio_names[i], stdio_devices[i]->name); + } + + gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ + + print_pre_console_buffer(flushpoint); + return 0; +} + +#endif /* CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */ diff --git a/roms/u-boot/common/cros_ec.c b/roms/u-boot/common/cros_ec.c new file mode 100644 index 000000000..249d1f194 --- /dev/null +++ b/roms/u-boot/common/cros_ec.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +#include <common.h> +#include <cros_ec.h> +#include <dm.h> +#include <errno.h> +#include <log.h> + +struct udevice *board_get_cros_ec_dev(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev); + if (ret) { + debug("%s: Error %d\n", __func__, ret); + return NULL; + } + return dev; +} diff --git a/roms/u-boot/common/ddr_spd.c b/roms/u-boot/common/ddr_spd.c new file mode 100644 index 000000000..58dc9b378 --- /dev/null +++ b/roms/u-boot/common/ddr_spd.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2008-2014 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <ddr_spd.h> + +/* used for ddr1 and ddr2 spd */ +static int +spd_check(const u8 *buf, u8 spd_rev, u8 spd_cksum) +{ + unsigned int cksum = 0; + unsigned int i; + + /* + * Check SPD revision supported + * Rev 1.X or less supported by this code + */ + if (spd_rev >= 0x20) { + printf("SPD revision %02X not supported by this code\n", + spd_rev); + return 1; + } + if (spd_rev > 0x13) { + printf("SPD revision %02X not verified by this code\n", + spd_rev); + } + + /* + * Calculate checksum + */ + for (i = 0; i < 63; i++) { + cksum += *buf++; + } + cksum &= 0xFF; + + if (cksum != spd_cksum) { + printf("SPD checksum unexpected. " + "Checksum in SPD = %02X, computed SPD = %02X\n", + spd_cksum, cksum); + return 1; + } + + return 0; +} + +unsigned int +ddr1_spd_check(const ddr1_spd_eeprom_t *spd) +{ + const u8 *p = (const u8 *)spd; + + return spd_check(p, spd->spd_rev, spd->cksum); +} + +unsigned int +ddr2_spd_check(const ddr2_spd_eeprom_t *spd) +{ + const u8 *p = (const u8 *)spd; + + return spd_check(p, spd->spd_rev, spd->cksum); +} + +/* + * CRC16 compute for DDR3 SPD + * Copied from DDR3 SPD spec. + */ +static int +crc16(char *ptr, int count) +{ + int crc, i; + + crc = 0; + while (--count >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + return crc & 0xffff; +} + +unsigned int +ddr3_spd_check(const ddr3_spd_eeprom_t *spd) +{ + char *p = (char *)spd; + int csum16; + int len; + char crc_lsb; /* byte 126 */ + char crc_msb; /* byte 127 */ + + /* + * SPD byte0[7] - CRC coverage + * 0 = CRC covers bytes 0~125 + * 1 = CRC covers bytes 0~116 + */ + + len = !(spd->info_size_crc & 0x80) ? 126 : 117; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->crc[0] == crc_lsb && spd->crc[1] == crc_msb) { + return 0; + } else { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->crc[0], crc_lsb, spd->crc[1], crc_msb); + return 1; + } +} + +unsigned int ddr4_spd_check(const struct ddr4_spd_eeprom_s *spd) +{ + char *p = (char *)spd; + int csum16; + int len; + char crc_lsb; /* byte 126 */ + char crc_msb; /* byte 127 */ + + len = 126; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->crc[0] != crc_lsb || spd->crc[1] != crc_msb) { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->crc[0], crc_lsb, spd->crc[1], crc_msb); + return 1; + } + + p = (char *)((ulong)spd + 128); + len = 126; + csum16 = crc16(p, len); + + crc_lsb = (char) (csum16 & 0xff); + crc_msb = (char) (csum16 >> 8); + + if (spd->mod_section.uc[126] != crc_lsb || + spd->mod_section.uc[127] != crc_msb) { + printf("SPD checksum unexpected.\n" + "Checksum lsb in SPD = %02X, computed SPD = %02X\n" + "Checksum msb in SPD = %02X, computed SPD = %02X\n", + spd->mod_section.uc[126], + crc_lsb, spd->mod_section.uc[127], + crc_msb); + return 1; + } + + return 0; +} diff --git a/roms/u-boot/common/dfu.c b/roms/u-boot/common/dfu.c new file mode 100644 index 000000000..16bd1ba58 --- /dev/null +++ b/roms/u-boot/common/dfu.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * dfu.c -- dfu command + * + * Copyright (C) 2015 + * Lukasz Majewski <l.majewski@majess.pl> + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Lukasz Majewski <l.majewski@samsung.com> + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <watchdog.h> +#include <dfu.h> +#include <console.h> +#include <g_dnl.h> +#include <usb.h> +#include <net.h> + +int run_usb_dnl_gadget(int usbctrl_index, char *usb_dnl_gadget) +{ + bool dfu_reset = false; + int ret, i = 0; + + ret = usb_gadget_initialize(usbctrl_index); + if (ret) { + pr_err("usb_gadget_initialize failed\n"); + return CMD_RET_FAILURE; + } + g_dnl_clear_detach(); + ret = g_dnl_register(usb_dnl_gadget); + if (ret) { + pr_err("g_dnl_register failed"); + return CMD_RET_FAILURE; + } + +#ifdef CONFIG_DFU_TIMEOUT + unsigned long start_time = get_timer(0); +#endif + + while (1) { + if (g_dnl_detach()) { + /* + * Check if USB bus reset is performed after detach, + * which indicates that -R switch has been passed to + * dfu-util. In this case reboot the device + */ + if (dfu_usb_get_reset()) { + dfu_reset = true; + goto exit; + } + + /* + * This extra number of usb_gadget_handle_interrupts() + * calls is necessary to assure correct transmission + * completion with dfu-util + */ + if (++i == 10000) + goto exit; + } + + if (ctrlc()) + goto exit; + + if (dfu_get_defer_flush()) { + /* + * Call to usb_gadget_handle_interrupts() is necessary + * to act on ZLP OUT transaction from HOST PC after + * transmitting the whole file. + * + * If this ZLP OUT packet is NAK'ed, the HOST libusb + * function fails after timeout (by default it is set to + * 5 seconds). In such situation the dfu-util program + * exits with error message. + */ + usb_gadget_handle_interrupts(usbctrl_index); + ret = dfu_flush(dfu_get_defer_flush(), NULL, 0, 0); + dfu_set_defer_flush(NULL); + if (ret) { + pr_err("Deferred dfu_flush() failed!"); + goto exit; + } + } + +#ifdef CONFIG_DFU_TIMEOUT + unsigned long wait_time = dfu_get_timeout(); + + if (wait_time) { + unsigned long current_time = get_timer(start_time); + + if (current_time > wait_time) { + debug("Inactivity timeout, abort DFU\n"); + goto exit; + } + } +#endif + + if (dfu_reinit_needed) + goto exit; + + WATCHDOG_RESET(); + usb_gadget_handle_interrupts(usbctrl_index); + } +exit: + g_dnl_unregister(); + usb_gadget_release(usbctrl_index); + + if (dfu_reset) + do_reset(NULL, 0, 0, NULL); + + g_dnl_clear_detach(); + + return ret; +} diff --git a/roms/u-boot/common/dlmalloc.c b/roms/u-boot/common/dlmalloc.c new file mode 100644 index 000000000..11729e8c8 --- /dev/null +++ b/roms/u-boot/common/dlmalloc.c @@ -0,0 +1,2549 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * This code is based on a version (aka dlmalloc) of malloc/free/realloc written + * by Doug Lea and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/- + * + * The original code is available at http://gee.cs.oswego.edu/pub/misc/ + * as file malloc-2.6.6.c. + */ + +#include <common.h> +#include <log.h> +#include <asm/global_data.h> + +#if CONFIG_IS_ENABLED(UNIT_TEST) +#define DEBUG +#endif + +#include <malloc.h> +#include <asm/io.h> + +#ifdef DEBUG +#if __STD_C +static void malloc_update_mallinfo (void); +void malloc_stats (void); +#else +static void malloc_update_mallinfo (); +void malloc_stats(); +#endif +#endif /* DEBUG */ + +DECLARE_GLOBAL_DATA_PTR; + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. + + Thanks to Martin Fong and others for supplying this. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ +~(malloc_getpagesize-1)) +#define AlignPage64K(add) (((add) + (0x10000 - 1)) & ~(0x10000 - 1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +typedef struct GmListElement GmListElement; + +struct GmListElement +{ + GmListElement* next; + void* base; +}; + +static GmListElement* head = 0; +static unsigned int gNextAddress = 0; +static unsigned int gAddressBase = 0; +static unsigned int gAllocatedSize = 0; + +static +GmListElement* makeGmListElement (void* bas) +{ + GmListElement* this; + this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement)); + assert (this); + if (this) + { + this->base = bas; + this->next = head; + head = this; + } + return this; +} + +void gcleanup () +{ + BOOL rval; + assert ( (head == NULL) || (head->base == (void*)gAddressBase)); + if (gAddressBase && (gNextAddress - gAddressBase)) + { + rval = VirtualFree ((void*)gAddressBase, + gNextAddress - gAddressBase, + MEM_DECOMMIT); + assert (rval); + } + while (head) + { + GmListElement* next = head->next; + rval = VirtualFree (head->base, 0, MEM_RELEASE); + assert (rval); + LocalFree (head); + head = next; + } +} + +static +void* findRegion (void* start_address, unsigned long size) +{ + MEMORY_BASIC_INFORMATION info; + if (size >= TOP_MEMORY) return NULL; + + while ((unsigned long)start_address + size < TOP_MEMORY) + { + VirtualQuery (start_address, &info, sizeof (info)); + if ((info.State == MEM_FREE) && (info.RegionSize >= size)) + return start_address; + else + { + /* Requested region is not available so see if the */ + /* next region is available. Set 'start_address' */ + /* to the next region and call 'VirtualQuery()' */ + /* again. */ + + start_address = (char*)info.BaseAddress + info.RegionSize; + + /* Make sure we start looking for the next region */ + /* on the *next* 64K boundary. Otherwise, even if */ + /* the new region is free according to */ + /* 'VirtualQuery()', the subsequent call to */ + /* 'VirtualAlloc()' (which follows the call to */ + /* this routine in 'wsbrk()') will round *down* */ + /* the requested address to a 64K boundary which */ + /* we already know is an address in the */ + /* unavailable region. Thus, the subsequent call */ + /* to 'VirtualAlloc()' will fail and bring us back */ + /* here, causing us to go into an infinite loop. */ + + start_address = + (void *) AlignPage64K((unsigned long) start_address); + } + } + return NULL; + +} + + +void* wsbrk (long size) +{ + void* tmp; + if (size > 0) + { + if (gAddressBase == 0) + { + gAllocatedSize = max (RESERVED_SIZE, AlignPage (size)); + gNextAddress = gAddressBase = + (unsigned int)VirtualAlloc (NULL, gAllocatedSize, + MEM_RESERVE, PAGE_NOACCESS); + } else if (AlignPage (gNextAddress + size) > (gAddressBase + +gAllocatedSize)) + { + long new_size = max (NEXT_SIZE, AlignPage (size)); + void* new_address = (void*)(gAddressBase+gAllocatedSize); + do + { + new_address = findRegion (new_address, new_size); + + if (!new_address) + return (void*)-1; + + gAddressBase = gNextAddress = + (unsigned int)VirtualAlloc (new_address, new_size, + MEM_RESERVE, PAGE_NOACCESS); + /* repeat in case of race condition */ + /* The region that we found has been snagged */ + /* by another thread */ + } + while (gAddressBase == 0); + + assert (new_address == (void*)gAddressBase); + + gAllocatedSize = new_size; + + if (!makeGmListElement ((void*)gAddressBase)) + return (void*)-1; + } + if ((size + gNextAddress) > AlignPage (gNextAddress)) + { + void* res; + res = VirtualAlloc ((void*)AlignPage (gNextAddress), + (size + gNextAddress - + AlignPage (gNextAddress)), + MEM_COMMIT, PAGE_READWRITE); + if (!res) + return (void*)-1; + } + tmp = (void*)gNextAddress; + gNextAddress = (unsigned int)tmp + size; + return tmp; + } + else if (size < 0) + { + unsigned int alignedGoal = AlignPage (gNextAddress + size); + /* Trim by releasing the virtual memory */ + if (alignedGoal >= gAddressBase) + { + VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal, + MEM_DECOMMIT); + gNextAddress = gNextAddress + size; + return (void*)gNextAddress; + } + else + { + VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase, + MEM_DECOMMIT); + gNextAddress = gAddressBase; + return (void*)-1; + } + } + else + { + return (void*)gNextAddress; + } +} + +#endif + + + +/* + Type declarations +*/ + + +struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +} __attribute__((__may_alias__)) ; + +typedef struct malloc_chunk* mchunkptr; + +/* + + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if allocated | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_space() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | + +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + (The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory.) + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. (This makes it easier to + deal with alignments etc). + + The two exceptions to all this are + + 1. The special chunk `top', which doesn't bother using the + trailing size field since there is no + next contiguous chunk that would have to index off it. (After + initialization, `top' is forced to always exist. If it would + become less than MINSIZE bytes long, it is replenished via + malloc_extend_top.) + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit (IS_MMAPPED) set in their size fields. Because they are + never merged or traversed from any other chunk, they have no + foot size or inuse information. + + Available chunks are kept in any of several places (all declared below): + + * `av': An array of chunks serving as bin headers for consolidated + chunks. Each bin is doubly linked. The bins are approximately + proportionally (log) spaced. There are a lot of these bins + (128). This may look excessive, but works very well in + practice. All procedures maintain the invariant that no + consolidated chunk physically borders another one. Chunks in + bins are kept in size order, with ties going to the + approximately least recently used chunk. + + The chunks in each bin are maintained in decreasing sorted order by + size. This is irrelevant for the small bins, which all contain + the same-sized chunks, but facilitates best-fit allocation for + larger chunks. (These lists are just sequential. Keeping them in + order almost never requires enough traversal to warrant using + fancier ordered data structures.) Chunks of the same size are + linked with the most recently freed at the front, and allocations + are taken from the back. This results in LRU or FIFO allocation + order, which tends to give each chunk an equal opportunity to be + consolidated with adjacent freed chunks, resulting in larger free + chunks and less fragmentation. + + * `top': The top-most available chunk (i.e., the one bordering the + end of available memory) is treated specially. It is never + included in any bin, is used only if no other chunk is + available, and is released back to the system if it is very + large (see M_TRIM_THRESHOLD). + + * `last_remainder': A bin holding only the remainder of the + most recently split (non-top) chunk. This bin is checked + before other non-fitting chunks, so as to provide better + locality for runs of sequentially allocated chunks. + + * Implicitly, through the host system's memory mapping tables. + If supported, requests greater than a threshold are usually + serviced via calls to mmap, and then later released via munmap. + +*/ + +/* sizes, alignments */ + +#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) +#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) +#define MINSIZE (sizeof(struct malloc_chunk)) + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* pad request bytes into a usable size */ + +#define request2size(req) \ + (((long)((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) < \ + (long)(MINSIZE + MALLOC_ALIGN_MASK)) ? MINSIZE : \ + (((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK))) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)((m)) & (MALLOC_ALIGN_MASK)) == 0) + + + + +/* + Physical chunk operations +*/ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + + +/* Ptr to next physical malloc_chunk. */ + +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) )) + +/* Ptr to previous physical malloc_chunk */ + +#define prev_chunk(p)\ + ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) )) + + +/* Treat space at ptr + offset as a chunk */ + +#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) + + + + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* check for mmap()'ed chunk */ + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + + + + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +#define NAV 128 /* number of bins */ + +typedef struct malloc_chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (av_[2]) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[NAV * 2 + 2] = { + NULL, NULL, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +static void malloc_bin_reloc(void) +{ + mbinptr *p = &av_[2]; + size_t i; + + for (i = 2; i < ARRAY_SIZE(av_); ++i, ++p) + *p = (mbinptr)((ulong)*p + gd->reloc_off); +} +#else +static inline void malloc_bin_reloc(void) {} +#endif + +#ifdef CONFIG_SYS_MALLOC_DEFAULT_TO_INIT +static void malloc_init(void); +#endif + +ulong mem_malloc_start = 0; +ulong mem_malloc_end = 0; +ulong mem_malloc_brk = 0; + +void *sbrk(ptrdiff_t increment) +{ + ulong old = mem_malloc_brk; + ulong new = old + increment; + + /* + * if we are giving memory back make sure we clear it out since + * we set MORECORE_CLEARS to 1 + */ + if (increment < 0) + memset((void *)new, 0, -increment); + + if ((new < mem_malloc_start) || (new > mem_malloc_end)) + return (void *)MORECORE_FAILURE; + + mem_malloc_brk = new; + + return (void *)old; +} + +void mem_malloc_init(ulong start, ulong size) +{ + mem_malloc_start = start; + mem_malloc_end = start + size; + mem_malloc_brk = start; + +#ifdef CONFIG_SYS_MALLOC_DEFAULT_TO_INIT + malloc_init(); +#endif + + debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start, + mem_malloc_end); +#ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT + memset((void *)mem_malloc_start, 0x0, size); +#endif + malloc_bin_reloc(); +} + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + + + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks_r ((INTERNAL_SIZE_T)av_[1]) /* bitvector of nonempty blocks */ +#define binblocks_w (av_[1]) + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks_w = (mbinptr)(binblocks_r | idx2binblock(ii))) +#define clear_binblock(ii) (binblocks_w = (mbinptr)(binblocks_r & ~(idx2binblock(ii)))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +#ifdef DEBUG +static unsigned int n_mmaps = 0; +#endif /* DEBUG */ +static unsigned long mmapped_mem = 0; +#if HAVE_MMAP +static unsigned int max_n_mmaps = 0; +static unsigned long max_mmapped_mem = 0; +#endif + +#ifdef CONFIG_SYS_MALLOC_DEFAULT_TO_INIT +static void malloc_init(void) +{ + int i, j; + + debug("bins (av_ array) are at %p\n", (void *)av_); + + av_[0] = NULL; av_[1] = NULL; + for (i = 2, j = 2; i < NAV * 2 + 2; i += 2, j++) { + av_[i] = bin_at(j - 2); + av_[i + 1] = bin_at(j - 2); + + /* Just print the first few bins so that + * we can see there are alright. + */ + if (i < 10) + debug("av_[%d]=%lx av_[%d]=%lx\n", + i, (ulong)av_[i], + i + 1, (ulong)av_[i + 1]); + } + + /* Init the static bookkeeping as well */ + sbrk_base = (char *)(-1); + max_sbrked_mem = 0; + max_total_mem = 0; +#ifdef DEBUG + memset((void *)¤t_mallinfo, 0, sizeof(struct mallinfo)); +#endif +} +#endif + +/* + Debugging support +*/ + +#ifdef DEBUG + + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* No checkable chunk is mmapped */ + assert(!chunk_is_mmapped(p)); + + /* Check for legal address ... */ + assert((char*)p >= sbrk_base); + if (p != top) + assert((char*)p + sz <= (char*)top); + else + assert((char*)p + sz <= sbrk_base + sbrked_mem); + +} + + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + assert(!inuse(p)); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(aligned_OK(chunk2mem(p))); + /* ... matching footer field */ + assert(next->prev_size == sz); + /* ... and is fully consolidated */ + assert(prev_inuse(p)); + assert (next == top || inuse(next)); + + /* ... and has minimally sane links */ + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + assert(sz == SIZE_SZ); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + mchunkptr next = next_chunk(p); + do_check_chunk(p); + + /* Check whether it claims to be in use ... */ + assert(inuse(p)); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + assert(next_chunk(prv) == p); + do_check_free_chunk(prv); + } + if (next == top) + { + assert(prev_inuse(next)); + assert(chunksize(next) >= MINSIZE); + } + else if (!inuse(next)) + do_check_free_chunk(next); + +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = sz - s; + + do_check_inuse_chunk(p); + + /* Legal size ... */ + assert((long)sz >= (long)MINSIZE); + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(room >= 0); + assert(room < (long)MINSIZE); + + /* ... and alignment */ + assert(aligned_OK(chunk2mem(p))); + + + /* ... and was allocated at front of an available chunk */ + assert(prev_inuse(p)); + +} + + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif + + + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + /* For mmapped chunks, the overhead is one SIZE_SZ unit larger, because + * there is no following chunk whose prev_size field could be used. + */ + size = (size + SIZE_SZ + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = (new_size + offset + SIZE_SZ + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert((p->prev_size == offset)); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* brk; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_brk; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + brk = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (brk == (char*)(MORECORE_FAILURE) || + (brk < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (brk == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = brk; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += brk - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = (MALLOC_ALIGNMENT) - front_misalign; + brk += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + + correction += ((((unsigned long)(brk + sbrk_size))+(pagesz-1)) & + ~(pagesz - 1)) - ((unsigned long)(brk + sbrk_size)); + + /* Allocate correction */ + new_brk = (char*)(MORECORE (correction)); + if (new_brk == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)brk; + top_size = new_brk - brk + correction; + set_head(top, top_size | PREV_INUSE); + + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + /* Also keep size a multiple of MALLOC_ALIGNMENT */ + old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head_size(old_top, old_top_size); + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) + fREe(chunk2mem(old_top)); + } + } + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) in a way that allows chunks obtained across different + sbrk calls to be consolidated, but does not require + contiguous memory. Thus, it should be safe to intersperse + mallocs with other sbrk calls. + + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb; + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) + return malloc_simple(bytes); +#endif + + /* check if mem_malloc_init() was run */ + if ((mem_malloc_start == 0) && (mem_malloc_end == 0)) { + /* not initialized yet */ + return NULL; + } + + if ((long)bytes < 0) return NULL; + + nb = request2size(bytes); /* padded request size; */ + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks_r) + { + + /* Get to the first marked block */ + + if ( (block & binblocks_r) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks_r) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + unlink(victim, bck, fwd); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + av_[1] = (mbinptr)(binblocks_r & ~block); + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks_r && (block != 0) ) + { + while ((block & binblocks_r) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb))) + return chunk2mem(victim); +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return NULL; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + /* free() is a no-op - all the memory will be freed on relocation */ + if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) + return; +#endif + + if (mem == NULL) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + check_inuse_chunk(p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + malloc_trim(top_pad); + return; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, if the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + * Extending into newly sbrked space + + Unless the #define REALLOC_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + Void_t* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + +#ifdef REALLOC_ZERO_BYTES_FREES + if (!bytes) { + fREe(oldmem); + return NULL; + } +#endif + + if ((long)bytes < 0) return NULL; + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == NULL) return mALLOc(bytes); + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) { + /* This is harder to support and should not be needed */ + panic("pre-reloc realloc() is not supported"); + } +#endif + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if(newp) return chunk2mem(newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */ + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (!newmem) + return NULL; /* propagate failure */ + MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ); + munmap_chunk(oldp); + return newmem; + } +#endif + + check_inuse_chunk(oldp); + + if ((long)(oldsize) < (long)(nb)) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(oldp, nb); + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = NULL; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != NULL) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(newp, nb); + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* backward only */ + if (prev != NULL && (long)(prevsize + newsize) >= (long)nb) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == NULL) /* propagate failure */ + return NULL; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + memalign requests more than enough space from malloc, finds a spot + within that chunk that meets the alignment request, and then + possibly frees the leading and trailing space. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + if ((long)bytes < 0) return NULL; + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) { + return memalign_simple(alignment, bytes); + } +#endif + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= MALLOC_ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)(mALLOc(nb + alignment + MINSIZE)); + + /* + * The attempt to over-allocate (with a size large enough to guarantee the + * ability to find an aligned region within allocated memory) failed. + * + * Try again, this time only allocating exactly the size the user wants. If + * the allocation now succeeds and just happens to be aligned, we can still + * fulfill the user's request. + */ + if (m == NULL) { + size_t extra, extra2; + /* + * Use bytes not nb, since mALLOc internally calls request2size too, and + * each call increases the size to allocate, to account for the header. + */ + m = (char*)(mALLOc(bytes)); + /* Aligned -> return it */ + if ((((unsigned long)(m)) % alignment) == 0) + return m; + /* + * Otherwise, try again, requesting enough extra space to be able to + * acquire alignment. + */ + fREe(m); + /* Add in extra bytes to match misalignment of unexpanded allocation */ + extra = alignment - (((unsigned long)(m)) % alignment); + m = (char*)(mALLOc(bytes + extra)); + /* + * m might not be the same as before. Validate that the previous value of + * extra still works for the current value of m. + * If (!m), extra2=alignment so + */ + if (m) { + extra2 = alignment - (((unsigned long)(m)) % alignment); + if (extra2 > extra) { + fREe(m); + m = NULL; + } + } + /* Fall through to original NULL check and chunk splitting logic */ + } + + if (m == NULL) return NULL; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + return chunk2mem(p); /* nothing more to do */ +#endif + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -((signed) alignment)); + if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; + + newp = (mchunkptr)brk; + leadsize = brk - (char*)(p); + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + fREe(chunk2mem(remainder)); + } + + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + + /* check if expand_top called, in which case don't need to clear */ +#ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif +#endif + Void_t* mem = mALLOc (sz); + + if ((long)n < 0) return NULL; + + if (mem == NULL) + return NULL; + else + { +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) { + memset(mem, 0, sz); + return mem; + } +#endif + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif +#endif + + MALLOC_ZERO(mem, csz - SIZE_SZ); + return mem; + } +} + +/* + + cfree just calls free. It is needed/defined on some systems + that pair it with calloc, presumably for odd historical reasons. + +*/ + +#if !defined(INTERNAL_LINUX_C_LIB) || !defined(__ELF__) +#if __STD_C +void cfree(Void_t *mem) +#else +void cfree(mem) Void_t *mem; +#endif +{ + fREe(mem); +} +#endif + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int malloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_brk; /* address returned by pre-check sbrk call */ + char* new_brk; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { + /* Test to make sure no one else called sbrk */ + current_brk = (char*)(MORECORE (0)); + if (current_brk != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else + { + new_brk = (char*)(MORECORE (-extra)); + + if (new_brk == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_brk = (char*)(MORECORE (0)); + top_size = current_brk - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_brk - sbrk_base; + set_head(top, top_size | PREV_INUSE); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t malloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == NULL) + return 0; + else + { + p = mem2chunk(mem); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + check_inuse_chunk(p); + return chunksize(p) - SIZE_SZ; + } + return chunksize(p) - 2*SIZE_SZ; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +#ifdef DEBUG +static void malloc_update_mallinfo() +{ + int i; + mbinptr b; + mchunkptr p; +#ifdef DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#ifdef DEBUG + check_free_chunk(p); + for (q = next_chunk(p); + q < top && inuse(q) && (long)(chunksize(q)) >= (long)MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} +#endif /* DEBUG */ + + + +/* + + malloc_stats: + + Prints on the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +#ifdef DEBUG +void malloc_stats() +{ + malloc_update_mallinfo(); + printf("max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + printf("system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + printf("in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + printf("max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} +#endif /* DEBUG */ + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +#ifdef DEBUG +struct mallinfo mALLINFo() +{ + malloc_update_mallinfo(); + return current_mallinfo; +} +#endif /* DEBUG */ + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + + default: + return 0; + } +} + +int initf_malloc(void) +{ +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + assert(gd->malloc_base); /* Set up by crt0.S */ + gd->malloc_limit = CONFIG_VAL(SYS_MALLOC_F_LEN); + gd->malloc_ptr = 0; +#endif + + return 0; +} + +/* + +History: + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong <mcfong@yahoo.com> + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup up header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/roms/u-boot/common/dlmalloc.src b/roms/u-boot/common/dlmalloc.src new file mode 100644 index 000000000..d86acffde --- /dev/null +++ b/roms/u-boot/common/dlmalloc.src @@ -0,0 +1,3265 @@ +/* ---------- To make a malloc.h, start cutting here ------------ */ + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl@cs.oswego.edu + +* VERSION 2.6.6 Sun Mar 5 19:10:03 2000 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://g.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator. For a high-level description, see + http://g.oswego.edu/dl/html/malloc.html + +* Synopsis of public routines + + (Much fuller descriptions are contained in the program documentation below.) + + malloc(size_t n); + Return a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. + free(Void_t* p); + Release the chunk of memory pointed to by p, or no effect if p is null. + realloc(Void_t* p, size_t n); + Return a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. The returned pointer may or may not be + the same as p. If p is null, equivalent to malloc. Unless the + #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + memalign(size_t alignment, size_t n); + Return a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument, which must be a power of + two. + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system (or as near to this as can be figured out from + all the includes/defines below.) + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + calloc(size_t unit, size_t quantity); + Returns a pointer to quantity * unit bytes, with all locations + set to zero. + cfree(Void_t* p); + Equivalent to free(p). + malloc_trim(size_t pad); + Release all but pad bytes of freed top-most memory back + to the system. Return 1 if successful, else 0. + malloc_usable_size(Void_t* p); + Report the number usable allocated bytes associated with allocated + chunk p. This may or may not report more bytes than were requested, + due to alignment and minimum size constraints. + malloc_stats(); + Prints brief summary statistics on stderr. + mallinfo() + Returns (by copy) a struct containing various summary statistics. + mallopt(int parameter_number, int parameter_value) + Changes one of the tunable parameters described below. Returns + 1 if successful in changing the parameter, else 0. + +* Vital statistics: + + Alignment: 8-byte + 8 byte alignment is currently hardwired into the design. This + seems to suffice for all current machines and C compilers. + + Assumed pointer representation: 4 or 8 bytes + Code for 8-byte pointers is untested by me but has worked + reliably by Wolfram Gloger, who contributed most of the + changes supporting this. + + Assumed size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden overhead of 4 bytes holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field + and 8 (16) bytes for free list pointers. Thus, the minimum + allocatable size is 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + Maximum allocated size: 4-byte size_t: 2^31 - 8 bytes + 8-byte size_t: 2^63 - 16 bytes + + It is assumed that (possibly signed) size_t bit values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. To be conservative, values that would appear + as negative numbers are avoided. + Requests for sizes with a negative sign bit when the request + size is treaded as a long will return null. + + Maximum overhead wastage per allocated chunk: normally 15 bytes + + Alignnment demands, plus the minimum allocatable size restriction + make the normal worst-case wastage 15 bytes (i.e., up to 15 + more bytes will be allocated than were requested in malloc), with + two exceptions: + 1. Because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + 2. For requests >= mmap_threshold that are serviced via + mmap(), the worst case wastage is 8 bytes plus the remainder + from a system page (the minimal mmap unit); typically 4096 bytes. + +* Limitations + + Here are some features that are NOT currently supported + + * No user-definable hooks for callbacks and the like. + * No automated mechanism for fully checking that all accesses + to malloced memory stay within their bounds. + * No support for compaction. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and + Linux. It is also reported to work on WIN32 platforms. + People have also reported adapting this malloc for use in + stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. Among other + consequences, it uses a lot of macros. Because of this, to be at + all usable, this code should be compiled using an optimizing compiler + (for example gcc -O2) that can simplify expressions and control + paths. + + __STD_C (default: derived from C compiler defines) + Nonzero if using ANSI-standard C compiler, a C++ compiler, or + a C compiler sufficiently close to ANSI to get away with it. + DEBUG (default: NOT defined) + Define to enable debugging. Adds fairly extensive assertion-based + checking to help track down memory errors, but noticeably slows down + execution. + REALLOC_ZERO_BYTES_FREES (default: NOT defined) + Define this if you think that realloc(p, 0) should be equivalent + to free(p). Otherwise, since malloc returns a unique pointer for + malloc(0), so does realloc(p, 0). + HAVE_MEMCPY (default: defined) + Define if you are not otherwise using ANSI STD C, but still + have memcpy and memset in your C library and want to use them. + Otherwise, simple internal versions are supplied. + USE_MEMCPY (default: 1 if HAVE_MEMCPY is defined, 0 otherwise) + Define as 1 if you want the C library versions of memset and + memcpy called in realloc and calloc (otherwise macro versions are used). + At least on some platforms, the simple macro versions usually + outperform libc versions. + HAVE_MMAP (default: defined as 1) + Define to non-zero to optionally make malloc() use mmap() to + allocate very large blocks. + HAVE_MREMAP (default: defined as 0 unless Linux libc set) + Define to non-zero to optionally make realloc() use mremap() to + reallocate very large blocks. + malloc_getpagesize (default: derived from system #includes) + Either a constant or routine call returning the system page size. + HAVE_USR_INCLUDE_MALLOC_H (default: NOT defined) + Optionally define if you are on a system with a /usr/include/malloc.h + that declares struct mallinfo. It is not at all necessary to + define this even if you do, but will ensure consistency. + INTERNAL_SIZE_T (default: size_t) + Define to a 32-bit type (probably `unsigned int') if you are on a + 64-bit machine, yet do not want or need to allow malloc requests of + greater than 2^31 to be handled. This saves space, especially for + very small chunks. + INTERNAL_LINUX_C_LIB (default: NOT defined) + Defined only when compiled as part of Linux libc. + Also note that there is some odd internal name-mangling via defines + (for example, internally, `malloc' is named `mALLOc') needed + when compiling in this case. These look funny but don't otherwise + affect anything. + WIN32 (default: undefined) + Define this on MS win (95, nt) platforms to compile in sbrk emulation. + LACKS_UNISTD_H (default: undefined if not WIN32) + Define this if your system does not have a <unistd.h>. + LACKS_SYS_PARAM_H (default: undefined if not WIN32) + Define this if your system does not have a <sys/param.h>. + MORECORE (default: sbrk) + The name of the routine to call to obtain more memory from the system. + MORECORE_FAILURE (default: -1) + The value returned upon failure of MORECORE. + MORECORE_CLEARS (default 1) + true (1) if the routine mapped to MORECORE zeroes out memory (which + holds for sbrk). + DEFAULT_TRIM_THRESHOLD + DEFAULT_TOP_PAD + DEFAULT_MMAP_THRESHOLD + DEFAULT_MMAP_MAX + Default values of tunable parameters (described in detail below) + controlling interaction with host system routines (sbrk, mmap, etc). + These values may also be changed dynamically via mallopt(). The + preset defaults are those that give best performance for typical + programs/systems. + USE_DL_PREFIX (default: undefined) + Prefix all public routines with the string 'dl'. Useful to + quickly avoid procedure declaration conflicts and linker symbol + conflicts with existing memory allocation routines. + + +*/ + + + + +/* Preliminaries */ + +#ifndef __STD_C +#ifdef __STDC__ +#define __STD_C 1 +#else +#if __cplusplus +#define __STD_C 1 +#else +#define __STD_C 0 +#endif /*__cplusplus*/ +#endif /*__STDC__*/ +#endif /*__STD_C*/ + +#ifndef Void_t +#if (__STD_C || defined(WIN32)) +#define Void_t void +#else +#define Void_t char +#endif +#endif /*Void_t*/ + +#if __STD_C +#include <stddef.h> /* for size_t */ +#else +#include <sys/types.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* needed for malloc_stats */ + + +/* + Compile-time options +*/ + + +/* + Debugging: + + Because freed chunks may be overwritten with link fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with -DDEBUG, a number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The + checking is fairly extensive, and will slow down execution + noticeably. Calling malloc_stats or mallinfo with DEBUG set will + attempt to check every non-mmapped allocated and free chunk in the + course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting DEBUG may also be helpful if you are trying to modify + this code. The assertions in the check routines spell out in more + detail the assumptions and invariants underlying the algorithms. + +*/ + +#if DEBUG +#include <assert.h> +#else +#define assert(x) ((void)0) +#endif + + +/* + INTERNAL_SIZE_T is the word-size used for internal bookkeeping + of chunk sizes. On a 64-bit machine, you can reduce malloc + overhead by defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' + at the expense of not being able to handle requests greater than + 2^31. This limitation is hardly ever a concern; you are encouraged + to set this. However, the default version is the same as size_t. +*/ + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T size_t +#endif + +/* + REALLOC_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + Some people think it should. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + + +/* #define REALLOC_ZERO_BYTES_FREES */ + + +/* + WIN32 causes an emulation of sbrk to be compiled in + mmap-based options are not currently supported in WIN32. +*/ + +/* #define WIN32 */ +#ifdef WIN32 +#define MORECORE wsbrk +#define HAVE_MMAP 0 + +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H + +/* + Include 'windows.h' to get the necessary declarations for the + Microsoft Visual C++ data structures and routines used in the 'sbrk' + emulation. + + Define WIN32_LEAN_AND_MEAN so that only the essential Microsoft + Visual C++ header files are included. +*/ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + + +/* + HAVE_MEMCPY should be defined if you are not otherwise using + ANSI STD C, but still have memcpy and memset in your C library + and want to use them in calloc and realloc. Otherwise simple + macro versions are defined here. + + USE_MEMCPY should be defined as 1 if you actually want to + have memset and memcpy called. People report that the macro + versions are often enough faster than libc versions on many + systems that it is better to use them. + +*/ + +#define HAVE_MEMCPY + +#ifndef USE_MEMCPY +#ifdef HAVE_MEMCPY +#define USE_MEMCPY 1 +#else +#define USE_MEMCPY 0 +#endif +#endif + +#if (__STD_C || defined(HAVE_MEMCPY)) + +#if __STD_C +void* memset(void*, int, size_t); +void* memcpy(void*, const void*, size_t); +#else +#ifdef WIN32 +/* On Win32 platforms, 'memset()' and 'memcpy()' are already declared in */ +/* 'windows.h' */ +#else +Void_t* memset(); +Void_t* memcpy(); +#endif +#endif +#endif + +#if USE_MEMCPY + +/* The following macros are only invoked with (2n+1)-multiples of + INTERNAL_SIZE_T units, with a positive integer n. This is exploited + for fast inline execution when n is small. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T mzsz = (nbytes); \ + if(mzsz <= 9*sizeof(mzsz)) { \ + INTERNAL_SIZE_T* mz = (INTERNAL_SIZE_T*) (charp); \ + if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; }}} \ + *mz++ = 0; \ + *mz++ = 0; \ + *mz = 0; \ + } else memset((charp), 0, mzsz); \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T mcsz = (nbytes); \ + if(mcsz <= 9*sizeof(mcsz)) { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) (src); \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) (dest); \ + if(mcsz >= 5*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 7*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 9*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; }}} \ + *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + *mcdst = *mcsrc ; \ + } else memcpy(dest, src, mcsz); \ +} while(0) + +#else /* !USE_MEMCPY */ + +/* Use Duff's device for good zeroing/copying performance. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T* mzp = (INTERNAL_SIZE_T*)(charp); \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mzp++ = 0; \ + case 7: *mzp++ = 0; \ + case 6: *mzp++ = 0; \ + case 5: *mzp++ = 0; \ + case 4: *mzp++ = 0; \ + case 3: *mzp++ = 0; \ + case 2: *mzp++ = 0; \ + case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) src; \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) dest; \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mcdst++ = *mcsrc++; \ + case 7: *mcdst++ = *mcsrc++; \ + case 6: *mcdst++ = *mcsrc++; \ + case 5: *mcdst++ = *mcsrc++; \ + case 4: *mcdst++ = *mcsrc++; \ + case 3: *mcdst++ = *mcsrc++; \ + case 2: *mcdst++ = *mcsrc++; \ + case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#endif + + +/* + Define HAVE_MMAP to optionally make malloc() use mmap() to + allocate very large blocks. These will be returned to the + operating system immediately after a free(). +*/ + +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. This is currently only possible on Linux with + kernel versions newer than 1.3.77. +*/ + +#ifndef HAVE_MREMAP +#ifdef INTERNAL_LINUX_C_LIB +#define HAVE_MREMAP 1 +#else +#define HAVE_MREMAP 0 +#endif +#endif + +#if HAVE_MMAP + +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif /* HAVE_MMAP */ + +/* + Access to system page size. To the extent possible, this malloc + manages memory from the system in page-size units. + + The following mechanics for getpagesize were adapted from + bsd/gnu getpagesize.h +*/ + +#ifndef LACKS_UNISTD_H +# include <unistd.h> +#endif + +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 +# define malloc_getpagesize (4096) /* TBD: Use 'GetSystemInfo' instead */ +# else +# ifndef LACKS_SYS_PARAM_H +# include <sys/param.h> +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else +# define malloc_getpagesize (4096) /* just guess */ +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif + + +/* + + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing the same kind of + information you can get from malloc_stats. It should work on + any SVID/XPG compliant system that has a /usr/include/malloc.h + defining struct mallinfo. (If you'd like to install such a thing + yourself, cut out the preliminary declarations as described above + and below and save them in a malloc.h file. But there's no + compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields, most of which are not even meaningful in this + version of malloc. Some of these fields are are instead filled by + mallinfo() with other numbers that might possibly be of interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else an SVID2/XPG2 compliant + version is declared below. These must be precisely the same for + mallinfo() to work. + +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#if HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* SVID2/XPG mallopt options */ + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + +#endif + +/* mallopt options that actually do something */ + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 + + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The default trim value is high enough to cause trimming only in + fairly extreme (by current memory consumption standards) cases. + It must be greater than page size to have any useful effect. To + disable trimming completely, you can set to (unsigned long)(-1); + + +*/ + + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. + +*/ + + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD (128 * 1024) +#endif + +/* + + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefit that mmapped space + can ALWAYS be individually released back to the system, which + helps keep the system level memory demands of a long-lived + program low. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + menas that even trimming via malloc_trim would not release them. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + All together, these considerations should lead you to use mmap + only for relatively large requests. + + +*/ + + +#ifndef DEFAULT_MMAP_MAX +#if HAVE_MMAP +#define DEFAULT_MMAP_MAX (64) +#else +#define DEFAULT_MMAP_MAX (0) +#endif +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because: + + 1. Some systems have a limited number of internal tables for + use by mmap. + 2. In most systems, overreliance on mmap can degrade overall + performance. + 3. If a program allocates many large regions, it is probably + better off using normal sbrk-based allocation routines that + can reclaim and reallocate normal heap memory. Using a + small value allows transition into this mode after the + first few allocations. + + Setting to 0 disables all use of mmap. If HAVE_MMAP is not set, + the default value is 0, and attempts to set it to non-zero values + in mallopt will fail. +*/ + + +/* + USE_DL_PREFIX will prefix all public routines with the string 'dl'. + Useful to quickly avoid procedure declaration conflicts and linker + symbol conflicts with existing memory allocation routines. + +*/ + +/* #define USE_DL_PREFIX */ + + +/* + + Special defines for linux libc + + Except when compiled using these special defines for Linux libc + using weak aliases, this malloc is NOT designed to work in + multithreaded applications. No semaphores or other concurrency + control are provided to ensure that multiple malloc or free calls + don't run at the same time, which could be disasterous. A single + semaphore could be used across malloc, realloc, and free (which is + essentially the effect of the linux weak alias approach). It would + be hard to obtain finer granularity. + +*/ + + +#ifdef INTERNAL_LINUX_C_LIB + +#if __STD_C + +Void_t * __default_morecore_init (ptrdiff_t); +Void_t *(*__morecore)(ptrdiff_t) = __default_morecore_init; + +#else + +Void_t * __default_morecore_init (); +Void_t *(*__morecore)() = __default_morecore_init; + +#endif + +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +#define MORECORE_CLEARS 1 + +#else /* INTERNAL_LINUX_C_LIB */ + +#if __STD_C +extern Void_t* sbrk(ptrdiff_t); +#else +extern Void_t* sbrk(); +#endif + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE -1 +#endif + +#ifndef MORECORE_CLEARS +#define MORECORE_CLEARS 1 +#endif + +#endif /* INTERNAL_LINUX_C_LIB */ + +#if defined(INTERNAL_LINUX_C_LIB) && defined(__ELF__) + +#define cALLOc __libc_calloc +#define fREe __libc_free +#define mALLOc __libc_malloc +#define mEMALIGn __libc_memalign +#define rEALLOc __libc_realloc +#define vALLOc __libc_valloc +#define pvALLOc __libc_pvalloc +#define mALLINFo __libc_mallinfo +#define mALLOPt __libc_mallopt + +#pragma weak calloc = __libc_calloc +#pragma weak free = __libc_free +#pragma weak cfree = __libc_free +#pragma weak malloc = __libc_malloc +#pragma weak memalign = __libc_memalign +#pragma weak realloc = __libc_realloc +#pragma weak valloc = __libc_valloc +#pragma weak pvalloc = __libc_pvalloc +#pragma weak mallinfo = __libc_mallinfo +#pragma weak mallopt = __libc_mallopt + +#else + +#ifdef USE_DL_PREFIX +#define cALLOc dlcalloc +#define fREe dlfree +#define mALLOc dlmalloc +#define mEMALIGn dlmemalign +#define rEALLOc dlrealloc +#define vALLOc dlvalloc +#define pvALLOc dlpvalloc +#define mALLINFo dlmallinfo +#define mALLOPt dlmallopt +#else /* USE_DL_PREFIX */ +#define cALLOc calloc +#define fREe free +#define mALLOc malloc +#define mEMALIGn memalign +#define rEALLOc realloc +#define vALLOc valloc +#define pvALLOc pvalloc +#define mALLINFo mallinfo +#define mALLOPt mallopt +#endif /* USE_DL_PREFIX */ + +#endif + +/* Public routines */ + +#if __STD_C + +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +Void_t* cALLOc(size_t, size_t); +void cfree(Void_t*); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +Void_t* cALLOc(); +void cfree(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif + + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* ---------- To make a malloc.h, end cutting here ------------ */ + + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. + + Thanks to Martin Fong and others for supplying this. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ +~(malloc_getpagesize-1)) +#define AlignPage64K(add) (((add) + (0x10000 - 1)) & ~(0x10000 - 1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +typedef struct GmListElement GmListElement; + +struct GmListElement +{ + GmListElement* next; + void* base; +}; + +static GmListElement* head = 0; +static unsigned int gNextAddress = 0; +static unsigned int gAddressBase = 0; +static unsigned int gAllocatedSize = 0; + +static +GmListElement* makeGmListElement (void* bas) +{ + GmListElement* this; + this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement)); + assert (this); + if (this) + { + this->base = bas; + this->next = head; + head = this; + } + return this; +} + +void gcleanup () +{ + BOOL rval; + assert ( (head == NULL) || (head->base == (void*)gAddressBase)); + if (gAddressBase && (gNextAddress - gAddressBase)) + { + rval = VirtualFree ((void*)gAddressBase, + gNextAddress - gAddressBase, + MEM_DECOMMIT); + assert (rval); + } + while (head) + { + GmListElement* next = head->next; + rval = VirtualFree (head->base, 0, MEM_RELEASE); + assert (rval); + LocalFree (head); + head = next; + } +} + +static +void* findRegion (void* start_address, unsigned long size) +{ + MEMORY_BASIC_INFORMATION info; + if (size >= TOP_MEMORY) return NULL; + + while ((unsigned long)start_address + size < TOP_MEMORY) + { + VirtualQuery (start_address, &info, sizeof (info)); + if ((info.State == MEM_FREE) && (info.RegionSize >= size)) + return start_address; + else + { + /* Requested region is not available so see if the */ + /* next region is available. Set 'start_address' */ + /* to the next region and call 'VirtualQuery()' */ + /* again. */ + + start_address = (char*)info.BaseAddress + info.RegionSize; + + /* Make sure we start looking for the next region */ + /* on the *next* 64K boundary. Otherwise, even if */ + /* the new region is free according to */ + /* 'VirtualQuery()', the subsequent call to */ + /* 'VirtualAlloc()' (which follows the call to */ + /* this routine in 'wsbrk()') will round *down* */ + /* the requested address to a 64K boundary which */ + /* we already know is an address in the */ + /* unavailable region. Thus, the subsequent call */ + /* to 'VirtualAlloc()' will fail and bring us back */ + /* here, causing us to go into an infinite loop. */ + + start_address = + (void *) AlignPage64K((unsigned long) start_address); + } + } + return NULL; + +} + + +void* wsbrk (long size) +{ + void* tmp; + if (size > 0) + { + if (gAddressBase == 0) + { + gAllocatedSize = max (RESERVED_SIZE, AlignPage (size)); + gNextAddress = gAddressBase = + (unsigned int)VirtualAlloc (NULL, gAllocatedSize, + MEM_RESERVE, PAGE_NOACCESS); + } else if (AlignPage (gNextAddress + size) > (gAddressBase + +gAllocatedSize)) + { + long new_size = max (NEXT_SIZE, AlignPage (size)); + void* new_address = (void*)(gAddressBase+gAllocatedSize); + do + { + new_address = findRegion (new_address, new_size); + + if (new_address == 0) + return (void*)-1; + + gAddressBase = gNextAddress = + (unsigned int)VirtualAlloc (new_address, new_size, + MEM_RESERVE, PAGE_NOACCESS); + /* repeat in case of race condition */ + /* The region that we found has been snagged */ + /* by another thread */ + } + while (gAddressBase == 0); + + assert (new_address == (void*)gAddressBase); + + gAllocatedSize = new_size; + + if (!makeGmListElement ((void*)gAddressBase)) + return (void*)-1; + } + if ((size + gNextAddress) > AlignPage (gNextAddress)) + { + void* res; + res = VirtualAlloc ((void*)AlignPage (gNextAddress), + (size + gNextAddress - + AlignPage (gNextAddress)), + MEM_COMMIT, PAGE_READWRITE); + if (res == 0) + return (void*)-1; + } + tmp = (void*)gNextAddress; + gNextAddress = (unsigned int)tmp + size; + return tmp; + } + else if (size < 0) + { + unsigned int alignedGoal = AlignPage (gNextAddress + size); + /* Trim by releasing the virtual memory */ + if (alignedGoal >= gAddressBase) + { + VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal, + MEM_DECOMMIT); + gNextAddress = gNextAddress + size; + return (void*)gNextAddress; + } + else + { + VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase, + MEM_DECOMMIT); + gNextAddress = gAddressBase; + return (void*)-1; + } + } + else + { + return (void*)gNextAddress; + } +} + +#endif + + + +/* + Type declarations +*/ + + +struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk* mchunkptr; + +/* + + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if allocated | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_space() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + (The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory.) + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. (This makes it easier to + deal with alignments etc). + + The two exceptions to all this are + + 1. The special chunk `top', which doesn't bother using the + trailing size field since there is no + next contiguous chunk that would have to index off it. (After + initialization, `top' is forced to always exist. If it would + become less than MINSIZE bytes long, it is replenished via + malloc_extend_top.) + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit (IS_MMAPPED) set in their size fields. Because they are + never merged or traversed from any other chunk, they have no + foot size or inuse information. + + Available chunks are kept in any of several places (all declared below): + + * `av': An array of chunks serving as bin headers for consolidated + chunks. Each bin is doubly linked. The bins are approximately + proportionally (log) spaced. There are a lot of these bins + (128). This may look excessive, but works very well in + practice. All procedures maintain the invariant that no + consolidated chunk physically borders another one. Chunks in + bins are kept in size order, with ties going to the + approximately least recently used chunk. + + The chunks in each bin are maintained in decreasing sorted order by + size. This is irrelevant for the small bins, which all contain + the same-sized chunks, but facilitates best-fit allocation for + larger chunks. (These lists are just sequential. Keeping them in + order almost never requires enough traversal to warrant using + fancier ordered data structures.) Chunks of the same size are + linked with the most recently freed at the front, and allocations + are taken from the back. This results in LRU or FIFO allocation + order, which tends to give each chunk an equal opportunity to be + consolidated with adjacent freed chunks, resulting in larger free + chunks and less fragmentation. + + * `top': The top-most available chunk (i.e., the one bordering the + end of available memory) is treated specially. It is never + included in any bin, is used only if no other chunk is + available, and is released back to the system if it is very + large (see M_TRIM_THRESHOLD). + + * `last_remainder': A bin holding only the remainder of the + most recently split (non-top) chunk. This bin is checked + before other non-fitting chunks, so as to provide better + locality for runs of sequentially allocated chunks. + + * Implicitly, through the host system's memory mapping tables. + If supported, requests greater than a threshold are usually + serviced via calls to mmap, and then later released via munmap. + +*/ + + + + + +/* sizes, alignments */ + +#define SIZE_SZ (sizeof(INTERNAL_SIZE_T)) +#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) +#define MINSIZE (sizeof(struct malloc_chunk)) + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* pad request bytes into a usable size */ + +#define request2size(req) \ + (((long)((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) < \ + (long)(MINSIZE + MALLOC_ALIGN_MASK)) ? MINSIZE : \ + (((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK))) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)((m)) & (MALLOC_ALIGN_MASK)) == 0) + + + + +/* + Physical chunk operations +*/ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + + +/* Ptr to next physical malloc_chunk. */ + +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) )) + +/* Ptr to previous physical malloc_chunk */ + +#define prev_chunk(p)\ + ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) )) + + +/* Treat space at ptr + offset as a chunk */ + +#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) + + + + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* check for mmap()'ed chunk */ + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + + + + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +#define NAV 128 /* number of bins */ + +typedef struct malloc_chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (bin_at(0)->fd) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[NAV * 2 + 2] = { + 0, 0, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; + + + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + + + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks (bin_at(0)->size) /* bitvector of nonempty blocks */ + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks |= idx2binblock(ii)) +#define clear_binblock(ii) (binblocks &= ~(idx2binblock(ii))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +static unsigned int n_mmaps = 0; +static unsigned int max_n_mmaps = 0; +static unsigned long mmapped_mem = 0; +static unsigned long max_mmapped_mem = 0; + + + +/* + Debugging support +*/ + +#if DEBUG + + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* No checkable chunk is mmapped */ + assert(!chunk_is_mmapped(p)); + + /* Check for legal address ... */ + assert((char*)p >= sbrk_base); + if (p != top) + assert((char*)p + sz <= (char*)top); + else + assert((char*)p + sz <= sbrk_base + sbrked_mem); + +} + + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + assert(!inuse(p)); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(aligned_OK(chunk2mem(p))); + /* ... matching footer field */ + assert(next->prev_size == sz); + /* ... and is fully consolidated */ + assert(prev_inuse(p)); + assert (next == top || inuse(next)); + + /* ... and has minimally sane links */ + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + assert(sz == SIZE_SZ); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + mchunkptr next = next_chunk(p); + do_check_chunk(p); + + /* Check whether it claims to be in use ... */ + assert(inuse(p)); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + assert(next_chunk(prv) == p); + do_check_free_chunk(prv); + } + if (next == top) + { + assert(prev_inuse(next)); + assert(chunksize(next) >= MINSIZE); + } + else if (!inuse(next)) + do_check_free_chunk(next); + +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = sz - s; + + do_check_inuse_chunk(p); + + /* Legal size ... */ + assert((long)sz >= (long)MINSIZE); + assert((sz & MALLOC_ALIGN_MASK) == 0); + assert(room >= 0); + assert(room < (long)MINSIZE); + + /* ... and alignment */ + assert(aligned_OK(chunk2mem(p))); + + + /* ... and was allocated at front of an available chunk */ + assert(prev_inuse(p)); + +} + + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif + + + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + /* For mmapped chunks, the overhead is one SIZE_SZ unit larger, because + * there is no following chunk whose prev_size field could be used. + */ + size = (size + SIZE_SZ + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = (new_size + offset + SIZE_SZ + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert((p->prev_size == offset)); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + + + + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* brk; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_brk; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + brk = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (brk == (char*)(MORECORE_FAILURE) || + (brk < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (brk == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = brk; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += brk - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = (MALLOC_ALIGNMENT) - front_misalign; + brk += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + + correction += ((((unsigned long)(brk + sbrk_size))+(pagesz-1)) & + ~(pagesz - 1)) - ((unsigned long)(brk + sbrk_size)); + + /* Allocate correction */ + new_brk = (char*)(MORECORE (correction)); + if (new_brk == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)brk; + top_size = new_brk - brk + correction; + set_head(top, top_size | PREV_INUSE); + + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + /* Also keep size a multiple of MALLOC_ALIGNMENT */ + old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head_size(old_top, old_top_size); + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) + fREe(chunk2mem(old_top)); + } + } + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) in a way that allows chunks obtained across different + sbrk calls to be consolidated, but does not require + contiguous memory. Thus, it should be safe to intersperse + mallocs with other sbrk calls. + + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb; + + if ((long)bytes < 0) return 0; + + nb = request2size(bytes); /* padded request size; */ + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks) + { + + /* Get to the first marked block */ + + if ( (block & binblocks) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + unlink(victim, bck, fwd); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + binblocks &= ~block; + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks && (block != 0) ) + { + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb)) != 0) + return chunk2mem(victim); +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return 0; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + if (mem == 0) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + check_inuse_chunk(p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + malloc_trim(top_pad); + return; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, if the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + * Extending into newly sbrked space + + Unless the #define REALLOC_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + Void_t* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { fREe(oldmem); return 0; } +#endif + + if ((long)bytes < 0) return 0; + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == 0) return mALLOc(bytes); + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if(newp) return chunk2mem(newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */ + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (newmem == 0) return 0; /* propagate failure */ + MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ); + munmap_chunk(oldp); + return newmem; + } +#endif + + check_inuse_chunk(oldp); + + if ((long)(oldsize) < (long)(nb)) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(oldp, nb); + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = 0; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != 0) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(newp, nb); + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* backward only */ + if (prev != 0 && (long)(prevsize + newsize) >= (long)nb) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == 0) /* propagate failure */ + return 0; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + memalign requests more than enough space from malloc, finds a spot + within that chunk that meets the alignment request, and then + possibly frees the leading and trailing space. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + if ((long)bytes < 0) return 0; + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= MALLOC_ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)(mALLOc(nb + alignment + MINSIZE)); + + if (m == 0) return 0; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + return chunk2mem(p); /* nothing more to do */ +#endif + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -((signed) alignment)); + if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; + + newp = (mchunkptr)brk; + leadsize = brk - (char*)(p); + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + fREe(chunk2mem(remainder)); + } + + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + + /* check if expand_top called, in which case don't need to clear */ +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif + Void_t* mem = mALLOc (sz); + + if ((long)n < 0) return 0; + + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + MALLOC_ZERO(mem, csz - SIZE_SZ); + return mem; + } +} + +/* + + cfree just calls free. It is needed/defined on some systems + that pair it with calloc, presumably for odd historical reasons. + +*/ + +#if !defined(INTERNAL_LINUX_C_LIB) || !defined(__ELF__) +#if __STD_C +void cfree(Void_t *mem) +#else +void cfree(mem) Void_t *mem; +#endif +{ + fREe(mem); +} +#endif + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int malloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_brk; /* address returned by pre-check sbrk call */ + char* new_brk; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { + /* Test to make sure no one else called sbrk */ + current_brk = (char*)(MORECORE (0)); + if (current_brk != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else + { + new_brk = (char*)(MORECORE (-extra)); + + if (new_brk == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_brk = (char*)(MORECORE (0)); + top_size = current_brk - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_brk - sbrk_base; + set_head(top, top_size | PREV_INUSE); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t malloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + check_inuse_chunk(p); + return chunksize(p) - SIZE_SZ; + } + return chunksize(p) - 2*SIZE_SZ; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +static void malloc_update_mallinfo() +{ + int i; + mbinptr b; + mchunkptr p; +#if DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#if DEBUG + check_free_chunk(p); + for (q = next_chunk(p); + q < top && inuse(q) && (long)(chunksize(q)) >= (long)MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} + + + +/* + + malloc_stats: + + Prints on stderr the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +void malloc_stats() +{ + malloc_update_mallinfo(); + fprintf(stderr, "max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + fprintf(stderr, "system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + fprintf(stderr, "in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + fprintf(stderr, "max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +struct mallinfo mALLINFo() +{ + malloc_update_mallinfo(); + return current_mallinfo; +} + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + + default: + return 0; + } +} + +/* + +History: + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong <mcfong@yahoo.com> + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup up header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/roms/u-boot/common/edid.c b/roms/u-boot/common/edid.c new file mode 100644 index 000000000..fa85bcd6a --- /dev/null +++ b/roms/u-boot/common/edid.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2010 + * Petr Stetiar <ynezz@true.cz> + * + * Contains stolen code from ddcprobe project which is: + * Copyright (C) Nalin Dahyabhai <bigfun@pobox.com> + */ + +#include <common.h> +#include <edid.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <linux/ctype.h> +#include <linux/string.h> + +int edid_check_info(struct edid1_info *edid_info) +{ + if ((edid_info == NULL) || (edid_info->version == 0)) + return -1; + + if (memcmp(edid_info->header, "\x0\xff\xff\xff\xff\xff\xff\x0", 8)) + return -1; + + if (edid_info->version == 0xff && edid_info->revision == 0xff) + return -1; + + return 0; +} + +int edid_check_checksum(u8 *edid_block) +{ + u8 checksum = 0; + int i; + + for (i = 0; i < 128; i++) + checksum += edid_block[i]; + + return (checksum == 0) ? 0 : -EINVAL; +} + +int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, + unsigned int *hmax, unsigned int *vmin, + unsigned int *vmax) +{ + int i; + struct edid_monitor_descriptor *monitor; + + *hmin = *hmax = *vmin = *vmax = 0; + if (edid_check_info(edid)) + return -1; + + for (i = 0; i < ARRAY_SIZE(edid->monitor_details.descriptor); i++) { + monitor = &edid->monitor_details.descriptor[i]; + if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE) { + *hmin = monitor->data.range_data.horizontal_min; + *hmax = monitor->data.range_data.horizontal_max; + *vmin = monitor->data.range_data.vertical_min; + *vmax = monitor->data.range_data.vertical_max; + return 0; + } + } + return -1; +} + +/* Set all parts of a timing entry to the same value */ +static void set_entry(struct timing_entry *entry, u32 value) +{ + entry->min = value; + entry->typ = value; + entry->max = value; +} + +/** + * decode_timing() - Decoding an 18-byte detailed timing record + * + * @buf: Pointer to EDID detailed timing record + * @timing: Place to put timing + */ +static void decode_timing(u8 *buf, struct display_timing *timing) +{ + uint x_mm, y_mm; + unsigned int ha, hbl, hso, hspw, hborder; + unsigned int va, vbl, vso, vspw, vborder; + struct edid_detailed_timing *t = (struct edid_detailed_timing *)buf; + + /* Edid contains pixel clock in terms of 10KHz */ + set_entry(&timing->pixelclock, (buf[0] + (buf[1] << 8)) * 10000); + x_mm = (buf[12] + ((buf[14] & 0xf0) << 4)); + y_mm = (buf[13] + ((buf[14] & 0x0f) << 8)); + ha = (buf[2] + ((buf[4] & 0xf0) << 4)); + hbl = (buf[3] + ((buf[4] & 0x0f) << 8)); + hso = (buf[8] + ((buf[11] & 0xc0) << 2)); + hspw = (buf[9] + ((buf[11] & 0x30) << 4)); + hborder = buf[15]; + va = (buf[5] + ((buf[7] & 0xf0) << 4)); + vbl = (buf[6] + ((buf[7] & 0x0f) << 8)); + vso = ((buf[10] >> 4) + ((buf[11] & 0x0c) << 2)); + vspw = ((buf[10] & 0x0f) + ((buf[11] & 0x03) << 4)); + vborder = buf[16]; + + set_entry(&timing->hactive, ha); + set_entry(&timing->hfront_porch, hso); + set_entry(&timing->hback_porch, hbl - hso - hspw); + set_entry(&timing->hsync_len, hspw); + + set_entry(&timing->vactive, va); + set_entry(&timing->vfront_porch, vso); + set_entry(&timing->vback_porch, vbl - vso - vspw); + set_entry(&timing->vsync_len, vspw); + + timing->flags = 0; + if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t)) + timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH; + else + timing->flags |= DISPLAY_FLAGS_HSYNC_LOW; + if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t)) + timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH; + else + timing->flags |= DISPLAY_FLAGS_VSYNC_LOW; + + if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t)) + timing->flags = DISPLAY_FLAGS_INTERLACED; + + debug("Detailed mode clock %u Hz, %d mm x %d mm\n" + " %04x %04x %04x %04x hborder %x\n" + " %04x %04x %04x %04x vborder %x\n", + timing->pixelclock.typ, + x_mm, y_mm, + ha, ha + hso, ha + hso + hspw, + ha + hbl, hborder, + va, va + vso, va + vso + vspw, + va + vbl, vborder); +} + +/** + * Check if HDMI vendor specific data block is present in CEA block + * @param info CEA extension block + * @return true if block is found + */ +static bool cea_is_hdmi_vsdb_present(struct edid_cea861_info *info) +{ + u8 end, i = 0; + + /* check for end of data block */ + end = info->dtd_offset; + if (end == 0) + end = sizeof(info->data); + if (end < 4 || end > sizeof(info->data)) + return false; + end -= 4; + + while (i < end) { + /* Look for vendor specific data block of appropriate size */ + if ((EDID_CEA861_DB_TYPE(*info, i) == EDID_CEA861_DB_VENDOR) && + (EDID_CEA861_DB_LEN(*info, i) >= 5)) { + u8 *db = &info->data[i + 1]; + u32 oui = db[0] | (db[1] << 8) | (db[2] << 16); + + if (oui == HDMI_IEEE_OUI) + return true; + } + i += EDID_CEA861_DB_LEN(*info, i) + 1; + } + + return false; +} + +static bool edid_find_valid_timing(void *buf, int count, + struct display_timing *timing, + bool (*mode_valid)(void *priv, + const struct display_timing *timing), + void *mode_valid_priv) +{ + struct edid_detailed_timing *t = buf; + bool found = false; + int i; + + for (i = 0; i < count && !found; i++, t++) + if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) != 0) { + decode_timing((u8 *)t, timing); + if (mode_valid) + found = mode_valid(mode_valid_priv, + timing); + else + found = true; + } + + return found; +} + +int edid_get_timing_validate(u8 *buf, int buf_size, + struct display_timing *timing, + int *panel_bits_per_colourp, + bool (*mode_valid)(void *priv, + const struct display_timing *timing), + void *mode_valid_priv) +{ + struct edid1_info *edid = (struct edid1_info *)buf; + bool found; + + if (buf_size < sizeof(*edid) || edid_check_info(edid)) { + debug("%s: Invalid buffer\n", __func__); + return -EINVAL; + } + + if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) { + debug("%s: Not a digital display\n", __func__); + return -ENOSYS; + } + + if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) { + debug("%s: No preferred timing\n", __func__); + return -ENOENT; + } + + /* Look for detailed timing in base EDID */ + found = edid_find_valid_timing(edid->monitor_details.descriptor, 4, + timing, mode_valid, mode_valid_priv); + + /* Look for detailed timing in CTA-861 Extension Block */ + if (!found && edid->extension_flag && buf_size >= EDID_EXT_SIZE) { + struct edid_cea861_info *info = + (struct edid_cea861_info *)(buf + sizeof(*edid)); + + if (info->extension_tag == EDID_CEA861_EXTENSION_TAG) { + int count = EDID_CEA861_DTD_COUNT(*info); + int offset = info->dtd_offset; + int size = count * sizeof(struct edid_detailed_timing); + + if (offset >= 4 && offset + size < EDID_SIZE) + found = edid_find_valid_timing( + (u8 *)info + offset, count, timing, + mode_valid, mode_valid_priv); + } + } + + if (!found) + return -EINVAL; + + if (edid->version != 1 || edid->revision < 4) { + debug("%s: EDID version %d.%d does not have required info\n", + __func__, edid->version, edid->revision); + *panel_bits_per_colourp = -1; + } else { + *panel_bits_per_colourp = + ((edid->video_input_definition & 0x70) >> 3) + 4; + } + + timing->hdmi_monitor = false; + if (edid->extension_flag && (buf_size >= EDID_EXT_SIZE)) { + struct edid_cea861_info *info = + (struct edid_cea861_info *)(buf + sizeof(*edid)); + + if (info->extension_tag == EDID_CEA861_EXTENSION_TAG) + timing->hdmi_monitor = cea_is_hdmi_vsdb_present(info); + } + + return 0; +} + +int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing, + int *panel_bits_per_colourp) +{ + return edid_get_timing_validate(buf, buf_size, timing, + panel_bits_per_colourp, NULL, NULL); +} + + +/** + * Snip the tailing whitespace/return of a string. + * + * @param string The string to be snipped + * @return the snipped string + */ +static char *snip(char *string) +{ + char *s; + + /* + * This is always a 13 character buffer + * and it's not always terminated. + */ + string[12] = '\0'; + s = &string[strlen(string) - 1]; + + while (s >= string && (isspace(*s) || *s == '\n' || *s == '\r' || + *s == '\0')) + *(s--) = '\0'; + + return string; +} + +/** + * Print an EDID monitor descriptor block + * + * @param monitor The EDID monitor descriptor block + * @have_timing Modifies to 1 if the desciptor contains timing info + */ +static void edid_print_dtd(struct edid_monitor_descriptor *monitor, + unsigned int *have_timing) +{ + unsigned char *bytes = (unsigned char *)monitor; + struct edid_detailed_timing *timing = + (struct edid_detailed_timing *)monitor; + + if (bytes[0] == 0 && bytes[1] == 0) { + if (monitor->type == EDID_MONITOR_DESCRIPTOR_SERIAL) + printf("Monitor serial number: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_ASCII) + printf("Monitor ID: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_NAME) + printf("Monitor name: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE) + printf("Monitor range limits, horizontal sync: " + "%d-%d kHz, vertical refresh: " + "%d-%d Hz, max pixel clock: " + "%d MHz\n", + monitor->data.range_data.horizontal_min, + monitor->data.range_data.horizontal_max, + monitor->data.range_data.vertical_min, + monitor->data.range_data.vertical_max, + monitor->data.range_data.pixel_clock_max * 10); + } else { + uint32_t pixclock, h_active, h_blanking, v_active, v_blanking; + uint32_t h_total, v_total, vfreq; + + pixclock = EDID_DETAILED_TIMING_PIXEL_CLOCK(*timing); + h_active = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*timing); + h_blanking = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*timing); + v_active = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*timing); + v_blanking = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*timing); + + h_total = h_active + h_blanking; + v_total = v_active + v_blanking; + if (v_total > 0 && h_total > 0) + vfreq = pixclock / (v_total * h_total); + else + vfreq = 1; /* Error case */ + printf("\t%dx%d\%c\t%d Hz (detailed)\n", h_active, + v_active, h_active > 1000 ? ' ' : '\t', vfreq); + *have_timing = 1; + } +} + +/** + * Get the manufacturer name from an EDID info. + * + * @param edid_info The EDID info to be printed + * @param name Returns the string of the manufacturer name + */ +static void edid_get_manufacturer_name(struct edid1_info *edid, char *name) +{ + name[0] = EDID1_INFO_MANUFACTURER_NAME_CHAR1(*edid) + 'A' - 1; + name[1] = EDID1_INFO_MANUFACTURER_NAME_CHAR2(*edid) + 'A' - 1; + name[2] = EDID1_INFO_MANUFACTURER_NAME_CHAR3(*edid) + 'A' - 1; + name[3] = '\0'; +} + +void edid_print_info(struct edid1_info *edid_info) +{ + int i; + char manufacturer[4]; + unsigned int have_timing = 0; + uint32_t serial_number; + + if (edid_check_info(edid_info)) { + printf("Not a valid EDID\n"); + return; + } + + printf("EDID version: %d.%d\n", + edid_info->version, edid_info->revision); + + printf("Product ID code: %04x\n", EDID1_INFO_PRODUCT_CODE(*edid_info)); + + edid_get_manufacturer_name(edid_info, manufacturer); + printf("Manufacturer: %s\n", manufacturer); + + serial_number = EDID1_INFO_SERIAL_NUMBER(*edid_info); + if (serial_number != 0xffffffff) { + if (strcmp(manufacturer, "MAG") == 0) + serial_number -= 0x7000000; + if (strcmp(manufacturer, "OQI") == 0) + serial_number -= 456150000; + if (strcmp(manufacturer, "VSC") == 0) + serial_number -= 640000000; + } + printf("Serial number: %08x\n", serial_number); + printf("Manufactured in week: %d year: %d\n", + edid_info->week, edid_info->year + 1990); + + printf("Video input definition: %svoltage level %d%s%s%s%s%s\n", + EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid_info) ? + "digital signal, " : "analog signal, ", + EDID1_INFO_VIDEO_INPUT_VOLTAGE_LEVEL(*edid_info), + EDID1_INFO_VIDEO_INPUT_BLANK_TO_BLACK(*edid_info) ? + ", blank to black" : "", + EDID1_INFO_VIDEO_INPUT_SEPARATE_SYNC(*edid_info) ? + ", separate sync" : "", + EDID1_INFO_VIDEO_INPUT_COMPOSITE_SYNC(*edid_info) ? + ", composite sync" : "", + EDID1_INFO_VIDEO_INPUT_SYNC_ON_GREEN(*edid_info) ? + ", sync on green" : "", + EDID1_INFO_VIDEO_INPUT_SERRATION_V(*edid_info) ? + ", serration v" : ""); + + printf("Monitor is %s\n", + EDID1_INFO_FEATURE_RGB(*edid_info) ? "RGB" : "non-RGB"); + + printf("Maximum visible display size: %d cm x %d cm\n", + edid_info->max_size_horizontal, + edid_info->max_size_vertical); + + printf("Power management features: %s%s, %s%s, %s%s\n", + EDID1_INFO_FEATURE_ACTIVE_OFF(*edid_info) ? + "" : "no ", "active off", + EDID1_INFO_FEATURE_SUSPEND(*edid_info) ? "" : "no ", "suspend", + EDID1_INFO_FEATURE_STANDBY(*edid_info) ? "" : "no ", "standby"); + + printf("Estabilished timings:\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_720X400_70(*edid_info)) + printf("\t720x400\t\t70 Hz (VGA 640x400, IBM)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_720X400_88(*edid_info)) + printf("\t720x400\t\t88 Hz (XGA2)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_60(*edid_info)) + printf("\t640x480\t\t60 Hz (VGA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_67(*edid_info)) + printf("\t640x480\t\t67 Hz (Mac II, Apple)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_72(*edid_info)) + printf("\t640x480\t\t72 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_75(*edid_info)) + printf("\t640x480\t\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_56(*edid_info)) + printf("\t800x600\t\t56 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_60(*edid_info)) + printf("\t800x600\t\t60 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_72(*edid_info)) + printf("\t800x600\t\t72 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_75(*edid_info)) + printf("\t800x600\t\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_832X624_75(*edid_info)) + printf("\t832x624\t\t75 Hz (Mac II)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_87I(*edid_info)) + printf("\t1024x768\t87 Hz Interlaced (8514A)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_60(*edid_info)) + printf("\t1024x768\t60 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_70(*edid_info)) + printf("\t1024x768\t70 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_75(*edid_info)) + printf("\t1024x768\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1280X1024_75(*edid_info)) + printf("\t1280x1024\t75 (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1152X870_75(*edid_info)) + printf("\t1152x870\t75 (Mac II)\n"); + + /* Standard timings. */ + printf("Standard timings:\n"); + for (i = 0; i < ARRAY_SIZE(edid_info->standard_timings); i++) { + unsigned int aspect = 10000; + unsigned int x, y; + unsigned char xres, vfreq; + + xres = EDID1_INFO_STANDARD_TIMING_XRESOLUTION(*edid_info, i); + vfreq = EDID1_INFO_STANDARD_TIMING_VFREQ(*edid_info, i); + if ((xres != vfreq) || + ((xres != 0) && (xres != 1)) || + ((vfreq != 0) && (vfreq != 1))) { + switch (EDID1_INFO_STANDARD_TIMING_ASPECT(*edid_info, + i)) { + case ASPECT_625: + aspect = 6250; + break; + case ASPECT_75: + aspect = 7500; + break; + case ASPECT_8: + aspect = 8000; + break; + case ASPECT_5625: + aspect = 5625; + break; + } + x = (xres + 31) * 8; + y = x * aspect / 10000; + printf("\t%dx%d%c\t%d Hz\n", x, y, + x > 1000 ? ' ' : '\t', (vfreq & 0x3f) + 60); + have_timing = 1; + } + } + + /* Detailed timing information. */ + for (i = 0; i < ARRAY_SIZE(edid_info->monitor_details.descriptor); + i++) { + edid_print_dtd(&edid_info->monitor_details.descriptor[i], + &have_timing); + } + + if (!have_timing) + printf("\tNone\n"); +} diff --git a/roms/u-boot/common/eeprom/eeprom_field.c b/roms/u-boot/common/eeprom/eeprom_field.c new file mode 100644 index 000000000..f56eebe67 --- /dev/null +++ b/roms/u-boot/common/eeprom/eeprom_field.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009-2016 CompuLab, Ltd. + * + * Authors: Nikita Kiryanov <nikita@compulab.co.il> + * Igor Grinberg <grinberg@compulab.co.il> + */ + +#include <common.h> +#include <linux/string.h> +#include <eeprom_field.h> + +static void __eeprom_field_print_bin(const struct eeprom_field *field, + char *delimiter, bool reverse) +{ + int i; + int from = reverse ? field->size - 1 : 0; + int to = reverse ? 0 : field->size - 1; + + printf(PRINT_FIELD_SEGMENT, field->name); + for (i = from; i != to; reverse ? i-- : i++) + printf("%02x%s", field->buf[i], delimiter); + + printf("%02x\n", field->buf[i]); +} + +static int __eeprom_field_update_bin(struct eeprom_field *field, + const char *value, bool reverse) +{ + int len = strlen(value); + int k, j, i = reverse ? len - 1 : 0; + unsigned char byte; + char *endptr; + + /* each two characters in the string fit in one byte */ + if (len > field->size * 2) + return -1; + + memset(field->buf, 0, field->size); + + /* i - string iterator, j - buf iterator */ + for (j = 0; j < field->size; j++) { + byte = 0; + char tmp[3] = { 0, 0, 0 }; + + if ((reverse && i < 0) || (!reverse && i >= len)) + break; + + for (k = 0; k < 2; k++) { + if (reverse && i == 0) { + tmp[k] = value[i]; + break; + } + + tmp[k] = value[reverse ? i - 1 + k : i + k]; + } + + byte = simple_strtoul(tmp, &endptr, 0); + if (*endptr != '\0' || byte < 0) + return -1; + + field->buf[j] = byte; + i = reverse ? i - 2 : i + 2; + } + + return 0; +} + +static int __eeprom_field_update_bin_delim(struct eeprom_field *field, + char *value, char *delimiter) +{ + int count = 0; + int i, val; + const char *tmp = value; + char *tok; + char *endptr; + + tmp = strstr(tmp, delimiter); + while (tmp != NULL) { + count++; + tmp++; + tmp = strstr(tmp, delimiter); + } + + if (count > field->size) + return -1; + + tok = strtok(value, delimiter); + for (i = 0; tok && i < field->size; i++) { + val = simple_strtoul(tok, &endptr, 0); + if (*endptr != '\0') + return -1; + + /* here we assume that each tok is no more than byte long */ + field->buf[i] = (unsigned char)val; + tok = strtok(NULL, delimiter); + } + + return 0; +} + +/** + * eeprom_field_print_bin() - print a field which contains binary data + * + * Treat the field data as simple binary data, and print it as two digit + * hexadecimal values. + * Sample output: + * Field Name 0102030405060708090a + * + * @field: an initialized field to print + */ +void eeprom_field_print_bin(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, "", false); +} + +/** + * eeprom_field_update_bin() - Update field with new data in binary form + * + * @field: an initialized field + * @value: a string of values (i.e. "10b234a") + */ +int eeprom_field_update_bin(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin(field, value, false); +} + +/** + * eeprom_field_update_reserved() - Update reserved field with new data in + * binary form + * + * @field: an initialized field + * @value: a space delimited string of byte values (i.e. "1 02 3 0x4") + */ +int eeprom_field_update_reserved(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin_delim(field, value, " "); +} + +/** + * eeprom_field_print_bin_rev() - print a field which contains binary data in + * reverse order + * + * Treat the field data as simple binary data, and print it in reverse order + * as two digit hexadecimal values. + * + * Data in field: + * 0102030405060708090a + * Sample output: + * Field Name 0a090807060504030201 + * + * @field: an initialized field to print + */ +void eeprom_field_print_bin_rev(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, "", true); +} + +/** + * eeprom_field_update_bin_rev() - Update field with new data in binary form, + * storing it in reverse + * + * This function takes a string of byte values, and stores them + * in the field in the reverse order. i.e. if the input string was "1234", + * "3412" will be written to the field. + * + * @field: an initialized field + * @value: a string of byte values + */ +int eeprom_field_update_bin_rev(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin(field, value, true); +} + +/** + * eeprom_field_print_mac_addr() - print a field which contains a mac address + * + * Treat the field data as simple binary data, and print it formatted as a MAC + * address. + * Sample output: + * Field Name 01:02:03:04:05:06 + * + * @field: an initialized field to print + */ +void eeprom_field_print_mac(const struct eeprom_field *field) +{ + __eeprom_field_print_bin(field, ":", false); +} + +/** + * eeprom_field_update_mac() - Update a mac address field which contains binary + * data + * + * @field: an initialized field + * @value: a colon delimited string of byte values (i.e. "1:02:3:ff") + */ +int eeprom_field_update_mac(struct eeprom_field *field, char *value) +{ + return __eeprom_field_update_bin_delim(field, value, ":"); +} + +/** + * eeprom_field_print_ascii() - print a field which contains ASCII data + * @field: an initialized field to print + */ +void eeprom_field_print_ascii(const struct eeprom_field *field) +{ + char format[8]; + + sprintf(format, "%%.%ds\n", field->size); + printf(PRINT_FIELD_SEGMENT, field->name); + printf(format, field->buf); +} + +/** + * eeprom_field_update_ascii() - Update field with new data in ASCII form + * @field: an initialized field + * @value: the new string data + * + * Returns 0 on success, -1 of failure (new string too long). + */ +int eeprom_field_update_ascii(struct eeprom_field *field, char *value) +{ + if (strlen(value) >= field->size) { + printf("%s: new data too long\n", field->name); + return -1; + } + + strncpy((char *)field->buf, value, field->size - 1); + field->buf[field->size - 1] = '\0'; + + return 0; +} + +/** + * eeprom_field_print_reserved() - print the "Reserved fields" field + * + * Print a notice that the following field_size bytes are reserved. + * + * Sample output: + * Reserved fields (64 bytes) + * + * @field: an initialized field to print + */ +void eeprom_field_print_reserved(const struct eeprom_field *field) +{ + printf(PRINT_FIELD_SEGMENT, "Reserved fields\t"); + printf("(%d bytes)\n", field->size); +} diff --git a/roms/u-boot/common/eeprom/eeprom_layout.c b/roms/u-boot/common/eeprom/eeprom_layout.c new file mode 100644 index 000000000..5a9be1da0 --- /dev/null +++ b/roms/u-boot/common/eeprom/eeprom_layout.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009-2016 CompuLab, Ltd. + * + * Authors: Nikita Kiryanov <nikita@compulab.co.il> + * Igor Grinberg <grinberg@compulab.co.il> + */ + +#include <common.h> +#include <linux/kernel.h> +#include <eeprom_layout.h> +#include <eeprom_field.h> + +#define NO_LAYOUT_FIELDS "Unknown layout. Dumping raw data\n" + +struct eeprom_field layout_unknown[1] = { + { NO_LAYOUT_FIELDS, 256, NULL, eeprom_field_print_bin, + eeprom_field_update_bin }, +}; + +/* + * eeprom_layout_detect() - detect layout based on the contents of the data. + * @data: Pointer to the data to be analyzed. + * + * Returns: the detected layout version. + */ +__weak int eeprom_layout_detect(unsigned char *data) +{ + return LAYOUT_VERSION_UNRECOGNIZED; +} + +/* + * __eeprom_layout_assign() - set the layout fields + * @layout: A pointer to an existing struct layout. + * @layout_version: The version number of the desired layout + */ +__weak void __eeprom_layout_assign(struct eeprom_layout *layout, + int layout_version) +{ + layout->fields = layout_unknown; + layout->num_of_fields = ARRAY_SIZE(layout_unknown); +} +void eeprom_layout_assign(struct eeprom_layout *layout, int layout_version) \ + __attribute__((weak, alias("__eeprom_layout_assign"))); + +/* + * eeprom_layout_print() - print the layout and the data which is assigned to it + * @layout: A pointer to an existing struct layout. + */ +static void eeprom_layout_print(const struct eeprom_layout *layout) +{ + int i; + struct eeprom_field *fields = layout->fields; + + for (i = 0; i < layout->num_of_fields; i++) + fields[i].print(&fields[i]); +} + +/* + * eeprom_layout_update_field() - update a single field in the layout data. + * @layout: A pointer to an existing struct layout. + * @field_name: The name of the field to update. + * @new_data: The new field data (a string. Format depends on the field) + * + * Returns: 0 on success, negative error value on failure. + */ +static int eeprom_layout_update_field(struct eeprom_layout *layout, + char *field_name, char *new_data) +{ + int i, err; + struct eeprom_field *fields = layout->fields; + + if (new_data == NULL) + return 0; + + if (field_name == NULL) + return -1; + + for (i = 0; i < layout->num_of_fields; i++) { + if (fields[i].name == RESERVED_FIELDS || + strcmp(fields[i].name, field_name)) + continue; + + err = fields[i].update(&fields[i], new_data); + if (err) + printf("Invalid data for field %s\n", field_name); + + return err; + } + + printf("No such field '%s'\n", field_name); + + return -1; +} + +/* + * eeprom_layout_setup() - setup layout struct with the layout data and + * metadata as dictated by layout_version + * @layout: A pointer to an existing struct layout. + * @buf: A buffer initialized with the eeprom data. + * @buf_size: Size of buf in bytes. + * @layout version: The version number of the layout. + */ +void eeprom_layout_setup(struct eeprom_layout *layout, unsigned char *buf, + unsigned int buf_size, int layout_version) +{ + int i; + + if (layout_version == LAYOUT_VERSION_AUTODETECT) + layout->layout_version = eeprom_layout_detect(buf); + else + layout->layout_version = layout_version; + + eeprom_layout_assign(layout, layout_version); + layout->data = buf; + for (i = 0; i < layout->num_of_fields; i++) { + layout->fields[i].buf = buf; + buf += layout->fields[i].size; + } + + layout->data_size = buf_size; + layout->print = eeprom_layout_print; + layout->update = eeprom_layout_update_field; +} diff --git a/roms/u-boot/common/exports.c b/roms/u-boot/common/exports.c new file mode 100644 index 000000000..20d8b759b --- /dev/null +++ b/roms/u-boot/common/exports.c @@ -0,0 +1,35 @@ +#include <common.h> +#include <command.h> +#include <exports.h> +#include <malloc.h> +#include <spi.h> +#include <i2c.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +__attribute__((unused)) static void dummy(void) +{ +} + +unsigned long get_version(void) +{ + return XF_VERSION; +} + +#define EXPORT_FUNC(f, a, x, ...) gd->jt->x = f; + +#ifndef CONFIG_PHY_AQUANTIA +# define mdio_get_current_dev dummy +# define phy_find_by_mask dummy +# define mdio_phydev_for_ethname dummy +# define miiphy_set_current_dev dummy +#endif + +int jumptable_init(void) +{ + gd->jt = malloc(sizeof(struct jt_funcs)); +#include <_exports.h> + + return 0; +} diff --git a/roms/u-boot/common/fdt_region.c b/roms/u-boot/common/fdt_region.c new file mode 100644 index 000000000..e4ef0ca77 --- /dev/null +++ b/roms/u-boot/common/fdt_region.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2013 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <fdt_support.h> +#include <linux/libfdt_env.h> +#include <fdt_region.h> + +#ifndef USE_HOSTCC +#include <fdt.h> +#include <linux/libfdt.h> +#else +#include "fdt_host.h" +#endif + +#define FDT_MAX_DEPTH 32 + +static int str_in_list(const char *str, char * const list[], int count) +{ + int i; + + for (i = 0; i < count; i++) + if (!strcmp(list[i], str)) + return 1; + + return 0; +} + +int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + char * const exc_prop[], int exc_prop_count, + struct fdt_region region[], int max_regions, + char *path, int path_len, int add_string_tab) +{ + int stack[FDT_MAX_DEPTH] = { 0 }; + char *end; + int nextoffset = 0; + uint32_t tag; + int count = 0; + int start = -1; + int depth = -1; + int want = 0; + int base = fdt_off_dt_struct(fdt); + bool expect_end = false; + + end = path; + *end = '\0'; + do { + const struct fdt_property *prop; + const char *name; + const char *str; + int include = 0; + int stop_at = 0; + int offset; + int len; + + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + stop_at = nextoffset; + + /* If we see two root nodes, something is wrong */ + if (expect_end && tag != FDT_END) + return -FDT_ERR_BADLAYOUT; + + switch (tag) { + case FDT_PROP: + include = want >= 2; + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + if (!str) + return -FDT_ERR_BADSTRUCTURE; + if (str_in_list(str, exc_prop, exc_prop_count)) + include = 0; + break; + + case FDT_NOP: + include = want >= 2; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + depth++; + if (depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); + + /* The root node must have an empty name */ + if (!depth && *name) + return -FDT_ERR_BADLAYOUT; + if (end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + if (end != path + 1) + *end++ = '/'; + strcpy(end, name); + end += len; + stack[depth] = want; + if (want == 1) + stop_at = offset; + if (str_in_list(path, inc, inc_count)) + want = 2; + else if (want) + want--; + else + stop_at = offset; + include = want; + break; + + case FDT_END_NODE: + /* Depth must never go below -1 */ + if (depth < 0) + return -FDT_ERR_BADSTRUCTURE; + include = want; + want = stack[depth--]; + while (end > path && *--end != '/') + ; + *end = '\0'; + if (depth == -1) + expect_end = true; + break; + + case FDT_END: + include = 1; + break; + } + + if (include && start == -1) { + /* Should we merge with previous? */ + if (count && count <= max_regions && + offset == region[count - 1].offset + + region[count - 1].size - base) + start = region[--count].offset - base; + else + start = offset; + } + + if (!include && start != -1) { + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = stop_at - start; + } + count++; + start = -1; + } + } while (tag != FDT_END); + + if (nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADLAYOUT; + + /* Add a region for the END tag and the string table */ + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = nextoffset - start; + if (add_string_tab) + region[count].size += fdt_size_dt_strings(fdt); + } + count++; + + return count; +} + +/** + * fdt_add_region() - Add a new region to our list + * @info: State information + * @offset: Start offset of region + * @size: Size of region + * + * The region is added if there is space, but in any case we increment the + * count. If permitted, and the new region overlaps the last one, we merge + * them. + */ +static int fdt_add_region(struct fdt_region_state *info, int offset, int size) +{ + struct fdt_region *reg; + + reg = info->region ? &info->region[info->count - 1] : NULL; + if (info->can_merge && info->count && + info->count <= info->max_regions && + reg && offset <= reg->offset + reg->size) { + reg->size = offset + size - reg->offset; + } else if (info->count++ < info->max_regions) { + if (reg) { + reg++; + reg->offset = offset; + reg->size = size; + } + } else { + return -1; + } + + return 0; +} + +static int region_list_contains_offset(struct fdt_region_state *info, + const void *fdt, int target) +{ + struct fdt_region *reg; + int num; + + target += fdt_off_dt_struct(fdt); + for (reg = info->region, num = 0; num < info->count; reg++, num++) { + if (target >= reg->offset && target < reg->offset + reg->size) + return 1; + } + + return 0; +} + +/** + * fdt_add_alias_regions() - Add regions covering the aliases that we want + * + * The /aliases node is not automatically included by fdtgrep unless the + * command-line arguments cause to be included (or not excluded). However + * aliases are special in that we generally want to include those which + * reference a node that fdtgrep includes. + * + * In fact we want to include only aliases for those nodes still included in + * the fdt, and drop the other aliases since they point to nodes that will not + * be present. + * + * This function scans the aliases and adds regions for those which we want + * to keep. + * + * @fdt: Device tree to scan + * @region: List of regions + * @count: Number of regions in the list so far (i.e. starting point for this + * function) + * @max_regions: Maximum number of regions in @region list + * @info: Place to put the region state + * @return number of regions after processing, or -FDT_ERR_NOSPACE if we did + * not have enough room in the regions table for the regions we wanted to add. + */ +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int node, node_end, offset; + int did_alias_header; + + node = fdt_subnode_offset(fdt, 0, "aliases"); + if (node < 0) + return -FDT_ERR_NOTFOUND; + + /* + * Find the next node so that we know where the /aliases node ends. We + * need special handling if /aliases is the last node. + */ + node_end = fdt_next_subnode(fdt, node); + if (node_end == -FDT_ERR_NOTFOUND) + /* Move back to the FDT_END_NODE tag of '/' */ + node_end = fdt_size_dt_struct(fdt) - sizeof(fdt32_t) * 2; + else if (node_end < 0) /* other error */ + return node_end; + node_end -= sizeof(fdt32_t); /* Move to FDT_END_NODE tag of /aliases */ + + did_alias_header = 0; + info->region = region; + info->count = count; + info->can_merge = 0; + info->max_regions = max_regions; + + for (offset = fdt_first_property_offset(fdt, node); + offset >= 0; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *name; + int target, next; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + target = fdt_path_offset(fdt, name); + if (!region_list_contains_offset(info, fdt, target)) + continue; + next = fdt_next_property_offset(fdt, offset); + if (next < 0) + next = node_end; + + if (!did_alias_header) { + fdt_add_region(info, base + node, 12); + did_alias_header = 1; + } + fdt_add_region(info, base + offset, next - offset); + } + + /* Add the FDT_END_NODE tag */ + if (did_alias_header) + fdt_add_region(info, base + node_end, sizeof(fdt32_t)); + + return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE; +} + +/** + * fdt_include_supernodes() - Include supernodes required by this node + * @info: State information + * @depth: Current stack depth + * + * When we decided to include a node or property which is not at the top + * level, this function forces the inclusion of higher level nodes. For + * example, given this tree: + * + * / { + * testing { + * } + * } + * + * If we decide to include testing then we need the root node to have a valid + * tree. This function adds those regions. + */ +static int fdt_include_supernodes(struct fdt_region_state *info, int depth) +{ + int base = fdt_off_dt_struct(info->fdt); + int start, stop_at; + int i; + + /* + * Work down the stack looking for supernodes that we didn't include. + * The algortihm here is actually pretty simple, since we know that + * no previous subnode had to include these nodes, or if it did, we + * marked them as included (on the stack) already. + */ + for (i = 0; i <= depth; i++) { + if (!info->stack[i].included) { + start = info->stack[i].offset; + + /* Add the FDT_BEGIN_NODE tag of this supernode */ + fdt_next_tag(info->fdt, start, &stop_at); + if (fdt_add_region(info, base + start, stop_at - start)) + return -1; + + /* Remember that this supernode is now included */ + info->stack[i].included = 1; + info->can_merge = 1; + } + + /* Force (later) generation of the FDT_END_NODE tag */ + if (!info->stack[i].want) + info->stack[i].want = WANT_NODES_ONLY; + } + + return 0; +} + +enum { + FDT_DONE_NOTHING, + FDT_DONE_MEM_RSVMAP, + FDT_DONE_STRUCT, + FDT_DONE_END, + FDT_DONE_STRINGS, + FDT_DONE_ALL, +}; + +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + struct fdt_region_ptrs *p = &info->ptrs; + + /* Set up our state */ + info->fdt = fdt; + info->can_merge = 1; + info->max_regions = 1; + info->start = -1; + p->want = WANT_NOTHING; + p->end = path; + *p->end = '\0'; + p->nextoffset = 0; + p->depth = -1; + p->done = FDT_DONE_NOTHING; + + return fdt_next_region(fdt, h_include, priv, region, + path, path_len, flags, info); +} + +/*********************************************************************** + * + * Theory of operation + * + * Note: in this description 'included' means that a node (or other part + * of the tree) should be included in the region list, i.e. it will have + * a region which covers its part of the tree. + * + * This function maintains some state from the last time it is called. + * It checks the next part of the tree that it is supposed to look at + * (p.nextoffset) to see if that should be included or not. When it + * finds something to include, it sets info->start to its offset. This + * marks the start of the region we want to include. + * + * Once info->start is set to the start (i.e. not -1), we continue + * scanning until we find something that we don't want included. This + * will be the end of a region. At this point we can close off the + * region and add it to the list. So we do so, and reset info->start + * to -1. + * + * One complication here is that we want to merge regions. So when we + * come to add another region later, we may in fact merge it with the + * previous one if one ends where the other starts. + * + * The function fdt_add_region() will return -1 if it fails to add the + * region, because we already have a region ready to be returned, and + * the new one cannot be merged in with it. In this case, we must return + * the region we found, and wait for another call to this function. + * When it comes, we will repeat the processing of the tag and again + * try to add a region. This time it will succeed. + * + * The current state of the pointers (stack, offset, etc.) is maintained + * in a ptrs member. At the start of every loop iteration we make a copy + * of it. The copy is then updated as the tag is processed. Only if we + * get to the end of the loop iteration (and successfully call + * fdt_add_region() if we need to) can we commit the changes we have + * made to these pointers. For example, if we see an FDT_END_NODE tag, + * we will decrement the depth value. But if we need to add a region + * for this tag (let's say because the previous tag is included and this + * FDT_END_NODE tag is not included) then we will only commit the result + * if we were able to add the region. That allows us to retry again next + * time. + * + * We keep track of a variable called 'want' which tells us what we want + * to include when there is no specific information provided by the + * h_include function for a particular property. This basically handles + * the inclusion of properties which are pulled in by virtue of the node + * they are in. So if you include a node, its properties are also + * included. In this case 'want' will be WANT_NODES_AND_PROPS. The + * FDT_REG_DIRECT_SUBNODES feature also makes use of 'want'. While we + * are inside the subnode, 'want' will be set to WANT_NODES_ONLY, so + * that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE tags will be + * included, and properties will be skipped. If WANT_NOTHING is + * selected, then we will just rely on what the h_include() function + * tells us. + * + * Using 'want' we work out 'include', which tells us whether this + * current tag should be included or not. As you can imagine, if the + * value of 'include' changes, that means we are on a boundary between + * nodes to include and nodes to exclude. At this point we either close + * off a previous region and add it to the list, or mark the start of a + * new region. + * + * Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the + * string list. Each of these dealt with as a whole (i.e. we create a + * region for each if it is to be included). For mem_rsvmap we don't + * allow it to merge with the first struct region. For the stringlist, + * we don't allow it to merge with the last struct region (which + * contains at minimum the FDT_END tag). + * + *********************************************************************/ + +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int last_node = 0; + const char *str; + + info->region = region; + info->count = 0; + if (info->ptrs.done < FDT_DONE_MEM_RSVMAP && + (flags & FDT_REG_ADD_MEM_RSVMAP)) { + /* Add the memory reserve map into its own region */ + if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt), + fdt_off_dt_struct(fdt) - + fdt_off_mem_rsvmap(fdt))) + return 0; + info->can_merge = 0; /* Don't allow merging with this */ + info->ptrs.done = FDT_DONE_MEM_RSVMAP; + } + + /* + * Work through the tags one by one, deciding whether each needs to + * be included or not. We set the variable 'include' to indicate our + * decision. 'want' is used to track what we want to include - it + * allows us to pick up all the properties (and/or subnode tags) of + * a node. + */ + while (info->ptrs.done < FDT_DONE_STRUCT) { + const struct fdt_property *prop; + struct fdt_region_ptrs p; + const char *name; + int include = 0; + int stop_at = 0; + uint32_t tag; + int offset; + int val; + int len; + + /* + * Make a copy of our pointers. If we make it to the end of + * this block then we will commit them back to info->ptrs. + * Otherwise we can try again from the same starting state + * next time we are called. + */ + p = info->ptrs; + + /* + * Find the tag, and the offset of the next one. If we need to + * stop including tags, then by default we stop *after* + * including the current tag + */ + offset = p.nextoffset; + tag = fdt_next_tag(fdt, offset, &p.nextoffset); + stop_at = p.nextoffset; + + switch (tag) { + case FDT_PROP: + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + val = h_include(priv, fdt, last_node, FDT_IS_PROP, str, + strlen(str) + 1); + if (val == -1) { + include = p.want >= WANT_NODES_AND_PROPS; + } else { + include = val; + /* + * Make sure we include the } for this block. + * It might be more correct to have this done + * by the call to fdt_include_supernodes() in + * the case where it adds the node we are + * currently in, but this is equivalent. + */ + if ((flags & FDT_REG_SUPERNODES) && val && + !p.want) + p.want = WANT_NODES_ONLY; + } + + /* Value grepping is not yet supported */ + break; + + case FDT_NOP: + include = p.want >= WANT_NODES_AND_PROPS; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + last_node = offset; + p.depth++; + if (p.depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); + if (p.end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + + /* Build the full path of this node */ + if (p.end != path + 1) + *p.end++ = '/'; + strcpy(p.end, name); + p.end += len; + info->stack[p.depth].want = p.want; + info->stack[p.depth].offset = offset; + + /* + * If we are not intending to include this node unless + * it matches, make sure we stop *before* its tag. + */ + if (p.want == WANT_NODES_ONLY || + !(flags & (FDT_REG_DIRECT_SUBNODES | + FDT_REG_ALL_SUBNODES))) { + stop_at = offset; + p.want = WANT_NOTHING; + } + val = h_include(priv, fdt, offset, FDT_IS_NODE, path, + p.end - path + 1); + + /* Include this if requested */ + if (val) { + p.want = (flags & FDT_REG_ALL_SUBNODES) ? + WANT_ALL_NODES_AND_PROPS : + WANT_NODES_AND_PROPS; + } + + /* If not requested, decay our 'p.want' value */ + else if (p.want) { + if (p.want != WANT_ALL_NODES_AND_PROPS) + p.want--; + + /* Not including this tag, so stop now */ + } else { + stop_at = offset; + } + + /* + * Decide whether to include this tag, and update our + * stack with the state for this node + */ + include = p.want; + info->stack[p.depth].included = include; + break; + + case FDT_END_NODE: + include = p.want; + if (p.depth < 0) + return -FDT_ERR_BADSTRUCTURE; + + /* + * If we don't want this node, stop right away, unless + * we are including subnodes + */ + if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES)) + stop_at = offset; + p.want = info->stack[p.depth].want; + p.depth--; + while (p.end > path && *--p.end != '/') + ; + *p.end = '\0'; + break; + + case FDT_END: + /* We always include the end tag */ + include = 1; + p.done = FDT_DONE_STRUCT; + break; + } + + /* If this tag is to be included, mark it as region start */ + if (include && info->start == -1) { + /* Include any supernodes required by this one */ + if (flags & FDT_REG_SUPERNODES) { + if (fdt_include_supernodes(info, p.depth)) + return 0; + } + info->start = offset; + } + + /* + * If this tag is not to be included, finish up the current + * region. + */ + if (!include && info->start != -1) { + if (fdt_add_region(info, base + info->start, + stop_at - info->start)) + return 0; + info->start = -1; + info->can_merge = 1; + } + + /* If we have made it this far, we can commit our pointers */ + info->ptrs = p; + } + + /* Add a region for the END tag and a separate one for string table */ + if (info->ptrs.done < FDT_DONE_END) { + if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTRUCTURE; + + if (fdt_add_region(info, base + info->start, + info->ptrs.nextoffset - info->start)) + return 0; + info->ptrs.done++; + } + if (info->ptrs.done < FDT_DONE_STRINGS) { + if (flags & FDT_REG_ADD_STRING_TAB) { + info->can_merge = 0; + if (fdt_off_dt_strings(fdt) < + base + info->ptrs.nextoffset) + return -FDT_ERR_BADLAYOUT; + if (fdt_add_region(info, fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return 0; + } + info->ptrs.done++; + } + + return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND; +} diff --git a/roms/u-boot/common/fdt_support.c b/roms/u-boot/common/fdt_support.c new file mode 100644 index 000000000..a9a32df1e --- /dev/null +++ b/roms/u-boot/common/fdt_support.c @@ -0,0 +1,1952 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <env.h> +#include <log.h> +#include <mapmem.h> +#include <net.h> +#include <stdio_dev.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <fdt_support.h> +#include <exports.h> +#include <fdtdec.h> + +/** + * fdt_getprop_u32_default_node - Return a node's property or a default + * + * @fdt: ptr to device tree + * @off: offset of node + * @cell: cell offset in property + * @prop: property name + * @dflt: default value if the property isn't found + * + * Convenience function to return a node's property or a default value if + * the property doesn't exist. + */ +u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell, + const char *prop, const u32 dflt) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdt, off, prop, &len); + + /* Check if property exists */ + if (!val) + return dflt; + + /* Check if property is long enough */ + if (len < ((cell + 1) * sizeof(uint32_t))) + return dflt; + + return fdt32_to_cpu(*val); +} + +/** + * fdt_getprop_u32_default - Find a node and return it's property or a default + * + * @fdt: ptr to device tree + * @path: path of node + * @prop: property name + * @dflt: default value if the property isn't found + * + * Convenience function to find a node and return it's property or a + * default value if it doesn't exist. + */ +u32 fdt_getprop_u32_default(const void *fdt, const char *path, + const char *prop, const u32 dflt) +{ + int off; + + off = fdt_path_offset(fdt, path); + if (off < 0) + return dflt; + + return fdt_getprop_u32_default_node(fdt, off, 0, prop, dflt); +} + +/** + * fdt_find_and_setprop: Find a node and set it's property + * + * @fdt: ptr to device tree + * @node: path of node + * @prop: property name + * @val: ptr to new value + * @len: length of new property value + * @create: flag to create the property if it doesn't exist + * + * Convenience function to directly set a property given the path to the node. + */ +int fdt_find_and_setprop(void *fdt, const char *node, const char *prop, + const void *val, int len, int create) +{ + int nodeoff = fdt_path_offset(fdt, node); + + if (nodeoff < 0) + return nodeoff; + + if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL)) + return 0; /* create flag not set; so exit quietly */ + + return fdt_setprop(fdt, nodeoff, prop, val, len); +} + +/** + * fdt_find_or_add_subnode() - find or possibly add a subnode of a given node + * + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node with a given name. + * If the subnode does not exist, it will be created. + */ +int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name) +{ + int offset; + + offset = fdt_subnode_offset(fdt, parentoffset, name); + + if (offset == -FDT_ERR_NOTFOUND) + offset = fdt_add_subnode(fdt, parentoffset, name); + + if (offset < 0) + printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset)); + + return offset; +} + +#if defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) +static int fdt_fixup_stdout(void *fdt, int chosenoff) +{ + int err; + int aliasoff; + char sername[9] = { 0 }; + const void *path; + int len; + char tmp[256]; /* long enough */ + + sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); + + aliasoff = fdt_path_offset(fdt, "/aliases"); + if (aliasoff < 0) { + err = aliasoff; + goto noalias; + } + + path = fdt_getprop(fdt, aliasoff, sername, &len); + if (!path) { + err = len; + goto noalias; + } + + /* fdt_setprop may break "path" so we copy it to tmp buffer */ + memcpy(tmp, path, len); + + err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len); + if (err < 0) + printf("WARNING: could not set linux,stdout-path %s.\n", + fdt_strerror(err)); + + return err; + +noalias: + printf("WARNING: %s: could not read %s alias: %s\n", + __func__, sername, fdt_strerror(err)); + + return 0; +} +#else +static int fdt_fixup_stdout(void *fdt, int chosenoff) +{ + return 0; +} +#endif + +static inline int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name, + uint64_t val, int is_u64) +{ + if (is_u64) + return fdt_setprop_u64(fdt, nodeoffset, name, val); + else + return fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val); +} + +int fdt_root(void *fdt) +{ + char *serial; + int err; + + err = fdt_check_header(fdt); + if (err < 0) { + printf("fdt_root: %s\n", fdt_strerror(err)); + return err; + } + + serial = env_get("serial#"); + if (serial) { + err = fdt_setprop(fdt, 0, "serial-number", serial, + strlen(serial) + 1); + + if (err < 0) { + printf("WARNING: could not set serial-number %s.\n", + fdt_strerror(err)); + return err; + } + } + + return 0; +} + +int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) +{ + int nodeoffset; + int err, j, total; + int is_u64; + uint64_t addr, size; + + /* just return if the size of initrd is zero */ + if (initrd_start == initrd_end) + return 0; + + /* find or create "/chosen" node. */ + nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); + if (nodeoffset < 0) + return nodeoffset; + + total = fdt_num_mem_rsv(fdt); + + /* + * Look for an existing entry and update it. If we don't find + * the entry, we will j be the next available slot. + */ + for (j = 0; j < total; j++) { + err = fdt_get_mem_rsv(fdt, j, &addr, &size); + if (addr == initrd_start) { + fdt_del_mem_rsv(fdt, j); + break; + } + } + + err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); + if (err < 0) { + printf("fdt_initrd: %s\n", fdt_strerror(err)); + return err; + } + + is_u64 = (fdt_address_cells(fdt, 0) == 2); + + err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start", + (uint64_t)initrd_start, is_u64); + + if (err < 0) { + printf("WARNING: could not set linux,initrd-start %s.\n", + fdt_strerror(err)); + return err; + } + + err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end", + (uint64_t)initrd_end, is_u64); + + if (err < 0) { + printf("WARNING: could not set linux,initrd-end %s.\n", + fdt_strerror(err)); + + return err; + } + + return 0; +} + +/** + * board_fdt_chosen_bootargs - boards may override this function to use + * alternative kernel command line arguments + */ +__weak char *board_fdt_chosen_bootargs(void) +{ + return env_get("bootargs"); +} + +int fdt_chosen(void *fdt) +{ + int nodeoffset; + int err; + char *str; /* used to set string properties */ + + err = fdt_check_header(fdt); + if (err < 0) { + printf("fdt_chosen: %s\n", fdt_strerror(err)); + return err; + } + + /* find or create "/chosen" node. */ + nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); + if (nodeoffset < 0) + return nodeoffset; + + str = board_fdt_chosen_bootargs(); + + if (str) { + err = fdt_setprop(fdt, nodeoffset, "bootargs", str, + strlen(str) + 1); + if (err < 0) { + printf("WARNING: could not set bootargs %s.\n", + fdt_strerror(err)); + return err; + } + } + + return fdt_fixup_stdout(fdt, nodeoffset); +} + +void do_fixup_by_path(void *fdt, const char *path, const char *prop, + const void *val, int len, int create) +{ +#if defined(DEBUG) + int i; + debug("Updating property '%s/%s' = ", path, prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create); + if (rc) + printf("Unable to update property %s:%s, err=%s\n", + path, prop, fdt_strerror(rc)); +} + +void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop, + u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create); +} + +void do_fixup_by_prop(void *fdt, + const char *pname, const void *pval, int plen, + const char *prop, const void *val, int len, + int create) +{ + int off; +#if defined(DEBUG) + int i; + debug("Updating property '%s' = ", prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen); + while (off != -FDT_ERR_NOTFOUND) { + if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) + fdt_setprop(fdt, off, prop, val, len); + off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen); + } +} + +void do_fixup_by_prop_u32(void *fdt, + const char *pname, const void *pval, int plen, + const char *prop, u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_prop(fdt, pname, pval, plen, prop, &tmp, 4, create); +} + +void do_fixup_by_compat(void *fdt, const char *compat, + const char *prop, const void *val, int len, int create) +{ + int off = -1; +#if defined(DEBUG) + int i; + debug("Updating property '%s' = ", prop); + for (i = 0; i < len; i++) + debug(" %.2x", *(u8*)(val+i)); + debug("\n"); +#endif + off = fdt_node_offset_by_compatible(fdt, -1, compat); + while (off != -FDT_ERR_NOTFOUND) { + if (create || (fdt_get_property(fdt, off, prop, NULL) != NULL)) + fdt_setprop(fdt, off, prop, val, len); + off = fdt_node_offset_by_compatible(fdt, off, compat); + } +} + +void do_fixup_by_compat_u32(void *fdt, const char *compat, + const char *prop, u32 val, int create) +{ + fdt32_t tmp = cpu_to_fdt32(val); + do_fixup_by_compat(fdt, compat, prop, &tmp, 4, create); +} + +#ifdef CONFIG_ARCH_FIXUP_FDT_MEMORY +/* + * fdt_pack_reg - pack address and size array into the "reg"-suitable stream + */ +static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size, + int n) +{ + int i; + int address_cells = fdt_address_cells(fdt, 0); + int size_cells = fdt_size_cells(fdt, 0); + char *p = buf; + + for (i = 0; i < n; i++) { + if (address_cells == 2) + *(fdt64_t *)p = cpu_to_fdt64(address[i]); + else + *(fdt32_t *)p = cpu_to_fdt32(address[i]); + p += 4 * address_cells; + + if (size_cells == 2) + *(fdt64_t *)p = cpu_to_fdt64(size[i]); + else + *(fdt32_t *)p = cpu_to_fdt32(size[i]); + p += 4 * size_cells; + } + + return p - (char *)buf; +} + +#if CONFIG_NR_DRAM_BANKS > 4 +#define MEMORY_BANKS_MAX CONFIG_NR_DRAM_BANKS +#else +#define MEMORY_BANKS_MAX 4 +#endif +int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks) +{ + int err, nodeoffset; + int len, i; + u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */ + + if (banks > MEMORY_BANKS_MAX) { + printf("%s: num banks %d exceeds hardcoded limit %d." + " Recompile with higher MEMORY_BANKS_MAX?\n", + __FUNCTION__, banks, MEMORY_BANKS_MAX); + return -1; + } + + err = fdt_check_header(blob); + if (err < 0) { + printf("%s: %s\n", __FUNCTION__, fdt_strerror(err)); + return err; + } + + /* find or create "/memory" node. */ + nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory"); + if (nodeoffset < 0) + return nodeoffset; + + err = fdt_setprop(blob, nodeoffset, "device_type", "memory", + sizeof("memory")); + if (err < 0) { + printf("WARNING: could not set %s %s.\n", "device_type", + fdt_strerror(err)); + return err; + } + + for (i = 0; i < banks; i++) { + if (start[i] == 0 && size[i] == 0) + break; + } + + banks = i; + + if (!banks) + return 0; + + len = fdt_pack_reg(blob, tmp, start, size, banks); + + err = fdt_setprop(blob, nodeoffset, "reg", tmp, len); + if (err < 0) { + printf("WARNING: could not set %s %s.\n", + "reg", fdt_strerror(err)); + return err; + } + return 0; +} + +int fdt_set_usable_memory(void *blob, u64 start[], u64 size[], int areas) +{ + int err, nodeoffset; + int len; + u8 tmp[8 * 16]; /* Up to 64-bit address + 64-bit size */ + + if (areas > 8) { + printf("%s: num areas %d exceeds hardcoded limit %d\n", + __func__, areas, 8); + return -1; + } + + err = fdt_check_header(blob); + if (err < 0) { + printf("%s: %s\n", __func__, fdt_strerror(err)); + return err; + } + + /* find or create "/memory" node. */ + nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory"); + if (nodeoffset < 0) + return nodeoffset; + + len = fdt_pack_reg(blob, tmp, start, size, areas); + + err = fdt_setprop(blob, nodeoffset, "linux,usable-memory", tmp, len); + if (err < 0) { + printf("WARNING: could not set %s %s.\n", + "reg", fdt_strerror(err)); + return err; + } + + return 0; +} +#endif + +int fdt_fixup_memory(void *blob, u64 start, u64 size) +{ + return fdt_fixup_memory_banks(blob, &start, &size, 1); +} + +void fdt_fixup_ethernet(void *fdt) +{ + int i = 0, j, prop; + char *tmp, *end; + char mac[16]; + const char *path; + unsigned char mac_addr[ARP_HLEN]; + int offset; +#ifdef FDT_SEQ_MACADDR_FROM_ENV + int nodeoff; + const struct fdt_property *fdt_prop; +#endif + + if (fdt_path_offset(fdt, "/aliases") < 0) + return; + + /* Cycle through all aliases */ + for (prop = 0; ; prop++) { + const char *name; + + /* FDT might have been edited, recompute the offset */ + offset = fdt_first_property_offset(fdt, + fdt_path_offset(fdt, "/aliases")); + /* Select property number 'prop' */ + for (j = 0; j < prop; j++) + offset = fdt_next_property_offset(fdt, offset); + + if (offset < 0) + break; + + path = fdt_getprop_by_offset(fdt, offset, &name, NULL); + if (!strncmp(name, "ethernet", 8)) { + /* Treat plain "ethernet" same as "ethernet0". */ + if (!strcmp(name, "ethernet") +#ifdef FDT_SEQ_MACADDR_FROM_ENV + || !strcmp(name, "ethernet0") +#endif + ) + i = 0; +#ifndef FDT_SEQ_MACADDR_FROM_ENV + else + i = trailing_strtol(name); +#endif + if (i != -1) { + if (i == 0) + strcpy(mac, "ethaddr"); + else + sprintf(mac, "eth%daddr", i); + } else { + continue; + } +#ifdef FDT_SEQ_MACADDR_FROM_ENV + nodeoff = fdt_path_offset(fdt, path); + fdt_prop = fdt_get_property(fdt, nodeoff, "status", + NULL); + if (fdt_prop && !strcmp(fdt_prop->data, "disabled")) + continue; + i++; +#endif + tmp = env_get(mac); + if (!tmp) + continue; + + for (j = 0; j < 6; j++) { + mac_addr[j] = tmp ? + simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + + do_fixup_by_path(fdt, path, "mac-address", + &mac_addr, 6, 0); + do_fixup_by_path(fdt, path, "local-mac-address", + &mac_addr, 6, 1); + } + } +} + +int fdt_record_loadable(void *blob, u32 index, const char *name, + uintptr_t load_addr, u32 size, uintptr_t entry_point, + const char *type, const char *os) +{ + int err, node; + + err = fdt_check_header(blob); + if (err < 0) { + printf("%s: %s\n", __func__, fdt_strerror(err)); + return err; + } + + /* find or create "/fit-images" node */ + node = fdt_find_or_add_subnode(blob, 0, "fit-images"); + if (node < 0) + return node; + + /* find or create "/fit-images/<name>" node */ + node = fdt_find_or_add_subnode(blob, node, name); + if (node < 0) + return node; + + fdt_setprop_u64(blob, node, "load", load_addr); + if (entry_point != -1) + fdt_setprop_u64(blob, node, "entry", entry_point); + fdt_setprop_u32(blob, node, "size", size); + if (type) + fdt_setprop_string(blob, node, "type", type); + if (os) + fdt_setprop_string(blob, node, "os", os); + + return node; +} + +/* Resize the fdt to its actual size + a bit of padding */ +int fdt_shrink_to_minimum(void *blob, uint extrasize) +{ + int i; + uint64_t addr, size; + int total, ret; + uint actualsize; + int fdt_memrsv = 0; + + if (!blob) + return 0; + + total = fdt_num_mem_rsv(blob); + for (i = 0; i < total; i++) { + fdt_get_mem_rsv(blob, i, &addr, &size); + if (addr == (uintptr_t)blob) { + fdt_del_mem_rsv(blob, i); + fdt_memrsv = 1; + break; + } + } + + /* + * Calculate the actual size of the fdt + * plus the size needed for 5 fdt_add_mem_rsv, one + * for the fdt itself and 4 for a possible initrd + * ((initrd-start + initrd-end) * 2 (name & value)) + */ + actualsize = fdt_off_dt_strings(blob) + + fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); + + actualsize += extrasize; + /* Make it so the fdt ends on a page boundary */ + actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000); + actualsize = actualsize - ((uintptr_t)blob & 0xfff); + + /* Change the fdt header to reflect the correct size */ + fdt_set_totalsize(blob, actualsize); + + if (fdt_memrsv) { + /* Add the new reservation */ + ret = fdt_add_mem_rsv(blob, map_to_sysmem(blob), actualsize); + if (ret < 0) + return ret; + } + + return actualsize; +} + +#ifdef CONFIG_PCI +#define CONFIG_SYS_PCI_NR_INBOUND_WIN 4 + +#define FDT_PCI_PREFETCH (0x40000000) +#define FDT_PCI_MEM32 (0x02000000) +#define FDT_PCI_IO (0x01000000) +#define FDT_PCI_MEM64 (0x03000000) + +int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) { + + int addrcell, sizecell, len, r; + u32 *dma_range; + /* sized based on pci addr cells, size-cells, & address-cells */ + u32 dma_ranges[(3 + 2 + 2) * CONFIG_SYS_PCI_NR_INBOUND_WIN]; + + addrcell = fdt_getprop_u32_default(blob, "/", "#address-cells", 1); + sizecell = fdt_getprop_u32_default(blob, "/", "#size-cells", 1); + + dma_range = &dma_ranges[0]; + for (r = 0; r < hose->region_count; r++) { + u64 bus_start, phys_start, size; + + /* skip if !PCI_REGION_SYS_MEMORY */ + if (!(hose->regions[r].flags & PCI_REGION_SYS_MEMORY)) + continue; + + bus_start = (u64)hose->regions[r].bus_start; + phys_start = (u64)hose->regions[r].phys_start; + size = (u64)hose->regions[r].size; + + dma_range[0] = 0; + if (size >= 0x100000000ull) + dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM64); + else + dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM32); + if (hose->regions[r].flags & PCI_REGION_PREFETCH) + dma_range[0] |= cpu_to_fdt32(FDT_PCI_PREFETCH); +#ifdef CONFIG_SYS_PCI_64BIT + dma_range[1] = cpu_to_fdt32(bus_start >> 32); +#else + dma_range[1] = 0; +#endif + dma_range[2] = cpu_to_fdt32(bus_start & 0xffffffff); + + if (addrcell == 2) { + dma_range[3] = cpu_to_fdt32(phys_start >> 32); + dma_range[4] = cpu_to_fdt32(phys_start & 0xffffffff); + } else { + dma_range[3] = cpu_to_fdt32(phys_start & 0xffffffff); + } + + if (sizecell == 2) { + dma_range[3 + addrcell + 0] = + cpu_to_fdt32(size >> 32); + dma_range[3 + addrcell + 1] = + cpu_to_fdt32(size & 0xffffffff); + } else { + dma_range[3 + addrcell + 0] = + cpu_to_fdt32(size & 0xffffffff); + } + + dma_range += (3 + addrcell + sizecell); + } + + len = dma_range - &dma_ranges[0]; + if (len) + fdt_setprop(blob, phb_off, "dma-ranges", &dma_ranges[0], len*4); + + return 0; +} +#endif + +int fdt_increase_size(void *fdt, int add_len) +{ + int newlen; + + newlen = fdt_totalsize(fdt) + add_len; + + /* Open in place with a new len */ + return fdt_open_into(fdt, fdt, newlen); +} + +#ifdef CONFIG_FDT_FIXUP_PARTITIONS +#include <jffs2/load_kernel.h> +#include <mtd_node.h> + +static int fdt_del_subnodes(const void *blob, int parent_offset) +{ + int off, ndepth; + int ret; + + for (ndepth = 0, off = fdt_next_node(blob, parent_offset, &ndepth); + (off >= 0) && (ndepth > 0); + off = fdt_next_node(blob, off, &ndepth)) { + if (ndepth == 1) { + debug("delete %s: offset: %x\n", + fdt_get_name(blob, off, 0), off); + ret = fdt_del_node((void *)blob, off); + if (ret < 0) { + printf("Can't delete node: %s\n", + fdt_strerror(ret)); + return ret; + } else { + ndepth = 0; + off = parent_offset; + } + } + } + return 0; +} + +static int fdt_del_partitions(void *blob, int parent_offset) +{ + const void *prop; + int ndepth = 0; + int off; + int ret; + + off = fdt_next_node(blob, parent_offset, &ndepth); + if (off > 0 && ndepth == 1) { + prop = fdt_getprop(blob, off, "label", NULL); + if (prop == NULL) { + /* + * Could not find label property, nand {}; node? + * Check subnode, delete partitions there if any. + */ + return fdt_del_partitions(blob, off); + } else { + ret = fdt_del_subnodes(blob, parent_offset); + if (ret < 0) { + printf("Can't remove subnodes: %s\n", + fdt_strerror(ret)); + return ret; + } + } + } + return 0; +} + +static int fdt_node_set_part_info(void *blob, int parent_offset, + struct mtd_device *dev) +{ + struct list_head *pentry; + struct part_info *part; + int off, ndepth = 0; + int part_num, ret; + int sizecell; + char buf[64]; + + ret = fdt_del_partitions(blob, parent_offset); + if (ret < 0) + return ret; + + /* + * Check if size/address is 1 or 2 cells. + * We assume #address-cells and #size-cells have same value. + */ + sizecell = fdt_getprop_u32_default_node(blob, parent_offset, + 0, "#size-cells", 1); + + /* + * Check if it is nand {}; subnode, adjust + * the offset in this case + */ + off = fdt_next_node(blob, parent_offset, &ndepth); + if (off > 0 && ndepth == 1) + parent_offset = off; + + part_num = 0; + list_for_each_prev(pentry, &dev->parts) { + int newoff; + + part = list_entry(pentry, struct part_info, link); + + debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", + part_num, part->name, part->size, + part->offset, part->mask_flags); + + sprintf(buf, "partition@%llx", part->offset); +add_sub: + ret = fdt_add_subnode(blob, parent_offset, buf); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_sub; + else + goto err_size; + } else if (ret < 0) { + printf("Can't add partition node: %s\n", + fdt_strerror(ret)); + return ret; + } + newoff = ret; + + /* Check MTD_WRITEABLE_CMD flag */ + if (part->mask_flags & 1) { +add_ro: + ret = fdt_setprop(blob, newoff, "read_only", NULL, 0); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_ro; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + } + +add_reg: + if (sizecell == 2) { + ret = fdt_setprop_u64(blob, newoff, + "reg", part->offset); + if (!ret) + ret = fdt_appendprop_u64(blob, newoff, + "reg", part->size); + } else { + ret = fdt_setprop_u32(blob, newoff, + "reg", part->offset); + if (!ret) + ret = fdt_appendprop_u32(blob, newoff, + "reg", part->size); + } + + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_reg; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + +add_label: + ret = fdt_setprop_string(blob, newoff, "label", part->name); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_label; + else + goto err_size; + } else if (ret < 0) + goto err_prop; + + part_num++; + } + return 0; +err_size: + printf("Can't increase blob size: %s\n", fdt_strerror(ret)); + return ret; +err_prop: + printf("Can't add property: %s\n", fdt_strerror(ret)); + return ret; +} + +/* + * Update partitions in nor/nand nodes using info from + * mtdparts environment variable. The nodes to update are + * specified by node_info structure which contains mtd device + * type and compatible string: E. g. the board code in + * ft_board_setup() could use: + * + * struct node_info nodes[] = { + * { "fsl,mpc5121-nfc", MTD_DEV_TYPE_NAND, }, + * { "cfi-flash", MTD_DEV_TYPE_NOR, }, + * }; + * + * fdt_fixup_mtdparts(blob, nodes, ARRAY_SIZE(nodes)); + */ +void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info, + int node_info_size) +{ + struct mtd_device *dev; + int i, idx; + int noff; + bool inited = false; + + for (i = 0; i < node_info_size; i++) { + idx = 0; + noff = -1; + + while ((noff = fdt_node_offset_by_compatible(blob, noff, + node_info[i].compat)) >= 0) { + const char *prop; + + prop = fdt_getprop(blob, noff, "status", NULL); + if (prop && !strcmp(prop, "disabled")) + continue; + + debug("%s: %s, mtd dev type %d\n", + fdt_get_name(blob, noff, 0), + node_info[i].compat, node_info[i].type); + + if (!inited) { + if (mtdparts_init() != 0) + return; + inited = true; + } + + dev = device_find(node_info[i].type, idx++); + if (dev) { + if (fdt_node_set_part_info(blob, noff, dev)) + return; /* return on error */ + } + } + } +} +#endif + +void fdt_del_node_and_alias(void *blob, const char *alias) +{ + int off = fdt_path_offset(blob, alias); + + if (off < 0) + return; + + fdt_del_node(blob, off); + + off = fdt_path_offset(blob, "/aliases"); + fdt_delprop(blob, off, alias); +} + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_BAD_ADDR FDT_ADDR_T_NONE +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void of_dump_addr(const char *s, const fdt32_t *addr, int na) +{ + printf("%s", s); + while(na--) + printf(" %08x", *(addr++)); + printf("\n"); +} +#else +static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { } +#endif + +/** + * struct of_bus - Callbacks for bus specific translators + * @name: A string used to identify this bus in debug output. + * @addresses: The name of the DT property from which addresses are + * to be read, typically "reg". + * @match: Return non-zero if the node whose parent is at + * parentoffset in the FDT blob corresponds to a bus + * of this type, otherwise return zero. If NULL a match + * is assumed. + * @count_cells:Count how many cells (be32 values) a node whose parent + * is at parentoffset in the FDT blob will require to + * represent its address (written to *addrc) & size + * (written to *sizec). + * @map: Map the address addr from the address space of this + * bus to that of its parent, making use of the ranges + * read from DT to an array at range. na and ns are the + * number of cells (be32 values) used to hold and address + * or size, respectively, for this bus. pna is the number + * of cells used to hold an address for the parent bus. + * Returns the address in the address space of the parent + * bus. + * @translate: Update the value of the address cells at addr within an + * FDT by adding offset to it. na specifies the number of + * cells used to hold the address being translated. Returns + * zero on success, non-zero on error. + * + * Each bus type will include a struct of_bus in the of_busses array, + * providing implementations of some or all of the functions used to + * match the bus & handle address translation for its children. + */ +struct of_bus { + const char *name; + const char *addresses; + int (*match)(const void *blob, int parentoffset); + void (*count_cells)(const void *blob, int parentoffset, + int *addrc, int *sizec); + u64 (*map)(fdt32_t *addr, const fdt32_t *range, + int na, int ns, int pna); + int (*translate)(fdt32_t *addr, u64 offset, int na); +}; + +/* Default translator (generic bus) */ +void fdt_support_default_count_cells(const void *blob, int parentoffset, + int *addrc, int *sizec) +{ + const fdt32_t *prop; + + if (addrc) + *addrc = fdt_address_cells(blob, parentoffset); + + if (sizec) { + prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); + if (prop) + *sizec = be32_to_cpup(prop); + else + *sizec = 1; + } +} + +static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = fdt_read_number(range, na); + s = fdt_read_number(range + na + pna, ns); + da = fdt_read_number(addr, na); + + debug("OF: default map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na) +{ + u64 a = fdt_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_fdt32(a >> 32); + addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); + + return 0; +} + +#ifdef CONFIG_OF_ISA_BUS + +/* ISA bus translator */ +static int of_bus_isa_match(const void *blob, int parentoffset) +{ + const char *name; + + name = fdt_get_name(blob, parentoffset, NULL); + if (!name) + return 0; + + return !strcmp(name, "isa"); +} + +static void of_bus_isa_count_cells(const void *blob, int parentoffset, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 2; + if (sizec) + *sizec = 1; +} + +static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + /* Check address type match */ + if ((addr[0] ^ range[0]) & cpu_to_be32(1)) + return OF_BAD_ADDR; + + cp = fdt_read_number(range + 1, na - 1); + s = fdt_read_number(range + na + pna, ns); + da = fdt_read_number(addr + 1, na - 1); + + debug("OF: ISA map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na) +{ + return of_bus_default_translate(addr + 1, offset, na - 1); +} + +#endif /* CONFIG_OF_ISA_BUS */ + +/* Array of bus specific translators */ +static struct of_bus of_busses[] = { +#ifdef CONFIG_OF_ISA_BUS + /* ISA */ + { + .name = "isa", + .addresses = "reg", + .match = of_bus_isa_match, + .count_cells = of_bus_isa_count_cells, + .map = of_bus_isa_map, + .translate = of_bus_isa_translate, + }, +#endif /* CONFIG_OF_ISA_BUS */ + /* Default */ + { + .name = "default", + .addresses = "reg", + .count_cells = fdt_support_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + }, +}; + +static struct of_bus *of_match_bus(const void *blob, int parentoffset) +{ + struct of_bus *bus; + + if (ARRAY_SIZE(of_busses) == 1) + return of_busses; + + for (bus = of_busses; bus; bus++) { + if (!bus->match || bus->match(blob, parentoffset)) + return bus; + } + + /* + * We should always have matched the default bus at least, since + * it has a NULL match field. If we didn't then it somehow isn't + * in the of_busses array or something equally catastrophic has + * gone wrong. + */ + assert(0); + return NULL; +} + +static int of_translate_one(const void *blob, int parent, struct of_bus *bus, + struct of_bus *pbus, fdt32_t *addr, + int na, int ns, int pna, const char *rprop) +{ + const fdt32_t *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + /* Normally, an absence of a "ranges" property means we are + * crossing a non-translatable boundary, and thus the addresses + * below the current not cannot be converted to CPU physical ones. + * Unfortunately, while this is very clear in the spec, it's not + * what Apple understood, and they do have things like /uni-n or + * /ht nodes with no "ranges" property and a lot of perfectly + * useable mapped devices below them. Thus we treat the absence of + * "ranges" as equivalent to an empty "ranges" property which means + * a 1:1 translation at that level. It's up to the caller not to try + * to translate addresses that aren't supposed to be translated in + * the first place. --BenH. + */ + ranges = fdt_getprop(blob, parent, rprop, &rlen); + if (ranges == NULL || rlen == 0) { + offset = fdt_read_number(addr, na); + memset(addr, 0, pna * 4); + debug("OF: no ranges, 1:1 translation\n"); + goto finish; + } + + debug("OF: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + debug("OF: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("OF: parent translation for:", addr, pna); + debug("OF: with offset: %llu\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __of_translate_address(const void *blob, int node_offset, + const fdt32_t *in_addr, const char *rprop) +{ + int parent; + struct of_bus *bus, *pbus; + fdt32_t addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + debug("OF: ** translation for device %s **\n", + fdt_get_name(blob, node_offset, NULL)); + + /* Get parent & match bus type */ + parent = fdt_parent_offset(blob, node_offset); + if (parent < 0) + goto bail; + bus = of_match_bus(blob, parent); + + /* Cound address cells & copy address locally */ + bus->count_cells(blob, parent, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + debug("OF: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, fdt_get_name(blob, parent, NULL)); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + node_offset = parent; + parent = fdt_parent_offset(blob, node_offset); + + /* If root, we have finished */ + if (parent < 0) { + debug("OF: reached root node\n"); + result = fdt_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = of_match_bus(blob, parent); + pbus->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node_offset, NULL)); + break; + } + + debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, fdt_get_name(blob, parent, NULL)); + + /* Apply bus translation */ + if (of_translate_one(blob, node_offset, bus, pbus, + addr, na, ns, pna, rprop)) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("OF: one level translation:", addr, na); + } + bail: + + return result; +} + +u64 fdt_translate_address(const void *blob, int node_offset, + const fdt32_t *in_addr) +{ + return __of_translate_address(blob, node_offset, in_addr, "ranges"); +} + +u64 fdt_translate_dma_address(const void *blob, int node_offset, + const fdt32_t *in_addr) +{ + return __of_translate_address(blob, node_offset, in_addr, "dma-ranges"); +} + +int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu, + dma_addr_t *bus, u64 *size) +{ + bool found_dma_ranges = false; + struct of_bus *bus_node; + const fdt32_t *ranges; + int na, ns, pna, pns; + int parent = node; + int ret = 0; + int len; + + /* Find the closest dma-ranges property */ + while (parent >= 0) { + ranges = fdt_getprop(blob, parent, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -EINVAL; + goto out; + } + + if (ranges) + found_dma_ranges = true; + + parent = fdt_parent_offset(blob, parent); + } + + if (!ranges || parent < 0) { + debug("no dma-ranges found for node %s\n", + fdt_get_name(blob, node, NULL)); + ret = -ENOENT; + goto out; + } + + /* switch to that node */ + node = parent; + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + printf("Found dma-ranges in root node, shoudln't happen\n"); + ret = -EINVAL; + goto out; + } + + /* Get the address sizes both for the bus and its parent */ + bus_node = of_match_bus(blob, node); + bus_node->count_cells(blob, node, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + goto out; + } + + bus_node = of_match_bus(blob, parent); + bus_node->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printf("%s: Bad cell count for %s\n", __FUNCTION__, + fdt_get_name(blob, parent, NULL)); + return -EINVAL; + goto out; + } + + *bus = fdt_read_number(ranges, na); + *cpu = fdt_translate_dma_address(blob, node, ranges + na); + *size = fdt_read_number(ranges + na + pna, ns); +out: + return ret; +} + +/** + * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and + * who's reg property matches a physical cpu address + * + * @blob: ptr to device tree + * @compat: compatiable string to match + * @compat_off: property name + * + */ +int fdt_node_offset_by_compat_reg(void *blob, const char *compat, + phys_addr_t compat_off) +{ + int len, off = fdt_node_offset_by_compatible(blob, -1, compat); + while (off != -FDT_ERR_NOTFOUND) { + const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len); + if (reg) { + if (compat_off == fdt_translate_address(blob, off, reg)) + return off; + } + off = fdt_node_offset_by_compatible(blob, off, compat); + } + + return -FDT_ERR_NOTFOUND; +} + +/** + * fdt_alloc_phandle: Return next free phandle value + * + * @blob: ptr to device tree + */ +int fdt_alloc_phandle(void *blob) +{ + int offset; + uint32_t phandle = 0; + + for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; + offset = fdt_next_node(blob, offset, NULL)) { + phandle = max(phandle, fdt_get_phandle(blob, offset)); + } + + return phandle + 1; +} + +/* + * fdt_set_phandle: Create a phandle property for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + * @phandle: phandle value to set (must be unique) + */ +int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle) +{ + int ret; + +#ifdef DEBUG + int off = fdt_node_offset_by_phandle(fdt, phandle); + + if ((off >= 0) && (off != nodeoffset)) { + char buf[64]; + + fdt_get_path(fdt, nodeoffset, buf, sizeof(buf)); + printf("Trying to update node %s with phandle %u ", + buf, phandle); + + fdt_get_path(fdt, off, buf, sizeof(buf)); + printf("that already exists in node %s.\n", buf); + return -FDT_ERR_BADPHANDLE; + } +#endif + + ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle); + if (ret < 0) + return ret; + + /* + * For now, also set the deprecated "linux,phandle" property, so that we + * don't break older kernels. + */ + ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle); + + return ret; +} + +/* + * fdt_create_phandle: Create a phandle property for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + */ +unsigned int fdt_create_phandle(void *fdt, int nodeoffset) +{ + /* see if there is a phandle already */ + int phandle = fdt_get_phandle(fdt, nodeoffset); + + /* if we got 0, means no phandle so create one */ + if (phandle == 0) { + int ret; + + phandle = fdt_alloc_phandle(fdt); + ret = fdt_set_phandle(fdt, nodeoffset, phandle); + if (ret < 0) { + printf("Can't set phandle %u: %s\n", phandle, + fdt_strerror(ret)); + return 0; + } + } + + return phandle; +} + +/* + * fdt_set_node_status: Set status for the given node + * + * @fdt: ptr to device tree + * @nodeoffset: node to update + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, + * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE + * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + */ +int fdt_set_node_status(void *fdt, int nodeoffset, + enum fdt_status status, unsigned int error_code) +{ + char buf[16]; + int ret = 0; + + if (nodeoffset < 0) + return nodeoffset; + + switch (status) { + case FDT_STATUS_OKAY: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay"); + break; + case FDT_STATUS_DISABLED: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled"); + break; + case FDT_STATUS_FAIL: + ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail"); + break; + case FDT_STATUS_FAIL_ERROR_CODE: + sprintf(buf, "fail-%d", error_code); + ret = fdt_setprop_string(fdt, nodeoffset, "status", buf); + break; + default: + printf("Invalid fdt status: %x\n", status); + ret = -1; + break; + } + + return ret; +} + +/* + * fdt_set_status_by_alias: Set status for the given node given an alias + * + * @fdt: ptr to device tree + * @alias: alias of node to update + * @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED, + * FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE + * @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE + */ +int fdt_set_status_by_alias(void *fdt, const char* alias, + enum fdt_status status, unsigned int error_code) +{ + int offset = fdt_path_offset(fdt, alias); + + return fdt_set_node_status(fdt, offset, status, error_code); +} + +#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) +int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) +{ + int noff; + int ret; + + noff = fdt_node_offset_by_compatible(blob, -1, compat); + if (noff != -FDT_ERR_NOTFOUND) { + debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat); +add_edid: + ret = fdt_setprop(blob, noff, "edid", edid_buf, 128); + if (ret == -FDT_ERR_NOSPACE) { + ret = fdt_increase_size(blob, 512); + if (!ret) + goto add_edid; + else + goto err_size; + } else if (ret < 0) { + printf("Can't add property: %s\n", fdt_strerror(ret)); + return ret; + } + } + return 0; +err_size: + printf("Can't increase blob size: %s\n", fdt_strerror(ret)); + return ret; +} +#endif + +/* + * Verify the physical address of device tree node for a given alias + * + * This function locates the device tree node of a given alias, and then + * verifies that the physical address of that device matches the given + * parameter. It displays a message if there is a mismatch. + * + * Returns 1 on success, 0 on failure + */ +int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr) +{ + const char *path; + const fdt32_t *reg; + int node, len; + u64 dt_addr; + + path = fdt_getprop(fdt, anode, alias, NULL); + if (!path) { + /* If there's no such alias, then it's not a failure */ + return 1; + } + + node = fdt_path_offset(fdt, path); + if (node < 0) { + printf("Warning: device tree alias '%s' points to invalid " + "node %s.\n", alias, path); + return 0; + } + + reg = fdt_getprop(fdt, node, "reg", &len); + if (!reg) { + printf("Warning: device tree node '%s' has no address.\n", + path); + return 0; + } + + dt_addr = fdt_translate_address(fdt, node, reg); + if (addr != dt_addr) { + printf("Warning: U-Boot configured device %s at address %llu,\n" + "but the device tree has it address %llx.\n", + alias, addr, dt_addr); + return 0; + } + + return 1; +} + +/* + * Returns the base address of an SOC or PCI node + */ +u64 fdt_get_base_address(const void *fdt, int node) +{ + int size; + const fdt32_t *prop; + + prop = fdt_getprop(fdt, node, "reg", &size); + + return prop ? fdt_translate_address(fdt, node, prop) : OF_BAD_ADDR; +} + +/* + * Read a property of size <prop_len>. Currently only supports 1 or 2 cells, + * or 3 cells specially for a PCI address. + */ +static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, + uint64_t *val, int cells) +{ + const fdt32_t *prop32; + const unaligned_fdt64_t *prop64; + + if ((cell_off + cells) > prop_len) + return -FDT_ERR_NOSPACE; + + prop32 = &prop[cell_off]; + + /* + * Special handling for PCI address in PCI bus <ranges> + * + * PCI child address is made up of 3 cells. Advance the cell offset + * by 1 so that the PCI child address can be correctly read. + */ + if (cells == 3) + cell_off += 1; + prop64 = (const fdt64_t *)&prop[cell_off]; + + switch (cells) { + case 1: + *val = fdt32_to_cpu(*prop32); + break; + case 2: + case 3: + *val = fdt64_to_cpu(*prop64); + break; + default: + return -FDT_ERR_NOSPACE; + } + + return 0; +} + +/** + * fdt_read_range - Read a node's n'th range property + * + * @fdt: ptr to device tree + * @node: offset of node + * @n: range index + * @child_addr: pointer to storage for the "child address" field + * @addr: pointer to storage for the CPU view translated physical start + * @len: pointer to storage for the range length + * + * Convenience function that reads and interprets a specific range out of + * a number of the "ranges" property array. + */ +int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, + uint64_t *addr, uint64_t *len) +{ + int pnode = fdt_parent_offset(fdt, node); + const fdt32_t *ranges; + int pacells; + int acells; + int scells; + int ranges_len; + int cell = 0; + int r = 0; + + /* + * The "ranges" property is an array of + * { <child address> <parent address> <size in child address space> } + * + * All 3 elements can span a diffent number of cells. Fetch their size. + */ + pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1); + acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1); + scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1); + + /* Now try to get the ranges property */ + ranges = fdt_getprop(fdt, node, "ranges", &ranges_len); + if (!ranges) + return -FDT_ERR_NOTFOUND; + ranges_len /= sizeof(uint32_t); + + /* Jump to the n'th entry */ + cell = n * (pacells + acells + scells); + + /* Read <child address> */ + if (child_addr) { + r = fdt_read_prop(ranges, ranges_len, cell, child_addr, + acells); + if (r) + return r; + } + cell += acells; + + /* Read <parent address> */ + if (addr) + *addr = fdt_translate_address(fdt, node, ranges + cell); + cell += pacells; + + /* Read <size in child address space> */ + if (len) { + r = fdt_read_prop(ranges, ranges_len, cell, len, scells); + if (r) + return r; + } + + return 0; +} + +/** + * fdt_setup_simplefb_node - Fill and enable a simplefb node + * + * @fdt: ptr to device tree + * @node: offset of the simplefb node + * @base_address: framebuffer base address + * @width: width in pixels + * @height: height in pixels + * @stride: bytes per line + * @format: pixel format string + * + * Convenience function to fill and enable a simplefb node. + */ +int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width, + u32 height, u32 stride, const char *format) +{ + char name[32]; + fdt32_t cells[4]; + int i, addrc, sizec, ret; + + fdt_support_default_count_cells(fdt, fdt_parent_offset(fdt, node), + &addrc, &sizec); + i = 0; + if (addrc == 2) + cells[i++] = cpu_to_fdt32(base_address >> 32); + cells[i++] = cpu_to_fdt32(base_address); + if (sizec == 2) + cells[i++] = 0; + cells[i++] = cpu_to_fdt32(height * stride); + + ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i); + if (ret < 0) + return ret; + + snprintf(name, sizeof(name), "framebuffer@%llx", base_address); + ret = fdt_set_name(fdt, node, name); + if (ret < 0) + return ret; + + ret = fdt_setprop_u32(fdt, node, "width", width); + if (ret < 0) + return ret; + + ret = fdt_setprop_u32(fdt, node, "height", height); + if (ret < 0) + return ret; + + ret = fdt_setprop_u32(fdt, node, "stride", stride); + if (ret < 0) + return ret; + + ret = fdt_setprop_string(fdt, node, "format", format); + if (ret < 0) + return ret; + + ret = fdt_setprop_string(fdt, node, "status", "okay"); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Update native-mode in display-timings from display environment variable. + * The node to update are specified by path. + */ +int fdt_fixup_display(void *blob, const char *path, const char *display) +{ + int off, toff; + + if (!display || !path) + return -FDT_ERR_NOTFOUND; + + toff = fdt_path_offset(blob, path); + if (toff >= 0) + toff = fdt_subnode_offset(blob, toff, "display-timings"); + if (toff < 0) + return toff; + + for (off = fdt_first_subnode(blob, toff); + off >= 0; + off = fdt_next_subnode(blob, off)) { + uint32_t h = fdt_get_phandle(blob, off); + debug("%s:0x%x\n", fdt_get_name(blob, off, NULL), + fdt32_to_cpu(h)); + if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0) + return fdt_setprop_u32(blob, toff, "native-mode", h); + } + return toff; +} + +#ifdef CONFIG_OF_LIBFDT_OVERLAY +/** + * fdt_overlay_apply_verbose - Apply an overlay with verbose error reporting + * + * @fdt: ptr to device tree + * @fdto: ptr to device tree overlay + * + * Convenience function to apply an overlay and display helpful messages + * in the case of an error + */ +int fdt_overlay_apply_verbose(void *fdt, void *fdto) +{ + int err; + bool has_symbols; + + err = fdt_path_offset(fdt, "/__symbols__"); + has_symbols = err >= 0; + + err = fdt_overlay_apply(fdt, fdto); + if (err < 0) { + printf("failed on fdt_overlay_apply(): %s\n", + fdt_strerror(err)); + if (!has_symbols) { + printf("base fdt does did not have a /__symbols__ node\n"); + printf("make sure you've compiled with -@\n"); + } + } + return err; +} +#endif + +/** + * fdt_valid() - Check if an FDT is valid. If not, change it to NULL + * + * @blobp: Pointer to FDT pointer + * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL) + */ +int fdt_valid(struct fdt_header **blobp) +{ + const void *blob = *blobp; + int err; + + if (!blob) { + printf("The address of the fdt is invalid (NULL).\n"); + return 0; + } + + err = fdt_check_header(blob); + if (err == 0) + return 1; /* valid */ + + if (err < 0) { + printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); + /* + * Be more informative on bad version. + */ + if (err == -FDT_ERR_BADVERSION) { + if (fdt_version(blob) < + FDT_FIRST_SUPPORTED_VERSION) { + printf(" - too old, fdt %d < %d", + fdt_version(blob), + FDT_FIRST_SUPPORTED_VERSION); + } + if (fdt_last_comp_version(blob) > + FDT_LAST_SUPPORTED_VERSION) { + printf(" - too new, fdt %d > %d", + fdt_version(blob), + FDT_LAST_SUPPORTED_VERSION); + } + } + printf("\n"); + *blobp = NULL; + return 0; + } + return 1; +} diff --git a/roms/u-boot/common/flash.c b/roms/u-boot/common/flash.c new file mode 100644 index 000000000..bb82385c1 --- /dev/null +++ b/roms/u-boot/common/flash.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* #define DEBUG */ + +#include <common.h> +#include <flash.h> +#include <log.h> +#include <uuid.h> + +#include <mtd/cfi_flash.h> + +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ + +/*----------------------------------------------------------------------- + * Set protection status for monitor sectors + * + * The monitor is always located in the _first_ Flash bank. + * If necessary you have to map the second bank at lower addresses. + */ +void +flash_protect(int flag, ulong from, ulong to, flash_info_t *info) +{ + ulong b_end; + short s_end; + int i; + + /* Do nothing if input data is bad. */ + if (!info || info->sector_count == 0 || info->size == 0 || to < from) { + return; + } + + s_end = info->sector_count - 1; /* index of last sector */ + b_end = info->start[0] + info->size - 1; /* bank end address */ + + debug("%s %s: from 0x%08lX to 0x%08lX\n", __func__, + (flag & FLAG_PROTECT_SET) ? "ON" : + (flag & FLAG_PROTECT_CLEAR) ? "OFF" : "???", + from, to); + + /* There is nothing to do if we have no data about the flash + * or the protect range and flash range don't overlap. + */ + if (info->flash_id == FLASH_UNKNOWN || + to < info->start[0] || from > b_end) { + return; + } + + for (i=0; i<info->sector_count; ++i) { + ulong end; /* last address in current sect */ + + end = (i == s_end) ? b_end : info->start[i + 1] - 1; + + /* Update protection if any part of the sector + * is in the specified range. + */ + if (from <= end && to >= info->start[i]) { + if (flag & FLAG_PROTECT_CLEAR) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + flash_real_protect(info, i, 0); +#else + info->protect[i] = 0; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + debug("protect off %d\n", i); + } + else if (flag & FLAG_PROTECT_SET) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + flash_real_protect(info, i, 1); +#else + info->protect[i] = 1; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + debug("protect on %d\n", i); + } + } + } +} + +/*----------------------------------------------------------------------- + */ + +flash_info_t * +addr2info(ulong addr) +{ + flash_info_t *info; + int i; + + for (i=0, info = &flash_info[0]; i<CONFIG_SYS_MAX_FLASH_BANKS; ++i, ++info) { + if (info->flash_id != FLASH_UNKNOWN && + addr >= info->start[0] && + /* WARNING - The '- 1' is needed if the flash + * is at the end of the address space, since + * info->start[0] + info->size wraps back to 0. + * Please don't change this unless you understand this. + */ + addr <= info->start[0] + info->size - 1) { + return (info); + } + } + + return (NULL); +} + +/*----------------------------------------------------------------------- + * Copy memory to flash. + * Make sure all target addresses are within Flash bounds, + * and no protected sectors are hit. + * Returns: + * ERR_OK 0 - OK + * ERR_TIMEOUT 1 - write timeout + * ERR_NOT_ERASED 2 - Flash not erased + * ERR_PROTECTED 4 - target range includes protected sectors + * ERR_INVAL 8 - target address not in Flash memory + * ERR_ALIGN 16 - target address not aligned on boundary + * (only some targets require alignment) + */ +int +flash_write(char *src, ulong addr, ulong cnt) +{ + int i; + ulong end = addr + cnt - 1; + flash_info_t *info_first = addr2info(addr); + flash_info_t *info_last = addr2info(end); + flash_info_t *info; + __maybe_unused char *src_orig = src; + __maybe_unused char *addr_orig = (char *)addr; + __maybe_unused ulong cnt_orig = cnt; + + if (cnt == 0) { + return (ERR_OK); + } + + if (!info_first || !info_last) { + return (ERR_INVAL); + } + + for (info = info_first; info <= info_last; ++info) { + ulong b_end = info->start[0] + info->size; /* bank end addr */ + short s_end = info->sector_count - 1; + for (i=0; i<info->sector_count; ++i) { + ulong e_addr = (i == s_end) ? b_end : info->start[i + 1]; + + if ((end >= info->start[i]) && (addr < e_addr) && + (info->protect[i] != 0) ) { + return (ERR_PROTECTED); + } + } + } + + /* finally write data to flash */ + for (info = info_first; info <= info_last && cnt>0; ++info) { + ulong len; + + len = info->start[0] + info->size - addr; + if (len > cnt) + len = cnt; + if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) { + return (i); + } + cnt -= len; + addr += len; + src += len; + } + +#if defined(CONFIG_FLASH_VERIFY) + if (memcmp(src_orig, addr_orig, cnt_orig)) { + printf("\nVerify failed!\n"); + return ERR_PROG_ERROR; + } +#endif /* CONFIG_SYS_FLASH_VERIFY_AFTER_WRITE */ + + return (ERR_OK); +} + +/*----------------------------------------------------------------------- + */ + +void flash_perror(int err) +{ + switch (err) { + case ERR_OK: + break; + case ERR_TIMEOUT: + puts ("Timeout writing to Flash\n"); + break; + case ERR_NOT_ERASED: + puts ("Flash not Erased\n"); + break; + case ERR_PROTECTED: + puts ("Can't write to protected Flash sectors\n"); + break; + case ERR_INVAL: + puts ("Outside available Flash\n"); + break; + case ERR_ALIGN: + puts ("Start and/or end address not on sector boundary\n"); + break; + case ERR_UNKNOWN_FLASH_VENDOR: + puts ("Unknown Vendor of Flash\n"); + break; + case ERR_UNKNOWN_FLASH_TYPE: + puts ("Unknown Type of Flash\n"); + break; + case ERR_PROG_ERROR: + puts ("General Flash Programming Error\n"); + break; + case ERR_ABORTED: + puts("Flash Programming Aborted\n"); + break; + default: + printf ("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); + break; + } +} diff --git a/roms/u-boot/common/hash.c b/roms/u-boot/common/hash.c new file mode 100644 index 000000000..90cf46bcb --- /dev/null +++ b/roms/u-boot/common/hash.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <command.h> +#include <env.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <hw_sha.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <u-boot/crc.h> +#else +#include "mkimage.h" +#include <time.h> +#endif /* !USE_HOSTCC*/ + +#include <hash.h> +#include <image.h> +#include <u-boot/crc.h> +#include <u-boot/sha1.h> +#include <u-boot/sha256.h> +#include <u-boot/sha512.h> +#include <u-boot/md5.h> + +#if !defined(USE_HOSTCC) && defined(CONFIG_NEEDS_MANUAL_RELOC) +DECLARE_GLOBAL_DATA_PTR; +#endif + +static void reloc_update(void); + +#if defined(CONFIG_SHA1) && !defined(CONFIG_SHA_PROG_HW_ACCEL) +static int hash_init_sha1(struct hash_algo *algo, void **ctxp) +{ + sha1_context *ctx = malloc(sizeof(sha1_context)); + sha1_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha1(struct hash_algo *algo, void *ctx, const void *buf, + unsigned int size, int is_last) +{ + sha1_update((sha1_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha1(struct hash_algo *algo, void *ctx, void *dest_buf, + int size) +{ + if (size < algo->digest_size) + return -1; + + sha1_finish((sha1_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + +#if defined(CONFIG_SHA256) && !defined(CONFIG_SHA_PROG_HW_ACCEL) +static int hash_init_sha256(struct hash_algo *algo, void **ctxp) +{ + sha256_context *ctx = malloc(sizeof(sha256_context)); + sha256_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha256(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + sha256_update((sha256_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha256(struct hash_algo *algo, void *ctx, void + *dest_buf, int size) +{ + if (size < algo->digest_size) + return -1; + + sha256_finish((sha256_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + +#if defined(CONFIG_SHA384) && !defined(CONFIG_SHA_PROG_HW_ACCEL) +static int hash_init_sha384(struct hash_algo *algo, void **ctxp) +{ + sha512_context *ctx = malloc(sizeof(sha512_context)); + sha384_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha384(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + sha384_update((sha512_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha384(struct hash_algo *algo, void *ctx, void + *dest_buf, int size) +{ + if (size < algo->digest_size) + return -1; + + sha384_finish((sha512_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + +#if defined(CONFIG_SHA512) && !defined(CONFIG_SHA_PROG_HW_ACCEL) +static int hash_init_sha512(struct hash_algo *algo, void **ctxp) +{ + sha512_context *ctx = malloc(sizeof(sha512_context)); + sha512_starts(ctx); + *ctxp = ctx; + return 0; +} + +static int hash_update_sha512(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + sha512_update((sha512_context *)ctx, buf, size); + return 0; +} + +static int hash_finish_sha512(struct hash_algo *algo, void *ctx, void + *dest_buf, int size) +{ + if (size < algo->digest_size) + return -1; + + sha512_finish((sha512_context *)ctx, dest_buf); + free(ctx); + return 0; +} +#endif + + +static int hash_init_crc16_ccitt(struct hash_algo *algo, void **ctxp) +{ + uint16_t *ctx = malloc(sizeof(uint16_t)); + *ctx = 0; + *ctxp = ctx; + return 0; +} + +static int hash_update_crc16_ccitt(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, + int is_last) +{ + *((uint16_t *)ctx) = crc16_ccitt(*((uint16_t *)ctx), buf, size); + return 0; +} + +static int hash_finish_crc16_ccitt(struct hash_algo *algo, void *ctx, + void *dest_buf, int size) +{ + if (size < algo->digest_size) + return -1; + + *((uint16_t *)dest_buf) = *((uint16_t *)ctx); + free(ctx); + return 0; +} + +static int hash_init_crc32(struct hash_algo *algo, void **ctxp) +{ + uint32_t *ctx = malloc(sizeof(uint32_t)); + *ctx = 0; + *ctxp = ctx; + return 0; +} + +static int hash_update_crc32(struct hash_algo *algo, void *ctx, + const void *buf, unsigned int size, int is_last) +{ + *((uint32_t *)ctx) = crc32(*((uint32_t *)ctx), buf, size); + return 0; +} + +static int hash_finish_crc32(struct hash_algo *algo, void *ctx, void *dest_buf, + int size) +{ + if (size < algo->digest_size) + return -1; + + *((uint32_t *)dest_buf) = *((uint32_t *)ctx); + free(ctx); + return 0; +} + +/* + * These are the hash algorithms we support. If we have hardware acceleration + * is enable we will use that, otherwise a software version of the algorithm. + * Note that algorithm names must be in lower case. + */ +static struct hash_algo hash_algo[] = { +#ifdef CONFIG_SHA1 + { + .name = "sha1", + .digest_size = SHA1_SUM_LEN, + .chunk_size = CHUNKSZ_SHA1, +#ifdef CONFIG_SHA_HW_ACCEL + .hash_func_ws = hw_sha1, +#else + .hash_func_ws = sha1_csum_wd, +#endif +#ifdef CONFIG_SHA_PROG_HW_ACCEL + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else + .hash_init = hash_init_sha1, + .hash_update = hash_update_sha1, + .hash_finish = hash_finish_sha1, +#endif + }, +#endif +#ifdef CONFIG_SHA256 + { + .name = "sha256", + .digest_size = SHA256_SUM_LEN, + .chunk_size = CHUNKSZ_SHA256, +#ifdef CONFIG_SHA_HW_ACCEL + .hash_func_ws = hw_sha256, +#else + .hash_func_ws = sha256_csum_wd, +#endif +#ifdef CONFIG_SHA_PROG_HW_ACCEL + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else + .hash_init = hash_init_sha256, + .hash_update = hash_update_sha256, + .hash_finish = hash_finish_sha256, +#endif + }, +#endif +#ifdef CONFIG_SHA384 + { + .name = "sha384", + .digest_size = SHA384_SUM_LEN, + .chunk_size = CHUNKSZ_SHA384, +#ifdef CONFIG_SHA512_HW_ACCEL + .hash_func_ws = hw_sha384, +#else + .hash_func_ws = sha384_csum_wd, +#endif +#if defined(CONFIG_SHA512_HW_ACCEL) && defined(CONFIG_SHA_PROG_HW_ACCEL) + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else + .hash_init = hash_init_sha384, + .hash_update = hash_update_sha384, + .hash_finish = hash_finish_sha384, +#endif + }, +#endif +#ifdef CONFIG_SHA512 + { + .name = "sha512", + .digest_size = SHA512_SUM_LEN, + .chunk_size = CHUNKSZ_SHA512, +#ifdef CONFIG_SHA512_HW_ACCEL + .hash_func_ws = hw_sha512, +#else + .hash_func_ws = sha512_csum_wd, +#endif +#if defined(CONFIG_SHA512_HW_ACCEL) && defined(CONFIG_SHA_PROG_HW_ACCEL) + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else + .hash_init = hash_init_sha512, + .hash_update = hash_update_sha512, + .hash_finish = hash_finish_sha512, +#endif + }, +#endif + { + .name = "crc16-ccitt", + .digest_size = 2, + .chunk_size = CHUNKSZ, + .hash_func_ws = crc16_ccitt_wd_buf, + .hash_init = hash_init_crc16_ccitt, + .hash_update = hash_update_crc16_ccitt, + .hash_finish = hash_finish_crc16_ccitt, + }, + { + .name = "crc32", + .digest_size = 4, + .chunk_size = CHUNKSZ_CRC32, + .hash_func_ws = crc32_wd_buf, + .hash_init = hash_init_crc32, + .hash_update = hash_update_crc32, + .hash_finish = hash_finish_crc32, + }, +}; + +/* Try to minimize code size for boards that don't want much hashing */ +#if defined(CONFIG_SHA256) || defined(CONFIG_CMD_SHA1SUM) || \ + defined(CONFIG_CRC32_VERIFY) || defined(CONFIG_CMD_HASH) || \ + defined(CONFIG_SHA384) || defined(CONFIG_SHA512) +#define multi_hash() 1 +#else +#define multi_hash() 0 +#endif + +static void reloc_update(void) +{ +#if !defined(USE_HOSTCC) && defined(CONFIG_NEEDS_MANUAL_RELOC) + int i; + static bool done; + + if (!done) { + done = true; + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + hash_algo[i].name += gd->reloc_off; + hash_algo[i].hash_func_ws += gd->reloc_off; + hash_algo[i].hash_init += gd->reloc_off; + hash_algo[i].hash_update += gd->reloc_off; + hash_algo[i].hash_finish += gd->reloc_off; + } + } +#endif +} + +int hash_lookup_algo(const char *algo_name, struct hash_algo **algop) +{ + int i; + + reloc_update(); + + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + if (!strcmp(algo_name, hash_algo[i].name)) { + *algop = &hash_algo[i]; + return 0; + } + } + + debug("Unknown hash algorithm '%s'\n", algo_name); + return -EPROTONOSUPPORT; +} + +int hash_progressive_lookup_algo(const char *algo_name, + struct hash_algo **algop) +{ + int i; + + reloc_update(); + + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + if (!strcmp(algo_name, hash_algo[i].name)) { + if (hash_algo[i].hash_init) { + *algop = &hash_algo[i]; + return 0; + } + } + } + + debug("Unknown hash algorithm '%s'\n", algo_name); + return -EPROTONOSUPPORT; +} + +#ifndef USE_HOSTCC +int hash_parse_string(const char *algo_name, const char *str, uint8_t *result) +{ + struct hash_algo *algo; + int ret; + int i; + + ret = hash_lookup_algo(algo_name, &algo); + if (ret) + return ret; + + for (i = 0; i < algo->digest_size; i++) { + char chr[3]; + + strncpy(chr, &str[i * 2], 2); + result[i] = simple_strtoul(chr, NULL, 16); + } + + return 0; +} + +int hash_block(const char *algo_name, const void *data, unsigned int len, + uint8_t *output, int *output_size) +{ + struct hash_algo *algo; + int ret; + + ret = hash_lookup_algo(algo_name, &algo); + if (ret) + return ret; + + if (output_size && *output_size < algo->digest_size) { + debug("Output buffer size %d too small (need %d bytes)", + *output_size, algo->digest_size); + return -ENOSPC; + } + if (output_size) + *output_size = algo->digest_size; + algo->hash_func_ws(data, len, output, algo->chunk_size); + + return 0; +} + +#if defined(CONFIG_CMD_HASH) || defined(CONFIG_CMD_SHA1SUM) || defined(CONFIG_CMD_CRC32) +/** + * store_result: Store the resulting sum to an address or variable + * + * @algo: Hash algorithm being used + * @sum: Hash digest (algo->digest_size bytes) + * @dest: Destination, interpreted as a hex address if it starts + * with * (or allow_env_vars is 0) or otherwise as an + * environment variable. + * @allow_env_vars: non-zero to permit storing the result to an + * variable environment + */ +static void store_result(struct hash_algo *algo, const uint8_t *sum, + const char *dest, int allow_env_vars) +{ + unsigned int i; + int env_var = 0; + + /* + * If environment variables are allowed, then we assume that 'dest' + * is an environment variable, unless it starts with *, in which + * case we assume it is an address. If not allowed, it is always an + * address. This is to support the crc32 command. + */ + if (allow_env_vars) { + if (*dest == '*') + dest++; + else + env_var = 1; + } + + if (env_var) { + char str_output[HASH_MAX_DIGEST_SIZE * 2 + 1]; + char *str_ptr = str_output; + + for (i = 0; i < algo->digest_size; i++) { + sprintf(str_ptr, "%02x", sum[i]); + str_ptr += 2; + } + *str_ptr = '\0'; + env_set(dest, str_output); + } else { + ulong addr; + void *buf; + + addr = simple_strtoul(dest, NULL, 16); + buf = map_sysmem(addr, algo->digest_size); + memcpy(buf, sum, algo->digest_size); + unmap_sysmem(buf); + } +} + +/** + * parse_verify_sum: Parse a hash verification parameter + * + * @algo: Hash algorithm being used + * @verify_str: Argument to parse. If it starts with * then it is + * interpreted as a hex address containing the hash. + * If the length is exactly the right number of hex digits + * for the digest size, then we assume it is a hex digest. + * Otherwise we assume it is an environment variable, and + * look up its value (it must contain a hex digest). + * @vsum: Returns binary digest value (algo->digest_size bytes) + * @allow_env_vars: non-zero to permit storing the result to an environment + * variable. If 0 then verify_str is assumed to be an + * address, and the * prefix is not expected. + * @return 0 if ok, non-zero on error + */ +static int parse_verify_sum(struct hash_algo *algo, char *verify_str, + uint8_t *vsum, int allow_env_vars) +{ + int env_var = 0; + + /* See comment above in store_result() */ + if (allow_env_vars) { + if (*verify_str == '*') + verify_str++; + else + env_var = 1; + } + + if (!env_var) { + ulong addr; + void *buf; + + addr = simple_strtoul(verify_str, NULL, 16); + buf = map_sysmem(addr, algo->digest_size); + memcpy(vsum, buf, algo->digest_size); + } else { + char *vsum_str; + int digits = algo->digest_size * 2; + + /* + * As with the original code from sha1sum.c, we assume that a + * string which matches the digest size exactly is a hex + * string and not an environment variable. + */ + if (strlen(verify_str) == digits) + vsum_str = verify_str; + else { + vsum_str = env_get(verify_str); + if (vsum_str == NULL || strlen(vsum_str) != digits) { + printf("Expected %d hex digits in env var\n", + digits); + return 1; + } + } + + hash_parse_string(algo->name, vsum_str, vsum); + } + return 0; +} + +static void hash_show(struct hash_algo *algo, ulong addr, ulong len, uint8_t *output) +{ + int i; + + printf("%s for %08lx ... %08lx ==> ", algo->name, addr, addr + len - 1); + for (i = 0; i < algo->digest_size; i++) + printf("%02x", output[i]); +} + +int hash_command(const char *algo_name, int flags, struct cmd_tbl *cmdtp, + int flag, int argc, char *const argv[]) +{ + ulong addr, len; + + if ((argc < 2) || ((flags & HASH_FLAG_VERIFY) && (argc < 3))) + return CMD_RET_USAGE; + + addr = simple_strtoul(*argv++, NULL, 16); + len = simple_strtoul(*argv++, NULL, 16); + + if (multi_hash()) { + struct hash_algo *algo; + u8 *output; + uint8_t vsum[HASH_MAX_DIGEST_SIZE]; + void *buf; + + if (hash_lookup_algo(algo_name, &algo)) { + printf("Unknown hash algorithm '%s'\n", algo_name); + return CMD_RET_USAGE; + } + argc -= 2; + + if (algo->digest_size > HASH_MAX_DIGEST_SIZE) { + puts("HASH_MAX_DIGEST_SIZE exceeded\n"); + return 1; + } + + output = memalign(ARCH_DMA_MINALIGN, + sizeof(uint32_t) * HASH_MAX_DIGEST_SIZE); + + buf = map_sysmem(addr, len); + algo->hash_func_ws(buf, len, output, algo->chunk_size); + unmap_sysmem(buf); + + /* Try to avoid code bloat when verify is not needed */ +#if defined(CONFIG_CRC32_VERIFY) || defined(CONFIG_SHA1SUM_VERIFY) || \ + defined(CONFIG_HASH_VERIFY) + if (flags & HASH_FLAG_VERIFY) { +#else + if (0) { +#endif + if (parse_verify_sum(algo, *argv, vsum, + flags & HASH_FLAG_ENV)) { + printf("ERROR: %s does not contain a valid " + "%s sum\n", *argv, algo->name); + return 1; + } + if (memcmp(output, vsum, algo->digest_size) != 0) { + int i; + + hash_show(algo, addr, len, output); + printf(" != "); + for (i = 0; i < algo->digest_size; i++) + printf("%02x", vsum[i]); + puts(" ** ERROR **\n"); + return 1; + } + } else { + hash_show(algo, addr, len, output); + printf("\n"); + + if (argc) { + store_result(algo, output, *argv, + flags & HASH_FLAG_ENV); + } + unmap_sysmem(output); + + } + + /* Horrible code size hack for boards that just want crc32 */ + } else { + ulong crc; + ulong *ptr; + + crc = crc32_wd(0, (const uchar *)addr, len, CHUNKSZ_CRC32); + + printf("CRC32 for %08lx ... %08lx ==> %08lx\n", + addr, addr + len - 1, crc); + + if (argc >= 3) { + ptr = (ulong *)simple_strtoul(argv[0], NULL, 16); + *ptr = crc; + } + } + + return 0; +} +#endif /* CONFIG_CMD_HASH || CONFIG_CMD_SHA1SUM || CONFIG_CMD_CRC32) */ +#endif /* !USE_HOSTCC */ diff --git a/roms/u-boot/common/hwconfig.c b/roms/u-boot/common/hwconfig.c new file mode 100644 index 000000000..26a561c36 --- /dev/null +++ b/roms/u-boot/common/hwconfig.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * An inteface for configuring a hardware via u-boot environment. + * + * Copyright (c) 2009 MontaVista Software, Inc. + * Copyright 2011 Freescale Semiconductor, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + */ + +#ifndef HWCONFIG_TEST +#include <config.h> +#include <common.h> +#include <env.h> +#include <exports.h> +#include <hwconfig.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/types.h> +#include <linux/string.h> +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* HWCONFIG_TEST */ + +DECLARE_GLOBAL_DATA_PTR; + +static const char *hwconfig_parse(const char *opts, size_t maxlen, + const char *opt, char *stopchs, char eqch, + size_t *arglen) +{ + size_t optlen = strlen(opt); + char *str; + const char *start = opts; + const char *end; + +next: + str = strstr(opts, opt); + end = str + optlen; + if (end - start > maxlen) + return NULL; + + if (str && (str == opts || strpbrk(str - 1, stopchs) == str - 1) && + (strpbrk(end, stopchs) == end || *end == eqch || + *end == '\0')) { + const char *arg_end; + + if (!arglen) + return str; + + if (*end != eqch) + return NULL; + + arg_end = strpbrk(str, stopchs); + if (!arg_end) + *arglen = min(maxlen, strlen(str)) - optlen - 1; + else + *arglen = arg_end - end - 1; + + return end + 1; + } else if (str) { + opts = end; + goto next; + } + return NULL; +} + +const char cpu_hwconfig[] __attribute__((weak)) = ""; +const char board_hwconfig[] __attribute__((weak)) = ""; + +static const char *__hwconfig(const char *opt, size_t *arglen, + const char *env_hwconfig) +{ + const char *ret; + + /* if we are passed a buffer use it, otherwise try the environment */ + if (!env_hwconfig) { + if (!(gd->flags & GD_FLG_ENV_READY)) { + printf("WARNING: Calling __hwconfig without a buffer " + "and before environment is ready\n"); + return NULL; + } + env_hwconfig = env_get("hwconfig"); + } + + if (env_hwconfig) { + ret = hwconfig_parse(env_hwconfig, strlen(env_hwconfig), + opt, ";", ':', arglen); + if (ret) + return ret; + } + + ret = hwconfig_parse(board_hwconfig, strlen(board_hwconfig), + opt, ";", ':', arglen); + if (ret) + return ret; + + return hwconfig_parse(cpu_hwconfig, strlen(cpu_hwconfig), + opt, ";", ':', arglen); +} + +/* + * hwconfig_f - query if a particular hwconfig option is specified + * @opt: a string representing an option + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call can be used to find out whether U-Boot should configure + * a particular hardware option. + * + * Returns non-zero value if the hardware option can be used and thus + * should be configured, 0 otherwise. + * + * This function also returns non-zero value if CONFIG_HWCONFIG is + * undefined. + * + * Returning non-zero value without CONFIG_HWCONFIG has its crucial + * purpose: the hwconfig() call should be a "transparent" interface, + * e.g. if a board doesn't need hwconfig facility, then we assume + * that the board file only calls things that are actually used, so + * hwconfig() will always return true result. + */ +int hwconfig_f(const char *opt, char *buf) +{ + return !!__hwconfig(opt, NULL, buf); +} + +/* + * hwconfig_arg_f - get hwconfig option's argument + * @opt: a string representing an option + * @arglen: a pointer to an allocated size_t variable + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * Unlike hwconfig_f() function, this function returns a pointer to the + * start of the hwconfig arguments, if option is not found or it has + * no specified arguments, the function returns NULL pointer. + * + * If CONFIG_HWCONFIG is undefined, the function returns "", and + * arglen is set to 0. + */ +const char *hwconfig_arg_f(const char *opt, size_t *arglen, char *buf) +{ + return __hwconfig(opt, arglen, buf); +} + +/* + * hwconfig_arg_cmp_f - compare hwconfig option's argument + * @opt: a string representing an option + * @arg: a string for comparing an option's argument + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_f, but instead of returning + * hwconfig argument and its length, it is comparing it to @arg. + * + * Returns non-zero value if @arg matches, 0 otherwise. + * + * If CONFIG_HWCONFIG is undefined, the function returns a non-zero + * value, i.e. the argument matches. + */ +int hwconfig_arg_cmp_f(const char *opt, const char *arg, char *buf) +{ + const char *argstr; + size_t arglen; + + argstr = hwconfig_arg_f(opt, &arglen, buf); + if (!argstr || arglen != strlen(arg)) + return 0; + + return !strncmp(argstr, arg, arglen); +} + +/* + * hwconfig_sub_f - query if a particular hwconfig sub-option is specified + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_f(), except that it takes additional + * argument @subopt. In this example: + * "dr_usb:mode=peripheral" + * "dr_usb" is an option, "mode" is a sub-option, and "peripheral" is its + * argument. + */ +int hwconfig_sub_f(const char *opt, const char *subopt, char *buf) +{ + size_t arglen; + const char *arg; + + arg = __hwconfig(opt, &arglen, buf); + if (!arg) + return 0; + return !!hwconfig_parse(arg, arglen, subopt, ",;", '=', NULL); +} + +/* + * hwconfig_subarg_f - get hwconfig sub-option's argument + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @subarglen: a pointer to an allocated size_t variable + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_f(), except that it takes an + * additional argument @subopt, and so works with sub-options. + */ +const char *hwconfig_subarg_f(const char *opt, const char *subopt, + size_t *subarglen, char *buf) +{ + size_t arglen; + const char *arg; + + arg = __hwconfig(opt, &arglen, buf); + if (!arg) + return NULL; + return hwconfig_parse(arg, arglen, subopt, ",;", '=', subarglen); +} + +/* + * hwconfig_arg_cmp_f - compare hwconfig sub-option's argument + * @opt: a string representing an option + * @subopt: a string representing a sub-option + * @subarg: a string for comparing an sub-option's argument + * @buf: if non-NULL use this buffer to parse, otherwise try env + * + * This call is similar to hwconfig_arg_cmp_f, except that it takes an + * additional argument @subopt, and so works with sub-options. + */ +int hwconfig_subarg_cmp_f(const char *opt, const char *subopt, + const char *subarg, char *buf) +{ + const char *argstr; + size_t arglen; + + argstr = hwconfig_subarg_f(opt, subopt, &arglen, buf); + if (!argstr || arglen != strlen(subarg)) + return 0; + + return !strncmp(argstr, subarg, arglen); +} + +#ifdef HWCONFIG_TEST +int main() +{ + const char *ret; + size_t len; + + env_set("hwconfig", "key1:subkey1=value1,subkey2=value2;key2:value3;;;;" + "key3;:,:=;key4", 1); + + ret = hwconfig_arg("key1", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 29); + assert(hwconfig_arg_cmp("key1", "subkey1=value1,subkey2=value2")); + assert(!strncmp(ret, "subkey1=value1,subkey2=value2", len)); + + ret = hwconfig_subarg("key1", "subkey1", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_subarg_cmp("key1", "subkey1", "value1")); + assert(!strncmp(ret, "value1", len)); + + ret = hwconfig_subarg("key1", "subkey2", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_subarg_cmp("key1", "subkey2", "value2")); + assert(!strncmp(ret, "value2", len)); + + ret = hwconfig_arg("key2", &len); + printf("%zd %.*s\n", len, (int)len, ret); + assert(len == 6); + assert(hwconfig_arg_cmp("key2", "value3")); + assert(!strncmp(ret, "value3", len)); + + assert(hwconfig("key3")); + assert(hwconfig_arg("key4", &len) == NULL); + assert(hwconfig_arg("bogus", &len) == NULL); + + unenv_set("hwconfig"); + + assert(hwconfig(NULL) == 0); + assert(hwconfig("") == 0); + assert(hwconfig("key3") == 0); + + return 0; +} +#endif /* HWCONFIG_TEST */ diff --git a/roms/u-boot/common/image-android-dt.c b/roms/u-boot/common/image-android-dt.c new file mode 100644 index 000000000..a2d52df4a --- /dev/null +++ b/roms/u-boot/common/image-android-dt.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + */ + +#include <image-android-dt.h> +#include <dt_table.h> +#include <common.h> +#include <linux/libfdt.h> +#include <mapmem.h> + +/** + * Check if image header is correct. + * + * @param hdr_addr Start address of DT image + * @return true if header is correct or false if header is incorrect + */ +bool android_dt_check_header(ulong hdr_addr) +{ + const struct dt_table_header *hdr; + u32 magic; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + magic = fdt32_to_cpu(hdr->magic); + unmap_sysmem(hdr); + + return magic == DT_TABLE_MAGIC; +} + +/** + * Get the address of FDT (dtb or dtbo) in memory by its index in image. + * + * @param hdr_addr Start address of DT image + * @param index Index of desired FDT in image (starting from 0) + * @param[out] addr If not NULL, will contain address to specified FDT + * @param[out] size If not NULL, will contain size of specified FDT + * + * @return true on success or false on error + */ +bool android_dt_get_fdt_by_index(ulong hdr_addr, u32 index, ulong *addr, + u32 *size) +{ + const struct dt_table_header *hdr; + const struct dt_table_entry *e; + u32 entry_count, entries_offset, entry_size; + ulong e_addr; + u32 dt_offset, dt_size; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + entry_count = fdt32_to_cpu(hdr->dt_entry_count); + entries_offset = fdt32_to_cpu(hdr->dt_entries_offset); + entry_size = fdt32_to_cpu(hdr->dt_entry_size); + unmap_sysmem(hdr); + + if (index >= entry_count) { + printf("Error: index >= dt_entry_count (%u >= %u)\n", index, + entry_count); + return false; + } + + e_addr = hdr_addr + entries_offset + index * entry_size; + e = map_sysmem(e_addr, sizeof(*e)); + dt_offset = fdt32_to_cpu(e->dt_offset); + dt_size = fdt32_to_cpu(e->dt_size); + unmap_sysmem(e); + + if (addr) + *addr = hdr_addr + dt_offset; + if (size) + *size = dt_size; + + return true; +} + +#if !defined(CONFIG_SPL_BUILD) +static void android_dt_print_fdt_info(const struct fdt_header *fdt) +{ + u32 fdt_size; + int root_node_off; + const char *compatible; + + root_node_off = fdt_path_offset(fdt, "/"); + if (root_node_off < 0) { + printf("Error: Root node not found\n"); + return; + } + + fdt_size = fdt_totalsize(fdt); + compatible = fdt_getprop(fdt, root_node_off, "compatible", + NULL); + + printf(" (FDT)size = %d\n", fdt_size); + printf(" (FDT)compatible = %s\n", + compatible ? compatible : "(unknown)"); +} + +/** + * Print information about DT image structure. + * + * @param hdr_addr Start address of DT image + */ +void android_dt_print_contents(ulong hdr_addr) +{ + const struct dt_table_header *hdr; + u32 entry_count, entries_offset, entry_size; + u32 i; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + entry_count = fdt32_to_cpu(hdr->dt_entry_count); + entries_offset = fdt32_to_cpu(hdr->dt_entries_offset); + entry_size = fdt32_to_cpu(hdr->dt_entry_size); + + /* Print image header info */ + printf("dt_table_header:\n"); + printf(" magic = %08x\n", fdt32_to_cpu(hdr->magic)); + printf(" total_size = %d\n", fdt32_to_cpu(hdr->total_size)); + printf(" header_size = %d\n", fdt32_to_cpu(hdr->header_size)); + printf(" dt_entry_size = %d\n", entry_size); + printf(" dt_entry_count = %d\n", entry_count); + printf(" dt_entries_offset = %d\n", entries_offset); + printf(" page_size = %d\n", fdt32_to_cpu(hdr->page_size)); + printf(" version = %d\n", fdt32_to_cpu(hdr->version)); + + unmap_sysmem(hdr); + + /* Print image entries info */ + for (i = 0; i < entry_count; ++i) { + const ulong e_addr = hdr_addr + entries_offset + i * entry_size; + const struct dt_table_entry *e; + const struct fdt_header *fdt; + u32 dt_offset, dt_size; + u32 j; + + e = map_sysmem(e_addr, sizeof(*e)); + dt_offset = fdt32_to_cpu(e->dt_offset); + dt_size = fdt32_to_cpu(e->dt_size); + + printf("dt_table_entry[%d]:\n", i); + printf(" dt_size = %d\n", dt_size); + printf(" dt_offset = %d\n", dt_offset); + printf(" id = %08x\n", fdt32_to_cpu(e->id)); + printf(" rev = %08x\n", fdt32_to_cpu(e->rev)); + for (j = 0; j < 4; ++j) { + printf(" custom[%d] = %08x\n", j, + fdt32_to_cpu(e->custom[j])); + } + + unmap_sysmem(e); + + /* Print FDT info for this entry */ + fdt = map_sysmem(hdr_addr + dt_offset, sizeof(*fdt)); + android_dt_print_fdt_info(fdt); + unmap_sysmem(fdt); + } +} +#endif diff --git a/roms/u-boot/common/image-android.c b/roms/u-boot/common/image-android.c new file mode 100644 index 000000000..d07b0e0f0 --- /dev/null +++ b/roms/u-boot/common/image-android.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> + */ + +#include <common.h> +#include <env.h> +#include <image.h> +#include <image-android-dt.h> +#include <android_image.h> +#include <malloc.h> +#include <errno.h> +#include <asm/unaligned.h> +#include <mapmem.h> +#include <linux/libfdt.h> + +#define ANDROID_IMAGE_DEFAULT_KERNEL_ADDR 0x10008000 + +static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; + +static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) +{ + /* + * All the Android tools that generate a boot.img use this + * address as the default. + * + * Even though it doesn't really make a lot of sense, and it + * might be valid on some platforms, we treat that adress as + * the default value for this field, and try to execute the + * kernel in place in such a case. + * + * Otherwise, we will return the actual value set by the user. + */ + if (hdr->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) + return (ulong)hdr + hdr->page_size; + + /* + * abootimg creates images where all load addresses are 0 + * and we need to fix them. + */ + if (hdr->kernel_addr == 0 && hdr->ramdisk_addr == 0) + return env_get_ulong("kernel_addr_r", 16, 0); + + return hdr->kernel_addr; +} + +/** + * android_image_get_kernel() - processes kernel part of Android boot images + * @hdr: Pointer to image header, which is at the start + * of the image. + * @verify: Checksum verification flag. Currently unimplemented. + * @os_data: Pointer to a ulong variable, will hold os data start + * address. + * @os_len: Pointer to a ulong variable, will hold os data length. + * + * This function returns the os image's start address and length. Also, + * it appends the kernel command line to the bootargs env variable. + * + * Return: Zero, os start address and length on success, + * otherwise on failure. + */ +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len) +{ + u32 kernel_addr = android_image_get_kernel_addr(hdr); + const struct image_header *ihdr = (const struct image_header *) + ((uintptr_t)hdr + hdr->page_size); + + /* + * Not all Android tools use the id field for signing the image with + * sha1 (or anything) so we don't check it. It is not obvious that the + * string is null terminated so we take care of this. + */ + strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; + if (strlen(andr_tmp_str)) + printf("Android's image name: %s\n", andr_tmp_str); + + printf("Kernel load addr 0x%08x size %u KiB\n", + kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + + int len = 0; + if (*hdr->cmdline) { + printf("Kernel command line: %s\n", hdr->cmdline); + len += strlen(hdr->cmdline); + } + + char *bootargs = env_get("bootargs"); + if (bootargs) + len += strlen(bootargs); + + char *newbootargs = malloc(len + 2); + if (!newbootargs) { + puts("Error: malloc in android_image_get_kernel failed!\n"); + return -ENOMEM; + } + *newbootargs = '\0'; + + if (bootargs) { + strcpy(newbootargs, bootargs); + strcat(newbootargs, " "); + } + if (*hdr->cmdline) + strcat(newbootargs, hdr->cmdline); + + env_set("bootargs", newbootargs); + + if (os_data) { + if (image_get_magic(ihdr) == IH_MAGIC) { + *os_data = image_get_data(ihdr); + } else { + *os_data = (ulong)hdr; + *os_data += hdr->page_size; + } + } + if (os_len) { + if (image_get_magic(ihdr) == IH_MAGIC) + *os_len = image_get_data_size(ihdr); + else + *os_len = hdr->kernel_size; + } + return 0; +} + +int android_image_check_header(const struct andr_img_hdr *hdr) +{ + return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); +} + +ulong android_image_get_end(const struct andr_img_hdr *hdr) +{ + ulong end; + + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + end = (ulong)hdr; + end += hdr->page_size; + end += ALIGN(hdr->kernel_size, hdr->page_size); + end += ALIGN(hdr->ramdisk_size, hdr->page_size); + end += ALIGN(hdr->second_size, hdr->page_size); + + if (hdr->header_version >= 1) + end += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + + if (hdr->header_version >= 2) + end += ALIGN(hdr->dtb_size, hdr->page_size); + + return end; +} + +ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{ + return android_image_get_kernel_addr(hdr); +} + +ulong android_image_get_kcomp(const struct andr_img_hdr *hdr) +{ + const void *p = (void *)((uintptr_t)hdr + hdr->page_size); + + if (image_get_magic((image_header_t *)p) == IH_MAGIC) + return image_get_comp((image_header_t *)p); + else if (get_unaligned_le32(p) == LZ4F_MAGIC) + return IH_COMP_LZ4; + else + return IH_COMP_NONE; +} + +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len) +{ + if (!hdr->ramdisk_size) { + *rd_data = *rd_len = 0; + return -1; + } + + printf("RAM disk load addr 0x%08x size %u KiB\n", + hdr->ramdisk_addr, DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + + *rd_data = (unsigned long)hdr; + *rd_data += hdr->page_size; + *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + + *rd_len = hdr->ramdisk_size; + return 0; +} + +int android_image_get_second(const struct andr_img_hdr *hdr, + ulong *second_data, ulong *second_len) +{ + if (!hdr->second_size) { + *second_data = *second_len = 0; + return -1; + } + + *second_data = (unsigned long)hdr; + *second_data += hdr->page_size; + *second_data += ALIGN(hdr->kernel_size, hdr->page_size); + *second_data += ALIGN(hdr->ramdisk_size, hdr->page_size); + + printf("second address is 0x%lx\n",*second_data); + + *second_len = hdr->second_size; + return 0; +} + +/** + * android_image_get_dtbo() - Get address and size of recovery DTBO image. + * @hdr_addr: Boot image header address + * @addr: If not NULL, will contain address of recovery DTBO image + * @size: If not NULL, will contain size of recovery DTBO image + * + * Get the address and size of DTBO image in "Recovery DTBO" area of Android + * Boot Image in RAM. The format of this image is Android DTBO (see + * corresponding "DTB/DTBO Partitions" AOSP documentation for details). Once + * the address is obtained from this function, one can use 'adtimg' U-Boot + * command or android_dt_*() functions to extract desired DTBO blob. + * + * This DTBO (included in boot image) is only needed for non-A/B devices, and it + * only can be found in recovery image. On A/B devices we can always rely on + * "dtbo" partition. See "Including DTBO in Recovery for Non-A/B Devices" in + * AOSP documentation for details. + * + * Return: true on success or false on error. + */ +bool android_image_get_dtbo(ulong hdr_addr, ulong *addr, u32 *size) +{ + const struct andr_img_hdr *hdr; + ulong dtbo_img_addr; + bool ret = true; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + ret = false; + goto exit; + } + + if (hdr->header_version < 1) { + printf("Error: header_version must be >= 1 to get dtbo\n"); + ret = false; + goto exit; + } + + if (hdr->recovery_dtbo_size == 0) { + printf("Error: recovery_dtbo_size is 0\n"); + ret = false; + goto exit; + } + + /* Calculate the address of DTB area in boot image */ + dtbo_img_addr = hdr_addr; + dtbo_img_addr += hdr->page_size; + dtbo_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); + dtbo_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); + dtbo_img_addr += ALIGN(hdr->second_size, hdr->page_size); + + if (addr) + *addr = dtbo_img_addr; + if (size) + *size = hdr->recovery_dtbo_size; + +exit: + unmap_sysmem(hdr); + return ret; +} + +/** + * android_image_get_dtb_img_addr() - Get the address of DTB area in boot image. + * @hdr_addr: Boot image header address + * @addr: Will contain the address of DTB area in boot image + * + * Return: true on success or false on fail. + */ +static bool android_image_get_dtb_img_addr(ulong hdr_addr, ulong *addr) +{ + const struct andr_img_hdr *hdr; + ulong dtb_img_addr; + bool ret = true; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + ret = false; + goto exit; + } + + if (hdr->header_version < 2) { + printf("Error: header_version must be >= 2 to get dtb\n"); + ret = false; + goto exit; + } + + if (hdr->dtb_size == 0) { + printf("Error: dtb_size is 0\n"); + ret = false; + goto exit; + } + + /* Calculate the address of DTB area in boot image */ + dtb_img_addr = hdr_addr; + dtb_img_addr += hdr->page_size; + dtb_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->second_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + + *addr = dtb_img_addr; + +exit: + unmap_sysmem(hdr); + return ret; +} + +/** + * android_image_get_dtb_by_index() - Get address and size of blob in DTB area. + * @hdr_addr: Boot image header address + * @index: Index of desired DTB in DTB area (starting from 0) + * @addr: If not NULL, will contain address to specified DTB + * @size: If not NULL, will contain size of specified DTB + * + * Get the address and size of DTB blob by its index in DTB area of Android + * Boot Image in RAM. + * + * Return: true on success or false on error. + */ +bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, + u32 *size) +{ + const struct andr_img_hdr *hdr; + bool res; + ulong dtb_img_addr; /* address of DTB part in boot image */ + u32 dtb_img_size; /* size of DTB payload in boot image */ + ulong dtb_addr; /* address of DTB blob with specified index */ + u32 i; /* index iterator */ + + res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); + if (!res) + return false; + + /* Check if DTB area of boot image is in DTBO format */ + if (android_dt_check_header(dtb_img_addr)) { + return android_dt_get_fdt_by_index(dtb_img_addr, index, addr, + size); + } + + /* Find out the address of DTB with specified index in concat blobs */ + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + dtb_img_size = hdr->dtb_size; + unmap_sysmem(hdr); + i = 0; + dtb_addr = dtb_img_addr; + while (dtb_addr < dtb_img_addr + dtb_img_size) { + const struct fdt_header *fdt; + u32 dtb_size; + + fdt = map_sysmem(dtb_addr, sizeof(*fdt)); + if (fdt_check_header(fdt) != 0) { + unmap_sysmem(fdt); + printf("Error: Invalid FDT header for index %u\n", i); + return false; + } + + dtb_size = fdt_totalsize(fdt); + unmap_sysmem(fdt); + + if (i == index) { + if (size) + *size = dtb_size; + if (addr) + *addr = dtb_addr; + return true; + } + + dtb_addr += dtb_size; + ++i; + } + + printf("Error: Index is out of bounds (%u/%u)\n", index, i); + return false; +} + +#if !defined(CONFIG_SPL_BUILD) +/** + * android_print_contents - prints out the contents of the Android format image + * @hdr: pointer to the Android format image header + * + * android_print_contents() formats a multi line Android image contents + * description. + * The routine prints out Android image properties + * + * returns: + * no returned results + */ +void android_print_contents(const struct andr_img_hdr *hdr) +{ + const char * const p = IMAGE_INDENT_STRING; + /* os_version = ver << 11 | lvl */ + u32 os_ver = hdr->os_version >> 11; + u32 os_lvl = hdr->os_version & ((1U << 11) - 1); + + printf("%skernel size: %x\n", p, hdr->kernel_size); + printf("%skernel address: %x\n", p, hdr->kernel_addr); + printf("%sramdisk size: %x\n", p, hdr->ramdisk_size); + printf("%sramdisk address: %x\n", p, hdr->ramdisk_addr); + printf("%ssecond size: %x\n", p, hdr->second_size); + printf("%ssecond address: %x\n", p, hdr->second_addr); + printf("%stags address: %x\n", p, hdr->tags_addr); + printf("%spage size: %x\n", p, hdr->page_size); + /* ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) + * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) */ + printf("%sos_version: %x (ver: %u.%u.%u, level: %u.%u)\n", + p, hdr->os_version, + (os_ver >> 7) & 0x7F, (os_ver >> 14) & 0x7F, os_ver & 0x7F, + (os_lvl >> 4) + 2000, os_lvl & 0x0F); + printf("%sname: %s\n", p, hdr->name); + printf("%scmdline: %s\n", p, hdr->cmdline); + printf("%sheader_version: %d\n", p, hdr->header_version); + + if (hdr->header_version >= 1) { + printf("%srecovery dtbo size: %x\n", p, + hdr->recovery_dtbo_size); + printf("%srecovery dtbo offset: %llx\n", p, + hdr->recovery_dtbo_offset); + printf("%sheader size: %x\n", p, + hdr->header_size); + } + + if (hdr->header_version >= 2) { + printf("%sdtb size: %x\n", p, hdr->dtb_size); + printf("%sdtb addr: %llx\n", p, hdr->dtb_addr); + } +} + +/** + * android_image_print_dtb_info - Print info for one DTB blob in DTB area. + * @fdt: DTB header + * @index: Number of DTB blob in DTB area. + * + * Return: true on success or false on error. + */ +static bool android_image_print_dtb_info(const struct fdt_header *fdt, + u32 index) +{ + int root_node_off; + u32 fdt_size; + const char *model; + const char *compatible; + + root_node_off = fdt_path_offset(fdt, "/"); + if (root_node_off < 0) { + printf("Error: Root node not found\n"); + return false; + } + + fdt_size = fdt_totalsize(fdt); + compatible = fdt_getprop(fdt, root_node_off, "compatible", + NULL); + model = fdt_getprop(fdt, root_node_off, "model", NULL); + + printf(" - DTB #%u:\n", index); + printf(" (DTB)size = %d\n", fdt_size); + printf(" (DTB)model = %s\n", model ? model : "(unknown)"); + printf(" (DTB)compatible = %s\n", + compatible ? compatible : "(unknown)"); + + return true; +} + +/** + * android_image_print_dtb_contents() - Print info for DTB blobs in DTB area. + * @hdr_addr: Boot image header address + * + * DTB payload in Android Boot Image v2+ can be in one of following formats: + * 1. Concatenated DTB blobs + * 2. Android DTBO format (see CONFIG_CMD_ADTIMG for details) + * + * This function does next: + * 1. Prints out the format used in DTB area + * 2. Iterates over all DTB blobs in DTB area and prints out the info for + * each blob. + * + * Return: true on success or false on error. + */ +bool android_image_print_dtb_contents(ulong hdr_addr) +{ + const struct andr_img_hdr *hdr; + bool res; + ulong dtb_img_addr; /* address of DTB part in boot image */ + u32 dtb_img_size; /* size of DTB payload in boot image */ + ulong dtb_addr; /* address of DTB blob with specified index */ + u32 i; /* index iterator */ + + res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); + if (!res) + return false; + + /* Check if DTB area of boot image is in DTBO format */ + if (android_dt_check_header(dtb_img_addr)) { + printf("## DTB area contents (DTBO format):\n"); + android_dt_print_contents(dtb_img_addr); + return true; + } + + printf("## DTB area contents (concat format):\n"); + + /* Iterate over concatenated DTB blobs */ + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + dtb_img_size = hdr->dtb_size; + unmap_sysmem(hdr); + i = 0; + dtb_addr = dtb_img_addr; + while (dtb_addr < dtb_img_addr + dtb_img_size) { + const struct fdt_header *fdt; + u32 dtb_size; + + fdt = map_sysmem(dtb_addr, sizeof(*fdt)); + if (fdt_check_header(fdt) != 0) { + unmap_sysmem(fdt); + printf("Error: Invalid FDT header for index %u\n", i); + return false; + } + + res = android_image_print_dtb_info(fdt, i); + if (!res) { + unmap_sysmem(fdt); + return false; + } + + dtb_size = fdt_totalsize(fdt); + unmap_sysmem(fdt); + dtb_addr += dtb_size; + ++i; + } + + return true; +} +#endif diff --git a/roms/u-boot/common/image-cipher.c b/roms/u-boot/common/image-cipher.c new file mode 100644 index 000000000..b90614893 --- /dev/null +++ b/roms/u-boot/common/image-cipher.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Softathome + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSdTCC*/ +#include <image.h> +#include <uboot_aes.h> +#include <u-boot/aes.h> + +struct cipher_algo cipher_algos[] = { + { + .name = "aes128", + .key_len = AES128_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_128_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + }, + { + .name = "aes192", + .key_len = AES192_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_192_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + }, + { + .name = "aes256", + .key_len = AES256_KEY_LENGTH, + .iv_len = AES_BLOCK_LENGTH, +#if IMAGE_ENABLE_ENCRYPT + .calculate_type = EVP_aes_256_cbc, +#endif + .encrypt = image_aes_encrypt, + .decrypt = image_aes_decrypt, + .add_cipher_data = image_aes_add_cipher_data + } +}; + +struct cipher_algo *image_get_cipher_algo(const char *full_name) +{ + int i; + const char *name; + + for (i = 0; i < ARRAY_SIZE(cipher_algos); i++) { + name = cipher_algos[i].name; + if (!strncmp(name, full_name, strlen(name))) + return &cipher_algos[i]; + } + + return NULL; +} + +static int fit_image_setup_decrypt(struct image_cipher_info *info, + const void *fit, int image_noffset, + int cipher_noffset) +{ + const void *fdt = gd_fdt_blob(); + const char *node_name; + char node_path[128]; + int noffset; + char *algo_name; + int ret; + + node_name = fit_get_name(fit, image_noffset, NULL); + if (!node_name) { + printf("Can't get node name\n"); + return -1; + } + + if (fit_image_cipher_get_algo(fit, cipher_noffset, &algo_name)) { + printf("Can't get algo name for cipher '%s' in image '%s'\n", + node_name, node_name); + return -1; + } + + info->keyname = fdt_getprop(fit, cipher_noffset, FIT_KEY_HINT, NULL); + if (!info->keyname) { + printf("Can't get key name\n"); + return -1; + } + + info->iv = fdt_getprop(fit, cipher_noffset, "iv", NULL); + info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL); + + if (!info->iv && !info->ivname) { + printf("Can't get IV or IV name\n"); + return -1; + } + + info->fit = fit; + info->node_noffset = image_noffset; + info->name = algo_name; + info->cipher = image_get_cipher_algo(algo_name); + if (!info->cipher) { + printf("Can't get cipher\n"); + return -1; + } + + ret = fit_image_get_data_size_unciphered(fit, image_noffset, + &info->size_unciphered); + if (ret) { + printf("Can't get size of unciphered data\n"); + return -1; + } + + /* + * Search the cipher node in the u-boot fdt + * the path should be: /cipher/key-<algo>-<key>-<iv> + */ + if (info->ivname) + snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s-%s", + FIT_CIPHER_NODENAME, algo_name, info->keyname, info->ivname); + else + snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s", + FIT_CIPHER_NODENAME, algo_name, info->keyname); + + noffset = fdt_path_offset(fdt, node_path); + if (noffset < 0) { + printf("Can't found cipher node offset\n"); + return -1; + } + + /* read key */ + info->key = fdt_getprop(fdt, noffset, "key", NULL); + if (!info->key) { + printf("Can't get key in cipher node '%s'\n", node_path); + return -1; + } + + /* read iv */ + if (!info->iv) { + info->iv = fdt_getprop(fdt, noffset, "iv", NULL); + if (!info->iv) { + printf("Can't get IV in cipher node '%s'\n", node_path); + return -1; + } + } + + return 0; +} + +int fit_image_decrypt_data(const void *fit, + int image_noffset, int cipher_noffset, + const void *data_ciphered, size_t size_ciphered, + void **data_unciphered, size_t *size_unciphered) +{ + struct image_cipher_info info; + int ret; + + ret = fit_image_setup_decrypt(&info, fit, image_noffset, + cipher_noffset); + if (ret < 0) + goto out; + + ret = info.cipher->decrypt(&info, data_ciphered, size_ciphered, + data_unciphered, size_unciphered); + + out: + return ret; +} diff --git a/roms/u-boot/common/image-fdt.c b/roms/u-boot/common/image-fdt.c new file mode 100644 index 000000000..d50e1ba3f --- /dev/null +++ b/roms/u-boot/common/image-fdt.c @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <fdt_support.h> +#include <fdtdec.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <mapmem.h> +#include <asm/io.h> +#include <tee/optee.h> + +#ifndef CONFIG_SYS_FDT_PAD +#define CONFIG_SYS_FDT_PAD 0x3000 +#endif + +/* adding a ramdisk needs 0x44 bytes in version 2008.10 */ +#define FDT_RAMDISK_OVERHEAD 0x80 + +DECLARE_GLOBAL_DATA_PTR; + +static void fdt_error(const char *msg) +{ + puts("ERROR: "); + puts(msg); + puts(" - must RESET the board to recover.\n"); +} + +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +static const image_header_t *image_get_fdt(ulong fdt_addr) +{ + const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0); + + image_print_contents(fdt_hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_hcrc(fdt_hdr)) { + fdt_error("fdt header checksum invalid"); + return NULL; + } + + if (!image_check_dcrc(fdt_hdr)) { + fdt_error("fdt checksum invalid"); + return NULL; + } + puts("OK\n"); + + if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) { + fdt_error("uImage is not a fdt"); + return NULL; + } + if (image_get_comp(fdt_hdr) != IH_COMP_NONE) { + fdt_error("uImage is compressed"); + return NULL; + } + if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) { + fdt_error("uImage data is not a fdt"); + return NULL; + } + return fdt_hdr; +} +#endif + +static void boot_fdt_reserve_region(struct lmb *lmb, uint64_t addr, + uint64_t size) +{ + long ret; + + ret = lmb_reserve(lmb, addr, size); + if (ret >= 0) { + debug(" reserving fdt memory region: addr=%llx size=%llx\n", + (unsigned long long)addr, (unsigned long long)size); + } else { + puts("ERROR: reserving fdt memory region failed "); + printf("(addr=%llx size=%llx)\n", + (unsigned long long)addr, (unsigned long long)size); + } +} + +/** + * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory + * sections as unusable + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @fdt_blob: pointer to fdt blob base address + * + * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block. + * Adding the memreserve regions prevents u-boot from using them to store the + * initrd or the fdt blob. + */ +void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob) +{ + uint64_t addr, size; + int i, total, ret; + int nodeoffset, subnode; + struct fdt_resource res; + + if (fdt_check_header(fdt_blob) != 0) + return; + + /* process memreserve sections */ + total = fdt_num_mem_rsv(fdt_blob); + for (i = 0; i < total; i++) { + if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0) + continue; + boot_fdt_reserve_region(lmb, addr, size); + } + + /* process reserved-memory */ + nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory"); + if (nodeoffset >= 0) { + subnode = fdt_first_subnode(fdt_blob, nodeoffset); + while (subnode >= 0) { + /* check if this subnode has a reg property */ + ret = fdt_get_resource(fdt_blob, subnode, "reg", 0, + &res); + if (!ret && fdtdec_get_is_enabled(fdt_blob, subnode)) { + addr = res.start; + size = res.end - res.start + 1; + boot_fdt_reserve_region(lmb, addr, size); + } + + subnode = fdt_next_subnode(fdt_blob, subnode); + } + } +} + +/** + * boot_relocate_fdt - relocate flat device tree + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_relocate_fdt() allocates a region of memory within the bootmap and + * relocates the of_flat_tree into that region, even if the fdt is already in + * the bootmap. It also expands the size of the fdt by CONFIG_SYS_FDT_PAD + * bytes. + * + * of_flat_tree and of_size are set to final (after relocation) values + * + * returns: + * 0 - success + * 1 - failure + */ +int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) +{ + void *fdt_blob = *of_flat_tree; + void *of_start = NULL; + char *fdt_high; + ulong of_len = 0; + int err; + int disable_relocation = 0; + + /* nothing to do */ + if (*of_size == 0) + return 0; + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + /* position on a 4K boundary before the alloc_current */ + /* Pad the FDT by a specified amount */ + of_len = *of_size + CONFIG_SYS_FDT_PAD; + + /* If fdt_high is set use it to select the relocation address */ + fdt_high = env_get("fdt_high"); + if (fdt_high) { + void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16); + + if (((ulong) desired_addr) == ~0UL) { + /* All ones means use fdt in place */ + of_start = fdt_blob; + lmb_reserve(lmb, (ulong)of_start, of_len); + disable_relocation = 1; + } else if (desired_addr) { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + (ulong)desired_addr); + if (of_start == NULL) { + puts("Failed using fdt_high value for Device Tree"); + goto error; + } + } else { + of_start = + (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000); + } + } else { + of_start = + (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, + env_get_bootm_mapsize() + + env_get_bootm_low()); + } + + if (of_start == NULL) { + puts("device tree - allocation error\n"); + goto error; + } + + if (disable_relocation) { + /* + * We assume there is space after the existing fdt to use + * for padding + */ + fdt_set_totalsize(of_start, of_len); + printf(" Using Device Tree in place at %p, end %p\n", + of_start, of_start + of_len - 1); + } else { + debug("## device tree at %p ... %p (len=%ld [0x%lX])\n", + fdt_blob, fdt_blob + *of_size - 1, of_len, of_len); + + printf(" Loading Device Tree to %p, end %p ... ", + of_start, of_start + of_len - 1); + + err = fdt_open_into(fdt_blob, of_start, of_len); + if (err != 0) { + fdt_error("fdt move failed"); + goto error; + } + puts("OK\n"); + } + + *of_flat_tree = of_start; + *of_size = of_len; + + if (CONFIG_IS_ENABLED(CMD_FDT)) + set_working_fdt_addr(map_to_sysmem(*of_flat_tree)); + return 0; + +error: + return 1; +} + +/** + * boot_get_fdt - main fdt handling routine + * @argc: command argument count + * @argv: command argument list + * @arch: architecture (IH_ARCH_...) + * @images: pointer to the bootm images structure + * @of_flat_tree: pointer to a char* variable, will hold fdt start address + * @of_size: pointer to a ulong variable, will hold fdt length + * + * boot_get_fdt() is responsible for finding a valid flat device tree image. + * Curently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if fdt image was found and valid, or skipped + * of_flat_tree and of_size are set to fdt start address and length if + * fdt image is found and valid + * + * 1, if fdt image is found but corrupted + * of_flat_tree and of_size are set to 0 if no fdt exists + */ +int boot_get_fdt(int flag, int argc, char *const argv[], uint8_t arch, + bootm_headers_t *images, char **of_flat_tree, ulong *of_size) +{ +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + const image_header_t *fdt_hdr; + ulong load, load_end; + ulong image_start, image_data, image_end; +#endif + ulong img_addr; + ulong fdt_addr; + char *fdt_blob = NULL; + void *buf; +#if CONFIG_IS_ENABLED(FIT) + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_fdt = NULL; + ulong default_addr; + int fdt_noffset; +#endif + const char *select = NULL; + + *of_flat_tree = NULL; + *of_size = 0; + + img_addr = (argc == 0) ? image_load_addr : + simple_strtoul(argv[0], NULL, 16); + buf = map_sysmem(img_addr, 0); + + if (argc > 2) + select = argv[2]; + if (select || genimg_has_config(images)) { +#if CONFIG_IS_ENABLED(FIT) + if (select) { + /* + * If the FDT blob comes from the FIT image and the + * FIT image address is omitted in the command line + * argument, try to use ramdisk or os FIT image + * address or default load address. + */ + if (images->fit_uname_rd) + default_addr = (ulong)images->fit_hdr_rd; + else if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = image_load_addr; + + if (fit_parse_conf(select, default_addr, + &fdt_addr, &fit_uname_config)) { + debug("* fdt: config '%s' from image at 0x%08lx\n", + fit_uname_config, fdt_addr); + } else if (fit_parse_subimage(select, default_addr, + &fdt_addr, &fit_uname_fdt)) { + debug("* fdt: subimage '%s' from image at 0x%08lx\n", + fit_uname_fdt, fdt_addr); + } else +#endif + { + fdt_addr = simple_strtoul(select, NULL, 16); + debug("* fdt: cmdline image address = 0x%08lx\n", + fdt_addr); + } +#if CONFIG_IS_ENABLED(FIT) + } else { + /* use FIT configuration provided in first bootm + * command argument + */ + fdt_addr = map_to_sysmem(images->fit_hdr_os); + fdt_noffset = fit_get_node_from_config(images, + FIT_FDT_PROP, + fdt_addr); + if (fdt_noffset == -ENOENT) + return 0; + else if (fdt_noffset < 0) + return 1; + } +#endif + debug("## Checking for 'FDT'/'FDT Image' at %08lx\n", + fdt_addr); + + /* + * Check if there is an FDT image at the + * address provided in the second bootm argument + * check image type, for FIT images get a FIT node. + */ + buf = map_sysmem(fdt_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + /* verify fdt_addr points to a valid image header */ + printf("## Flattened Device Tree from Legacy Image at %08lx\n", + fdt_addr); + fdt_hdr = image_get_fdt(fdt_addr); + if (!fdt_hdr) + goto no_fdt; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = (ulong)fdt_hdr; + image_data = (ulong)image_get_data(fdt_hdr); + image_end = image_get_image_end(fdt_hdr); + + load = image_get_load(fdt_hdr); + load_end = load + image_get_data_size(fdt_hdr); + + if (load == image_start || + load == image_data) { + fdt_addr = load; + break; + } + + if ((load < image_end) && (load_end > image_start)) { + fdt_error("fdt overwritten"); + goto error; + } + + debug(" Loading FDT from 0x%08lx to 0x%08lx\n", + image_data, load); + + memmove((void *)load, + (void *)image_data, + image_get_data_size(fdt_hdr)); + + fdt_addr = load; + break; +#endif + case IMAGE_FORMAT_FIT: + /* + * This case will catch both: new uImage format + * (libfdt based) and raw FDT blob (also libfdt + * based). + */ +#if CONFIG_IS_ENABLED(FIT) + /* check FDT blob vs FIT blob */ + if (!fit_check_format(buf, IMAGE_SIZE_INVAL)) { + ulong load, len; + + fdt_noffset = boot_get_fdt_fit(images, + fdt_addr, &fit_uname_fdt, + &fit_uname_config, + arch, &load, &len); + + if (fdt_noffset < 0) + goto error; + + images->fit_hdr_fdt = map_sysmem(fdt_addr, 0); + images->fit_uname_fdt = fit_uname_fdt; + images->fit_noffset_fdt = fdt_noffset; + fdt_addr = load; + + break; + } else +#endif + { + /* + * FDT blob + */ + debug("* fdt: raw FDT blob\n"); + printf("## Flattened Device Tree blob at %08lx\n", + (long)fdt_addr); + } + break; + default: + puts("ERROR: Did not find a cmdline Flattened Device Tree\n"); + goto error; + } + + printf(" Booting using the fdt blob at %#08lx\n", fdt_addr); + fdt_blob = map_sysmem(fdt_addr, 0); + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + ulong fdt_data, fdt_len; + + /* + * Now check if we have a legacy multi-component image, + * get second entry data start address and len. + */ + printf("## Flattened Device Tree from multi component Image at %08lX\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data, + &fdt_len); + if (fdt_len) { + fdt_blob = (char *)fdt_data; + printf(" Booting using the fdt at 0x%p\n", fdt_blob); + + if (fdt_check_header(fdt_blob) != 0) { + fdt_error("image is not a fdt"); + goto error; + } + + if (fdt_totalsize(fdt_blob) != fdt_len) { + fdt_error("fdt size != image size"); + goto error; + } + } else { + debug("## No Flattened Device Tree\n"); + goto no_fdt; + } +#ifdef CONFIG_ANDROID_BOOT_IMAGE + } else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) { + struct andr_img_hdr *hdr = buf; + ulong fdt_data, fdt_len; + u32 fdt_size, dtb_idx; + /* + * Firstly check if this android boot image has dtb field. + */ + dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0); + if (android_image_get_dtb_by_index((ulong)hdr, dtb_idx, &fdt_addr, &fdt_size)) { + fdt_blob = (char *)map_sysmem(fdt_addr, 0); + if (fdt_check_header(fdt_blob)) + goto no_fdt; + + debug("## Using FDT in Android image dtb area with idx %u\n", dtb_idx); + } else if (!android_image_get_second(hdr, &fdt_data, &fdt_len) && + !fdt_check_header((char *)fdt_data)) { + fdt_blob = (char *)fdt_data; + if (fdt_totalsize(fdt_blob) != fdt_len) + goto error; + + debug("## Using FDT in Android image second area\n"); + } else { + fdt_addr = env_get_hex("fdtaddr", 0); + if (!fdt_addr) + goto no_fdt; + + fdt_blob = map_sysmem(fdt_addr, 0); + if (fdt_check_header(fdt_blob)) + goto no_fdt; + + debug("## Using FDT at ${fdtaddr}=Ox%lx\n", fdt_addr); + } +#endif + } else { + debug("## No Flattened Device Tree\n"); + goto no_fdt; + } + + *of_flat_tree = fdt_blob; + *of_size = fdt_totalsize(fdt_blob); + debug(" of_flat_tree at 0x%08lx size 0x%08lx\n", + (ulong)*of_flat_tree, *of_size); + + return 0; + +no_fdt: + debug("Continuing to boot without FDT\n"); + return 0; +error: + return 1; +} + +/* + * Verify the device tree. + * + * This function is called after all device tree fix-ups have been enacted, + * so that the final device tree can be verified. The definition of "verified" + * is up to the specific implementation. However, it generally means that the + * addresses of some of the devices in the device tree are compared with the + * actual addresses at which U-Boot has placed them. + * + * Returns 1 on success, 0 on failure. If 0 is returned, U-Boot will halt the + * boot process. + */ +__weak int ft_verify_fdt(void *fdt) +{ + return 1; +} + +__weak int arch_fixup_fdt(void *blob) +{ + return 0; +} + +int image_setup_libfdt(bootm_headers_t *images, void *blob, + int of_size, struct lmb *lmb) +{ + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + int ret = -EPERM; + int fdt_ret; + + if (fdt_root(blob) < 0) { + printf("ERROR: root node setup failed\n"); + goto err; + } + if (fdt_chosen(blob) < 0) { + printf("ERROR: /chosen node create failed\n"); + goto err; + } + if (arch_fixup_fdt(blob) < 0) { + printf("ERROR: arch-specific fdt fixup failed\n"); + goto err; + } + + fdt_ret = optee_copy_fdt_nodes(blob); + if (fdt_ret) { + printf("ERROR: transfer of optee nodes to new fdt failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + + /* Update ethernet nodes */ + fdt_fixup_ethernet(blob); +#if CONFIG_IS_ENABLED(CMD_PSTORE) + /* Append PStore configuration */ + fdt_fixup_pstore(blob); +#endif + if (IMAGE_OF_BOARD_SETUP) { + const char *skip_board_fixup; + + skip_board_fixup = env_get("skip_board_fixup"); + if (skip_board_fixup && ((int)simple_strtol(skip_board_fixup, NULL, 10) == 1)) { + printf("skip board fdt fixup\n"); + } else { + fdt_ret = ft_board_setup(blob, gd->bd); + if (fdt_ret) { + printf("ERROR: board-specific fdt fixup failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + } + } + if (IMAGE_OF_SYSTEM_SETUP) { + fdt_ret = ft_system_setup(blob, gd->bd); + if (fdt_ret) { + printf("ERROR: system-specific fdt fixup failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; + } + } + + /* Delete the old LMB reservation */ + if (lmb) + lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, + (phys_size_t)fdt_totalsize(blob)); + + ret = fdt_shrink_to_minimum(blob, 0); + if (ret < 0) + goto err; + of_size = ret; + + if (*initrd_start && *initrd_end) { + of_size += FDT_RAMDISK_OVERHEAD; + fdt_set_totalsize(blob, of_size); + } + /* Create a new LMB reservation */ + if (lmb) + lmb_reserve(lmb, (ulong)blob, of_size); + + fdt_initrd(blob, *initrd_start, *initrd_end); + if (!ft_verify_fdt(blob)) + goto err; + +#if defined(CONFIG_SOC_KEYSTONE) + if (IMAGE_OF_BOARD_SETUP) + ft_board_setup_ex(blob, gd->bd); +#endif + + return 0; +err: + printf(" - must RESET the board to recover.\n\n"); + + return ret; +} diff --git a/roms/u-boot/common/image-fit-sig.c b/roms/u-boot/common/image-fit-sig.c new file mode 100644 index 000000000..55ddf1879 --- /dev/null +++ b/roms/u-boot/common/image-fit-sig.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include <fdt_region.h> +#include <image.h> +#include <u-boot/rsa.h> +#include <u-boot/hash-checksum.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +/** + * fit_region_make_list() - Make a list of image regions + * + * Given a list of fdt_regions, create a list of image_regions. This is a + * simple conversion routine since the FDT and image code use different + * structures. + * + * @fit: FIT image + * @fdt_regions: Pointer to FDT regions + * @count: Number of FDT regions + * @region: Pointer to image regions, which must hold @count records. If + * region is NULL, then (except for an SPL build) the array will be + * allocated. + * @return: Pointer to image regions + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, + int count, + struct image_region *region) +{ + int i; + + debug("Hash regions:\n"); + debug("%10s %10s\n", "Offset", "Size"); + + /* + * Use malloc() except in SPL (to save code size). In SPL the caller + * must allocate the array. + */ +#ifndef CONFIG_SPL_BUILD + if (!region) + region = calloc(sizeof(*region), count); +#endif + if (!region) + return NULL; + for (i = 0; i < count; i++) { + debug("%10x %10x\n", fdt_regions[i].offset, + fdt_regions[i].size); + region[i].data = fit + fdt_regions[i].offset; + region[i].size = fdt_regions[i].size; + } + + return region; +} + +static int fit_image_setup_verify(struct image_sign_info *info, + const void *fit, int noffset, + int required_keynode, char **err_msgp) +{ + char *algo_name; + const char *padding_name; + + if (fdt_totalsize(fit) > CONFIG_FIT_SIGNATURE_MAX_SIZE) { + *err_msgp = "Total size too large"; + return 1; + } + + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + + padding_name = fdt_getprop(fit, noffset, "padding", NULL); + if (!padding_name) + padding_name = RSA_DEFAULT_PADDING_NAME; + + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + info->fit = (void *)fit; + info->node_offset = noffset; + info->name = algo_name; + info->checksum = image_get_checksum_algo(algo_name); + info->crypto = image_get_crypto_algo(algo_name); + info->padding = image_get_padding_algo(padding_name); + info->fdt_blob = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->checksum || !info->crypto || !info->padding) { + *err_msgp = "Unknown signature algorithm"; + return -1; + } + + return 0; +} + +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp) +{ + struct image_sign_info info; + struct image_region region; + uint8_t *fit_value; + int fit_value_len; + + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + region.data = data; + region.size = size; + + if (info.crypto->verify(&info, ®ion, 1, fit_value, fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_image_verify_sig(const void *fit, int image_noffset, + const char *data, size_t size, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + if (strchr(name, '@')) { + err_msg = "Node name contains @"; + goto error; + } + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return -1; +} + +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, + const void *sig_blob, int *no_sigsp) +{ + int verify_count = 0; + int noffset; + int sig_node; + + /* Work out what we need to verify */ + *no_sigsp = 1; + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, + sig_blob, noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + verify_count++; + } + + if (verify_count) + *no_sigsp = 0; + + return 0; +} + +/** + * fit_config_check_sig() - Check the signature of a config + * + * @fit: FIT to check + * @noffset: Offset of configuration node (e.g. /configurations/conf-1) + * @required_keynode: Offset in the control FDT of the required key node, + * if any. If this is given, then the configuration wil not + * pass verification unless that key is used. If this is + * -1 then any signature will do. + * @conf_noffset: Offset of the configuration subnode being checked (e.g. + * /configurations/conf-1/kernel) + * @err_msgp: In the event of an error, this will be pointed to a + * help error string to display to the user. + * @return 0 if all verified ok, <0 on error + */ +static int fit_config_check_sig(const void *fit, int noffset, + int required_keynode, int conf_noffset, + char **err_msgp) +{ + char * const exc_prop[] = {"data", "data-size", "data-position"}; + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; + const char *config_name; + uint8_t *fit_value; + int fit_value_len; + bool found_config; + int max_regions; + int i, prop_len; + char path[200]; + int count; + + config_name = fit_get_name(fit, conf_noffset, NULL); + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + /* Count the number of strings in the property */ + prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); + end = prop ? prop + prop_len : prop; + for (name = prop, count = 0; name < end; name++) + if (!*name) + count++; + if (!count) { + *err_msgp = "Can't get hashed-nodes property"; + return -1; + } + + if (prop && prop_len > 0 && prop[prop_len - 1] != '\0') { + *err_msgp = "hashed-nodes property must be null-terminated"; + return -1; + } + + /* Add a sanity check here since we are using the stack */ + if (count > IMAGE_MAX_HASHED_NODES) { + *err_msgp = "Number of hashed nodes exceeds maximum"; + return -1; + } + + /* Create a list of node names from those strings */ + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); + found_config = false; + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; + if (!strncmp(FIT_CONFS_PATH, name, strlen(FIT_CONFS_PATH)) && + name[sizeof(FIT_CONFS_PATH) - 1] == '/' && + !strcmp(name + sizeof(FIT_CONFS_PATH), config_name)) { + debug(" (found config node %s)", config_name); + found_config = true; + } + } + if (!found_config) { + *err_msgp = "Selected config not in hashed nodes"; + return -1; + } + + /* + * Each node can generate one region for each sub-node. Allow for + * 7 sub-nodes (hash-1, signature-1, etc.) and some extra. + */ + max_regions = 20 + count * 7; + struct fdt_region fdt_regions[max_regions]; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc, count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, max_regions - 1, + path, sizeof(path), 0); + if (count < 0) { + *err_msgp = "Failed to hash configuration"; + return -1; + } + if (count == 0) { + *err_msgp = "No data to hash"; + return -1; + } + if (count >= max_regions - 1) { + *err_msgp = "Too many hash regions"; + return -1; + } + + /* Add the strings */ + strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); + if (strings) { + /* + * The strings region offset must be a static 0x0. + * This is set in tool/image-host.c + */ + fdt_regions[count].offset = fdt_off_dt_strings(fit); + fdt_regions[count].size = fdt32_to_cpu(strings[1]); + count++; + } + + /* Allocate the region list on the stack */ + struct image_region region[count]; + + fit_region_make_list(fit, fdt_regions, count, region); + if (info.crypto->verify(&info, region, count, fit_value, + fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_config_verify_sig(const void *fit, int conf_noffset, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = "No 'signature' subnode found"; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component conf node */ + fdt_for_each_subnode(noffset, fit, conf_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, + conf_noffset, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + if (verified) + return 0; + +error: + printf(" error!\n%s for '%s' hash node in '%s' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); + return -EPERM; +} + +static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const void *sig_blob) +{ + const char *name = fit_get_name(fit, conf_noffset, NULL); + int noffset; + int sig_node; + int verified = 0; + int reqd_sigs = 0; + bool reqd_policy_all = true; + const char *reqd_mode; + + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + if (strchr(name, '@')) { + printf("Configuration node '%s' contains '@'\n", name); + return -EPERM; + } + + /* Work out what we need to verify */ + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + /* Get required-mode policy property from DTB */ + reqd_mode = fdt_getprop(sig_blob, sig_node, "required-mode", NULL); + if (reqd_mode && !strcmp(reqd_mode, "any")) + reqd_policy_all = false; + + debug("%s: required-mode policy set to '%s'\n", __func__, + reqd_policy_all ? "all" : "any"); + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + NULL); + if (!required || strcmp(required, "conf")) + continue; + + reqd_sigs++; + + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + noffset); + if (ret) { + if (reqd_policy_all) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + } else { + verified++; + if (!reqd_policy_all) + break; + } + } + + if (reqd_sigs && !verified) { + printf("Failed to verify 'any' of the required signature(s)\n"); + return -EPERM; + } + + return 0; +} + +int fit_config_verify(const void *fit, int conf_noffset) +{ + return fit_config_verify_required_sigs(fit, conf_noffset, + gd_fdt_blob()); +} diff --git a/roms/u-boot/common/image-fit.c b/roms/u-boot/common/image-fit.c new file mode 100644 index 000000000..e614643fe --- /dev/null +++ b/roms/u-boot/common/image-fit.c @@ -0,0 +1,2407 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#define LOG_CATEGORY LOGC_BOOT + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#include <linux/libfdt.h> +#include <u-boot/crc.h> +#else +#include <linux/compiler.h> +#include <common.h> +#include <errno.h> +#include <log.h> +#include <mapmem.h> +#include <asm/io.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ + +#include <bootm.h> +#include <image.h> +#include <bootstage.h> +#include <linux/kconfig.h> +#include <u-boot/crc.h> +#include <u-boot/md5.h> +#include <u-boot/sha1.h> +#include <u-boot/sha256.h> +#include <u-boot/sha512.h> + +/*****************************************************************************/ +/* New uImage format routines */ +/*****************************************************************************/ +#ifndef USE_HOSTCC +static int fit_parse_spec(const char *spec, char sepc, ulong addr_curr, + ulong *addr, const char **name) +{ + const char *sep; + + *addr = addr_curr; + *name = NULL; + + sep = strchr(spec, sepc); + if (sep) { + if (sep - spec > 0) + *addr = simple_strtoul(spec, NULL, 16); + + *name = sep + 1; + return 1; + } + + return 0; +} + +/** + * fit_parse_conf - parse FIT configuration spec + * @spec: input string, containing configuration spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * configuration + * @conf_name double pointer to a char, will hold pointer to a configuration + * unit name + * + * fit_parse_conf() expects configuration spec in the form of [<addr>]#<conf>, + * where <addr> is a FIT image address that contains configuration + * with a <conf> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid configuration string, + * addr and conf_name are set accordingly + * 0 otherwise + */ +int fit_parse_conf(const char *spec, ulong addr_curr, + ulong *addr, const char **conf_name) +{ + return fit_parse_spec(spec, '#', addr_curr, addr, conf_name); +} + +/** + * fit_parse_subimage - parse FIT subimage spec + * @spec: input string, containing subimage spec + * @add_curr: current image address (to be used as a possible default) + * @addr: pointer to a ulong variable, will hold FIT image address of a given + * subimage + * @image_name: double pointer to a char, will hold pointer to a subimage name + * + * fit_parse_subimage() expects subimage spec in the form of + * [<addr>]:<subimage>, where <addr> is a FIT image address that contains + * subimage with a <subimg> unit name. + * + * Address part is optional, and if omitted default add_curr will + * be used instead. + * + * returns: + * 1 if spec is a valid subimage string, + * addr and image_name are set accordingly + * 0 otherwise + */ +int fit_parse_subimage(const char *spec, ulong addr_curr, + ulong *addr, const char **image_name) +{ + return fit_parse_spec(spec, ':', addr_curr, addr, image_name); +} +#endif /* !USE_HOSTCC */ + +#ifdef USE_HOSTCC +/* Host tools use these implementations for Cipher and Signature support */ +static void *host_blob; + +void image_set_host_blob(void *blob) +{ + host_blob = blob; +} + +void *image_get_host_blob(void) +{ + return host_blob; +} +#endif /* USE_HOSTCC */ + +static void fit_get_debug(const void *fit, int noffset, + char *prop_name, int err) +{ + debug("Can't get '%s' property from FIT 0x%08lx, node: offset %d, name %s (%s)\n", + prop_name, (ulong)fit, noffset, fit_get_name(fit, noffset, NULL), + fdt_strerror(err)); +} + +/** + * fit_get_subimage_count - get component (sub-image) count + * @fit: pointer to the FIT format image header + * @images_noffset: offset of images node + * + * returns: + * number of image components + */ +int fit_get_subimage_count(const void *fit, int images_noffset) +{ + int noffset; + int ndepth; + int count = 0; + + /* Process its subnodes, print out component images details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + count++; + } + } + + return count; +} + +#if CONFIG_IS_ENABLED(FIT_PRINT) || CONFIG_IS_ENABLED(SPL_FIT_PRINT) +/** + * fit_image_print_data() - prints out the hash node details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash node + * @p: pointer to prefix string + * @type: Type of information to print ("hash" or "sign") + * + * fit_image_print_data() lists properties for the processed hash node + * + * This function avoid using puts() since it prints a newline on the host + * but does not in U-Boot. + * + * returns: + * no returned results + */ +static void fit_image_print_data(const void *fit, int noffset, const char *p, + const char *type) +{ + const char *keyname; + uint8_t *value; + int value_len; + char *algo; + const char *padding; + bool required; + int ret, i; + + debug("%s %s node: '%s'\n", p, type, + fit_get_name(fit, noffset, NULL)); + printf("%s %s algo: ", p, type); + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + printf("invalid/unsupported\n"); + return; + } + printf("%s", algo); + keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + required = fdt_getprop(fit, noffset, FIT_KEY_REQUIRED, NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) + printf(" (required)"); + printf("\n"); + + padding = fdt_getprop(fit, noffset, "padding", NULL); + if (padding) + printf("%s %s padding: %s\n", p, type, padding); + + ret = fit_image_hash_get_value(fit, noffset, &value, + &value_len); + printf("%s %s value: ", p, type); + if (ret) { + printf("unavailable\n"); + } else { + for (i = 0; i < value_len; i++) + printf("%02x", value[i]); + printf("\n"); + } + + debug("%s %s len: %d\n", p, type, value_len); + + /* Signatures have a time stamp */ + if (IMAGE_ENABLE_TIMESTAMP && keyname) { + time_t timestamp; + + printf("%s Timestamp: ", p); + if (fit_get_timestamp(fit, noffset, ×tamp)) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } +} + +/** + * fit_image_print_verification_data() - prints out the hash/signature details + * @fit: pointer to the FIT format image header + * @noffset: offset of the hash or signature node + * @p: pointer to prefix string + * + * This lists properties for the processed hash node + * + * returns: + * no returned results + */ +static void fit_image_print_verification_data(const void *fit, int noffset, + const char *p) +{ + const char *name; + + /* + * Check subnode name, must be equal to "hash" or "signature". + * Multiple hash/signature nodes require unique unit node + * names, e.g. hash-1, hash-2, signature-1, signature-2, etc. + */ + name = fit_get_name(fit, noffset, NULL); + if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Hash"); + } else if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Sign"); + } +} + +/** + * fit_conf_print - prints out the FIT configuration details + * @fit: pointer to the FIT format image header + * @noffset: offset of the configuration node + * @p: pointer to prefix string + * + * fit_conf_print() lists all mandatory properties for the processed + * configuration node. + * + * returns: + * no returned results + */ +static void fit_conf_print(const void *fit, int noffset, const char *p) +{ + char *desc; + const char *uname; + int ret; + int fdt_index, loadables_index; + int ndepth; + + /* Mandatory properties */ + ret = fit_get_desc(fit, noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + uname = fdt_getprop(fit, noffset, FIT_KERNEL_PROP, NULL); + printf("%s Kernel: ", p); + if (!uname) + printf("unavailable\n"); + else + printf("%s\n", uname); + + /* Optional properties */ + uname = fdt_getprop(fit, noffset, FIT_RAMDISK_PROP, NULL); + if (uname) + printf("%s Init Ramdisk: %s\n", p, uname); + + uname = fdt_getprop(fit, noffset, FIT_FIRMWARE_PROP, NULL); + if (uname) + printf("%s Firmware: %s\n", p, uname); + + for (fdt_index = 0; + uname = fdt_stringlist_get(fit, noffset, FIT_FDT_PROP, + fdt_index, NULL), uname; + fdt_index++) { + if (fdt_index == 0) + printf("%s FDT: ", p); + else + printf("%s ", p); + printf("%s\n", uname); + } + + uname = fdt_getprop(fit, noffset, FIT_FPGA_PROP, NULL); + if (uname) + printf("%s FPGA: %s\n", p, uname); + + /* Print out all of the specified loadables */ + for (loadables_index = 0; + uname = fdt_stringlist_get(fit, noffset, FIT_LOADABLE_PROP, + loadables_index, NULL), uname; + loadables_index++) { + if (loadables_index == 0) { + printf("%s Loadables: ", p); + } else { + printf("%s ", p); + } + printf("%s\n", uname); + } + + /* Process all hash subnodes of the component configuration node */ + for (ndepth = 0, noffset = fdt_next_node(fit, noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* Direct child node of the component configuration node */ + fit_image_print_verification_data(fit, noffset, p); + } + } +} + +/** + * fit_print_contents - prints out the contents of the FIT format image + * @fit: pointer to the FIT format image header + * @p: pointer to prefix string + * + * fit_print_contents() formats a multi line FIT image contents description. + * The routine prints out FIT image properties (root node level) followed by + * the details of each component image. + * + * returns: + * no returned results + */ +void fit_print_contents(const void *fit) +{ + char *desc; + char *uname; + int images_noffset; + int confs_noffset; + int noffset; + int ndepth; + int count = 0; + int ret; + const char *p; + time_t timestamp; + + /* Indent string is defined in header image.h */ + p = IMAGE_INDENT_STRING; + + /* Root node properties */ + ret = fit_get_desc(fit, 0, &desc); + printf("%sFIT description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%sCreated: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return; + } + + /* Process its subnodes, print out component images details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf("%s Image %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_image_print(fit, noffset, p); + } + } + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't get configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return; + } + + /* get default configuration unit name from default property */ + uname = (char *)fdt_getprop(fit, noffset, FIT_DEFAULT_PROP, NULL); + if (uname) + printf("%s Default Configuration: '%s'\n", p, uname); + + /* Process its subnodes, print out configurations details */ + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the configurations parent node, + * i.e. configuration node. + */ + printf("%s Configuration %u (%s)\n", p, count++, + fit_get_name(fit, noffset, NULL)); + + fit_conf_print(fit, noffset, p); + } + } +} + +/** + * fit_image_print - prints out the FIT component image details + * @fit: pointer to the FIT format image header + * @image_noffset: offset of the component image node + * @p: pointer to prefix string + * + * fit_image_print() lists all mandatory properties for the processed component + * image. If present, hash nodes are printed out as well. Load + * address for images of type firmware is also printed out. Since the load + * address is not mandatory for firmware images, it will be output as + * "unavailable" when not present. + * + * returns: + * no returned results + */ +void fit_image_print(const void *fit, int image_noffset, const char *p) +{ + char *desc; + uint8_t type, arch, os, comp; + size_t size; + ulong load, entry; + const void *data; + int noffset; + int ndepth; + int ret; + + /* Mandatory properties */ + ret = fit_get_desc(fit, image_noffset, &desc); + printf("%s Description: ", p); + if (ret) + printf("unavailable\n"); + else + printf("%s\n", desc); + + if (IMAGE_ENABLE_TIMESTAMP) { + time_t timestamp; + + ret = fit_get_timestamp(fit, 0, ×tamp); + printf("%s Created: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } + + fit_image_get_type(fit, image_noffset, &type); + printf("%s Type: %s\n", p, genimg_get_type_name(type)); + + fit_image_get_comp(fit, image_noffset, &comp); + printf("%s Compression: %s\n", p, genimg_get_comp_name(comp)); + + ret = fit_image_get_data_and_size(fit, image_noffset, &data, &size); + + if (!host_build()) { + printf("%s Data Start: ", p); + if (ret) { + printf("unavailable\n"); + } else { + void *vdata = (void *)data; + + printf("0x%08lx\n", (ulong)map_to_sysmem(vdata)); + } + } + + printf("%s Data Size: ", p); + if (ret) + printf("unavailable\n"); + else + genimg_print_size(size); + + /* Remaining, type dependent properties */ + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK) || (type == IH_TYPE_FIRMWARE) || + (type == IH_TYPE_FLATDT)) { + fit_image_get_arch(fit, image_noffset, &arch); + printf("%s Architecture: %s\n", p, genimg_get_arch_name(arch)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_RAMDISK) || + (type == IH_TYPE_FIRMWARE)) { + fit_image_get_os(fit, image_noffset, &os); + printf("%s OS: %s\n", p, genimg_get_os_name(os)); + } + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_FIRMWARE) || (type == IH_TYPE_RAMDISK) || + (type == IH_TYPE_FPGA)) { + ret = fit_image_get_load(fit, image_noffset, &load); + printf("%s Load Address: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", load); + } + + /* optional load address for FDT */ + if (type == IH_TYPE_FLATDT && !fit_image_get_load(fit, image_noffset, &load)) + printf("%s Load Address: 0x%08lx\n", p, load); + + if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) || + (type == IH_TYPE_RAMDISK)) { + ret = fit_image_get_entry(fit, image_noffset, &entry); + printf("%s Entry Point: ", p); + if (ret) + printf("unavailable\n"); + else + printf("0x%08lx\n", entry); + } + + /* Process all hash subnodes of the component image node */ + for (ndepth = 0, noffset = fdt_next_node(fit, image_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* Direct child node of the component image node */ + fit_image_print_verification_data(fit, noffset, p); + } + } +} +#else +void fit_print_contents(const void *fit) { } +void fit_image_print(const void *fit, int image_noffset, const char *p) { } +#endif /* CONFIG_IS_ENABLED(FIR_PRINT) || CONFIG_IS_ENABLED(SPL_FIT_PRINT) */ + +/** + * fit_get_desc - get node description property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @desc: double pointer to the char, will hold pointer to the description + * + * fit_get_desc() reads description property from a given node, if + * description is found pointer to it is returned in third call argument. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_get_desc(const void *fit, int noffset, char **desc) +{ + int len; + + *desc = (char *)fdt_getprop(fit, noffset, FIT_DESC_PROP, &len); + if (*desc == NULL) { + fit_get_debug(fit, noffset, FIT_DESC_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_get_timestamp - get node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: pointer to the time_t, will hold read timestamp + * + * fit_get_timestamp() reads timestamp property from given node, if timestamp + * is found and has a correct size its value is returned in third call + * argument. + * + * returns: + * 0, on success + * -1, on property read failure + * -2, on wrong timestamp size + */ +int fit_get_timestamp(const void *fit, int noffset, time_t *timestamp) +{ + int len; + const void *data; + + data = fdt_getprop(fit, noffset, FIT_TIMESTAMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TIMESTAMP_PROP, len); + return -1; + } + if (len != sizeof(uint32_t)) { + debug("FIT timestamp with incorrect size of (%u)\n", len); + return -2; + } + + *timestamp = uimage_to_cpu(*((uint32_t *)data)); + return 0; +} + +/** + * fit_image_get_node - get node offset for component image of a given unit name + * @fit: pointer to the FIT format image header + * @image_uname: component image node unit name + * + * fit_image_get_node() finds a component image (within the '/images' + * node) of a provided unit name. If image is found its node offset is + * returned to the caller. + * + * returns: + * image node offset when found (>=0) + * negative number on failure (FDT_ERR_* code) + */ +int fit_image_get_node(const void *fit, const char *image_uname) +{ + int noffset, images_noffset; + + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + debug("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return images_noffset; + } + + noffset = fdt_subnode_offset(fit, images_noffset, image_uname); + if (noffset < 0) { + debug("Can't get node offset for image unit name: '%s' (%s)\n", + image_uname, fdt_strerror(noffset)); + } + + return noffset; +} + +/** + * fit_image_get_os - get os id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: pointer to the uint8_t, will hold os numeric id + * + * fit_image_get_os() finds os property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_os(const void *fit, int noffset, uint8_t *os) +{ + int len; + const void *data; + + /* Get OS name from property data */ + data = fdt_getprop(fit, noffset, FIT_OS_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_OS_PROP, len); + *os = -1; + return -1; + } + + /* Translate OS name to id */ + *os = genimg_get_os_id(data); + return 0; +} + +/** + * fit_image_get_arch - get arch id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: pointer to the uint8_t, will hold arch numeric id + * + * fit_image_get_arch() finds arch property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_arch(const void *fit, int noffset, uint8_t *arch) +{ + int len; + const void *data; + + /* Get architecture name from property data */ + data = fdt_getprop(fit, noffset, FIT_ARCH_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_ARCH_PROP, len); + *arch = -1; + return -1; + } + + /* Translate architecture name to id */ + *arch = genimg_get_arch_id(data); + return 0; +} + +/** + * fit_image_get_type - get type id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: pointer to the uint8_t, will hold type numeric id + * + * fit_image_get_type() finds type property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_type(const void *fit, int noffset, uint8_t *type) +{ + int len; + const void *data; + + /* Get image type name from property data */ + data = fdt_getprop(fit, noffset, FIT_TYPE_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_TYPE_PROP, len); + *type = -1; + return -1; + } + + /* Translate image type name to id */ + *type = genimg_get_type_id(data); + return 0; +} + +/** + * fit_image_get_comp - get comp id for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: pointer to the uint8_t, will hold comp numeric id + * + * fit_image_get_comp() finds comp property in a given component image node. + * If the property is found, its (string) value is translated to the numeric + * id which is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_comp(const void *fit, int noffset, uint8_t *comp) +{ + int len; + const void *data; + + /* Get compression name from property data */ + data = fdt_getprop(fit, noffset, FIT_COMP_PROP, &len); + if (data == NULL) { + fit_get_debug(fit, noffset, FIT_COMP_PROP, len); + *comp = -1; + return -1; + } + + /* Translate compression name to id */ + *comp = genimg_get_comp_id(data); + return 0; +} + +static int fit_image_get_address(const void *fit, int noffset, char *name, + ulong *load) +{ + int len, cell_len; + const fdt32_t *cell; + uint64_t load64 = 0; + + cell = fdt_getprop(fit, noffset, name, &len); + if (cell == NULL) { + fit_get_debug(fit, noffset, name, len); + return -1; + } + + cell_len = len >> 2; + /* Use load64 to avoid compiling warning for 32-bit target */ + while (cell_len--) { + load64 = (load64 << 32) | uimage_to_cpu(*cell); + cell++; + } + + if (len > sizeof(ulong) && (uint32_t)(load64 >> 32)) { + printf("Unsupported %s address size\n", name); + return -1; + } + + *load = (ulong)load64; + + return 0; +} +/** + * fit_image_get_load() - get load addr property for given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @load: pointer to the uint32_t, will hold load address + * + * fit_image_get_load() finds load address property in a given component + * image node. If the property is found, its value is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_load(const void *fit, int noffset, ulong *load) +{ + return fit_image_get_address(fit, noffset, FIT_LOAD_PROP, load); +} + +/** + * fit_image_get_entry() - get entry point address property + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @entry: pointer to the uint32_t, will hold entry point address + * + * This gets the entry point address property for a given component image + * node. + * + * fit_image_get_entry() finds entry point address property in a given + * component image node. If the property is found, its value is returned + * to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_entry(const void *fit, int noffset, ulong *entry) +{ + return fit_image_get_address(fit, noffset, FIT_ENTRY_PROP, entry); +} + +/** + * fit_image_get_data - get data property and its size for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data() finds data property in a given component image node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_get_data(const void *fit, int noffset, + const void **data, size_t *size) +{ + int len; + + *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len); + if (*data == NULL) { + fit_get_debug(fit, noffset, FIT_DATA_PROP, len); + *size = 0; + return -1; + } + + *size = len; + return 0; +} + +/** + * Get 'data-offset' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_offset: holds the data-offset property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL); + if (!val) + return -ENOENT; + + *data_offset = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-position' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_position: holds the data-position property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_position(const void *fit, int noffset, + int *data_position) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL); + if (!val) + return -ENOENT; + + *data_position = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-size' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_size: holds the data-size property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_size(const void *fit, int noffset, int *data_size) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL); + if (!val) + return -ENOENT; + + *data_size = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-size-unciphered' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_size: holds the data-size property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +int fit_image_get_data_size_unciphered(const void *fit, int noffset, + size_t *data_size) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, "data-size-unciphered", NULL); + if (!val) + return -ENOENT; + + *data_size = (size_t)fdt32_to_cpu(*val); + + return 0; +} + +/** + * fit_image_get_data_and_size - get data and its size including + * both embedded and external data + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data_and_size() finds data and its size including + * both embedded and external data. If the property is found + * its data start address and size are returned to the caller. + * + * returns: + * 0, on success + * otherwise, on failure + */ +int fit_image_get_data_and_size(const void *fit, int noffset, + const void **data, size_t *size) +{ + bool external_data = false; + int offset; + int len; + int ret; + + if (!fit_image_get_data_position(fit, noffset, &offset)) { + external_data = true; + } else if (!fit_image_get_data_offset(fit, noffset, &offset)) { + external_data = true; + /* + * For FIT with external data, figure out where + * the external images start. This is the base + * for the data-offset properties in each image. + */ + offset += ((fdt_totalsize(fit) + 3) & ~3); + } + + if (external_data) { + debug("External Data\n"); + ret = fit_image_get_data_size(fit, noffset, &len); + if (!ret) { + *data = fit + offset; + *size = len; + } + } else { + ret = fit_image_get_data(fit, noffset, data, size); + } + + return ret; +} + +/** + * fit_image_hash_get_algo - get hash algorithm name + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @algo: double pointer to char, will hold pointer to the algorithm name + * + * fit_image_hash_get_algo() finds hash algorithm property in a given hash node. + * If the property is found its data start address is returned to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_algo(const void *fit, int noffset, char **algo) +{ + int len; + + *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); + if (*algo == NULL) { + fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); + return -1; + } + + return 0; +} + +/** + * fit_image_hash_get_value - get hash value and length + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: double pointer to uint8_t, will hold address of a hash value data + * @value_len: pointer to an int, will hold hash data length + * + * fit_image_hash_get_value() finds hash value property in a given hash node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, + int *value_len) +{ + int len; + + *value = (uint8_t *)fdt_getprop(fit, noffset, FIT_VALUE_PROP, &len); + if (*value == NULL) { + fit_get_debug(fit, noffset, FIT_VALUE_PROP, len); + *value_len = 0; + return -1; + } + + *value_len = len; + return 0; +} + +/** + * fit_image_hash_get_ignore - get hash ignore flag + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @ignore: pointer to an int, will hold hash ignore flag + * + * fit_image_hash_get_ignore() finds hash ignore property in a given hash node. + * If the property is found and non-zero, the hash algorithm is not verified by + * u-boot automatically. + * + * returns: + * 0, on ignore not found + * value, on ignore found + */ +static int fit_image_hash_get_ignore(const void *fit, int noffset, int *ignore) +{ + int len; + int *value; + + value = (int *)fdt_getprop(fit, noffset, FIT_IGNORE_PROP, &len); + if (value == NULL || len != sizeof(int)) + *ignore = 0; + else + *ignore = *value; + + return 0; +} + +/** + * fit_image_cipher_get_algo - get cipher algorithm name + * @fit: pointer to the FIT format image header + * @noffset: cipher node offset + * @algo: double pointer to char, will hold pointer to the algorithm name + * + * fit_image_cipher_get_algo() finds cipher algorithm property in a given + * cipher node. If the property is found its data start address is returned + * to the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo) +{ + int len; + + *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); + if (!*algo) { + fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); + return -1; + } + + return 0; +} + +ulong fit_get_end(const void *fit) +{ + return map_to_sysmem((void *)(fit + fdt_totalsize(fit))); +} + +/** + * fit_set_timestamp - set node timestamp property + * @fit: pointer to the FIT format image header + * @noffset: node offset + * @timestamp: timestamp value to be set + * + * fit_set_timestamp() attempts to set timestamp property in the requested + * node and returns operation status to the caller. + * + * returns: + * 0, on success + * -ENOSPC if no space in device tree, -1 for other error + */ +int fit_set_timestamp(void *fit, int noffset, time_t timestamp) +{ + uint32_t t; + int ret; + + t = cpu_to_uimage(timestamp); + ret = fdt_setprop(fit, noffset, FIT_TIMESTAMP_PROP, &t, + sizeof(uint32_t)); + if (ret) { + debug("Can't set '%s' property for '%s' node (%s)\n", + FIT_TIMESTAMP_PROP, fit_get_name(fit, noffset, NULL), + fdt_strerror(ret)); + return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -1; + } + + return 0; +} + +/** + * calculate_hash - calculate and return hash for provided input data + * @data: pointer to the input data + * @data_len: data length + * @algo: requested hash algorithm + * @value: pointer to the char, will hold hash value data (caller must + * allocate enough free space) + * value_len: length of the calculated hash + * + * calculate_hash() computes input data hash according to the requested + * algorithm. + * Resulting hash value is placed in caller provided 'value' buffer, length + * of the calculated hash is returned via value_len pointer argument. + * + * returns: + * 0, on success + * -1, when algo is unsupported + */ +int calculate_hash(const void *data, int data_len, const char *algo, + uint8_t *value, int *value_len) +{ + if (IMAGE_ENABLE_CRC32 && strcmp(algo, "crc32") == 0) { + *((uint32_t *)value) = crc32_wd(0, data, data_len, + CHUNKSZ_CRC32); + *((uint32_t *)value) = cpu_to_uimage(*((uint32_t *)value)); + *value_len = 4; + } else if (IMAGE_ENABLE_SHA1 && strcmp(algo, "sha1") == 0) { + sha1_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA1); + *value_len = 20; + } else if (IMAGE_ENABLE_SHA256 && strcmp(algo, "sha256") == 0) { + sha256_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA256); + *value_len = SHA256_SUM_LEN; + } else if (IMAGE_ENABLE_SHA384 && strcmp(algo, "sha384") == 0) { + sha384_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA384); + *value_len = SHA384_SUM_LEN; + } else if (IMAGE_ENABLE_SHA512 && strcmp(algo, "sha512") == 0) { + sha512_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA512); + *value_len = SHA512_SUM_LEN; + } else if (IMAGE_ENABLE_MD5 && strcmp(algo, "md5") == 0) { + md5_wd((unsigned char *)data, data_len, value, CHUNKSZ_MD5); + *value_len = 16; + } else { + debug("Unsupported hash alogrithm\n"); + return -1; + } + return 0; +} + +static int fit_image_check_hash(const void *fit, int noffset, const void *data, + size_t size, char **err_msgp) +{ + uint8_t value[FIT_MAX_HASH_LEN]; + int value_len; + char *algo; + uint8_t *fit_value; + int fit_value_len; + int ignore; + + *err_msgp = NULL; + + if (fit_image_hash_get_algo(fit, noffset, &algo)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + printf("%s", algo); + + if (IMAGE_ENABLE_IGNORE) { + fit_image_hash_get_ignore(fit, noffset, &ignore); + if (ignore) { + printf("-skipped "); + return 0; + } + } + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + if (calculate_hash(data, size, algo, value, &value_len)) { + *err_msgp = "Unsupported hash algorithm"; + return -1; + } + + if (value_len != fit_value_len) { + *err_msgp = "Bad hash value len"; + return -1; + } else if (memcmp(value, fit_value, value_len) != 0) { + *err_msgp = "Bad hash value"; + return -1; + } + + return 0; +} + +int fit_image_verify_with_data(const void *fit, int image_noffset, + const void *data, size_t size) +{ + int noffset = 0; + char *err_msg = ""; + int verify_all = 1; + int ret; + + /* Verify all required signatures */ + if (FIT_IMAGE_ENABLE_VERIFY && + fit_image_verify_required_sigs(fit, image_noffset, data, size, + gd_fdt_blob(), &verify_all)) { + err_msg = "Unable to verify required signature"; + goto error; + } + + /* Process all hash subnodes of the component image node */ + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + /* + * Check subnode name, must be equal to "hash". + * Multiple hash nodes require unique unit node + * names, e.g. hash-1, hash-2, etc. + */ + if (!strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) { + if (fit_image_check_hash(fit, noffset, data, size, + &err_msg)) + goto error; + puts("+ "); + } else if (FIT_IMAGE_ENABLE_VERIFY && verify_all && + !strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + + /* + * Show an indication on failure, but do not return + * an error. Only keys marked 'required' can cause + * an image validation failure. See the call to + * fit_image_verify_required_sigs() above. + */ + if (ret) + puts("- "); + else + puts("+ "); + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return 1; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return 0; +} + +/** + * fit_image_verify - verify data integrity + * @fit: pointer to the FIT format image header + * @image_noffset: component image node offset + * + * fit_image_verify() goes over component image hash nodes, + * re-calculates each data hash and compares with the value stored in hash + * node. + * + * returns: + * 1, if all hashes are valid + * 0, otherwise (or on error) + */ +int fit_image_verify(const void *fit, int image_noffset) +{ + const char *name = fit_get_name(fit, image_noffset, NULL); + const void *data; + size_t size; + char *err_msg = ""; + + if (strchr(name, '@')) { + /* + * We don't support this since libfdt considers names with the + * name root but different @ suffix to be equal + */ + err_msg = "Node name contains @"; + goto err; + } + /* Get image data and data length */ + if (fit_image_get_data_and_size(fit, image_noffset, &data, &size)) { + err_msg = "Can't get image data/size"; + goto err; + } + + return fit_image_verify_with_data(fit, image_noffset, data, size); + +err: + printf("error!\n%s in '%s' image node\n", err_msg, + fit_get_name(fit, image_noffset, NULL)); + return 0; +} + +/** + * fit_all_image_verify - verify data integrity for all images + * @fit: pointer to the FIT format image header + * + * fit_all_image_verify() goes over all images in the FIT and + * for every images checks if all it's hashes are valid. + * + * returns: + * 1, if all hashes of all images are valid + * 0, otherwise (or on error) + */ +int fit_all_image_verify(const void *fit) +{ + int images_noffset; + int noffset; + int ndepth; + int count; + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return 0; + } + + /* Process all image subnodes, check hashes for each */ + printf("## Checking hash(es) for FIT Image at %08lx ...\n", + (ulong)fit); + for (ndepth = 0, count = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + printf(" Hash(es) for Image %u (%s): ", count, + fit_get_name(fit, noffset, NULL)); + count++; + + if (!fit_image_verify(fit, noffset)) + return 0; + printf("\n"); + } + } + return 1; +} + +static int fit_image_uncipher(const void *fit, int image_noffset, + void **data, size_t *size) +{ + int cipher_noffset, ret; + void *dst; + size_t size_dst; + + cipher_noffset = fdt_subnode_offset(fit, image_noffset, + FIT_CIPHER_NODENAME); + if (cipher_noffset < 0) + return 0; + + ret = fit_image_decrypt_data(fit, image_noffset, cipher_noffset, + *data, *size, &dst, &size_dst); + if (ret) + goto out; + + *data = dst; + *size = size_dst; + + out: + return ret; +} + +/** + * fit_image_check_os - check whether image node is of a given os type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @os: requested image os + * + * fit_image_check_os() reads image os property and compares its numeric + * id with the requested os. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given os type + * 0 otherwise (or on error) + */ +int fit_image_check_os(const void *fit, int noffset, uint8_t os) +{ + uint8_t image_os; + + if (fit_image_get_os(fit, noffset, &image_os)) + return 0; + return (os == image_os); +} + +/** + * fit_image_check_arch - check whether image node is of a given arch + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @arch: requested imagearch + * + * fit_image_check_arch() reads image arch property and compares its numeric + * id with the requested arch. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given arch + * 0 otherwise (or on error) + */ +int fit_image_check_arch(const void *fit, int noffset, uint8_t arch) +{ + uint8_t image_arch; + int aarch32_support = 0; + + /* Let's assume that sandbox can load any architecture */ + if (IS_ENABLED(CONFIG_SANDBOX)) + return true; + + if (IS_ENABLED(CONFIG_ARM64_SUPPORT_AARCH32)) + aarch32_support = 1; + + if (fit_image_get_arch(fit, noffset, &image_arch)) + return 0; + return (arch == image_arch) || + (arch == IH_ARCH_I386 && image_arch == IH_ARCH_X86_64) || + (arch == IH_ARCH_ARM64 && image_arch == IH_ARCH_ARM && + aarch32_support); +} + +/** + * fit_image_check_type - check whether image node is of a given type + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @type: requested image type + * + * fit_image_check_type() reads image type property and compares its numeric + * id with the requested type. Comparison result is returned to the caller. + * + * returns: + * 1 if image is of given type + * 0 otherwise (or on error) + */ +int fit_image_check_type(const void *fit, int noffset, uint8_t type) +{ + uint8_t image_type; + + if (fit_image_get_type(fit, noffset, &image_type)) + return 0; + return (type == image_type); +} + +/** + * fit_image_check_comp - check whether image node uses given compression + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @comp: requested image compression type + * + * fit_image_check_comp() reads image compression property and compares its + * numeric id with the requested compression type. Comparison result is + * returned to the caller. + * + * returns: + * 1 if image uses requested compression + * 0 otherwise (or on error) + */ +int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) +{ + uint8_t image_comp; + + if (fit_image_get_comp(fit, noffset, &image_comp)) + return 0; + return (comp == image_comp); +} + +/** + * fdt_check_no_at() - Check for nodes whose names contain '@' + * + * This checks the parent node and all subnodes recursively + * + * @fit: FIT to check + * @parent: Parent node to check + * @return 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@' + */ +static int fdt_check_no_at(const void *fit, int parent) +{ + const char *name; + int node; + int ret; + + name = fdt_get_name(fit, parent, NULL); + if (!name || strchr(name, '@')) + return -EADDRNOTAVAIL; + + fdt_for_each_subnode(node, fit, parent) { + ret = fdt_check_no_at(fit, node); + if (ret) + return ret; + } + + return 0; +} + +int fit_check_format(const void *fit, ulong size) +{ + int ret; + + /* A FIT image must be a valid FDT */ + ret = fdt_check_header(fit); + if (ret) { + log_debug("Wrong FIT format: not a flattened device tree (err=%d)\n", + ret); + return -ENOEXEC; + } + + if (CONFIG_IS_ENABLED(FIT_FULL_CHECK)) { + /* + * If we are not given the size, make do wtih calculating it. + * This is not as secure, so we should consider a flag to + * control this. + */ + if (size == IMAGE_SIZE_INVAL) + size = fdt_totalsize(fit); + ret = fdt_check_full(fit, size); + if (ret) + ret = -EINVAL; + + /* + * U-Boot stopped using unit addressed in 2017. Since libfdt + * can match nodes ignoring any unit address, signature + * verification can see the wrong node if one is inserted with + * the same name as a valid node but with a unit address + * attached. Protect against this by disallowing unit addresses. + */ + if (!ret && CONFIG_IS_ENABLED(FIT_SIGNATURE)) { + ret = fdt_check_no_at(fit, 0); + + if (ret) { + log_debug("FIT check error %d\n", ret); + return ret; + } + } + if (ret) { + log_debug("FIT check error %d\n", ret); + return ret; + } + } + + /* mandatory / node 'description' property */ + if (!fdt_getprop(fit, 0, FIT_DESC_PROP, NULL)) { + log_debug("Wrong FIT format: no description\n"); + return -ENOMSG; + } + + if (IMAGE_ENABLE_TIMESTAMP) { + /* mandatory / node 'timestamp' property */ + if (!fdt_getprop(fit, 0, FIT_TIMESTAMP_PROP, NULL)) { + log_debug("Wrong FIT format: no timestamp\n"); + return -EBADMSG; + } + } + + /* mandatory subimages parent '/images' node */ + if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) { + log_debug("Wrong FIT format: no images parent node\n"); + return -ENOENT; + } + + return 0; +} + +/** + * fit_conf_find_compat + * @fit: pointer to the FIT format image header + * @fdt: pointer to the device tree to compare against + * + * fit_conf_find_compat() attempts to find the configuration whose fdt is the + * most compatible with the passed in device tree. + * + * Example: + * + * / o image-tree + * |-o images + * | |-o fdt-1 + * | |-o fdt-2 + * | + * |-o configurations + * |-o config-1 + * | |-fdt = fdt-1 + * | + * |-o config-2 + * |-fdt = fdt-2 + * + * / o U-Boot fdt + * |-compatible = "foo,bar", "bim,bam" + * + * / o kernel fdt1 + * |-compatible = "foo,bar", + * + * / o kernel fdt2 + * |-compatible = "bim,bam", "baz,biz" + * + * Configuration 1 would be picked because the first string in U-Boot's + * compatible list, "foo,bar", matches a compatible string in the root of fdt1. + * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1. + * + * As an optimization, the compatible property from the FDT's root node can be + * copied into the configuration node in the FIT image. This is required to + * match configurations with compressed FDTs. + * + * returns: + * offset to the configuration to use if one was found + * -1 otherwise + */ +int fit_conf_find_compat(const void *fit, const void *fdt) +{ + int ndepth = 0; + int noffset, confs_noffset, images_noffset; + const void *fdt_compat; + int fdt_compat_len; + int best_match_offset = 0; + int best_match_pos = 0; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (confs_noffset < 0 || images_noffset < 0) { + debug("Can't find configurations or images nodes.\n"); + return -1; + } + + fdt_compat = fdt_getprop(fdt, 0, "compatible", &fdt_compat_len); + if (!fdt_compat) { + debug("Fdt for comparison has no \"compatible\" property.\n"); + return -1; + } + + /* + * Loop over the configurations in the FIT image. + */ + for (noffset = fdt_next_node(fit, confs_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + const void *fdt; + const char *kfdt_name; + int kfdt_noffset, compat_noffset; + const char *cur_fdt_compat; + int len; + size_t sz; + int i; + + if (ndepth > 1) + continue; + + /* If there's a compat property in the config node, use that. */ + if (fdt_getprop(fit, noffset, "compatible", NULL)) { + fdt = fit; /* search in FIT image */ + compat_noffset = noffset; /* search under config node */ + } else { /* Otherwise extract it from the kernel FDT. */ + kfdt_name = fdt_getprop(fit, noffset, "fdt", &len); + if (!kfdt_name) { + debug("No fdt property found.\n"); + continue; + } + kfdt_noffset = fdt_subnode_offset(fit, images_noffset, + kfdt_name); + if (kfdt_noffset < 0) { + debug("No image node named \"%s\" found.\n", + kfdt_name); + continue; + } + + if (!fit_image_check_comp(fit, kfdt_noffset, + IH_COMP_NONE)) { + debug("Can't extract compat from \"%s\" " + "(compressed)\n", kfdt_name); + continue; + } + + /* search in this config's kernel FDT */ + if (fit_image_get_data(fit, kfdt_noffset, &fdt, &sz)) { + debug("Failed to get fdt \"%s\".\n", kfdt_name); + continue; + } + + compat_noffset = 0; /* search kFDT under root node */ + } + + len = fdt_compat_len; + cur_fdt_compat = fdt_compat; + /* + * Look for a match for each U-Boot compatibility string in + * turn in the compat string property. + */ + for (i = 0; len > 0 && + (!best_match_offset || best_match_pos > i); i++) { + int cur_len = strlen(cur_fdt_compat) + 1; + + if (!fdt_node_check_compatible(fdt, compat_noffset, + cur_fdt_compat)) { + best_match_offset = noffset; + best_match_pos = i; + break; + } + len -= cur_len; + cur_fdt_compat += cur_len; + } + } + if (!best_match_offset) { + debug("No match found.\n"); + return -1; + } + + return best_match_offset; +} + +int fit_conf_get_node(const void *fit, const char *conf_uname) +{ + int noffset, confs_noffset; + int len; + const char *s; + char *conf_uname_copy = NULL; + + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + debug("Can't find configurations parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return confs_noffset; + } + + if (conf_uname == NULL) { + /* get configuration unit name from the default property */ + debug("No configuration specified, trying default...\n"); + if (!host_build() && IS_ENABLED(CONFIG_MULTI_DTB_FIT)) { + noffset = fit_find_config_node(fit); + if (noffset < 0) + return noffset; + conf_uname = fdt_get_name(fit, noffset, NULL); + } else { + conf_uname = (char *)fdt_getprop(fit, confs_noffset, + FIT_DEFAULT_PROP, &len); + if (conf_uname == NULL) { + fit_get_debug(fit, confs_noffset, FIT_DEFAULT_PROP, + len); + return len; + } + } + debug("Found default configuration: '%s'\n", conf_uname); + } + + s = strchr(conf_uname, '#'); + if (s) { + len = s - conf_uname; + conf_uname_copy = malloc(len + 1); + if (!conf_uname_copy) { + debug("Can't allocate uname copy: '%s'\n", + conf_uname); + return -ENOMEM; + } + memcpy(conf_uname_copy, conf_uname, len); + conf_uname_copy[len] = '\0'; + conf_uname = conf_uname_copy; + } + + noffset = fdt_subnode_offset(fit, confs_noffset, conf_uname); + if (noffset < 0) { + debug("Can't get node offset for configuration unit name: '%s' (%s)\n", + conf_uname, fdt_strerror(noffset)); + } + + if (conf_uname_copy) + free(conf_uname_copy); + + return noffset; +} + +int fit_conf_get_prop_node_count(const void *fit, int noffset, + const char *prop_name) +{ + return fdt_stringlist_count(fit, noffset, prop_name); +} + +int fit_conf_get_prop_node_index(const void *fit, int noffset, + const char *prop_name, int index) +{ + const char *uname; + int len; + + /* get kernel image unit name from configuration kernel property */ + uname = fdt_stringlist_get(fit, noffset, prop_name, index, &len); + if (uname == NULL) + return len; + + return fit_image_get_node(fit, uname); +} + +int fit_conf_get_prop_node(const void *fit, int noffset, + const char *prop_name) +{ + return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0); +} + +static int fit_image_select(const void *fit, int rd_noffset, int verify) +{ + fit_image_print(fit, rd_noffset, " "); + + if (verify) { + puts(" Verifying Hash Integrity ... "); + if (!fit_image_verify(fit, rd_noffset)) { + puts("Bad Data Hash\n"); + return -EACCES; + } + puts("OK\n"); + } + + return 0; +} + +int fit_get_node_from_config(bootm_headers_t *images, const char *prop_name, + ulong addr) +{ + int cfg_noffset; + void *fit_hdr; + int noffset; + + debug("* %s: using config '%s' from image at 0x%08lx\n", + prop_name, images->fit_uname_cfg, addr); + + /* Check whether configuration has this property defined */ + fit_hdr = map_sysmem(addr, 0); + cfg_noffset = fit_conf_get_node(fit_hdr, images->fit_uname_cfg); + if (cfg_noffset < 0) { + debug("* %s: no such config\n", prop_name); + return -EINVAL; + } + + noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name); + if (noffset < 0) { + debug("* %s: no '%s' in config\n", prop_name, prop_name); + return -ENOENT; + } + + return noffset; +} + +/** + * fit_get_image_type_property() - get property name for IH_TYPE_... + * + * @return the properly name where we expect to find the image in the + * config node + */ +static const char *fit_get_image_type_property(int type) +{ + /* + * This is sort-of available in the uimage_type[] table in image.c + * but we don't have access to the short name, and "fdt" is different + * anyway. So let's just keep it here. + */ + switch (type) { + case IH_TYPE_FLATDT: + return FIT_FDT_PROP; + case IH_TYPE_KERNEL: + return FIT_KERNEL_PROP; + case IH_TYPE_FIRMWARE: + return FIT_FIRMWARE_PROP; + case IH_TYPE_RAMDISK: + return FIT_RAMDISK_PROP; + case IH_TYPE_X86_SETUP: + return FIT_SETUP_PROP; + case IH_TYPE_LOADABLE: + return FIT_LOADABLE_PROP; + case IH_TYPE_FPGA: + return FIT_FPGA_PROP; + case IH_TYPE_STANDALONE: + return FIT_STANDALONE_PROP; + } + + return "unknown"; +} + +int fit_image_load(bootm_headers_t *images, ulong addr, + const char **fit_unamep, const char **fit_uname_configp, + int arch, int image_type, int bootstage_id, + enum fit_load_op load_op, ulong *datap, ulong *lenp) +{ + int cfg_noffset, noffset; + const char *fit_uname; + const char *fit_uname_config; + const char *fit_base_uname_config; + const void *fit; + void *buf; + void *loadbuf; + size_t size; + int type_ok, os_ok; + ulong load, load_end, data, len; + uint8_t os, comp; +#ifndef USE_HOSTCC + uint8_t os_arch; +#endif + const char *prop_name; + int ret; + + fit = map_sysmem(addr, 0); + fit_uname = fit_unamep ? *fit_unamep : NULL; + fit_uname_config = fit_uname_configp ? *fit_uname_configp : NULL; + fit_base_uname_config = NULL; + prop_name = fit_get_image_type_property(image_type); + printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr); + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT); + ret = fit_check_format(fit, IMAGE_SIZE_INVAL); + if (ret) { + printf("Bad FIT %s image format! (err=%d)\n", prop_name, ret); + if (CONFIG_IS_ENABLED(FIT_SIGNATURE) && ret == -EADDRNOTAVAIL) + printf("Signature checking prevents use of unit addresses (@) in nodes\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT); + return ret; + } + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK); + if (fit_uname) { + /* get FIT component image node offset */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_UNIT_NAME); + noffset = fit_image_get_node(fit, fit_uname); + } else { + /* + * no image node unit name, try to get config + * node first. If config unit node name is NULL + * fit_conf_get_node() will try to find default config node + */ + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_NO_UNIT_NAME); + if (IMAGE_ENABLE_BEST_MATCH && !fit_uname_config) { + cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob()); + } else { + cfg_noffset = fit_conf_get_node(fit, + fit_uname_config); + } + if (cfg_noffset < 0) { + puts("Could not find configuration node\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_NO_UNIT_NAME); + return -ENOENT; + } + + fit_base_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + printf(" Using '%s' configuration\n", fit_base_uname_config); + /* Remember this config */ + if (image_type == IH_TYPE_KERNEL) + images->fit_uname_cfg = fit_base_uname_config; + + if (FIT_IMAGE_ENABLE_VERIFY && images->verify) { + puts(" Verifying Hash Integrity ... "); + if (fit_config_verify(fit, cfg_noffset)) { + puts("Bad Data Hash\n"); + bootstage_error(bootstage_id + + BOOTSTAGE_SUB_HASH); + return -EACCES; + } + puts("OK\n"); + } + + bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG); + + noffset = fit_conf_get_prop_node(fit, cfg_noffset, + prop_name); + fit_uname = fit_get_name(fit, noffset, NULL); + } + if (noffset < 0) { + printf("Could not find subimage node type '%s'\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_SUBNODE); + return -ENOENT; + } + + printf(" Trying '%s' %s subimage\n", fit_uname, prop_name); + + ret = fit_image_select(fit, noffset, images->verify); + if (ret) { + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH); + return ret; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + if (!host_build() && IS_ENABLED(CONFIG_SANDBOX)) { + if (!fit_image_check_target_arch(fit, noffset)) { + puts("Unsupported Architecture\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH); + return -ENOEXEC; + } + } + +#ifndef USE_HOSTCC + fit_image_get_arch(fit, noffset, &os_arch); + images->os.arch = os_arch; +#endif + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + type_ok = fit_image_check_type(fit, noffset, image_type) || + fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE) || + fit_image_check_type(fit, noffset, IH_TYPE_TEE) || + (image_type == IH_TYPE_KERNEL && + fit_image_check_type(fit, noffset, IH_TYPE_KERNEL_NOLOAD)); + + os_ok = image_type == IH_TYPE_FLATDT || + image_type == IH_TYPE_FPGA || + fit_image_check_os(fit, noffset, IH_OS_LINUX) || + fit_image_check_os(fit, noffset, IH_OS_U_BOOT) || + fit_image_check_os(fit, noffset, IH_OS_TEE) || + fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) || + fit_image_check_os(fit, noffset, IH_OS_EFI) || + fit_image_check_os(fit, noffset, IH_OS_VXWORKS); + + /* + * If either of the checks fail, we should report an error, but + * if the image type is coming from the "loadables" field, we + * don't care what it is + */ + if ((!type_ok || !os_ok) && image_type != IH_TYPE_LOADABLE) { + fit_image_get_os(fit, noffset, &os); + printf("No %s %s %s Image\n", + genimg_get_os_name(os), + genimg_get_arch_name(arch), + genimg_get_type_name(image_type)); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL); + return -EIO; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL_OK); + + /* get image data address and length */ + if (fit_image_get_data_and_size(fit, noffset, + (const void **)&buf, &size)) { + printf("Could not find %s subimage data!\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_GET_DATA); + return -ENOENT; + } + + /* Decrypt data before uncompress/move */ + if (IS_ENABLED(CONFIG_FIT_CIPHER) && IMAGE_ENABLE_DECRYPT) { + puts(" Decrypting Data ... "); + if (fit_image_uncipher(fit, noffset, &buf, &size)) { + puts("Error\n"); + return -EACCES; + } + puts("OK\n"); + } + + /* perform any post-processing on the image data */ + if (!host_build() && IS_ENABLED(CONFIG_FIT_IMAGE_POST_PROCESS)) + board_fit_image_post_process(&buf, &size); + + len = (ulong)size; + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_GET_DATA_OK); + + data = map_to_sysmem(buf); + load = data; + if (load_op == FIT_LOAD_IGNORED) { + /* Don't load */ + } else if (fit_image_get_load(fit, noffset, &load)) { + if (load_op == FIT_LOAD_REQUIRED) { + printf("Can't get %s subimage load address!\n", + prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_LOAD); + return -EBADF; + } + } else if (load_op != FIT_LOAD_OPTIONAL_NON_ZERO || load) { + ulong image_start, image_end; + + /* + * move image data to the load address, + * make sure we don't overwrite initial image + */ + image_start = addr; + image_end = addr + fit_get_size(fit); + + load_end = load + len; + if (image_type != IH_TYPE_KERNEL && + load < image_end && load_end > image_start) { + printf("Error: %s overwritten\n", prop_name); + return -EXDEV; + } + + printf(" Loading %s from 0x%08lx to 0x%08lx\n", + prop_name, data, load); + } else { + load = data; /* No load address specified */ + } + + comp = IH_COMP_NONE; + loadbuf = buf; + /* Kernel images get decompressed later in bootm_load_os(). */ + if (!fit_image_get_comp(fit, noffset, &comp) && + comp != IH_COMP_NONE && + !(image_type == IH_TYPE_KERNEL || + image_type == IH_TYPE_KERNEL_NOLOAD || + image_type == IH_TYPE_RAMDISK)) { + ulong max_decomp_len = len * 20; + if (load == data) { + loadbuf = malloc(max_decomp_len); + load = map_to_sysmem(loadbuf); + } else { + loadbuf = map_sysmem(load, max_decomp_len); + } + if (image_decomp(comp, load, data, image_type, + loadbuf, buf, len, max_decomp_len, &load_end)) { + printf("Error decompressing %s\n", prop_name); + + return -ENOEXEC; + } + len = load_end - load; + } else if (load != data) { + loadbuf = map_sysmem(load, len); + memcpy(loadbuf, buf, len); + } + + if (image_type == IH_TYPE_RAMDISK && comp != IH_COMP_NONE) + puts("WARNING: 'compression' nodes for ramdisks are deprecated," + " please fix your .its file!\n"); + + /* verify that image data is a proper FDT blob */ + if (image_type == IH_TYPE_FLATDT && fdt_check_header(loadbuf)) { + puts("Subimage data is not a FDT"); + return -ENOEXEC; + } + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD); + + *datap = load; + *lenp = len; + if (fit_unamep) + *fit_unamep = (char *)fit_uname; + if (fit_uname_configp) + *fit_uname_configp = (char *)(fit_uname_config ? : + fit_base_uname_config); + + return noffset; +} + +int boot_get_setup_fit(bootm_headers_t *images, uint8_t arch, + ulong *setup_start, ulong *setup_len) +{ + int noffset; + ulong addr; + ulong len; + int ret; + + addr = map_to_sysmem(images->fit_hdr_os); + noffset = fit_get_node_from_config(images, FIT_SETUP_PROP, addr); + if (noffset < 0) + return noffset; + + ret = fit_image_load(images, addr, NULL, NULL, arch, + IH_TYPE_X86_SETUP, BOOTSTAGE_ID_FIT_SETUP_START, + FIT_LOAD_REQUIRED, setup_start, &len); + + return ret; +} + +#ifndef USE_HOSTCC +int boot_get_fdt_fit(bootm_headers_t *images, ulong addr, + const char **fit_unamep, const char **fit_uname_configp, + int arch, ulong *datap, ulong *lenp) +{ + int fdt_noffset, cfg_noffset, count; + const void *fit; + const char *fit_uname = NULL; + const char *fit_uname_config = NULL; + char *fit_uname_config_copy = NULL; + char *next_config = NULL; + ulong load, len; +#ifdef CONFIG_OF_LIBFDT_OVERLAY + ulong image_start, image_end; + ulong ovload, ovlen; + const char *uconfig; + const char *uname; + void *base, *ov; + int i, err, noffset, ov_noffset; +#endif + + fit_uname = fit_unamep ? *fit_unamep : NULL; + + if (fit_uname_configp && *fit_uname_configp) { + fit_uname_config_copy = strdup(*fit_uname_configp); + if (!fit_uname_config_copy) + return -ENOMEM; + + next_config = strchr(fit_uname_config_copy, '#'); + if (next_config) + *next_config++ = '\0'; + if (next_config - 1 > fit_uname_config_copy) + fit_uname_config = fit_uname_config_copy; + } + + fdt_noffset = fit_image_load(images, + addr, &fit_uname, &fit_uname_config, + arch, IH_TYPE_FLATDT, + BOOTSTAGE_ID_FIT_FDT_START, + FIT_LOAD_OPTIONAL, &load, &len); + + if (fdt_noffset < 0) + goto out; + + debug("fit_uname=%s, fit_uname_config=%s\n", + fit_uname ? fit_uname : "<NULL>", + fit_uname_config ? fit_uname_config : "<NULL>"); + + fit = map_sysmem(addr, 0); + + cfg_noffset = fit_conf_get_node(fit, fit_uname_config); + + /* single blob, or error just return as well */ + count = fit_conf_get_prop_node_count(fit, cfg_noffset, FIT_FDT_PROP); + if (count <= 1 && !next_config) + goto out; + + /* we need to apply overlays */ + +#ifdef CONFIG_OF_LIBFDT_OVERLAY + image_start = addr; + image_end = addr + fit_get_size(fit); + /* verify that relocation took place by load address not being in fit */ + if (load >= image_start && load < image_end) { + /* check is simplified; fit load checks for overlaps */ + printf("Overlayed FDT requires relocation\n"); + fdt_noffset = -EBADF; + goto out; + } + + base = map_sysmem(load, len); + + /* apply extra configs in FIT first, followed by args */ + for (i = 1; ; i++) { + if (i < count) { + noffset = fit_conf_get_prop_node_index(fit, cfg_noffset, + FIT_FDT_PROP, i); + uname = fit_get_name(fit, noffset, NULL); + uconfig = NULL; + } else { + if (!next_config) + break; + uconfig = next_config; + next_config = strchr(next_config, '#'); + if (next_config) + *next_config++ = '\0'; + uname = NULL; + + /* + * fit_image_load() would load the first FDT from the + * extra config only when uconfig is specified. + * Check if the extra config contains multiple FDTs and + * if so, load them. + */ + cfg_noffset = fit_conf_get_node(fit, uconfig); + + i = 0; + count = fit_conf_get_prop_node_count(fit, cfg_noffset, + FIT_FDT_PROP); + } + + debug("%d: using uname=%s uconfig=%s\n", i, uname, uconfig); + + ov_noffset = fit_image_load(images, + addr, &uname, &uconfig, + arch, IH_TYPE_FLATDT, + BOOTSTAGE_ID_FIT_FDT_START, + FIT_LOAD_REQUIRED, &ovload, &ovlen); + if (ov_noffset < 0) { + printf("load of %s failed\n", uname); + continue; + } + debug("%s loaded at 0x%08lx len=0x%08lx\n", + uname, ovload, ovlen); + ov = map_sysmem(ovload, ovlen); + + base = map_sysmem(load, len + ovlen); + err = fdt_open_into(base, base, len + ovlen); + if (err < 0) { + printf("failed on fdt_open_into\n"); + fdt_noffset = err; + goto out; + } + /* the verbose method prints out messages on error */ + err = fdt_overlay_apply_verbose(base, ov); + if (err < 0) { + fdt_noffset = err; + goto out; + } + fdt_pack(base); + len = fdt_totalsize(base); + } +#else + printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not set\n"); + fdt_noffset = -EBADF; +#endif + +out: + if (datap) + *datap = load; + if (lenp) + *lenp = len; + if (fit_unamep) + *fit_unamep = fit_uname; + if (fit_uname_configp) + *fit_uname_configp = fit_uname_config; + + if (fit_uname_config_copy) + free(fit_uname_config_copy); + return fdt_noffset; +} +#endif diff --git a/roms/u-boot/common/image-sig.c b/roms/u-boot/common/image-sig.c new file mode 100644 index 000000000..0f8e592ab --- /dev/null +++ b/roms/u-boot/common/image-sig.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <fdt_support.h> +#include <time.h> +#include <linux/libfdt.h> +#else +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include <image.h> +#include <u-boot/ecdsa.h> +#include <u-boot/rsa.h> +#include <u-boot/hash-checksum.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +struct checksum_algo checksum_algos[] = { + { + .name = "sha1", + .checksum_len = SHA1_SUM_LEN, + .der_len = SHA1_DER_LEN, + .der_prefix = sha1_der_prefix, +#if IMAGE_ENABLE_SIGN + .calculate_sign = EVP_sha1, +#endif + .calculate = hash_calculate, + }, + { + .name = "sha256", + .checksum_len = SHA256_SUM_LEN, + .der_len = SHA256_DER_LEN, + .der_prefix = sha256_der_prefix, +#if IMAGE_ENABLE_SIGN + .calculate_sign = EVP_sha256, +#endif + .calculate = hash_calculate, + }, +#ifdef CONFIG_SHA384 + { + .name = "sha384", + .checksum_len = SHA384_SUM_LEN, + .der_len = SHA384_DER_LEN, + .der_prefix = sha384_der_prefix, +#if IMAGE_ENABLE_SIGN + .calculate_sign = EVP_sha384, +#endif + .calculate = hash_calculate, + }, +#endif +#ifdef CONFIG_SHA512 + { + .name = "sha512", + .checksum_len = SHA512_SUM_LEN, + .der_len = SHA512_DER_LEN, + .der_prefix = sha512_der_prefix, +#if IMAGE_ENABLE_SIGN + .calculate_sign = EVP_sha512, +#endif + .calculate = hash_calculate, + }, +#endif + +}; + +struct crypto_algo crypto_algos[] = { + { + .name = "rsa2048", + .key_len = RSA2048_BYTES, + .sign = rsa_sign, + .add_verify_data = rsa_add_verify_data, + .verify = rsa_verify, + }, + { + .name = "rsa4096", + .key_len = RSA4096_BYTES, + .sign = rsa_sign, + .add_verify_data = rsa_add_verify_data, + .verify = rsa_verify, + }, + { + .name = "ecdsa256", + .key_len = ECDSA256_BYTES, + .sign = ecdsa_sign, + .add_verify_data = ecdsa_add_verify_data, + .verify = ecdsa_verify, + }, +}; + +struct padding_algo padding_algos[] = { + { + .name = "pkcs-1.5", + .verify = padding_pkcs_15_verify, + }, +#ifdef CONFIG_FIT_ENABLE_RSASSA_PSS_SUPPORT + { + .name = "pss", + .verify = padding_pss_verify, + } +#endif /* CONFIG_FIT_ENABLE_RSASSA_PSS_SUPPORT */ +}; + +struct checksum_algo *image_get_checksum_algo(const char *full_name) +{ + int i; + const char *name; + +#if !defined(USE_HOSTCC) && defined(CONFIG_NEEDS_MANUAL_RELOC) + static bool done; + + if (!done) { + done = true; + for (i = 0; i < ARRAY_SIZE(checksum_algos); i++) { + checksum_algos[i].name += gd->reloc_off; +#if IMAGE_ENABLE_SIGN + checksum_algos[i].calculate_sign += gd->reloc_off; +#endif + checksum_algos[i].calculate += gd->reloc_off; + } + } +#endif + + for (i = 0; i < ARRAY_SIZE(checksum_algos); i++) { + name = checksum_algos[i].name; + /* Make sure names match and next char is a comma */ + if (!strncmp(name, full_name, strlen(name)) && + full_name[strlen(name)] == ',') + return &checksum_algos[i]; + } + + return NULL; +} + +struct crypto_algo *image_get_crypto_algo(const char *full_name) +{ + int i; + const char *name; + +#if !defined(USE_HOSTCC) && defined(CONFIG_NEEDS_MANUAL_RELOC) + static bool done; + + if (!done) { + done = true; + for (i = 0; i < ARRAY_SIZE(crypto_algos); i++) { + crypto_algos[i].name += gd->reloc_off; + crypto_algos[i].sign += gd->reloc_off; + crypto_algos[i].add_verify_data += gd->reloc_off; + crypto_algos[i].verify += gd->reloc_off; + } + } +#endif + + /* Move name to after the comma */ + name = strchr(full_name, ','); + if (!name) + return NULL; + name += 1; + + for (i = 0; i < ARRAY_SIZE(crypto_algos); i++) { + if (!strcmp(crypto_algos[i].name, name)) + return &crypto_algos[i]; + } + + return NULL; +} + +struct padding_algo *image_get_padding_algo(const char *name) +{ + int i; + + if (!name) + return NULL; + + for (i = 0; i < ARRAY_SIZE(padding_algos); i++) { + if (!strcmp(padding_algos[i].name, name)) + return &padding_algos[i]; + } + + return NULL; +} diff --git a/roms/u-boot/common/image.c b/roms/u-boot/common/image.c new file mode 100644 index 000000000..51854aae5 --- /dev/null +++ b/roms/u-boot/common/image.c @@ -0,0 +1,1753 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <bootstage.h> +#include <cpu_func.h> +#include <env.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <asm/cache.h> +#include <u-boot/crc.h> +#include <watchdog.h> + +#ifdef CONFIG_SHOW_BOOT_PROGRESS +#include <status_led.h> +#endif + +#include <rtc.h> + +#include <gzip.h> +#include <image.h> +#include <lz4.h> +#include <mapmem.h> + +#if IMAGE_ENABLE_FIT || IMAGE_ENABLE_OF_LIBFDT +#include <linux/libfdt.h> +#include <fdt_support.h> +#include <fpga.h> +#include <xilinx.h> +#endif + +#include <asm/global_data.h> +#include <u-boot/md5.h> +#include <u-boot/sha1.h> +#include <linux/errno.h> +#include <asm/io.h> + +#include <bzlib.h> +#include <linux/lzo.h> +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> +#include <linux/zstd.h> + +#ifdef CONFIG_CMD_BDI +extern int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, + int verify); +#endif +#else +#include "mkimage.h" +#include <u-boot/md5.h> +#include <time.h> +#include <image.h> + +#ifndef __maybe_unused +# define __maybe_unused /* unimplemented */ +#endif +#endif /* !USE_HOSTCC*/ + +#include <u-boot/crc.h> +#include <imximage.h> + +#ifndef CONFIG_SYS_BARGSIZE +#define CONFIG_SYS_BARGSIZE 512 +#endif + +static const table_entry_t uimage_arch[] = { + { IH_ARCH_INVALID, "invalid", "Invalid ARCH", }, + { IH_ARCH_ALPHA, "alpha", "Alpha", }, + { IH_ARCH_ARM, "arm", "ARM", }, + { IH_ARCH_I386, "x86", "Intel x86", }, + { IH_ARCH_IA64, "ia64", "IA64", }, + { IH_ARCH_M68K, "m68k", "M68K", }, + { IH_ARCH_MICROBLAZE, "microblaze", "MicroBlaze", }, + { IH_ARCH_MIPS, "mips", "MIPS", }, + { IH_ARCH_MIPS64, "mips64", "MIPS 64 Bit", }, + { IH_ARCH_NIOS2, "nios2", "NIOS II", }, + { IH_ARCH_PPC, "powerpc", "PowerPC", }, + { IH_ARCH_PPC, "ppc", "PowerPC", }, + { IH_ARCH_S390, "s390", "IBM S390", }, + { IH_ARCH_SH, "sh", "SuperH", }, + { IH_ARCH_SPARC, "sparc", "SPARC", }, + { IH_ARCH_SPARC64, "sparc64", "SPARC 64 Bit", }, + { IH_ARCH_BLACKFIN, "blackfin", "Blackfin", }, + { IH_ARCH_AVR32, "avr32", "AVR32", }, + { IH_ARCH_NDS32, "nds32", "NDS32", }, + { IH_ARCH_OPENRISC, "or1k", "OpenRISC 1000",}, + { IH_ARCH_SANDBOX, "sandbox", "Sandbox", }, + { IH_ARCH_ARM64, "arm64", "AArch64", }, + { IH_ARCH_ARC, "arc", "ARC", }, + { IH_ARCH_X86_64, "x86_64", "AMD x86_64", }, + { IH_ARCH_XTENSA, "xtensa", "Xtensa", }, + { IH_ARCH_RISCV, "riscv", "RISC-V", }, + { -1, "", "", }, +}; + +static const table_entry_t uimage_os[] = { + { IH_OS_INVALID, "invalid", "Invalid OS", }, + { IH_OS_ARM_TRUSTED_FIRMWARE, "arm-trusted-firmware", "ARM Trusted Firmware" }, + { IH_OS_LINUX, "linux", "Linux", }, +#if defined(CONFIG_LYNXKDI) || defined(USE_HOSTCC) + { IH_OS_LYNXOS, "lynxos", "LynxOS", }, +#endif + { IH_OS_NETBSD, "netbsd", "NetBSD", }, + { IH_OS_OSE, "ose", "Enea OSE", }, + { IH_OS_PLAN9, "plan9", "Plan 9", }, + { IH_OS_RTEMS, "rtems", "RTEMS", }, + { IH_OS_TEE, "tee", "Trusted Execution Environment" }, + { IH_OS_U_BOOT, "u-boot", "U-Boot", }, + { IH_OS_VXWORKS, "vxworks", "VxWorks", }, +#if defined(CONFIG_CMD_ELF) || defined(USE_HOSTCC) + { IH_OS_QNX, "qnx", "QNX", }, +#endif +#if defined(CONFIG_INTEGRITY) || defined(USE_HOSTCC) + { IH_OS_INTEGRITY,"integrity", "INTEGRITY", }, +#endif +#ifdef USE_HOSTCC + { IH_OS_4_4BSD, "4_4bsd", "4_4BSD", }, + { IH_OS_DELL, "dell", "Dell", }, + { IH_OS_ESIX, "esix", "Esix", }, + { IH_OS_FREEBSD, "freebsd", "FreeBSD", }, + { IH_OS_IRIX, "irix", "Irix", }, + { IH_OS_NCR, "ncr", "NCR", }, + { IH_OS_OPENBSD, "openbsd", "OpenBSD", }, + { IH_OS_PSOS, "psos", "pSOS", }, + { IH_OS_SCO, "sco", "SCO", }, + { IH_OS_SOLARIS, "solaris", "Solaris", }, + { IH_OS_SVR4, "svr4", "SVR4", }, +#endif +#if defined(CONFIG_BOOTM_OPENRTOS) || defined(USE_HOSTCC) + { IH_OS_OPENRTOS, "openrtos", "OpenRTOS", }, +#endif + { IH_OS_OPENSBI, "opensbi", "RISC-V OpenSBI", }, + { IH_OS_EFI, "efi", "EFI Firmware" }, + + { -1, "", "", }, +}; + +static const table_entry_t uimage_type[] = { + { IH_TYPE_AISIMAGE, "aisimage", "Davinci AIS image",}, + { IH_TYPE_FILESYSTEM, "filesystem", "Filesystem Image", }, + { IH_TYPE_FIRMWARE, "firmware", "Firmware", }, + { IH_TYPE_FLATDT, "flat_dt", "Flat Device Tree", }, + { IH_TYPE_GPIMAGE, "gpimage", "TI Keystone SPL Image",}, + { IH_TYPE_KERNEL, "kernel", "Kernel Image", }, + { IH_TYPE_KERNEL_NOLOAD, "kernel_noload", "Kernel Image (no loading done)", }, + { IH_TYPE_KWBIMAGE, "kwbimage", "Kirkwood Boot Image",}, + { IH_TYPE_IMXIMAGE, "imximage", "Freescale i.MX Boot Image",}, + { IH_TYPE_IMX8IMAGE, "imx8image", "NXP i.MX8 Boot Image",}, + { IH_TYPE_IMX8MIMAGE, "imx8mimage", "NXP i.MX8M Boot Image",}, + { IH_TYPE_INVALID, "invalid", "Invalid Image", }, + { IH_TYPE_MULTI, "multi", "Multi-File Image", }, + { IH_TYPE_OMAPIMAGE, "omapimage", "TI OMAP SPL With GP CH",}, + { IH_TYPE_PBLIMAGE, "pblimage", "Freescale PBL Boot Image",}, + { IH_TYPE_RAMDISK, "ramdisk", "RAMDisk Image", }, + { IH_TYPE_SCRIPT, "script", "Script", }, + { IH_TYPE_SOCFPGAIMAGE, "socfpgaimage", "Altera SoCFPGA CV/AV preloader",}, + { IH_TYPE_SOCFPGAIMAGE_V1, "socfpgaimage_v1", "Altera SoCFPGA A10 preloader",}, + { IH_TYPE_STANDALONE, "standalone", "Standalone Program", }, + { IH_TYPE_UBLIMAGE, "ublimage", "Davinci UBL image",}, + { IH_TYPE_MXSIMAGE, "mxsimage", "Freescale MXS Boot Image",}, + { IH_TYPE_ATMELIMAGE, "atmelimage", "ATMEL ROM-Boot Image",}, + { IH_TYPE_X86_SETUP, "x86_setup", "x86 setup.bin", }, + { IH_TYPE_LPC32XXIMAGE, "lpc32xximage", "LPC32XX Boot Image", }, + { IH_TYPE_RKIMAGE, "rkimage", "Rockchip Boot Image" }, + { IH_TYPE_RKSD, "rksd", "Rockchip SD Boot Image" }, + { IH_TYPE_RKSPI, "rkspi", "Rockchip SPI Boot Image" }, + { IH_TYPE_VYBRIDIMAGE, "vybridimage", "Vybrid Boot Image", }, + { IH_TYPE_ZYNQIMAGE, "zynqimage", "Xilinx Zynq Boot Image" }, + { IH_TYPE_ZYNQMPIMAGE, "zynqmpimage", "Xilinx ZynqMP Boot Image" }, + { IH_TYPE_ZYNQMPBIF, "zynqmpbif", "Xilinx ZynqMP Boot Image (bif)" }, + { IH_TYPE_FPGA, "fpga", "FPGA Image" }, + { IH_TYPE_TEE, "tee", "Trusted Execution Environment Image",}, + { IH_TYPE_FIRMWARE_IVT, "firmware_ivt", "Firmware with HABv4 IVT" }, + { IH_TYPE_PMMC, "pmmc", "TI Power Management Micro-Controller Firmware",}, + { IH_TYPE_STM32IMAGE, "stm32image", "STMicroelectronics STM32 Image" }, + { IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" }, + { IH_TYPE_COPRO, "copro", "Coprocessor Image"}, + { IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" }, + { -1, "", "", }, +}; + +static const table_entry_t uimage_comp[] = { + { IH_COMP_NONE, "none", "uncompressed", }, + { IH_COMP_BZIP2, "bzip2", "bzip2 compressed", }, + { IH_COMP_GZIP, "gzip", "gzip compressed", }, + { IH_COMP_LZMA, "lzma", "lzma compressed", }, + { IH_COMP_LZO, "lzo", "lzo compressed", }, + { IH_COMP_LZ4, "lz4", "lz4 compressed", }, + { IH_COMP_ZSTD, "zstd", "zstd compressed", }, + { -1, "", "", }, +}; + +struct table_info { + const char *desc; + int count; + const table_entry_t *table; +}; + +static const struct comp_magic_map image_comp[] = { + { IH_COMP_BZIP2, "bzip2", {0x42, 0x5a},}, + { IH_COMP_GZIP, "gzip", {0x1f, 0x8b},}, + { IH_COMP_LZMA, "lzma", {0x5d, 0x00},}, + { IH_COMP_LZO, "lzo", {0x89, 0x4c},}, + { IH_COMP_NONE, "none", {}, }, +}; + +static const struct table_info table_info[IH_COUNT] = { + { "architecture", IH_ARCH_COUNT, uimage_arch }, + { "compression", IH_COMP_COUNT, uimage_comp }, + { "operating system", IH_OS_COUNT, uimage_os }, + { "image type", IH_TYPE_COUNT, uimage_type }, +}; + +/*****************************************************************************/ +/* Legacy format routines */ +/*****************************************************************************/ +int image_check_hcrc(const image_header_t *hdr) +{ + ulong hcrc; + ulong len = image_get_header_size(); + image_header_t header; + + /* Copy header so we can blank CRC field for re-calculation */ + memmove(&header, (char *)hdr, image_get_header_size()); + image_set_hcrc(&header, 0); + + hcrc = crc32(0, (unsigned char *)&header, len); + + return (hcrc == image_get_hcrc(hdr)); +} + +int image_check_dcrc(const image_header_t *hdr) +{ + ulong data = image_get_data(hdr); + ulong len = image_get_data_size(hdr); + ulong dcrc = crc32_wd(0, (unsigned char *)data, len, CHUNKSZ_CRC32); + + return (dcrc == image_get_dcrc(hdr)); +} + +/** + * image_multi_count - get component (sub-image) count + * @hdr: pointer to the header of the multi component image + * + * image_multi_count() returns number of components in a multi + * component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * number of components + */ +ulong image_multi_count(const image_header_t *hdr) +{ + ulong i, count = 0; + uint32_t *size; + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* count non empty slots */ + for (i = 0; size[i]; ++i) + count++; + + return count; +} + +/** + * image_multi_getimg - get component data address and size + * @hdr: pointer to the header of the multi component image + * @idx: index of the requested component + * @data: pointer to a ulong variable, will hold component data address + * @len: pointer to a ulong variable, will hold component size + * + * image_multi_getimg() returns size and data address for the requested + * component in a multi component image. + * + * Note: no checking of the image type is done, caller must pass + * a valid multi component image. + * + * returns: + * data address and size of the component, if idx is valid + * 0 in data and len, if idx is out of range + */ +void image_multi_getimg(const image_header_t *hdr, ulong idx, + ulong *data, ulong *len) +{ + int i; + uint32_t *size; + ulong offset, count, img_data; + + /* get number of component */ + count = image_multi_count(hdr); + + /* get start of the image payload, which in case of multi + * component images that points to a table of component sizes */ + size = (uint32_t *)image_get_data(hdr); + + /* get address of the proper component data start, which means + * skipping sizes table (add 1 for last, null entry) */ + img_data = image_get_data(hdr) + (count + 1) * sizeof(uint32_t); + + if (idx < count) { + *len = uimage_to_cpu(size[idx]); + offset = 0; + + /* go over all indices preceding requested component idx */ + for (i = 0; i < idx; i++) { + /* add up i-th component size, rounding up to 4 bytes */ + offset += (uimage_to_cpu(size[i]) + 3) & ~3 ; + } + + /* calculate idx-th component data address */ + *data = img_data + offset; + } else { + *len = 0; + *data = 0; + } +} + +static void image_print_type(const image_header_t *hdr) +{ + const char __maybe_unused *os, *arch, *type, *comp; + + os = genimg_get_os_name(image_get_os(hdr)); + arch = genimg_get_arch_name(image_get_arch(hdr)); + type = genimg_get_type_name(image_get_type(hdr)); + comp = genimg_get_comp_name(image_get_comp(hdr)); + + printf("%s %s %s (%s)\n", arch, os, type, comp); +} + +/** + * image_print_contents - prints out the contents of the legacy format image + * @ptr: pointer to the legacy format image header + * @p: pointer to prefix string + * + * image_print_contents() formats a multi line legacy image contents description. + * The routine prints out all header fields followed by the size/offset data + * for MULTI/SCRIPT images. + * + * returns: + * no returned results + */ +void image_print_contents(const void *ptr) +{ + const image_header_t *hdr = (const image_header_t *)ptr; + const char __maybe_unused *p; + + p = IMAGE_INDENT_STRING; + printf("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name(hdr)); + if (IMAGE_ENABLE_TIMESTAMP) { + printf("%sCreated: ", p); + genimg_print_time((time_t)image_get_time(hdr)); + } + printf("%sImage Type: ", p); + image_print_type(hdr); + printf("%sData Size: ", p); + genimg_print_size(image_get_data_size(hdr)); + printf("%sLoad Address: %08x\n", p, image_get_load(hdr)); + printf("%sEntry Point: %08x\n", p, image_get_ep(hdr)); + + if (image_check_type(hdr, IH_TYPE_MULTI) || + image_check_type(hdr, IH_TYPE_SCRIPT)) { + int i; + ulong data, len; + ulong count = image_multi_count(hdr); + + printf("%sContents:\n", p); + for (i = 0; i < count; i++) { + image_multi_getimg(hdr, i, &data, &len); + + printf("%s Image %d: ", p, i); + genimg_print_size(len); + + if (image_check_type(hdr, IH_TYPE_SCRIPT) && i > 0) { + /* + * the user may need to know offsets + * if planning to do something with + * multiple files + */ + printf("%s Offset = 0x%08lx\n", p, data); + } + } + } else if (image_check_type(hdr, IH_TYPE_FIRMWARE_IVT)) { + printf("HAB Blocks: 0x%08x 0x0000 0x%08x\n", + image_get_load(hdr) - image_get_header_size(), + (int)(image_get_size(hdr) + image_get_header_size() + + sizeof(flash_header_v2_t) - 0x2060)); + } +} + +/** + * print_decomp_msg() - Print a suitable decompression/loading message + * + * @type: OS type (IH_OS_...) + * @comp_type: Compression type being used (IH_COMP_...) + * @is_xip: true if the load address matches the image start + */ +static void print_decomp_msg(int comp_type, int type, bool is_xip) +{ + const char *name = genimg_get_type_name(type); + + if (comp_type == IH_COMP_NONE) + printf(" %s %s\n", is_xip ? "XIP" : "Loading", name); + else + printf(" Uncompressing %s\n", name); +} + +int image_decomp_type(const unsigned char *buf, ulong len) +{ + const struct comp_magic_map *cmagic = image_comp; + + if (len < 2) + return -EINVAL; + + for (; cmagic->comp_id > 0; cmagic++) { + if (!memcmp(buf, cmagic->magic, 2)) + break; + } + + return cmagic->comp_id; +} + +int image_decomp(int comp, ulong load, ulong image_start, int type, + void *load_buf, void *image_buf, ulong image_len, + uint unc_len, ulong *load_end) +{ + int ret = 0; + + *load_end = load; + print_decomp_msg(comp, type, load == image_start); + + /* + * Load the image to the right place, decompressing if needed. After + * this, image_len will be set to the number of uncompressed bytes + * loaded, ret will be non-zero on error. + */ + switch (comp) { + case IH_COMP_NONE: + if (load == image_start) + break; + if (image_len <= unc_len) + memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); + else + ret = -ENOSPC; + break; +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(GZIP) + case IH_COMP_GZIP: { + ret = gunzip(load_buf, unc_len, image_buf, &image_len); + break; + } +#endif /* CONFIG_GZIP */ +#endif +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(BZIP2) + case IH_COMP_BZIP2: { + uint size = unc_len; + + /* + * If we've got less than 4 MB of malloc() space, + * use slower decompression algorithm which requires + * at most 2300 KB of memory. + */ + ret = BZ2_bzBuffToBuffDecompress(load_buf, &size, + image_buf, image_len, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0); + image_len = size; + break; + } +#endif /* CONFIG_BZIP2 */ +#endif +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(LZMA) + case IH_COMP_LZMA: { + SizeT lzma_len = unc_len; + + ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len, + image_buf, image_len); + image_len = lzma_len; + break; + } +#endif /* CONFIG_LZMA */ +#endif +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(LZO) + case IH_COMP_LZO: { + size_t size = unc_len; + + ret = lzop_decompress(image_buf, image_len, load_buf, &size); + image_len = size; + break; + } +#endif /* CONFIG_LZO */ +#endif +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(LZ4) + case IH_COMP_LZ4: { + size_t size = unc_len; + + ret = ulz4fn(image_buf, image_len, load_buf, &size); + image_len = size; + break; + } +#endif /* CONFIG_LZ4 */ +#endif +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(ZSTD) + case IH_COMP_ZSTD: { + size_t size = unc_len; + ZSTD_DStream *dstream; + ZSTD_inBuffer in_buf; + ZSTD_outBuffer out_buf; + void *workspace; + size_t wsize; + + wsize = ZSTD_DStreamWorkspaceBound(image_len); + workspace = malloc(wsize); + if (!workspace) { + debug("%s: cannot allocate workspace of size %zu\n", __func__, + wsize); + return -1; + } + + dstream = ZSTD_initDStream(image_len, workspace, wsize); + if (!dstream) { + printf("%s: ZSTD_initDStream failed\n", __func__); + return ZSTD_getErrorCode(ret); + } + + in_buf.src = image_buf; + in_buf.pos = 0; + in_buf.size = image_len; + + out_buf.dst = load_buf; + out_buf.pos = 0; + out_buf.size = size; + + while (1) { + size_t ret; + + ret = ZSTD_decompressStream(dstream, &out_buf, &in_buf); + if (ZSTD_isError(ret)) { + printf("%s: ZSTD_decompressStream error %d\n", __func__, + ZSTD_getErrorCode(ret)); + return ZSTD_getErrorCode(ret); + } + + if (in_buf.pos >= image_len || !ret) + break; + } + + image_len = out_buf.pos; + + break; + } +#endif /* CONFIG_ZSTD */ +#endif + default: + printf("Unimplemented compression type %d\n", comp); + return -ENOSYS; + } + + *load_end = load + image_len; + + return ret; +} + + +#ifndef USE_HOSTCC +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) +/** + * image_get_ramdisk - get and verify ramdisk image + * @rd_addr: ramdisk image start address + * @arch: expected ramdisk architecture + * @verify: checksum verification flag + * + * image_get_ramdisk() returns a pointer to the verified ramdisk image + * header. Routine receives image start address and expected architecture + * flag. Verification done covers data and header integrity and os/type/arch + * fields checking. + * + * returns: + * pointer to a ramdisk image header, if image was found and valid + * otherwise, return NULL + */ +static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, + int verify) +{ + const image_header_t *rd_hdr = (const image_header_t *)rd_addr; + + if (!image_check_magic(rd_hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_RD_MAGIC); + return NULL; + } + + if (!image_check_hcrc(rd_hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_RD_MAGIC); + image_print_contents(rd_hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(rd_hdr)) { + puts("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_RD_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + + bootstage_mark(BOOTSTAGE_ID_RD_HDR_CHECKSUM); + + if (!image_check_os(rd_hdr, IH_OS_LINUX) || + !image_check_arch(rd_hdr, arch) || + !image_check_type(rd_hdr, IH_TYPE_RAMDISK)) { + printf("No Linux %s Ramdisk Image\n", + genimg_get_arch_name(arch)); + bootstage_error(BOOTSTAGE_ID_RAMDISK); + return NULL; + } + + return rd_hdr; +} +#endif +#endif /* !USE_HOSTCC */ + +/*****************************************************************************/ +/* Shared dual-format routines */ +/*****************************************************************************/ +#ifndef USE_HOSTCC +ulong image_load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ +ulong image_save_addr; /* Default Save Address */ +ulong image_save_size; /* Default Save Size (in bytes) */ + +static int on_loadaddr(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + image_load_addr = simple_strtoul(value, NULL, 16); + break; + default: + break; + } + + return 0; +} +U_BOOT_ENV_CALLBACK(loadaddr, on_loadaddr); + +ulong env_get_bootm_low(void) +{ + char *s = env_get("bootm_low"); + if (s) { + ulong tmp = simple_strtoul(s, NULL, 16); + return tmp; + } + +#if defined(CONFIG_SYS_SDRAM_BASE) + return CONFIG_SYS_SDRAM_BASE; +#elif defined(CONFIG_ARM) || defined(CONFIG_MICROBLAZE) + return gd->bd->bi_dram[0].start; +#else + return 0; +#endif +} + +phys_size_t env_get_bootm_size(void) +{ + phys_size_t tmp, size; + phys_addr_t start; + char *s = env_get("bootm_size"); + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + + start = gd->ram_base; + size = gd->ram_size; + + if (start + size > gd->ram_top) + size = gd->ram_top - start; + + s = env_get("bootm_low"); + if (s) + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + else + tmp = start; + + return size - (tmp - start); +} + +phys_size_t env_get_bootm_mapsize(void) +{ + phys_size_t tmp; + char *s = env_get("bootm_mapsize"); + if (s) { + tmp = (phys_size_t)simple_strtoull(s, NULL, 16); + return tmp; + } + +#if defined(CONFIG_SYS_BOOTMAPSZ) + return CONFIG_SYS_BOOTMAPSZ; +#else + return env_get_bootm_size(); +#endif +} + +void memmove_wd(void *to, void *from, size_t len, ulong chunksz) +{ + if (to == from) + return; + +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + if (to > from) { + from += len; + to += len; + } + while (len > 0) { + size_t tail = (len > chunksz) ? chunksz : len; + WATCHDOG_RESET(); + if (to > from) { + to -= tail; + from -= tail; + } + memmove(to, from, tail); + if (to < from) { + to += tail; + from += tail; + } + len -= tail; + } +#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ + memmove(to, from, len); +#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ +} +#else /* USE_HOSTCC */ +void memmove_wd(void *to, void *from, size_t len, ulong chunksz) +{ + memmove(to, from, len); +} +#endif /* !USE_HOSTCC */ + +void genimg_print_size(uint32_t size) +{ +#ifndef USE_HOSTCC + printf("%d Bytes = ", size); + print_size(size, "\n"); +#else + printf("%d Bytes = %.2f KiB = %.2f MiB\n", + size, (double)size / 1.024e3, + (double)size / 1.048576e6); +#endif +} + +#if IMAGE_ENABLE_TIMESTAMP +void genimg_print_time(time_t timestamp) +{ +#ifndef USE_HOSTCC + struct rtc_time tm; + + rtc_to_tm(timestamp, &tm); + printf("%4d-%02d-%02d %2d:%02d:%02d UTC\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +#else + printf("%s", ctime(×tamp)); +#endif +} +#endif + +const table_entry_t *get_table_entry(const table_entry_t *table, int id) +{ + for (; table->id >= 0; ++table) { + if (table->id == id) + return table; + } + return NULL; +} + +static const char *unknown_msg(enum ih_category category) +{ + static const char unknown_str[] = "Unknown "; + static char msg[30]; + + strcpy(msg, unknown_str); + strncat(msg, table_info[category].desc, + sizeof(msg) - sizeof(unknown_str)); + + return msg; +} + +/** + * genimg_get_cat_name - translate entry id to long name + * @category: category to look up (enum ih_category) + * @id: entry id to be translated + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return long entry name if translation succeeds; error string on failure + */ +const char *genimg_get_cat_name(enum ih_category category, uint id) +{ + const table_entry_t *entry; + + entry = get_table_entry(table_info[category].table, id); + if (!entry) + return unknown_msg(category); +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return entry->lname; +#else + return entry->lname + gd->reloc_off; +#endif +} + +/** + * genimg_get_cat_short_name - translate entry id to short name + * @category: category to look up (enum ih_category) + * @id: entry id to be translated + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return short entry name if translation succeeds; error string on failure + */ +const char *genimg_get_cat_short_name(enum ih_category category, uint id) +{ + const table_entry_t *entry; + + entry = get_table_entry(table_info[category].table, id); + if (!entry) + return unknown_msg(category); +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return entry->sname; +#else + return entry->sname + gd->reloc_off; +#endif +} + +int genimg_get_cat_count(enum ih_category category) +{ + return table_info[category].count; +} + +const char *genimg_get_cat_desc(enum ih_category category) +{ + return table_info[category].desc; +} + +/** + * genimg_cat_has_id - check whether category has entry id + * @category: category to look up (enum ih_category) + * @id: entry id to be checked + * + * This will scan the translation table trying to find the entry that matches + * the given id. + * + * @return true if category has entry id; false if not + */ +bool genimg_cat_has_id(enum ih_category category, uint id) +{ + if (get_table_entry(table_info[category].table, id)) + return true; + + return false; +} + +/** + * get_table_entry_name - translate entry id to long name + * @table: pointer to a translation table for entries of a specific type + * @msg: message to be returned when translation fails + * @id: entry id to be translated + * + * get_table_entry_name() will go over translation table trying to find + * entry that matches given id. If matching entry is found, its long + * name is returned to the caller. + * + * returns: + * long entry name if translation succeeds + * msg otherwise + */ +char *get_table_entry_name(const table_entry_t *table, char *msg, int id) +{ + table = get_table_entry(table, id); + if (!table) + return msg; +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return table->lname; +#else + return table->lname + gd->reloc_off; +#endif +} + +const char *genimg_get_os_name(uint8_t os) +{ + return (get_table_entry_name(uimage_os, "Unknown OS", os)); +} + +const char *genimg_get_arch_name(uint8_t arch) +{ + return (get_table_entry_name(uimage_arch, "Unknown Architecture", + arch)); +} + +const char *genimg_get_type_name(uint8_t type) +{ + return (get_table_entry_name(uimage_type, "Unknown Image", type)); +} + +const char *genimg_get_comp_name(uint8_t comp) +{ + return (get_table_entry_name(uimage_comp, "Unknown Compression", + comp)); +} + +static const char *genimg_get_short_name(const table_entry_t *table, int val) +{ + table = get_table_entry(table, val); + if (!table) + return "unknown"; +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return table->sname; +#else + return table->sname + gd->reloc_off; +#endif +} + +const char *genimg_get_type_short_name(uint8_t type) +{ + return genimg_get_short_name(uimage_type, type); +} + +const char *genimg_get_comp_short_name(uint8_t comp) +{ + return genimg_get_short_name(uimage_comp, comp); +} + +const char *genimg_get_os_short_name(uint8_t os) +{ + return genimg_get_short_name(uimage_os, os); +} + +const char *genimg_get_arch_short_name(uint8_t arch) +{ + return genimg_get_short_name(uimage_arch, arch); +} + +/** + * get_table_entry_id - translate short entry name to id + * @table: pointer to a translation table for entries of a specific type + * @table_name: to be used in case of error + * @name: entry short name to be translated + * + * get_table_entry_id() will go over translation table trying to find + * entry that matches given short name. If matching entry is found, + * its id returned to the caller. + * + * returns: + * entry id if translation succeeds + * -1 otherwise + */ +int get_table_entry_id(const table_entry_t *table, + const char *table_name, const char *name) +{ + const table_entry_t *t; + + for (t = table; t->id >= 0; ++t) { +#ifdef CONFIG_NEEDS_MANUAL_RELOC + if (t->sname && strcasecmp(t->sname + gd->reloc_off, name) == 0) +#else + if (t->sname && strcasecmp(t->sname, name) == 0) +#endif + return (t->id); + } + debug("Invalid %s Type: %s\n", table_name, name); + + return -1; +} + +int genimg_get_os_id(const char *name) +{ + return (get_table_entry_id(uimage_os, "OS", name)); +} + +int genimg_get_arch_id(const char *name) +{ + return (get_table_entry_id(uimage_arch, "CPU", name)); +} + +int genimg_get_type_id(const char *name) +{ + return (get_table_entry_id(uimage_type, "Image", name)); +} + +int genimg_get_comp_id(const char *name) +{ + return (get_table_entry_id(uimage_comp, "Compression", name)); +} + +#ifndef USE_HOSTCC +/** + * genimg_get_kernel_addr_fit - get the real kernel address and return 2 + * FIT strings + * @img_addr: a string might contain real image address + * @fit_uname_config: double pointer to a char, will hold pointer to a + * configuration unit name + * @fit_uname_kernel: double pointer to a char, will hold pointer to a subimage + * name + * + * genimg_get_kernel_addr_fit get the real kernel start address from a string + * which is normally the first argv of bootm/bootz + * + * returns: + * kernel start address + */ +ulong genimg_get_kernel_addr_fit(char * const img_addr, + const char **fit_uname_config, + const char **fit_uname_kernel) +{ + ulong kernel_addr; + + /* find out kernel image address */ + if (!img_addr) { + kernel_addr = image_load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + image_load_addr); +#if CONFIG_IS_ENABLED(FIT) + } else if (fit_parse_conf(img_addr, image_load_addr, &kernel_addr, + fit_uname_config)) { + debug("* kernel: config '%s' from image at 0x%08lx\n", + *fit_uname_config, kernel_addr); + } else if (fit_parse_subimage(img_addr, image_load_addr, &kernel_addr, + fit_uname_kernel)) { + debug("* kernel: subimage '%s' from image at 0x%08lx\n", + *fit_uname_kernel, kernel_addr); +#endif + } else { + kernel_addr = simple_strtoul(img_addr, NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", + kernel_addr); + } + + return kernel_addr; +} + +/** + * genimg_get_kernel_addr() is the simple version of + * genimg_get_kernel_addr_fit(). It ignores those return FIT strings + */ +ulong genimg_get_kernel_addr(char * const img_addr) +{ + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; + + return genimg_get_kernel_addr_fit(img_addr, &fit_uname_config, + &fit_uname_kernel); +} + +/** + * genimg_get_format - get image format type + * @img_addr: image start address + * + * genimg_get_format() checks whether provided address points to a valid + * legacy or FIT image. + * + * New uImage format and FDT blob are based on a libfdt. FDT blob + * may be passed directly or embedded in a FIT image. In both situations + * genimg_get_format() must be able to dectect libfdt header. + * + * returns: + * image format type or IMAGE_FORMAT_INVALID if no image is present + */ +int genimg_get_format(const void *img_addr) +{ +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + const image_header_t *hdr; + + hdr = (const image_header_t *)img_addr; + if (image_check_magic(hdr)) + return IMAGE_FORMAT_LEGACY; +#endif +#if IMAGE_ENABLE_FIT || IMAGE_ENABLE_OF_LIBFDT + if (fdt_check_header(img_addr) == 0) + return IMAGE_FORMAT_FIT; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + if (android_image_check_header(img_addr) == 0) + return IMAGE_FORMAT_ANDROID; +#endif + + return IMAGE_FORMAT_INVALID; +} + +/** + * fit_has_config - check if there is a valid FIT configuration + * @images: pointer to the bootm command headers structure + * + * fit_has_config() checks if there is a FIT configuration in use + * (if FTI support is present). + * + * returns: + * 0, no FIT support or no configuration found + * 1, configuration found + */ +int genimg_has_config(bootm_headers_t *images) +{ +#if IMAGE_ENABLE_FIT + if (images->fit_uname_cfg) + return 1; +#endif + return 0; +} + +/** + * boot_get_ramdisk - main ramdisk handling routine + * @argc: command argument count + * @argv: command argument list + * @images: pointer to the bootm images structure + * @arch: expected ramdisk architecture + * @rd_start: pointer to a ulong variable, will hold ramdisk start address + * @rd_end: pointer to a ulong variable, will hold ramdisk end + * + * boot_get_ramdisk() is responsible for finding a valid ramdisk image. + * Curently supported are the following ramdisk sources: + * - multicomponent kernel/ramdisk image, + * - commandline provided address of decicated ramdisk image. + * + * returns: + * 0, if ramdisk image was found and valid, or skiped + * rd_start and rd_end are set to ramdisk start/end addresses if + * ramdisk image is found and valid + * + * 1, if ramdisk image is found but corrupted, or invalid + * rd_start and rd_end are set to 0 if no ramdisk exists + */ +int boot_get_ramdisk(int argc, char *const argv[], bootm_headers_t *images, + uint8_t arch, ulong *rd_start, ulong *rd_end) +{ + ulong rd_addr, rd_load; + ulong rd_data, rd_len; +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + const image_header_t *rd_hdr; +#endif + void *buf; +#ifdef CONFIG_SUPPORT_RAW_INITRD + char *end; +#endif +#if IMAGE_ENABLE_FIT + const char *fit_uname_config = images->fit_uname_cfg; + const char *fit_uname_ramdisk = NULL; + ulong default_addr; + int rd_noffset; +#endif + const char *select = NULL; + + *rd_start = 0; + *rd_end = 0; + +#ifdef CONFIG_ANDROID_BOOT_IMAGE + /* + * Look for an Android boot image. + */ + buf = map_sysmem(images->os.start, 0); + if (buf && genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) + select = (argc == 0) ? env_get("loadaddr") : argv[0]; +#endif + + if (argc >= 2) + select = argv[1]; + + /* + * Look for a '-' which indicates to ignore the + * ramdisk argument + */ + if (select && strcmp(select, "-") == 0) { + debug("## Skipping init Ramdisk\n"); + rd_len = rd_data = 0; + } else if (select || genimg_has_config(images)) { +#if IMAGE_ENABLE_FIT + if (select) { + /* + * If the init ramdisk comes from the FIT image and + * the FIT image address is omitted in the command + * line argument, try to use os FIT image address or + * default load address. + */ + if (images->fit_uname_os) + default_addr = (ulong)images->fit_hdr_os; + else + default_addr = image_load_addr; + + if (fit_parse_conf(select, default_addr, + &rd_addr, &fit_uname_config)) { + debug("* ramdisk: config '%s' from image at " + "0x%08lx\n", + fit_uname_config, rd_addr); + } else if (fit_parse_subimage(select, default_addr, + &rd_addr, &fit_uname_ramdisk)) { + debug("* ramdisk: subimage '%s' from image at " + "0x%08lx\n", + fit_uname_ramdisk, rd_addr); + } else +#endif + { + rd_addr = simple_strtoul(select, NULL, 16); + debug("* ramdisk: cmdline image address = " + "0x%08lx\n", + rd_addr); + } +#if IMAGE_ENABLE_FIT + } else { + /* use FIT configuration provided in first bootm + * command argument. If the property is not defined, + * quit silently. + */ + rd_addr = map_to_sysmem(images->fit_hdr_os); + rd_noffset = fit_get_node_from_config(images, + FIT_RAMDISK_PROP, rd_addr); + if (rd_noffset == -ENOENT) + return 0; + else if (rd_noffset < 0) + return 1; + } +#endif + + /* + * Check if there is an initrd image at the + * address provided in the second bootm argument + * check image type, for FIT images get FIT node. + */ + buf = map_sysmem(rd_addr, 0); + switch (genimg_get_format(buf)) { +#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + printf("## Loading init Ramdisk from Legacy " + "Image at %08lx ...\n", rd_addr); + + bootstage_mark(BOOTSTAGE_ID_CHECK_RAMDISK); + rd_hdr = image_get_ramdisk(rd_addr, arch, + images->verify); + + if (rd_hdr == NULL) + return 1; + + rd_data = image_get_data(rd_hdr); + rd_len = image_get_data_size(rd_hdr); + rd_load = image_get_load(rd_hdr); + break; +#endif +#if IMAGE_ENABLE_FIT + case IMAGE_FORMAT_FIT: + rd_noffset = fit_image_load(images, + rd_addr, &fit_uname_ramdisk, + &fit_uname_config, arch, + IH_TYPE_RAMDISK, + BOOTSTAGE_ID_FIT_RD_START, + FIT_LOAD_OPTIONAL_NON_ZERO, + &rd_data, &rd_len); + if (rd_noffset < 0) + return 1; + + images->fit_hdr_rd = map_sysmem(rd_addr, 0); + images->fit_uname_rd = fit_uname_ramdisk; + images->fit_noffset_rd = rd_noffset; + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + android_image_get_ramdisk((void *)images->os.start, + &rd_data, &rd_len); + break; +#endif + default: +#ifdef CONFIG_SUPPORT_RAW_INITRD + end = NULL; + if (select) + end = strchr(select, ':'); + if (end) { + rd_len = simple_strtoul(++end, NULL, 16); + rd_data = rd_addr; + } else +#endif + { + puts("Wrong Ramdisk Image Format\n"); + rd_data = rd_len = rd_load = 0; + return 1; + } + } + } else if (images->legacy_hdr_valid && + image_check_type(&images->legacy_hdr_os_copy, + IH_TYPE_MULTI)) { + + /* + * Now check if we have a legacy mult-component image, + * get second entry data start address and len. + */ + bootstage_mark(BOOTSTAGE_ID_RAMDISK); + printf("## Loading init Ramdisk from multi component " + "Legacy Image at %08lx ...\n", + (ulong)images->legacy_hdr_os); + + image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); + } else { + /* + * no initrd image + */ + bootstage_mark(BOOTSTAGE_ID_NO_RAMDISK); + rd_len = rd_data = 0; + } + + if (!rd_data) { + debug("## No init Ramdisk\n"); + } else { + *rd_start = rd_data; + *rd_end = rd_data + rd_len; + } + debug(" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n", + *rd_start, *rd_end); + + return 0; +} + +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH +/** + * boot_ramdisk_high - relocate init ramdisk + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @rd_data: ramdisk data start address + * @rd_len: ramdisk data length + * @initrd_start: pointer to a ulong variable, will hold final init ramdisk + * start address (after possible relocation) + * @initrd_end: pointer to a ulong variable, will hold final init ramdisk + * end address (after possible relocation) + * + * boot_ramdisk_high() takes a relocation hint from "initrd_high" environment + * variable and if requested ramdisk data is moved to a specified location. + * + * Initrd_start and initrd_end are set to final (after relocation) ramdisk + * start/end addresses if ramdisk image start and len were provided, + * otherwise set initrd_start and initrd_end set to zeros. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len, + ulong *initrd_start, ulong *initrd_end) +{ + char *s; + ulong initrd_high; + int initrd_copy_to_ram = 1; + + s = env_get("initrd_high"); + if (s) { + /* a value of "no" or a similar string will act like 0, + * turning the "load high" feature off. This is intentional. + */ + initrd_high = simple_strtoul(s, NULL, 16); + if (initrd_high == ~0) + initrd_copy_to_ram = 0; + } else { + initrd_high = env_get_bootm_mapsize() + env_get_bootm_low(); + } + + + debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n", + initrd_high, initrd_copy_to_ram); + + if (rd_data) { + if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */ + debug(" in-place initrd\n"); + *initrd_start = rd_data; + *initrd_end = rd_data + rd_len; + lmb_reserve(lmb, rd_data, rd_len); + } else { + if (initrd_high) + *initrd_start = (ulong)lmb_alloc_base(lmb, + rd_len, 0x1000, initrd_high); + else + *initrd_start = (ulong)lmb_alloc(lmb, rd_len, + 0x1000); + + if (*initrd_start == 0) { + puts("ramdisk - allocation error\n"); + goto error; + } + bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK); + + *initrd_end = *initrd_start + rd_len; + printf(" Loading Ramdisk to %08lx, end %08lx ... ", + *initrd_start, *initrd_end); + + memmove_wd((void *)*initrd_start, + (void *)rd_data, rd_len, CHUNKSZ); + +#ifdef CONFIG_MP + /* + * Ensure the image is flushed to memory to handle + * AMP boot scenarios in which we might not be + * HW cache coherent + */ + flush_cache((unsigned long)*initrd_start, + ALIGN(rd_len, ARCH_DMA_MINALIGN)); +#endif + puts("OK\n"); + } + } else { + *initrd_start = 0; + *initrd_end = 0; + } + debug(" ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n", + *initrd_start, *initrd_end); + + return 0; + +error: + return -1; +} +#endif /* CONFIG_SYS_BOOT_RAMDISK_HIGH */ + +int boot_get_setup(bootm_headers_t *images, uint8_t arch, + ulong *setup_start, ulong *setup_len) +{ +#if IMAGE_ENABLE_FIT + return boot_get_setup_fit(images, arch, setup_start, setup_len); +#else + return -ENOENT; +#endif +} + +#if IMAGE_ENABLE_FIT +#if defined(CONFIG_FPGA) +int boot_get_fpga(int argc, char *const argv[], bootm_headers_t *images, + uint8_t arch, const ulong *ld_start, ulong * const ld_len) +{ + ulong tmp_img_addr, img_data, img_len; + void *buf; + int conf_noffset; + int fit_img_result; + const char *uname, *name; + int err; + int devnum = 0; /* TODO support multi fpga platforms */ + + /* Check to see if the images struct has a FIT configuration */ + if (!genimg_has_config(images)) { + debug("## FIT configuration was not specified\n"); + return 0; + } + + /* + * Obtain the os FIT header from the images struct + */ + tmp_img_addr = map_to_sysmem(images->fit_hdr_os); + buf = map_sysmem(tmp_img_addr, 0); + /* + * Check image type. For FIT images get FIT node + * and attempt to locate a generic binary. + */ + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_FIT: + conf_noffset = fit_conf_get_node(buf, images->fit_uname_cfg); + + uname = fdt_stringlist_get(buf, conf_noffset, FIT_FPGA_PROP, 0, + NULL); + if (!uname) { + debug("## FPGA image is not specified\n"); + return 0; + } + fit_img_result = fit_image_load(images, + tmp_img_addr, + (const char **)&uname, + &(images->fit_uname_cfg), + arch, + IH_TYPE_FPGA, + BOOTSTAGE_ID_FPGA_INIT, + FIT_LOAD_OPTIONAL_NON_ZERO, + &img_data, &img_len); + + debug("FPGA image (%s) loaded to 0x%lx/size 0x%lx\n", + uname, img_data, img_len); + + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + if (!fpga_is_partial_data(devnum, img_len)) { + name = "full"; + err = fpga_loadbitstream(devnum, (char *)img_data, + img_len, BIT_FULL); + if (err) + err = fpga_load(devnum, (const void *)img_data, + img_len, BIT_FULL); + } else { + name = "partial"; + err = fpga_loadbitstream(devnum, (char *)img_data, + img_len, BIT_PARTIAL); + if (err) + err = fpga_load(devnum, (const void *)img_data, + img_len, BIT_PARTIAL); + } + + if (err) + return err; + + printf(" Programming %s bitstream... OK\n", name); + break; + default: + printf("The given image format is not supported (corrupt?)\n"); + return 1; + } + + return 0; +} +#endif + +static void fit_loadable_process(uint8_t img_type, + ulong img_data, + ulong img_len) +{ + int i; + const unsigned int count = + ll_entry_count(struct fit_loadable_tbl, fit_loadable); + struct fit_loadable_tbl *fit_loadable_handler = + ll_entry_start(struct fit_loadable_tbl, fit_loadable); + /* For each loadable handler */ + for (i = 0; i < count; i++, fit_loadable_handler++) + /* matching this type */ + if (fit_loadable_handler->type == img_type) + /* call that handler with this image data */ + fit_loadable_handler->handler(img_data, img_len); +} + +int boot_get_loadable(int argc, char *const argv[], bootm_headers_t *images, + uint8_t arch, const ulong *ld_start, ulong * const ld_len) +{ + /* + * These variables are used to hold the current image location + * in system memory. + */ + ulong tmp_img_addr; + /* + * These two variables are requirements for fit_image_load, but + * their values are not used + */ + ulong img_data, img_len; + void *buf; + int loadables_index; + int conf_noffset; + int fit_img_result; + const char *uname; + uint8_t img_type; + + /* Check to see if the images struct has a FIT configuration */ + if (!genimg_has_config(images)) { + debug("## FIT configuration was not specified\n"); + return 0; + } + + /* + * Obtain the os FIT header from the images struct + */ + tmp_img_addr = map_to_sysmem(images->fit_hdr_os); + buf = map_sysmem(tmp_img_addr, 0); + /* + * Check image type. For FIT images get FIT node + * and attempt to locate a generic binary. + */ + switch (genimg_get_format(buf)) { + case IMAGE_FORMAT_FIT: + conf_noffset = fit_conf_get_node(buf, images->fit_uname_cfg); + + for (loadables_index = 0; + uname = fdt_stringlist_get(buf, conf_noffset, + FIT_LOADABLE_PROP, loadables_index, + NULL), uname; + loadables_index++) + { + fit_img_result = fit_image_load(images, + tmp_img_addr, + &uname, + &(images->fit_uname_cfg), arch, + IH_TYPE_LOADABLE, + BOOTSTAGE_ID_FIT_LOADABLE_START, + FIT_LOAD_OPTIONAL_NON_ZERO, + &img_data, &img_len); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + fit_img_result = fit_image_get_node(buf, uname); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + fit_img_result = fit_image_get_type(buf, + fit_img_result, + &img_type); + if (fit_img_result < 0) { + /* Something went wrong! */ + return fit_img_result; + } + + fit_loadable_process(img_type, img_data, img_len); + } + break; + default: + printf("The given image format is not supported (corrupt?)\n"); + return 1; + } + + return 0; +} +#endif + +#ifdef CONFIG_SYS_BOOT_GET_CMDLINE +/** + * boot_get_cmdline - allocate and initialize kernel cmdline + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @cmd_start: pointer to a ulong variable, will hold cmdline start + * @cmd_end: pointer to a ulong variable, will hold cmdline end + * + * boot_get_cmdline() allocates space for kernel command line below + * BOOTMAPSZ + env_get_bootm_low() address. If "bootargs" U-Boot environment + * variable is present its contents is copied to allocated kernel + * command line. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_cmdline(struct lmb *lmb, ulong *cmd_start, ulong *cmd_end) +{ + char *cmdline; + char *s; + + cmdline = (char *)(ulong)lmb_alloc_base(lmb, CONFIG_SYS_BARGSIZE, 0xf, + env_get_bootm_mapsize() + env_get_bootm_low()); + + if (cmdline == NULL) + return -1; + + s = env_get("bootargs"); + if (!s) + s = ""; + + strcpy(cmdline, s); + + *cmd_start = (ulong) & cmdline[0]; + *cmd_end = *cmd_start + strlen(cmdline); + + debug("## cmdline at 0x%08lx ... 0x%08lx\n", *cmd_start, *cmd_end); + + return 0; +} +#endif /* CONFIG_SYS_BOOT_GET_CMDLINE */ + +#ifdef CONFIG_SYS_BOOT_GET_KBD +/** + * boot_get_kbd - allocate and initialize kernel copy of board info + * @lmb: pointer to lmb handle, will be used for memory mgmt + * @kbd: double pointer to board info data + * + * boot_get_kbd() allocates space for kernel copy of board info data below + * BOOTMAPSZ + env_get_bootm_low() address and kernel board info is initialized + * with the current u-boot board info data. + * + * returns: + * 0 - success + * -1 - failure + */ +int boot_get_kbd(struct lmb *lmb, struct bd_info **kbd) +{ + *kbd = (struct bd_info *)(ulong)lmb_alloc_base(lmb, + sizeof(struct bd_info), + 0xf, + env_get_bootm_mapsize() + env_get_bootm_low()); + if (*kbd == NULL) + return -1; + + **kbd = *(gd->bd); + + debug("## kernel board info at 0x%08lx\n", (ulong)*kbd); + +#if defined(DEBUG) && defined(CONFIG_CMD_BDI) + do_bdinfo(NULL, 0, 0, NULL); +#endif + + return 0; +} +#endif /* CONFIG_SYS_BOOT_GET_KBD */ + +#ifdef CONFIG_LMB +int image_setup_linux(bootm_headers_t *images) +{ + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + int ret; + + if (IMAGE_ENABLE_OF_LIBFDT) + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + if (IMAGE_BOOT_GET_CMDLINE) { + ret = boot_get_cmdline(lmb, &images->cmdline_start, + &images->cmdline_end); + if (ret) { + puts("ERROR with allocation of cmdline\n"); + return ret; + } + } + + if (IMAGE_ENABLE_OF_LIBFDT) { + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return ret; + } + + if (IMAGE_ENABLE_OF_LIBFDT && of_size) { + ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb); + if (ret) + return ret; + } + + return 0; +} +#endif /* CONFIG_LMB */ +#endif /* !USE_HOSTCC */ diff --git a/roms/u-boot/common/init/Makefile b/roms/u-boot/common/init/Makefile new file mode 100644 index 000000000..853b56d1e --- /dev/null +++ b/roms/u-boot/common/init/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += board_init.o +obj-$(CONFIG_$(SPL_TPL_)HANDOFF) += handoff.o diff --git a/roms/u-boot/common/init/board_init.c b/roms/u-boot/common/init/board_init.c new file mode 100644 index 000000000..3f183ee11 --- /dev/null +++ b/roms/u-boot/common/init/board_init.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Code shared between SPL and U-Boot proper + * + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <bootstage.h> +#include <init.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Unfortunately x86 or ARM can't compile this code as gd cannot be assigned */ +#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) +__weak void arch_setup_gd(struct global_data *gd_ptr) +{ + gd = gd_ptr; +} +#endif /* !CONFIG_X86 && !CONFIG_ARM */ + +/** + * This function is called from board_init_f_init_reserve to set up + * gd->start_addr_sp for stack protection if not already set otherwise + */ +__weak void board_init_f_init_stack_protection_addr(ulong base) +{ +#if CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE) + /* set up stack pointer for stack usage if not set yet */ + if (!gd->start_addr_sp) + gd->start_addr_sp = base; +#endif +} + +/** + * This function is called after the position of the initial stack is + * determined in gd->start_addr_sp. Boards can override it to set up + * stack-checking markers. + */ +__weak void board_init_f_init_stack_protection(void) +{ +#if CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE) + ulong stack_bottom = gd->start_addr_sp - + CONFIG_VAL(SIZE_LIMIT_PROVIDE_STACK); + + /* substact some safety margin (0x20) since stack is in use here */ + memset((void *)stack_bottom, CONFIG_VAL(SYS_STACK_F_CHECK_BYTE), + CONFIG_VAL(SIZE_LIMIT_PROVIDE_STACK) - 0x20); +#endif +} + +/* + * Allocate reserved space for use as 'globals' from 'top' address and + * return 'bottom' address of allocated space + * + * Notes: + * + * Actual reservation cannot be done from within this function as + * it requires altering the C stack pointer, so this will be done by + * the caller upon return from this function. + * + * IMPORTANT: + * + * Alignment constraints may differ for each 'chunk' allocated. For now: + * + * - GD is aligned down on a 16-byte boundary + * + * - the early malloc arena is not aligned, therefore it follows the stack + * alignment constraint of the architecture for which we are bulding. + * + * - GD is allocated last, so that the return value of this functions is + * both the bottom of the reserved area and the address of GD, should + * the calling context need it. + */ + +ulong board_init_f_alloc_reserve(ulong top) +{ + /* Reserve early malloc arena */ +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + top -= CONFIG_VAL(SYS_MALLOC_F_LEN); +#endif + /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ + top = rounddown(top-sizeof(struct global_data), 16); + + return top; +} + +/* + * Initialize reserved space (which has been safely allocated on the C + * stack from the C runtime environment handling code). + * + * Notes: + * + * Actual reservation was done by the caller; the locations from base + * to base+size-1 (where 'size' is the value returned by the allocation + * function above) can be accessed freely without risk of corrupting the + * C runtime environment. + * + * IMPORTANT: + * + * Upon return from the allocation function above, on some architectures + * the caller will set gd to the lowest reserved location. Therefore, in + * this initialization function, the global data MUST be placed at base. + * + * ALSO IMPORTANT: + * + * On some architectures, gd will already be good when entering this + * function. On others, it will only be good once arch_setup_gd() returns. + * Therefore, global data accesses must be done: + * + * - through gd_ptr if before the call to arch_setup_gd(); + * + * - through gd once arch_setup_gd() has been called. + * + * Do not use 'gd->' until arch_setup_gd() has been called! + * + * IMPORTANT TOO: + * + * Initialization for each "chunk" (GD, early malloc arena...) ends with + * an incrementation line of the form 'base += <some size>'. The last of + * these incrementations seems useless, as base will not be used any + * more after this incrementation; but if/when a new "chunk" is appended, + * this increment will be essential as it will give base right value for + * this new chunk (which will have to end with its own incrementation + * statement). Besides, the compiler's optimizer will silently detect + * and remove the last base incrementation, therefore leaving that last + * (seemingly useless) incrementation causes no code increase. + */ + +void board_init_f_init_reserve(ulong base) +{ + struct global_data *gd_ptr; + + /* + * clear GD entirely and set it up. + * Use gd_ptr, as gd may not be properly set yet. + */ + + gd_ptr = (struct global_data *)base; + /* zero the area */ + memset(gd_ptr, '\0', sizeof(*gd)); + /* set GD unless architecture did it already */ +#if !defined(CONFIG_ARM) + arch_setup_gd(gd_ptr); +#endif + + if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE)) + board_init_f_init_stack_protection_addr(base); + + /* next alloc will be higher by one GD plus 16-byte alignment */ + base += roundup(sizeof(struct global_data), 16); + + /* + * record early malloc arena start. + * Use gd as it is now properly set for all architectures. + */ + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + /* go down one 'early malloc arena' */ + gd->malloc_base = base; +#endif + + if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE)) + board_init_f_init_stack_protection(); +} + +/* + * Board-specific Platform code can reimplement show_boot_progress () if needed + */ +__weak void show_boot_progress(int val) {} diff --git a/roms/u-boot/common/init/handoff.c b/roms/u-boot/common/init/handoff.c new file mode 100644 index 000000000..d0be1bb17 --- /dev/null +++ b/roms/u-boot/common/init/handoff.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Passing basic information from SPL to U-Boot proper + * + * Copyright 2018 Google, Inc + */ + +#include <common.h> +#include <handoff.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +void handoff_save_dram(struct spl_handoff *ho) +{ + struct bd_info *bd = gd->bd; + int i; + + ho->ram_size = gd->ram_size; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + ho->ram_bank[i].start = bd->bi_dram[i].start; + ho->ram_bank[i].size = bd->bi_dram[i].size; + } +} + +void handoff_load_dram_size(struct spl_handoff *ho) +{ + gd->ram_size = ho->ram_size; +} + +void handoff_load_dram_banks(struct spl_handoff *ho) +{ + struct bd_info *bd = gd->bd; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + bd->bi_dram[i].start = ho->ram_bank[i].start; + bd->bi_dram[i].size = ho->ram_bank[i].size; + } +} diff --git a/roms/u-boot/common/iomux.c b/roms/u-boot/common/iomux.c new file mode 100644 index 000000000..c428f7110 --- /dev/null +++ b/roms/u-boot/common/iomux.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 + * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. + */ + +#include <common.h> +#include <console.h> +#include <serial.h> +#include <malloc.h> + +#if CONFIG_IS_ENABLED(CONSOLE_MUX) +void iomux_printdevs(const int console) +{ + int i; + struct stdio_dev *dev; + + for_each_console_dev(i, console, dev) + printf("%s ", dev->name); + printf("\n"); +} + +int iomux_match_device(struct stdio_dev **set, const int n, struct stdio_dev *sdev) +{ + int i; + + for (i = 0; i < n; i++) + if (sdev == set[i]) + return i; + return -ENOENT; +} + +/* This tries to preserve the old list if an error occurs. */ +int iomux_doenv(const int console, const char *arg) +{ + char *console_args, *temp, **start; + int i, j, io_flag, cs_idx, repeat; + struct stdio_dev **cons_set, **old_set; + struct stdio_dev *dev; + + console_args = strdup(arg); + if (console_args == NULL) + return 1; + /* + * Check whether a comma separated list of devices was + * entered and count how many devices were entered. + * The array start[] has pointers to the beginning of + * each device name (up to MAX_CONSARGS devices). + * + * Have to do this twice - once to count the number of + * commas and then again to populate start. + */ + i = 0; + temp = console_args; + for (;;) { + /* There's always one entry more than the number of commas. */ + i++; + + temp = strchr(temp, ','); + if (temp == NULL) + break; + + temp++; + } + start = (char **)malloc(i * sizeof(char *)); + if (start == NULL) { + free(console_args); + return 1; + } + i = 0; + start[0] = console_args; + for (;;) { + temp = strchr(start[i++], ','); + if (temp == NULL) + break; + *temp = '\0'; + start[i] = temp + 1; + } + cons_set = (struct stdio_dev **)calloc(i, sizeof(struct stdio_dev *)); + if (cons_set == NULL) { + free(start); + free(console_args); + return 1; + } + + io_flag = stdio_file_to_flags(console); + if (io_flag < 0) { + free(start); + free(console_args); + free(cons_set); + return 1; + } + + cs_idx = 0; + for (j = 0; j < i; j++) { + /* + * Check whether the device exists and is valid. + * console_assign() also calls console_search_dev(), + * but I need the pointer to the device. + */ + dev = console_search_dev(io_flag, start[j]); + if (dev == NULL) + continue; + /* + * Prevent multiple entries for a device. + */ + repeat = iomux_match_device(cons_set, cs_idx, dev); + if (repeat >= 0) + continue; + /* + * Try assigning the specified device. + * This could screw up the console settings for apps. + */ + if (console_assign(console, start[j]) < 0) + continue; + cons_set[cs_idx++] = dev; + } + free(console_args); + free(start); + /* failed to set any console */ + if (cs_idx == 0) { + free(cons_set); + return 1; + } + + old_set = console_devices[console]; + repeat = cd_count[console]; + + console_devices[console] = cons_set; + cd_count[console] = cs_idx; + + /* Stop dropped consoles */ + for (i = 0; i < repeat; i++) { + j = iomux_match_device(cons_set, cs_idx, old_set[i]); + if (j == cs_idx) + console_stop(console, old_set[i]); + } + + free(old_set); + return 0; +} + +int iomux_replace_device(const int console, const char *old, const char *new) +{ + struct stdio_dev *dev; + char *arg = NULL; /* Initial empty list */ + int size = 1; /* For NUL terminator */ + int i, ret; + + for_each_console_dev(i, console, dev) { + const char *name = strcmp(dev->name, old) ? dev->name : new; + char *tmp; + + /* Append name with a ',' (comma) separator */ + tmp = realloc(arg, size + strlen(name) + 1); + if (!tmp) { + free(arg); + return -ENOMEM; + } + + if (arg) { + strcat(tmp, ","); + strcat(tmp, name); + } + else + strcpy(tmp, name); + + arg = tmp; + size = strlen(tmp) + 1; + } + + ret = iomux_doenv(console, arg); + if (ret) + ret = -EINVAL; + + free(arg); + return ret; +} +#endif /* CONSOLE_MUX */ diff --git a/roms/u-boot/common/iotrace.c b/roms/u-boot/common/iotrace.c new file mode 100644 index 000000000..63d0cca3a --- /dev/null +++ b/roms/u-boot/common/iotrace.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc. + */ + +#define IOTRACE_IMPL + +#include <common.h> +#include <mapmem.h> +#include <time.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/bug.h> +#include <u-boot/crc.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct iotrace - current trace status and checksum + * + * @start: Start address of iotrace buffer + * @size: Actual size of iotrace buffer in bytes + * @needed_size: Needed of iotrace buffer in bytes + * @offset: Current write offset into iotrace buffer + * @region_start: Address of IO region to trace + * @region_size: Size of region to trace. if 0 will trace all address space + * @crc32: Current value of CRC chceksum of trace records + * @enabled: true if enabled, false if disabled + */ +static struct iotrace { + ulong start; + ulong size; + ulong needed_size; + ulong offset; + ulong region_start; + ulong region_size; + u32 crc32; + bool enabled; +} iotrace; + +static void add_record(int flags, const void *ptr, ulong value) +{ + struct iotrace_record srec, *rec = &srec; + + /* + * We don't support iotrace before relocation. Since the trace buffer + * is set up by a command, it can't be enabled at present. To change + * this we would need to set the iotrace buffer at build-time. See + * lib/trace.c for how this might be done if you are interested. + */ + if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled) + return; + + if (iotrace.region_size) + if ((ulong)ptr < iotrace.region_start || + (ulong)ptr > iotrace.region_start + iotrace.region_size) + return; + + /* Store it if there is room */ + if (iotrace.offset + sizeof(*rec) < iotrace.size) { + rec = (struct iotrace_record *)map_sysmem( + iotrace.start + iotrace.offset, + sizeof(value)); + } else { + WARN_ONCE(1, "WARNING: iotrace buffer exhausted, please check needed length using \"iotrace stats\"\n"); + iotrace.needed_size += sizeof(struct iotrace_record); + return; + } + + rec->timestamp = timer_get_us(); + rec->flags = flags; + rec->addr = map_to_sysmem(ptr); + rec->value = value; + + /* Update our checksum */ + iotrace.crc32 = crc32(iotrace.crc32, (unsigned char *)rec, + sizeof(*rec)); + + iotrace.needed_size += sizeof(struct iotrace_record); + iotrace.offset += sizeof(struct iotrace_record); +} + +u32 iotrace_readl(const void *ptr) +{ + u32 v; + + v = readl(ptr); + add_record(IOT_32 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writel(ulong value, void *ptr) +{ + add_record(IOT_32 | IOT_WRITE, ptr, value); + writel(value, ptr); +} + +u16 iotrace_readw(const void *ptr) +{ + u32 v; + + v = readw(ptr); + add_record(IOT_16 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writew(ulong value, void *ptr) +{ + add_record(IOT_16 | IOT_WRITE, ptr, value); + writew(value, ptr); +} + +u8 iotrace_readb(const void *ptr) +{ + u32 v; + + v = readb(ptr); + add_record(IOT_8 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writeb(ulong value, void *ptr) +{ + add_record(IOT_8 | IOT_WRITE, ptr, value); + writeb(value, ptr); +} + +void iotrace_reset_checksum(void) +{ + iotrace.crc32 = 0; +} + +u32 iotrace_get_checksum(void) +{ + return iotrace.crc32; +} + +void iotrace_set_region(ulong start, ulong size) +{ + iotrace.region_start = start; + iotrace.region_size = size; +} + +void iotrace_reset_region(void) +{ + iotrace.region_start = 0; + iotrace.region_size = 0; +} + +void iotrace_get_region(ulong *start, ulong *size) +{ + *start = iotrace.region_start; + *size = iotrace.region_size; +} + +void iotrace_set_enabled(int enable) +{ + iotrace.enabled = enable; +} + +int iotrace_get_enabled(void) +{ + return iotrace.enabled; +} + +void iotrace_set_buffer(ulong start, ulong size) +{ + iotrace.start = start; + iotrace.size = size; + iotrace.offset = 0; + iotrace.crc32 = 0; +} + +void iotrace_get_buffer(ulong *start, ulong *size, ulong *needed_size, ulong *offset, ulong *count) +{ + *start = iotrace.start; + *size = iotrace.size; + *needed_size = iotrace.needed_size; + *offset = iotrace.offset; + *count = iotrace.offset / sizeof(struct iotrace_record); +} diff --git a/roms/u-boot/common/kallsyms.c b/roms/u-boot/common/kallsyms.c new file mode 100644 index 000000000..ce42a932b --- /dev/null +++ b/roms/u-boot/common/kallsyms.c @@ -0,0 +1,44 @@ +/* + * Helper functions for working with the builtin symbol table + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <common.h> + +/* We need the weak marking as this symbol is provided specially */ +extern const char system_map[] __attribute__((weak)); + +/* Given an address, return a pointer to the symbol name and store + * the base address in caddr. So if the symbol map had an entry: + * 03fb9b7c_spi_cs_deactivate + * Then the following call: + * unsigned long base; + * const char *sym = symbol_lookup(0x03fb9b80, &base); + * Would end up setting the variables like so: + * base = 0x03fb9b7c; + * sym = "_spi_cs_deactivate"; + */ +const char *symbol_lookup(unsigned long addr, unsigned long *caddr) +{ + const char *sym, *csym; + char *esym; + unsigned long sym_addr; + + sym = system_map; + csym = NULL; + *caddr = 0; + + while (*sym) { + sym_addr = simple_strtoul(sym, &esym, 16); + sym = esym; + if (sym_addr > addr) + break; + *caddr = sym_addr; + csym = sym; + sym += strlen(sym) + 1; + } + + return csym; +} diff --git a/roms/u-boot/common/kgdb.c b/roms/u-boot/common/kgdb.c new file mode 100644 index 000000000..4493a1591 --- /dev/null +++ b/roms/u-boot/common/kgdb.c @@ -0,0 +1,599 @@ +/* taken from arch/powerpc/kernel/ppc-stub.c */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <common.h> +#include <asm/ptrace.h> + +#include <kgdb.h> +#include <command.h> + +#undef KGDB_DEBUG + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + */ +#define BUFMAX 1024 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static char remcomRegBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active; +static struct pt_regs entry_regs; +static long error_jmp_buf[BUFMAX/2]; +static int longjmp_on_fault = 0; +#ifdef KGDB_DEBUG +static int kdebug = 1; +#endif + +static const char hexchars[]="0123456789abcdef"; + +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null). + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + char *tmp; + unsigned char ch; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory copy. Hex conversion will work against this one. + */ + tmp = buf + count; + longjmp_on_fault = 1; + + memcpy(tmp, mem, count); + + while (count-- > 0) { + ch = *tmp++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + *buf = 0; + longjmp_on_fault = 0; + return (unsigned char *)buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte fetched from buf. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int hexValue; + char *tmp_raw, *tmp_hex; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory that is converted from hex. + */ + tmp_raw = buf + count * 2; + tmp_hex = tmp_raw - 1; + + longjmp_on_fault = 1; + while (tmp_hex >= buf) { + tmp_raw--; + hexValue = hex(*tmp_hex--); + if (hexValue < 0) + kgdb_error(KGDBERR_NOTHEXDIG); + *tmp_raw = hexValue; + hexValue = hex(*tmp_hex--); + if (hexValue < 0) + kgdb_error(KGDBERR_NOTHEXDIG); + *tmp_raw |= hexValue << 4; + + } + + memcpy(mem, tmp_raw, count); + + kgdb_flush_cache_range((void *)mem, (void *)(mem+count)); + longjmp_on_fault = 0; + + return buf; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + longjmp_on_fault = 1; + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + longjmp_on_fault = 0; + + return (numChars); +} + +/* scan for the sequence $<data>#<checksum> */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') { +#ifdef KGDB_DEBUG + if (kdebug) + putc(ch); +#endif + ; + } + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void +putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $<packet info>#<checksum>. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +/* + * This function does all command processing for interfacing to gdb. + */ +static int +handle_exception (struct pt_regs *regs) +{ + int addr; + int length; + char *ptr; + kgdb_data kd; + int i; + + if (!initialized) { + printf("kgdb: exception before kgdb is initialized! huh?\n"); + return (0); + } + + /* probably should check which exception occurred as well */ + if (longjmp_on_fault) { + longjmp_on_fault = 0; + kgdb_longjmp(error_jmp_buf, KGDBERR_MEMFAULT); + panic("kgdb longjump failed!\n"); + } + + if (kgdb_active) { + printf("kgdb: unexpected exception from within kgdb\n"); + return (0); + } + kgdb_active = 1; + + kgdb_interruptible(0); + + printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); + + if (kgdb_setjmp(error_jmp_buf) != 0) + panic("kgdb: error or fault in entry init!\n"); + + kgdb_enter(regs, &kd); + + entry_regs = *regs; + + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + + *ptr++ = hexchars[kd.sigval >> 4]; + *ptr++ = hexchars[kd.sigval & 0xf]; + + for (i = 0; i < kd.nregs; i++) { + kgdb_reg *rp = &kd.regs[i]; + + *ptr++ = hexchars[rp->num >> 4]; + *ptr++ = hexchars[rp->num & 0xf]; + *ptr++ = ':'; + ptr = (char *)mem2hex((char *)&rp->val, ptr, 4); + *ptr++ = ';'; + } + + *ptr = 0; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + putpacket((unsigned char *)&remcomOutBuffer); + + while (1) { + volatile int errnum; + + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + ptr = &remcomInBuffer[1]; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomInBuffer: %s\n", remcomInBuffer); +#endif + + errnum = kgdb_setjmp(error_jmp_buf); + + if (errnum == 0) switch (remcomInBuffer[0]) { + + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[kd.sigval >> 4]; + remcomOutBuffer[2] = hexchars[kd.sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; + +#ifdef KGDB_DEBUG + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; +#endif + + case 'g': /* return the value of the CPU registers. */ + length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX); + mem2hex(remcomRegBuffer, remcomOutBuffer, length); + break; + + case 'G': /* set the value of the CPU registers */ + length = strlen(ptr); + if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS); + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putregs(regs, remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + mem2hex((char *)addr, remcomOutBuffer, length); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + hex2mem(ptr, (char *)addr, length); + strcpy(remcomOutBuffer, "OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + + case 'k': /* kill the program, actually return to monitor */ + kd.extype = KGDBEXIT_KILL; + *regs = entry_regs; + goto doexit; + + case 'C': /* CSS continue with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + kd.extype = KGDBEXIT_CONTINUE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + goto doexit; + + case 'S': /* SSS single step with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 's': + kd.extype = KGDBEXIT_SINGLE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + doexit: +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + kgdb_exit(regs, &kd); + kgdb_active = 0; + kgdb_interruptible(1); + return (1); + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + + case 'P': /* Pr=v set reg r to value v (r and v are hex) */ + if (hexToInt(&ptr, &addr) + && *ptr++ == '=' + && ((length = strlen(ptr)) & 1) == 0) { + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putreg(regs, addr, + remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + } /* switch */ + + if (errnum != 0) + sprintf(remcomOutBuffer, "E%02d", errnum); + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + /* reply to the request */ + putpacket((unsigned char *)&remcomOutBuffer); + + } /* while(1) */ +} + +/* + * kgdb_init must be called *after* the + * monitor is relocated into ram + */ +void +kgdb_init(void) +{ + kgdb_serial_init(); + debugger_exception_handler = handle_exception; + initialized = 1; + + putDebugStr("kgdb ready\n"); + puts("ready\n"); +} + +void +kgdb_error(int errnum) +{ + longjmp_on_fault = 0; + kgdb_longjmp(error_jmp_buf, errnum); + panic("kgdb_error: longjmp failed!\n"); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex ((char *)s, &buffer[1], count); + putpacket((unsigned char *)&buffer); + + return 1; +} + +void +breakpoint(void) +{ + if (!initialized) { + printf("breakpoint() called b4 kgdb init\n"); + return; + } + + kgdb_breakpoint(0, 0); +} + +int +do_kgdb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + printf("Entering KGDB mode via exception handler...\n\n"); + kgdb_breakpoint(argc - 1, argv + 1); + printf("\nReturned from KGDB mode\n"); + return 0; +} + +U_BOOT_CMD( + kgdb, CONFIG_SYS_MAXARGS, 1, do_kgdb, + "enter gdb remote debug mode", + "[arg0 arg1 .. argN]\n" + " - executes a breakpoint so that kgdb mode is\n" + " entered via the exception handler. To return\n" + " to the monitor, the remote gdb debugger must\n" + " execute a \"continue\" or \"quit\" command.\n" + "\n" + " if a program is loaded by the remote gdb, any args\n" + " passed to the kgdb command are given to the loaded\n" + " program if it is executed (see the \"hello_world\"\n" + " example program in the U-Boot examples directory)." +); diff --git a/roms/u-boot/common/kgdb_stubs.c b/roms/u-boot/common/kgdb_stubs.c new file mode 100644 index 000000000..66aed7cea --- /dev/null +++ b/roms/u-boot/common/kgdb_stubs.c @@ -0,0 +1,67 @@ +/* + * U-Boot - stub functions for common kgdb code, + * can be overridden in board specific files + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <cpu_func.h> +#include <kgdb.h> +#include <serial.h> +#include <asm/ptrace.h> + +int (*debugger_exception_handler)(struct pt_regs *); + +__attribute__((weak)) +void kgdb_serial_init(void) +{ + puts("[on serial] "); +} + +__attribute__((weak)) +void putDebugChar(int c) +{ + serial_putc(c); +} + +__attribute__((weak)) +void putDebugStr(const char *str) +{ +#ifdef DEBUG + serial_puts(str); +#endif +} + +__attribute__((weak)) +int getDebugChar(void) +{ + return serial_getc(); +} + +__attribute__((weak)) +void kgdb_interruptible(int yes) +{ + return; +} + +__attribute__((weak)) +void kgdb_flush_cache_range(void *from, void *to) +{ + flush_cache((unsigned long)from, (unsigned long)(to - from)); +} + +__attribute__((weak)) +void kgdb_flush_cache_all(void) +{ + if (dcache_status()) { + dcache_disable(); + dcache_enable(); + } + if (icache_status()) { + icache_disable(); + icache_enable(); + } +} diff --git a/roms/u-boot/common/lcd.c b/roms/u-boot/common/lcd.c new file mode 100644 index 000000000..ab5614ad0 --- /dev/null +++ b/roms/u-boot/common/lcd.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Common LCD routines + * + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + */ + +/* #define DEBUG */ +#include <config.h> +#include <common.h> +#include <command.h> +#include <cpu_func.h> +#include <env_callback.h> +#include <log.h> +#include <asm/cache.h> +#include <init.h> +#include <asm/global_data.h> +#include <linux/types.h> +#include <stdio_dev.h> +#include <lcd.h> +#include <mapmem.h> +#include <watchdog.h> +#include <asm/unaligned.h> +#include <splash.h> +#include <asm/io.h> +#include <asm/unaligned.h> +#include <video_font.h> + +#ifdef CONFIG_LCD_LOGO +#include <bmp_logo.h> +#include <bmp_logo_data.h> +#if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16) +#error Default Color Map overlaps with Logo Color Map +#endif +#endif + +#ifndef CONFIG_LCD_ALIGNMENT +#define CONFIG_LCD_ALIGNMENT PAGE_SIZE +#endif + +#if (LCD_BPP != LCD_COLOR8) && (LCD_BPP != LCD_COLOR16) && \ + (LCD_BPP != LCD_COLOR32) +#error Unsupported LCD BPP. +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static int lcd_init(void *lcdbase); +static void lcd_logo(void); +static void lcd_setfgcolor(int color); +static void lcd_setbgcolor(int color); + +static int lcd_color_fg; +static int lcd_color_bg; +int lcd_line_length; +char lcd_is_enabled = 0; +static void *lcd_base; /* Start of framebuffer memory */ +static char lcd_flush_dcache; /* 1 to flush dcache after each lcd update */ + +/* Flush LCD activity to the caches */ +void lcd_sync(void) +{ + /* + * flush_dcache_range() is declared in common.h but it seems that some + * architectures do not actually implement it. Is there a way to find + * out whether it exists? For now, ARM is safe. + */ +#if defined(CONFIG_ARM) && !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) + int line_length; + + if (lcd_flush_dcache) + flush_dcache_range((ulong)lcd_base, + (ulong)(lcd_base + lcd_get_size(&line_length))); +#endif +} + +void lcd_set_flush_dcache(int flush) +{ + lcd_flush_dcache = (flush != 0); +} + +static void lcd_stub_putc(struct stdio_dev *dev, const char c) +{ + lcd_putc(c); +} + +static void lcd_stub_puts(struct stdio_dev *dev, const char *s) +{ + lcd_puts(s); +} + +/* Small utility to check that you got the colours right */ +#ifdef LCD_TEST_PATTERN + +#if LCD_BPP == LCD_COLOR8 +#define N_BLK_VERT 2 +#define N_BLK_HOR 3 + +static int test_colors[N_BLK_HOR * N_BLK_VERT] = { + CONSOLE_COLOR_RED, CONSOLE_COLOR_GREEN, CONSOLE_COLOR_YELLOW, + CONSOLE_COLOR_BLUE, CONSOLE_COLOR_MAGENTA, CONSOLE_COLOR_CYAN, +}; /*LCD_BPP == LCD_COLOR8 */ + +#elif LCD_BPP == LCD_COLOR16 +#define N_BLK_VERT 2 +#define N_BLK_HOR 4 + +static int test_colors[N_BLK_HOR * N_BLK_VERT] = { + CONSOLE_COLOR_RED, CONSOLE_COLOR_GREEN, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_BLUE, + CONSOLE_COLOR_MAGENTA, CONSOLE_COLOR_CYAN, CONSOLE_COLOR_GREY, CONSOLE_COLOR_WHITE, +}; +#endif /*LCD_BPP == LCD_COLOR16 */ + +static void test_pattern(void) +{ + ushort v_max = panel_info.vl_row; + ushort h_max = panel_info.vl_col; + ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT; + ushort h_step = (h_max + N_BLK_HOR - 1) / N_BLK_HOR; + ushort v, h; +#if LCD_BPP == LCD_COLOR8 + uchar *pix = (uchar *)lcd_base; +#elif LCD_BPP == LCD_COLOR16 + ushort *pix = (ushort *)lcd_base; +#endif + + printf("[LCD] Test Pattern: %d x %d [%d x %d]\n", + h_max, v_max, h_step, v_step); + + for (v = 0; v < v_max; ++v) { + uchar iy = v / v_step; + for (h = 0; h < h_max; ++h) { + uchar ix = N_BLK_HOR * iy + h / h_step; + *pix++ = test_colors[ix]; + } + } +} +#endif /* LCD_TEST_PATTERN */ + +/* + * With most lcd drivers the line length is set up + * by calculating it from panel_info parameters. Some + * drivers need to calculate the line length differently, + * so make the function weak to allow overriding it. + */ +__weak int lcd_get_size(int *line_length) +{ + *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; + return *line_length * panel_info.vl_row; +} + +int drv_lcd_init(void) +{ + struct stdio_dev lcddev; + int rc; + + lcd_base = map_sysmem(gd->fb_base, 0); + + lcd_init(lcd_base); + + /* Device initialization */ + memset(&lcddev, 0, sizeof(lcddev)); + + strcpy(lcddev.name, "lcd"); + lcddev.ext = 0; /* No extensions */ + lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */ + lcddev.putc = lcd_stub_putc; /* 'putc' function */ + lcddev.puts = lcd_stub_puts; /* 'puts' function */ + + rc = stdio_register(&lcddev); + + return (rc == 0) ? 1 : rc; +} + +void lcd_clear(void) +{ + int bg_color; + __maybe_unused ulong addr; + static int do_splash = 1; +#if LCD_BPP == LCD_COLOR8 + /* Setting the palette */ + lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0); + lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0); + lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0); + lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); + lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); + lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); + lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); +#endif + +#ifndef CONFIG_SYS_WHITE_ON_BLACK + lcd_setfgcolor(CONSOLE_COLOR_BLACK); + lcd_setbgcolor(CONSOLE_COLOR_WHITE); + bg_color = CONSOLE_COLOR_WHITE; +#else + lcd_setfgcolor(CONSOLE_COLOR_WHITE); + lcd_setbgcolor(CONSOLE_COLOR_BLACK); + bg_color = CONSOLE_COLOR_BLACK; +#endif /* CONFIG_SYS_WHITE_ON_BLACK */ + +#ifdef LCD_TEST_PATTERN + test_pattern(); +#else + /* set framebuffer to background color */ +#if (LCD_BPP != LCD_COLOR32) + memset((char *)lcd_base, bg_color, lcd_line_length * panel_info.vl_row); +#else + u32 *ppix = lcd_base; + u32 i; + for (i = 0; + i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix); + i++) { + *ppix++ = bg_color; + } +#endif +#endif + /* setup text-console */ + debug("[LCD] setting up console...\n"); + lcd_init_console(lcd_base, + panel_info.vl_col, + panel_info.vl_row, + panel_info.vl_rot); + /* Paint the logo and retrieve LCD base address */ + debug("[LCD] Drawing the logo...\n"); + if (do_splash) { + if (splash_display() == 0) { + do_splash = 0; + lcd_sync(); + return; + } + } + + lcd_logo(); +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) + addr = (ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length; + lcd_init_console((void *)addr, panel_info.vl_col, + panel_info.vl_row, panel_info.vl_rot); +#endif + lcd_sync(); +} + +static int lcd_init(void *lcdbase) +{ + debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase); + lcd_ctrl_init(lcdbase); + + /* + * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi) ignores + * the 'lcdbase' argument and uses custom lcd base address + * by setting up gd->fb_base. Check for this condition and fixup + * 'lcd_base' address. + */ + if (map_to_sysmem(lcdbase) != gd->fb_base) + lcd_base = map_sysmem(gd->fb_base, 0); + + debug("[LCD] Using LCD frambuffer at %p\n", lcd_base); + + lcd_get_size(&lcd_line_length); + lcd_is_enabled = 1; + lcd_clear(); + lcd_enable(); + + /* Initialize the console */ + lcd_set_col(0); +#ifdef CONFIG_LCD_INFO_BELOW_LOGO + lcd_set_row(7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT); +#else + lcd_set_row(1); /* leave 1 blank line below logo */ +#endif + + return 0; +} + +/* + * This is called early in the system initialization to grab memory + * for the LCD controller. + * Returns new address for monitor, after reserving LCD buffer memory + * + * Note that this is running from ROM, so no write access to global data. + */ +ulong lcd_setmem(ulong addr) +{ + ulong size; + int line_length; + + debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col, + panel_info.vl_row, NBITS(panel_info.vl_bpix)); + + size = lcd_get_size(&line_length); + + /* Round up to nearest full page, or MMU section if defined */ + size = ALIGN(size, CONFIG_LCD_ALIGNMENT); + addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT); + + /* Allocate pages for the frame buffer. */ + addr -= size; + + debug("Reserving %ldk for LCD Framebuffer at: %08lx\n", + size >> 10, addr); + + return addr; +} + +static void lcd_setfgcolor(int color) +{ + lcd_color_fg = color; +} + +int lcd_getfgcolor(void) +{ + return lcd_color_fg; +} + +static void lcd_setbgcolor(int color) +{ + lcd_color_bg = color; +} + +int lcd_getbgcolor(void) +{ + return lcd_color_bg; +} + +#ifdef CONFIG_LCD_LOGO +__weak void lcd_logo_set_cmap(void) +{ + int i; + ushort *cmap = configuration_get_cmap(); + + for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i) + *cmap++ = bmp_logo_palette[i]; +} + +void lcd_logo_plot(int x, int y) +{ + ushort i, j; + uchar *bmap = &bmp_logo_bitmap[0]; + unsigned bpix = NBITS(panel_info.vl_bpix); + uchar *fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8); + ushort *fb16; + + debug("Logo: width %d height %d colors %d\n", + BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS); + + if (bpix < 12) { + WATCHDOG_RESET(); + lcd_logo_set_cmap(); + WATCHDOG_RESET(); + + for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { + memcpy(fb, bmap, BMP_LOGO_WIDTH); + bmap += BMP_LOGO_WIDTH; + fb += panel_info.vl_col; + } + } + else { /* true color mode */ + u16 col16; + fb16 = (ushort *)fb; + for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { + for (j = 0; j < BMP_LOGO_WIDTH; j++) { + col16 = bmp_logo_palette[(bmap[j]-16)]; + fb16[j] = + ((col16 & 0x000F) << 1) | + ((col16 & 0x00F0) << 3) | + ((col16 & 0x0F00) << 4); + } + bmap += BMP_LOGO_WIDTH; + fb16 += panel_info.vl_col; + } + } + + WATCHDOG_RESET(); + lcd_sync(); +} +#else +static inline void lcd_logo_plot(int x, int y) {} +#endif /* CONFIG_LCD_LOGO */ + +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + +static void splash_align_axis(int *axis, unsigned long panel_size, + unsigned long picture_size) +{ + unsigned long panel_picture_delta = panel_size - picture_size; + unsigned long axis_alignment; + + if (*axis == BMP_ALIGN_CENTER) + axis_alignment = panel_picture_delta / 2; + else if (*axis < 0) + axis_alignment = panel_picture_delta + *axis + 1; + else + return; + + *axis = max(0, (int)axis_alignment); +} +#endif + +#ifdef CONFIG_LCD_BMP_RLE8 +#define BMP_RLE8_ESCAPE 0 +#define BMP_RLE8_EOL 0 +#define BMP_RLE8_EOBMP 1 +#define BMP_RLE8_DELTA 2 + +static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, + int cnt) +{ + while (cnt > 0) { + *(*fbp)++ = cmap[*bmap++]; + cnt--; + } +} + +static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt) +{ + ushort *fb = *fbp; + int cnt_8copy = cnt >> 3; + + cnt -= cnt_8copy << 3; + while (cnt_8copy > 0) { + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + *fb++ = c; + cnt_8copy--; + } + while (cnt > 0) { + *fb++ = c; + cnt--; + } + *fbp = fb; +} + +/* + * Do not call this function directly, must be called from lcd_display_bitmap. + */ +static void lcd_display_rle8_bitmap(struct bmp_image *bmp, ushort *cmap, + uchar *fb, int x_off, int y_off) +{ + uchar *bmap; + ulong width, height; + ulong cnt, runlen; + int x, y; + int decode = 1; + + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + + x = 0; + y = height - 1; + + while (decode) { + if (bmap[0] == BMP_RLE8_ESCAPE) { + switch (bmap[1]) { + case BMP_RLE8_EOL: + /* end of line */ + bmap += 2; + x = 0; + y--; + /* 16bpix, 2-byte per pixel, width should *2 */ + fb -= (width * 2 + lcd_line_length); + break; + case BMP_RLE8_EOBMP: + /* end of bitmap */ + decode = 0; + break; + case BMP_RLE8_DELTA: + /* delta run */ + x += bmap[2]; + y -= bmap[3]; + /* 16bpix, 2-byte per pixel, x should *2 */ + fb = (uchar *) (lcd_base + (y + y_off - 1) + * lcd_line_length + (x + x_off) * 2); + bmap += 4; + break; + default: + /* unencoded run */ + runlen = bmap[1]; + bmap += 2; + if (y < height) { + if (x < width) { + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_unencoded_bitmap( + (ushort **)&fb, + bmap, cmap, cnt); + } + x += runlen; + } + bmap += runlen; + if (runlen & 1) + bmap++; + } + } else { + /* encoded run */ + if (y < height) { + runlen = bmap[0]; + if (x < width) { + /* aggregate the same code */ + while (bmap[0] == 0xff && + bmap[2] != BMP_RLE8_ESCAPE && + bmap[1] == bmap[3]) { + runlen += bmap[2]; + bmap += 2; + } + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_encoded_bitmap((ushort **)&fb, + cmap[bmap[1]], cnt); + } + x += runlen; + } + bmap += 2; + } + } +} +#endif + +__weak void fb_put_byte(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; +} + +#if defined(CONFIG_BMP_16BPP) +__weak void fb_put_word(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; + *(*fb)++ = *(*from)++; +} +#endif /* CONFIG_BMP_16BPP */ + +__weak void lcd_set_cmap(struct bmp_image *bmp, unsigned colors) +{ + int i; + struct bmp_color_table_entry cte; + ushort *cmap = configuration_get_cmap(); + + for (i = 0; i < colors; ++i) { + cte = bmp->color_table[i]; + *cmap = (((cte.red) << 8) & 0xf800) | + (((cte.green) << 3) & 0x07e0) | + (((cte.blue) >> 3) & 0x001f); + cmap++; + } +} + +int lcd_display_bitmap(ulong bmp_image, int x, int y) +{ + ushort *cmap_base = NULL; + ushort i, j; + uchar *fb; + struct bmp_image *bmp = (struct bmp_image *)map_sysmem(bmp_image, 0); + uchar *bmap; + ushort padded_width; + unsigned long width, height, byte_width; + unsigned long pwidth = panel_info.vl_col; + unsigned colors, bpix, bmp_bpix; + int hdr_size; + struct bmp_color_table_entry *palette; + + if (!bmp || !(bmp->header.signature[0] == 'B' && + bmp->header.signature[1] == 'M')) { + printf("Error: no valid bmp image at %lx\n", bmp_image); + + return 1; + } + + palette = bmp->color_table; + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); + hdr_size = get_unaligned_le16(&bmp->header.size); + debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix); + + colors = 1 << bmp_bpix; + + bpix = NBITS(panel_info.vl_bpix); + + if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { + printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, bmp_bpix); + + return 1; + } + + /* + * We support displaying 8bpp BMPs on 16bpp LCDs + * and displaying 24bpp BMPs on 32bpp LCDs + * */ + if (bpix != bmp_bpix && + !(bmp_bpix == 8 && bpix == 16) && + !(bmp_bpix == 24 && bpix == 32)) { + printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, get_unaligned_le16(&bmp->header.bit_count)); + return 1; + } + + debug("Display-bmp: %d x %d with %d colors, display %d\n", + (int)width, (int)height, (int)colors, 1 << bpix); + + if (bmp_bpix == 8) + lcd_set_cmap(bmp, colors); + + padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + splash_align_axis(&x, pwidth, width); + splash_align_axis(&y, panel_info.vl_row, height); +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + if ((x + width) > pwidth) + width = pwidth - x; + if ((y + height) > panel_info.vl_row) + height = panel_info.vl_row - y; + + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + fb = (uchar *)(lcd_base + + (y + height - 1) * lcd_line_length + x * bpix / 8); + + switch (bmp_bpix) { + case 1: + case 8: { + cmap_base = configuration_get_cmap(); +#ifdef CONFIG_LCD_BMP_RLE8 + u32 compression = get_unaligned_le32(&bmp->header.compression); + debug("compressed %d %d\n", compression, BMP_BI_RLE8); + if (compression == BMP_BI_RLE8) { + if (bpix != 16) { + /* TODO implement render code for bpix != 16 */ + printf("Error: only support 16 bpix"); + return 1; + } + lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y); + break; + } +#endif + + if (bpix != 16) + byte_width = width; + else + byte_width = width * 2; + + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + if (bpix != 16) { + fb_put_byte(&fb, &bmap); + } else { + struct bmp_color_table_entry *entry; + uint val; + + if (cmap_base) { + val = cmap_base[*bmap]; + } else { + entry = &palette[*bmap]; + val = entry->blue >> 3 | + entry->green >> 2 << 5 | + entry->red >> 3 << 11; + } + *(uint16_t *)fb = val; + bmap++; + fb += sizeof(uint16_t) / sizeof(*fb); + } + } + bmap += (padded_width - width); + fb -= byte_width + lcd_line_length; + } + break; + } +#if defined(CONFIG_BMP_16BPP) + case 16: + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) + fb_put_word(&fb, &bmap); + + bmap += (padded_width - width) * 2; + fb -= width * 2 + lcd_line_length; + } + break; +#endif /* CONFIG_BMP_16BPP */ +#if defined(CONFIG_BMP_24BPP) + case 24: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = 0; + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_24BPP */ +#if defined(CONFIG_BMP_32BPP) + case 32: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_32BPP */ + default: + break; + }; + + lcd_sync(); + return 0; +} +#endif + +static void lcd_logo(void) +{ + lcd_logo_plot(0, 0); + +#ifdef CONFIG_LCD_INFO + lcd_set_col(LCD_INFO_X / VIDEO_FONT_WIDTH); + lcd_set_row(LCD_INFO_Y / VIDEO_FONT_HEIGHT); + lcd_show_board_info(); +#endif /* CONFIG_LCD_INFO */ +} + +#ifdef CONFIG_SPLASHIMAGE_GUARD +static int on_splashimage(const char *name, const char *value, enum env_op op, + int flags) +{ + ulong addr; + int aligned; + + if (op == env_op_delete) + return 0; + + addr = simple_strtoul(value, NULL, 16); + /* See README.displaying-bmps */ + aligned = (addr % 4 == 2); + if (!aligned) { + printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n"); + return -1; + } + + return 0; +} + +U_BOOT_ENV_CALLBACK(splashimage, on_splashimage); +#endif + +int lcd_get_pixel_width(void) +{ + return panel_info.vl_col; +} + +int lcd_get_pixel_height(void) +{ + return panel_info.vl_row; +} diff --git a/roms/u-boot/common/lcd_console.c b/roms/u-boot/common/lcd_console.c new file mode 100644 index 000000000..1a246c492 --- /dev/null +++ b/roms/u-boot/common/lcd_console.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001-2015 + * DENX Software Engineering -- wd@denx.de + * Compulab Ltd - http://compulab.co.il/ + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + */ + +#include <common.h> +#include <command.h> +#include <lcd.h> +#include <log.h> +#include <serial.h> +#include <video_font.h> /* Get font data, width and height */ +#if defined(CONFIG_LCD_LOGO) +#include <bmp_logo.h> +#endif + +static struct console_t cons; + +void lcd_set_col(short col) +{ + cons.curr_col = col; +} + +void lcd_set_row(short row) +{ + cons.curr_row = row; +} + +void lcd_position_cursor(unsigned col, unsigned row) +{ + cons.curr_col = min_t(short, col, cons.cols - 1); + cons.curr_row = min_t(short, row, cons.rows - 1); +} + +int lcd_get_screen_rows(void) +{ + return cons.rows; +} + +int lcd_get_screen_columns(void) +{ + return cons.cols; +} + +static void lcd_putc_xy0(struct console_t *pcons, ushort x, ushort y, char c) +{ + int fg_color = lcd_getfgcolor(); + int bg_color = lcd_getbgcolor(); + int i, row; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + y * pcons->lcdsizex + + x; + + for (row = 0; row < VIDEO_FONT_HEIGHT; row++) { + uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; + for (i = 0; i < VIDEO_FONT_WIDTH; ++i) { + *dst++ = (bits & 0x80) ? fg_color : bg_color; + bits <<= 1; + } + dst += (pcons->lcdsizex - VIDEO_FONT_WIDTH); + } +} + +static inline void console_setrow0(struct console_t *pcons, u32 row, int clr) +{ + int i; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + row * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++) + *dst++ = clr; +} + +static inline void console_moverow0(struct console_t *pcons, + u32 rowdst, u32 rowsrc) +{ + int i; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + rowdst * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + fbptr_t *src = (fbptr_t *)pcons->fbbase + + rowsrc * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++) + *dst++ = *src++; +} + +static inline void console_back(void) +{ + if (--cons.curr_col < 0) { + cons.curr_col = cons.cols - 1; + if (--cons.curr_row < 0) + cons.curr_row = 0; + } + + cons.fp_putc_xy(&cons, + cons.curr_col * VIDEO_FONT_WIDTH, + cons.curr_row * VIDEO_FONT_HEIGHT, ' '); +} + +static inline void console_newline(void) +{ + const int rows = CONFIG_CONSOLE_SCROLL_LINES; + int bg_color = lcd_getbgcolor(); + int i; + + cons.curr_col = 0; + + /* Check if we need to scroll the terminal */ + if (++cons.curr_row >= cons.rows) { + for (i = 0; i < cons.rows-rows; i++) + cons.fp_console_moverow(&cons, i, i+rows); + for (i = 0; i < rows; i++) + cons.fp_console_setrow(&cons, cons.rows-i-1, bg_color); + cons.curr_row -= rows; + } + lcd_sync(); +} + +void console_calc_rowcol(struct console_t *pcons, u32 sizex, u32 sizey) +{ + pcons->cols = sizex / VIDEO_FONT_WIDTH; +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) + pcons->rows = (pcons->lcdsizey - BMP_LOGO_HEIGHT); + pcons->rows /= VIDEO_FONT_HEIGHT; +#else + pcons->rows = sizey / VIDEO_FONT_HEIGHT; +#endif +} + +void __weak lcd_init_console_rot(struct console_t *pcons) +{ + return; +} + +void lcd_init_console(void *address, int vl_cols, int vl_rows, int vl_rot) +{ + memset(&cons, 0, sizeof(cons)); + cons.fbbase = address; + + cons.lcdsizex = vl_cols; + cons.lcdsizey = vl_rows; + cons.lcdrot = vl_rot; + + cons.fp_putc_xy = &lcd_putc_xy0; + cons.fp_console_moverow = &console_moverow0; + cons.fp_console_setrow = &console_setrow0; + console_calc_rowcol(&cons, cons.lcdsizex, cons.lcdsizey); + + lcd_init_console_rot(&cons); + + debug("lcd_console: have %d/%d col/rws on scr %dx%d (%d deg rotated)\n", + cons.cols, cons.rows, cons.lcdsizex, cons.lcdsizey, vl_rot); +} + +void lcd_putc(const char c) +{ + if (!lcd_is_enabled) { + serial_putc(c); + + return; + } + + switch (c) { + case '\r': + cons.curr_col = 0; + return; + case '\n': + console_newline(); + + return; + case '\t': /* Tab (8 chars alignment) */ + cons.curr_col += 8; + cons.curr_col &= ~7; + + if (cons.curr_col >= cons.cols) + console_newline(); + + return; + case '\b': + console_back(); + + return; + default: + cons.fp_putc_xy(&cons, + cons.curr_col * VIDEO_FONT_WIDTH, + cons.curr_row * VIDEO_FONT_HEIGHT, c); + if (++cons.curr_col >= cons.cols) + console_newline(); + } +} + +void lcd_puts(const char *s) +{ + if (!lcd_is_enabled) { + serial_puts(s); + + return; + } + + while (*s) + lcd_putc(*s++); + + lcd_sync(); +} + +void lcd_printf(const char *fmt, ...) +{ + va_list args; + char buf[CONFIG_SYS_PBSIZE]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + + lcd_puts(buf); +} + +static int do_lcd_setcursor(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned int col, row; + + if (argc != 3) + return CMD_RET_USAGE; + + col = simple_strtoul(argv[1], NULL, 10); + row = simple_strtoul(argv[2], NULL, 10); + lcd_position_cursor(col, row); + + return 0; +} + +static int do_lcd_puts(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 2) + return CMD_RET_USAGE; + + lcd_puts(argv[1]); + + return 0; +} + +U_BOOT_CMD( + setcurs, 3, 1, do_lcd_setcursor, + "set cursor position within screen", + " <col> <row> in character" +); + +U_BOOT_CMD( + lcdputs, 2, 1, do_lcd_puts, + "print string on lcd-framebuffer", + " <string>" +); + diff --git a/roms/u-boot/common/lcd_console_rotation.c b/roms/u-boot/common/lcd_console_rotation.c new file mode 100644 index 000000000..a5f5c6da7 --- /dev/null +++ b/roms/u-boot/common/lcd_console_rotation.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + */ + +#include <common.h> +#include <lcd.h> +#include <video_font.h> /* Get font data, width and height */ + +static void lcd_putc_xy90(struct console_t *pcons, ushort x, ushort y, char c) +{ + int fg_color = lcd_getfgcolor(); + int bg_color = lcd_getbgcolor(); + int col, i; + + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + (x+1) * pcons->lcdsizex - + y; + + uchar msk = 0x80; + uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (col = 0; col < VIDEO_FONT_WIDTH; ++col) { + for (i = 0; i < VIDEO_FONT_HEIGHT; ++i) + *dst-- = (*(pfont + i) & msk) ? fg_color : bg_color; + msk >>= 1; + dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT); + } +} + +static inline void console_setrow90(struct console_t *pcons, u32 row, int clr) +{ + int i, j; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + pcons->lcdsizex - + row*VIDEO_FONT_HEIGHT+1; + + for (j = 0; j < pcons->lcdsizey; j++) { + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst-- = clr; + dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT); + } +} + +static inline void console_moverow90(struct console_t *pcons, + u32 rowdst, u32 rowsrc) +{ + int i, j; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + pcons->lcdsizex - + (rowdst*VIDEO_FONT_HEIGHT+1); + + fbptr_t *src = (fbptr_t *)pcons->fbbase + + pcons->lcdsizex - + (rowsrc*VIDEO_FONT_HEIGHT+1); + + for (j = 0; j < pcons->lcdsizey; j++) { + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst-- = *src--; + src += (pcons->lcdsizex + VIDEO_FONT_HEIGHT); + dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT); + } +} +static void lcd_putc_xy180(struct console_t *pcons, ushort x, ushort y, char c) +{ + int fg_color = lcd_getfgcolor(); + int bg_color = lcd_getbgcolor(); + int i, row; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + pcons->lcdsizex + + pcons->lcdsizey * pcons->lcdsizex - + y * pcons->lcdsizex - + (x+1); + + for (row = 0; row < VIDEO_FONT_HEIGHT; row++) { + uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; + + for (i = 0; i < VIDEO_FONT_WIDTH; ++i) { + *dst-- = (bits & 0x80) ? fg_color : bg_color; + bits <<= 1; + } + dst -= (pcons->lcdsizex - VIDEO_FONT_WIDTH); + } +} + +static inline void console_setrow180(struct console_t *pcons, u32 row, int clr) +{ + int i; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + (pcons->rows-row-1) * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++) + *dst++ = clr; +} + +static inline void console_moverow180(struct console_t *pcons, + u32 rowdst, u32 rowsrc) +{ + int i; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + (pcons->rows-rowdst-1) * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + fbptr_t *src = (fbptr_t *)pcons->fbbase + + (pcons->rows-rowsrc-1) * VIDEO_FONT_HEIGHT * + pcons->lcdsizex; + + for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++) + *dst++ = *src++; +} + +static void lcd_putc_xy270(struct console_t *pcons, ushort x, ushort y, char c) +{ + int fg_color = lcd_getfgcolor(); + int bg_color = lcd_getbgcolor(); + int i, col; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + pcons->lcdsizey * pcons->lcdsizex - + (x+1) * pcons->lcdsizex + + y; + + uchar msk = 0x80; + uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (col = 0; col < VIDEO_FONT_WIDTH; ++col) { + for (i = 0; i < VIDEO_FONT_HEIGHT; ++i) + *dst++ = (*(pfont + i) & msk) ? fg_color : bg_color; + msk >>= 1; + dst -= (pcons->lcdsizex + VIDEO_FONT_HEIGHT); + } +} + +static inline void console_setrow270(struct console_t *pcons, u32 row, int clr) +{ + int i, j; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + row*VIDEO_FONT_HEIGHT; + + for (j = 0; j < pcons->lcdsizey; j++) { + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT); + } +} + +static inline void console_moverow270(struct console_t *pcons, + u32 rowdst, u32 rowsrc) +{ + int i, j; + fbptr_t *dst = (fbptr_t *)pcons->fbbase + + rowdst*VIDEO_FONT_HEIGHT; + + fbptr_t *src = (fbptr_t *)pcons->fbbase + + rowsrc*VIDEO_FONT_HEIGHT; + + for (j = 0; j < pcons->lcdsizey; j++) { + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = *src++; + src += (pcons->lcdsizex - VIDEO_FONT_HEIGHT); + dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT); + } +} + +static void console_calc_rowcol_rot(struct console_t *pcons) +{ + if (pcons->lcdrot == 1 || pcons->lcdrot == 3) + console_calc_rowcol(pcons, pcons->lcdsizey, pcons->lcdsizex); + else + console_calc_rowcol(pcons, pcons->lcdsizex, pcons->lcdsizey); +} + +void lcd_init_console_rot(struct console_t *pcons) +{ + if (pcons->lcdrot == 0) { + return; + } else if (pcons->lcdrot == 1) { + pcons->fp_putc_xy = &lcd_putc_xy90; + pcons->fp_console_moverow = &console_moverow90; + pcons->fp_console_setrow = &console_setrow90; + } else if (pcons->lcdrot == 2) { + pcons->fp_putc_xy = &lcd_putc_xy180; + pcons->fp_console_moverow = &console_moverow180; + pcons->fp_console_setrow = &console_setrow180; + } else if (pcons->lcdrot == 3) { + pcons->fp_putc_xy = &lcd_putc_xy270; + pcons->fp_console_moverow = &console_moverow270; + pcons->fp_console_setrow = &console_setrow270; + } else { + printf("%s: invalid framebuffer rotation (%d)!\n", + __func__, pcons->lcdrot); + return; + } + console_calc_rowcol_rot(pcons); +} diff --git a/roms/u-boot/common/lcd_simplefb.c b/roms/u-boot/common/lcd_simplefb.c new file mode 100644 index 000000000..1650615cd --- /dev/null +++ b/roms/u-boot/common/lcd_simplefb.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Simplefb device tree support + * + * (C) Copyright 2015 + * Stephen Warren <swarren@wwwdotorg.org> + */ + +#include <common.h> +#include <dm.h> +#include <lcd.h> +#include <fdt_support.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <video.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int lcd_dt_simplefb_configure_node(void *blob, int off) +{ + int xsize, ysize; + int bpix; /* log2 of bits per pixel */ + const char *name; + ulong fb_base; +#ifdef CONFIG_DM_VIDEO + struct video_uc_plat *plat; + struct video_priv *uc_priv; + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (ret) + return ret; + uc_priv = dev_get_uclass_priv(dev); + plat = dev_get_uclass_plat(dev); + xsize = uc_priv->xsize; + ysize = uc_priv->ysize; + bpix = uc_priv->bpix; + fb_base = plat->base; +#else + xsize = lcd_get_pixel_width(); + ysize = lcd_get_pixel_height(); + bpix = LCD_BPP; + fb_base = gd->fb_base; +#endif + switch (bpix) { + case 4: /* VIDEO_BPP16 */ + name = "r5g6b5"; + break; + case 5: /* VIDEO_BPP32 */ + name = "a8r8g8b8"; + break; + default: + return -EINVAL; + } + + return fdt_setup_simplefb_node(blob, off, fb_base, xsize, ysize, + xsize * (1 << bpix) / 8, name); +} + +int lcd_dt_simplefb_add_node(void *blob) +{ + static const char compat[] = "simple-framebuffer"; + static const char disabled[] = "disabled"; + int off, ret; + + off = fdt_add_subnode(blob, 0, "framebuffer"); + if (off < 0) + return -1; + + ret = fdt_setprop(blob, off, "status", disabled, sizeof(disabled)); + if (ret < 0) + return -1; + + ret = fdt_setprop(blob, off, "compatible", compat, sizeof(compat)); + if (ret < 0) + return -1; + + return lcd_dt_simplefb_configure_node(blob, off); +} + +int lcd_dt_simplefb_enable_existing_node(void *blob) +{ + int off; + + off = fdt_node_offset_by_compatible(blob, -1, "simple-framebuffer"); + if (off < 0) + return -1; + + return lcd_dt_simplefb_configure_node(blob, off); +} diff --git a/roms/u-boot/common/log.c b/roms/u-boot/common/log.c new file mode 100644 index 000000000..ea407c6db --- /dev/null +++ b/roms/u-boot/common/log.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Logging support + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <dm/uclass.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char *const log_cat_name[] = { + "none", + "arch", + "board", + "core", + "driver-model", + "device-tree", + "efi", + "alloc", + "sandbox", + "bloblist", + "devres", + "acpi", + "boot", +}; + +_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE, + "log_cat_name size"); + +static const char *const log_level_name[] = { + "EMERG", + "ALERT", + "CRIT", + "ERR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + "CONTENT", + "IO", +}; + +_Static_assert(ARRAY_SIZE(log_level_name) == LOGL_COUNT, "log_level_name size"); + +/* All error responses MUST begin with '<' */ +const char *log_get_cat_name(enum log_category_t cat) +{ + const char *name; + + if (cat < 0 || cat >= LOGC_COUNT) + return "<invalid>"; + if (cat >= LOGC_NONE) + return log_cat_name[cat - LOGC_NONE]; + +#if CONFIG_IS_ENABLED(DM) + name = uclass_get_name((enum uclass_id)cat); +#else + name = NULL; +#endif + + return name ? name : "<missing>"; +} + +enum log_category_t log_get_cat_by_name(const char *name) +{ + enum uclass_id id; + int i; + + for (i = LOGC_NONE; i < LOGC_COUNT; i++) + if (!strcmp(name, log_cat_name[i - LOGC_NONE])) + return i; + id = uclass_get_by_name(name); + if (id != UCLASS_INVALID) + return (enum log_category_t)id; + + return LOGC_NONE; +} + +const char *log_get_level_name(enum log_level_t level) +{ + if (level >= LOGL_COUNT) + return "INVALID"; + return log_level_name[level]; +} + +enum log_level_t log_get_level_by_name(const char *name) +{ + int i; + + for (i = 0; i < LOGL_COUNT; i++) { + if (!strcasecmp(log_level_name[i], name)) + return i; + } + + return LOGL_NONE; +} + +struct log_device *log_device_find_by_name(const char *drv_name) +{ + struct log_device *ldev; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + if (!strcmp(drv_name, ldev->drv->name)) + return ldev; + } + + return NULL; +} + +bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat) +{ + int i; + + for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) { + if (cat_list[i] == cat) + return true; + } + + return false; +} + +bool log_has_file(const char *file_list, const char *file) +{ + int file_len = strlen(file); + const char *s, *p; + int substr_len; + + for (s = file_list; *s; s = p + (*p != '\0')) { + p = strchrnul(s, ','); + substr_len = p - s; + if (file_len >= substr_len && + !strncmp(file + file_len - substr_len, s, substr_len)) + return true; + } + + return false; +} + +/** + * log_passes_filters() - check if a log record passes the filters for a device + * + * @ldev: Log device to check + * @rec: Log record to check + * @return true if @rec is not blocked by the filters in @ldev, false if it is + */ +static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) +{ + struct log_filter *filt; + + if (rec->flags & LOGRECF_FORCE_DEBUG) + return true; + + /* If there are no filters, filter on the default log level */ + if (list_empty(&ldev->filter_head)) { + if (rec->level > gd->default_log_level) + return false; + return true; + } + + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (filt->flags & LOGFF_LEVEL_MIN) { + if (rec->level < filt->level) + continue; + } else if (rec->level > filt->level) { + continue; + } + + if ((filt->flags & LOGFF_HAS_CAT) && + !log_has_cat(filt->cat_list, rec->cat)) + continue; + + if (filt->file_list && + !log_has_file(filt->file_list, rec->file)) + continue; + + if (filt->flags & LOGFF_DENY) + return false; + else + return true; + } + + return false; +} + +/** + * log_dispatch() - Send a log record to all log devices for processing + * + * The log record is sent to each log device in turn, skipping those which have + * filters which block the record. + * + * All log messages created while processing log record @rec are ignored. + * + * @rec: log record to dispatch + * Return: 0 msg sent, 1 msg not sent while already dispatching another msg + */ +static int log_dispatch(struct log_rec *rec, const char *fmt, va_list args) +{ + struct log_device *ldev; + char buf[CONFIG_SYS_CBSIZE]; + + /* + * When a log driver writes messages (e.g. via the network stack) this + * may result in further generated messages. We cannot process them here + * as this might result in infinite recursion. + */ + if (gd->processing_msg) + return 1; + + /* Emit message */ + gd->processing_msg = true; + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + if ((ldev->flags & LOGDF_ENABLE) && + log_passes_filters(ldev, rec)) { + if (!rec->msg) { + int len; + + len = vsnprintf(buf, sizeof(buf), fmt, args); + rec->msg = buf; + gd->log_cont = len && buf[len - 1] != '\n'; + } + ldev->drv->emit(ldev, rec); + } + } + gd->processing_msg = false; + return 0; +} + +int _log(enum log_category_t cat, enum log_level_t level, const char *file, + int line, const char *func, const char *fmt, ...) +{ + struct log_rec rec; + va_list args; + + if (!gd) + return -ENOSYS; + + /* Check for message continuation */ + if (cat == LOGC_CONT) + cat = gd->logc_prev; + if (level == LOGL_CONT) + level = gd->logl_prev; + + rec.cat = cat; + rec.level = level & LOGL_LEVEL_MASK; + rec.flags = 0; + if (level & LOGL_FORCE_DEBUG) + rec.flags |= LOGRECF_FORCE_DEBUG; + if (gd->log_cont) + rec.flags |= LOGRECF_CONT; + rec.file = file; + rec.line = line; + rec.func = func; + rec.msg = NULL; + + if (!(gd->flags & GD_FLG_LOG_READY)) { + gd->log_drop_count++; + + /* display dropped traces with console puts and DEBUG_UART */ + if (rec.level <= CONFIG_LOG_DEFAULT_LEVEL || + rec.flags & LOGRECF_FORCE_DEBUG) { + char buf[CONFIG_SYS_CBSIZE]; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + puts(buf); + va_end(args); + } + + return -ENOSYS; + } + va_start(args, fmt); + if (!log_dispatch(&rec, fmt, args)) { + gd->logc_prev = cat; + gd->logl_prev = level; + } + va_end(args); + + return 0; +} + +int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[], + enum log_level_t level, const char *file_list, + int flags) +{ + struct log_filter *filt; + struct log_device *ldev; + int ret; + int i; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) + return -ENOENT; + filt = calloc(1, sizeof(*filt)); + if (!filt) + return -ENOMEM; + + filt->flags = flags; + if (cat_list) { + filt->flags |= LOGFF_HAS_CAT; + for (i = 0; ; i++) { + if (i == ARRAY_SIZE(filt->cat_list)) { + ret = -ENOSPC; + goto err; + } + filt->cat_list[i] = cat_list[i]; + if (cat_list[i] == LOGC_END) + break; + } + } + filt->level = level; + if (file_list) { + filt->file_list = strdup(file_list); + if (!filt->file_list) { + ret = -ENOMEM; + goto err; + } + } + filt->filter_num = ldev->next_filter_num++; + /* Add deny filters to the beginning of the list */ + if (flags & LOGFF_DENY) + list_add(&filt->sibling_node, &ldev->filter_head); + else + list_add_tail(&filt->sibling_node, &ldev->filter_head); + + return filt->filter_num; + +err: + free(filt); + return ret; +} + +int log_remove_filter(const char *drv_name, int filter_num) +{ + struct log_filter *filt; + struct log_device *ldev; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) + return -ENOENT; + + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (filt->filter_num == filter_num) { + list_del(&filt->sibling_node); + free(filt); + + return 0; + } + } + + return -ENOENT; +} + +/** + * log_find_device_by_drv() - Find a device by its driver + * + * @drv: Log driver + * @return Device associated with that driver, or NULL if not found + */ +static struct log_device *log_find_device_by_drv(struct log_driver *drv) +{ + struct log_device *ldev; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) { + if (ldev->drv == drv) + return ldev; + } + /* + * It is quite hard to pass an invalid driver since passing an unknown + * LOG_GET_DRIVER(xxx) would normally produce a compilation error. But + * it is possible to pass NULL, for example, so this + */ + + return NULL; +} + +int log_device_set_enable(struct log_driver *drv, bool enable) +{ + struct log_device *ldev; + + ldev = log_find_device_by_drv(drv); + if (!ldev) + return -ENOENT; + if (enable) + ldev->flags |= LOGDF_ENABLE; + else + ldev->flags &= ~LOGDF_ENABLE; + + return 0; +} + +int log_init(void) +{ + struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); + const int count = ll_entry_count(struct log_driver, log_driver); + struct log_driver *end = drv + count; + + /* + * We cannot add runtime data to the driver since it is likely stored + * in rodata. Instead, set up a 'device' corresponding to each driver. + * We only support having a single device. + */ + INIT_LIST_HEAD((struct list_head *)&gd->log_head); + while (drv < end) { + struct log_device *ldev; + + ldev = calloc(1, sizeof(*ldev)); + if (!ldev) { + debug("%s: Cannot allocate memory\n", __func__); + return -ENOMEM; + } + INIT_LIST_HEAD(&ldev->filter_head); + ldev->drv = drv; + ldev->flags = drv->flags; + list_add_tail(&ldev->sibling_node, + (struct list_head *)&gd->log_head); + drv++; + } + gd->flags |= GD_FLG_LOG_READY; + if (!gd->default_log_level) + gd->default_log_level = CONFIG_LOG_DEFAULT_LEVEL; + gd->log_fmt = log_get_default_format(); + gd->logc_prev = LOGC_NONE; + gd->logl_prev = LOGL_INFO; + + return 0; +} diff --git a/roms/u-boot/common/log_console.c b/roms/u-boot/common/log_console.c new file mode 100644 index 000000000..3f6177499 --- /dev/null +++ b/roms/u-boot/common/log_console.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Logging support + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <log.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int log_console_emit(struct log_device *ldev, struct log_rec *rec) +{ + int fmt = gd->log_fmt; + bool add_space = false; + + /* + * The output format is designed to give someone a fighting chance of + * figuring out which field is which: + * - level is in CAPS + * - cat is lower case and ends with comma + * - file normally has a .c extension and ends with a colon + * - line is integer and ends with a - + * - function is an identifier and ends with () + * - message has a space before it unless it is on its own + */ + if (!(rec->flags & LOGRECF_CONT) && fmt != BIT(LOGF_MSG)) { + add_space = true; + if (fmt & BIT(LOGF_LEVEL)) + printf("%s.", log_get_level_name(rec->level)); + if (fmt & BIT(LOGF_CAT)) + printf("%s,", log_get_cat_name(rec->cat)); + if (fmt & BIT(LOGF_FILE)) + printf("%s:", rec->file); + if (fmt & BIT(LOGF_LINE)) + printf("%d-", rec->line); + if (fmt & BIT(LOGF_FUNC)) + printf("%s()", rec->func); + } + if (fmt & BIT(LOGF_MSG)) + printf("%s%s", add_space ? " " : "", rec->msg); + + return 0; +} + +LOG_DRIVER(console) = { + .name = "console", + .emit = log_console_emit, + .flags = LOGDF_ENABLE, +}; diff --git a/roms/u-boot/common/log_syslog.c b/roms/u-boot/common/log_syslog.c new file mode 100644 index 000000000..53c4def5d --- /dev/null +++ b/roms/u-boot/common/log_syslog.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Log to syslog. + * + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <log.h> +#include <net.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BUFFER_SIZE 480 + +static void append(char **buf, char *buf_end, const char *fmt, ...) +{ + va_list args; + size_t size = buf_end - *buf; + + va_start(args, fmt); + vsnprintf(*buf, size, fmt, args); + va_end(args); + *buf += strlen(*buf); +} + +static int log_syslog_emit(struct log_device *ldev, struct log_rec *rec) +{ + int ret; + int fmt = gd->log_fmt; + char msg[BUFFER_SIZE]; + char *msg_end = msg + BUFFER_SIZE; + char *ptr = msg; + char *iphdr; + char *log_msg; + int eth_hdr_size; + struct in_addr bcast_ip; + unsigned int log_level; + char *log_hostname; + + /* Setup packet buffers */ + ret = net_init(); + if (ret) + return ret; + /* Disable hardware and put it into the reset state */ + eth_halt(); + /* Set current device according to environment variables */ + eth_set_current(); + /* Get hardware ready for send and receive operations */ + ret = eth_init(); + if (ret < 0) { + eth_halt(); + goto out; + } + + memset(msg, 0, BUFFER_SIZE); + + /* Set ethernet header */ + eth_hdr_size = net_set_ether((uchar *)ptr, net_bcast_ethaddr, PROT_IP); + ptr += eth_hdr_size; + iphdr = ptr; + ptr += IP_UDP_HDR_SIZE; + log_msg = ptr; + + /* + * The syslog log levels defined in RFC 5424 match the U-Boot ones up to + * level 7 (debug). + */ + log_level = rec->level; + if (log_level > 7) + log_level = 7; + /* Leave high bits as 0 to write a 'kernel message' */ + + /* Write log message to buffer */ + append(&ptr, msg_end, "<%u>", log_level); + log_hostname = env_get("log_hostname"); + if (log_hostname) + append(&ptr, msg_end, "%s ", log_hostname); + append(&ptr, msg_end, "uboot: "); + if (fmt & BIT(LOGF_LEVEL)) + append(&ptr, msg_end, "%s.", + log_get_level_name(rec->level)); + if (fmt & BIT(LOGF_CAT)) + append(&ptr, msg_end, "%s,", + log_get_cat_name(rec->cat)); + if (fmt & BIT(LOGF_FILE)) + append(&ptr, msg_end, "%s:", rec->file); + if (fmt & BIT(LOGF_LINE)) + append(&ptr, msg_end, "%d-", rec->line); + if (fmt & BIT(LOGF_FUNC)) + append(&ptr, msg_end, "%s()", rec->func); + if (fmt & BIT(LOGF_MSG)) + append(&ptr, msg_end, "%s%s", + fmt != BIT(LOGF_MSG) ? " " : "", rec->msg); + /* Consider trailing 0x00 */ + ptr++; + + debug("log message: '%s'\n", log_msg); + + /* Broadcast message */ + bcast_ip.s_addr = 0xFFFFFFFFL; + net_set_udp_header((uchar *)iphdr, bcast_ip, 514, 514, ptr - log_msg); + net_send_packet((uchar *)msg, ptr - msg); + +out: + return ret; +} + +LOG_DRIVER(syslog) = { + .name = "syslog", + .emit = log_syslog_emit, +}; diff --git a/roms/u-boot/common/lynxkdi.c b/roms/u-boot/common/lynxkdi.c new file mode 100644 index 000000000..1c8e122c3 --- /dev/null +++ b/roms/u-boot/common/lynxkdi.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) Orbacom Systems, Inc <www.orbacom.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <common.h> +#include <asm/processor.h> +#include <image.h> +#include <net.h> + +#include <lynxkdi.h> + +#error "Lynx KDI support not implemented for configured CPU" diff --git a/roms/u-boot/common/main.c b/roms/u-boot/common/main.c new file mode 100644 index 000000000..ae5bcdb32 --- /dev/null +++ b/roms/u-boot/common/main.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* #define DEBUG */ + +#include <common.h> +#include <autoboot.h> +#include <bootstage.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <init.h> +#include <net.h> +#include <version.h> +#include <efi_loader.h> + +static void run_preboot_environment_command(void) +{ + char *p; + + p = env_get("preboot"); + if (p != NULL) { + int prev = 0; + + if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED)) + prev = disable_ctrlc(1); /* disable Ctrl-C checking */ + + run_command_list(p, -1, 0); + + if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED)) + disable_ctrlc(prev); /* restore Ctrl-C checking */ + } +} + +/* We come here after U-Boot is initialised and ready to process commands */ +void main_loop(void) +{ + const char *s; + + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); + + if (IS_ENABLED(CONFIG_VERSION_VARIABLE)) + env_set("ver", version_string); /* set version variable */ + + cli_init(); + + if (IS_ENABLED(CONFIG_USE_PREBOOT)) + run_preboot_environment_command(); + + if (IS_ENABLED(CONFIG_UPDATE_TFTP)) + update_tftp(0UL, NULL, NULL); + + if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) + efi_launch_capsules(); + + s = bootdelay_process(); + if (cli_process_fdt(&s)) + cli_secure_boot_cmd(s); + + autoboot_command(s); + + cli_loop(); + panic("No CLI available"); +} diff --git a/roms/u-boot/common/malloc_simple.c b/roms/u-boot/common/malloc_simple.c new file mode 100644 index 000000000..0267fb6be --- /dev/null +++ b/roms/u-boot/common/malloc_simple.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Simple malloc implementation + * + * Copyright (c) 2014 Google, Inc + */ + +#define LOG_CATEGORY LOGC_ALLOC + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <asm/global_data.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void *alloc_simple(size_t bytes, int align) +{ + ulong addr, new_ptr; + void *ptr; + + addr = ALIGN(gd->malloc_base + gd->malloc_ptr, align); + new_ptr = addr + bytes - gd->malloc_base; + log_debug("size=%zx, ptr=%lx, limit=%lx: ", bytes, new_ptr, + gd->malloc_limit); + if (new_ptr > gd->malloc_limit) { + log_err("alloc space exhausted\n"); + return NULL; + } + + ptr = map_sysmem(addr, bytes); + gd->malloc_ptr = ALIGN(new_ptr, sizeof(new_ptr)); + + return ptr; +} + +void *malloc_simple(size_t bytes) +{ + void *ptr; + + ptr = alloc_simple(bytes, 1); + if (!ptr) + return ptr; + + log_debug("%lx\n", (ulong)ptr); + + return ptr; +} + +void *memalign_simple(size_t align, size_t bytes) +{ + void *ptr; + + ptr = alloc_simple(bytes, align); + if (!ptr) + return ptr; + log_debug("aligned to %lx\n", (ulong)ptr); + + return ptr; +} + +#if CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE) +void *calloc(size_t nmemb, size_t elem_size) +{ + size_t size = nmemb * elem_size; + void *ptr; + + ptr = malloc(size); + if (!ptr) + return ptr; + memset(ptr, '\0', size); + + return ptr; +} +#endif + +void malloc_simple_info(void) +{ + log_info("malloc_simple: %lx bytes used, %lx remain\n", gd->malloc_ptr, + CONFIG_VAL(SYS_MALLOC_F_LEN) - gd->malloc_ptr); +} diff --git a/roms/u-boot/common/memsize.c b/roms/u-boot/common/memsize.c new file mode 100644 index 000000000..d5d13d51b --- /dev/null +++ b/roms/u-boot/common/memsize.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef __PPC__ +/* + * At least on G2 PowerPC cores, sequential accesses to non-existent + * memory must be synchronized. + */ +# include <asm/io.h> /* for sync() */ +#else +# define sync() /* nothing */ +#endif + +/* + * Check memory range for valid RAM. A simple memory test determines + * the actually available RAM size between addresses `base' and + * `base + maxsize'. + */ +long get_ram_size(long *base, long maxsize) +{ + volatile long *addr; + long save[BITS_PER_LONG - 1]; + long save_base; + long cnt; + long val; + long size; + int i = 0; + + for (cnt = (maxsize / sizeof(long)) >> 1; cnt > 0; cnt >>= 1) { + addr = base + cnt; /* pointer arith! */ + sync(); + save[i++] = *addr; + sync(); + *addr = ~cnt; + } + + addr = base; + sync(); + save_base = *addr; + sync(); + *addr = 0; + + sync(); + if ((val = *addr) != 0) { + /* Restore the original data before leaving the function. */ + sync(); + *base = save_base; + for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) { + addr = base + cnt; + sync(); + *addr = save[--i]; + } + return (0); + } + + for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) { + addr = base + cnt; /* pointer arith! */ + val = *addr; + *addr = save[--i]; + if (val != ~cnt) { + size = cnt * sizeof(long); + /* + * Restore the original data + * before leaving the function. + */ + for (cnt <<= 1; + cnt < maxsize / sizeof(long); + cnt <<= 1) { + addr = base + cnt; + *addr = save[--i]; + } + /* warning: don't restore save_base in this case, + * it is already done in the loop because + * base and base+size share the same physical memory + * and *base is saved after *(base+size) modification + * in first loop + */ + return (size); + } + } + *base = save_base; + + return (maxsize); +} + +phys_size_t __weak get_effective_memsize(void) +{ +#ifndef CONFIG_VERY_BIG_RAM + return gd->ram_size; +#else + /* limit stack to what we can reasonable map */ + return ((gd->ram_size > CONFIG_MAX_MEM_MAPPED) ? + CONFIG_MAX_MEM_MAPPED : gd->ram_size); +#endif +} diff --git a/roms/u-boot/common/menu.c b/roms/u-boot/common/menu.c new file mode 100644 index 000000000..5fb2ffbd0 --- /dev/null +++ b/roms/u-boot/common/menu.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2010-2011 Calxeda, Inc. + * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + */ + +#include <common.h> +#include <cli.h> +#include <malloc.h> +#include <errno.h> +#include <linux/list.h> + +#include "menu.h" + +/* + * Internally, each item in a menu is represented by a struct menu_item. + * + * These items will be alloc'd and initialized by menu_item_add and destroyed + * by menu_item_destroy, and the consumer of the interface never sees that + * this struct is used at all. + */ +struct menu_item { + char *key; + void *data; + struct list_head list; +}; + +/* + * The menu is composed of a list of items along with settings and callbacks + * provided by the user. An incomplete definition of this struct is available + * in menu.h, but the full definition is here to prevent consumers from + * relying on its contents. + */ +struct menu { + struct menu_item *default_item; + int timeout; + char *title; + int prompt; + void (*display_statusline)(struct menu *); + void (*item_data_print)(void *); + char *(*item_choice)(void *); + void *item_choice_data; + struct list_head items; + int item_cnt; +}; + +/* + * An iterator function for menu items. callback will be called for each item + * in m, with m, a pointer to the item, and extra being passed to callback. If + * callback returns a value other than NULL, iteration stops and the value + * return by callback is returned from menu_items_iter. This allows it to be + * used for search type operations. It is also safe for callback to remove the + * item from the list of items. + */ +static inline void *menu_items_iter(struct menu *m, + void *(*callback)(struct menu *, struct menu_item *, void *), + void *extra) +{ + struct list_head *pos, *n; + struct menu_item *item; + void *ret; + + list_for_each_safe(pos, n, &m->items) { + item = list_entry(pos, struct menu_item, list); + + ret = callback(m, item, extra); + + if (ret) + return ret; + } + + return NULL; +} + +/* + * Print a menu_item. If the consumer provided an item_data_print function + * when creating the menu, call it with a pointer to the item's private data. + * Otherwise, print the key of the item. + */ +static inline void *menu_item_print(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (!m->item_data_print) { + puts(item->key); + putc('\n'); + } else { + m->item_data_print(item->data); + } + + return NULL; +} + +/* + * Free the memory used by a menu item. This includes the memory used by its + * key. + */ +static inline void *menu_item_destroy(struct menu *m, + struct menu_item *item, + void *extra) +{ + if (item->key) + free(item->key); + + free(item); + + return NULL; +} + +/* + * Display a menu so the user can make a choice of an item. First display its + * title, if any, and then each item in the menu. + */ +static inline void menu_display(struct menu *m) +{ + if (m->title) { + puts(m->title); + putc('\n'); + } + if (m->display_statusline) + m->display_statusline(m); + + menu_items_iter(m, menu_item_print, NULL); +} + +/* + * Check if an item's key matches a provided string, pointed to by extra. If + * extra is NULL, an item with a NULL key will match. Otherwise, the item's + * key has to match according to strcmp. + * + * This is called via menu_items_iter, so it returns a pointer to the item if + * the key matches, and returns NULL otherwise. + */ +static inline void *menu_item_key_match(struct menu *m, + struct menu_item *item, void *extra) +{ + char *item_key = extra; + + if (!item_key || !item->key) { + if (item_key == item->key) + return item; + + return NULL; + } + + if (strcmp(item->key, item_key) == 0) + return item; + + return NULL; +} + +/* + * Find the first item with a key matching item_key, if any exists. + */ +static inline struct menu_item *menu_item_by_key(struct menu *m, + char *item_key) +{ + return menu_items_iter(m, menu_item_key_match, item_key); +} + +/* + * Set *choice to point to the default item's data, if any default item was + * set, and returns 1. If no default item was set, returns -ENOENT. + */ +int menu_default_choice(struct menu *m, void **choice) +{ + if (m->default_item) { + *choice = m->default_item->data; + return 1; + } + + return -ENOENT; +} + +/* + * Displays the menu and asks the user to choose an item. *choice will point + * to the private data of the item the user chooses. The user makes a choice + * by inputting a string matching the key of an item. Invalid choices will + * cause the user to be prompted again, repeatedly, until the user makes a + * valid choice. The user can exit the menu without making a choice via ^c. + * + * Returns 1 if the user made a choice, or -EINTR if they bail via ^c. + */ +static inline int menu_interactive_choice(struct menu *m, void **choice) +{ + char cbuf[CONFIG_SYS_CBSIZE]; + struct menu_item *choice_item = NULL; + int readret; + + while (!choice_item) { + cbuf[0] = '\0'; + + menu_display(m); + + if (!m->item_choice) { + readret = cli_readline_into_buffer("Enter choice: ", + cbuf, m->timeout); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else if (readret == -1) { + printf("<INTERRUPT>\n"); + return -EINTR; + } else { + return menu_default_choice(m, choice); + } + } else { + char *key = m->item_choice(m->item_choice_data); + + if (key) + choice_item = menu_item_by_key(m, key); + } + + if (!choice_item) + m->timeout = 0; + } + + *choice = choice_item->data; + + return 1; +} + +/* + * menu_default_set() - Sets the default choice for the menu. This is safe to + * call more than once on a menu. + * + * m - Points to a menu created by menu_create(). + * + * item_key - Points to a string that, when compared using strcmp, matches the + * key for an existing item in the menu. + * + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a + * key matching item_key is found. + */ +int menu_default_set(struct menu *m, char *item_key) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, item_key); + + if (!item) + return -ENOENT; + + m->default_item = item; + + return 1; +} + +/* + * menu_get_choice() - Returns the user's selected menu entry, or the default + * if the menu is set to not prompt or the timeout expires. This is safe to + * call more than once. + * + * m - Points to a menu created by menu_create(). + * + * choice - Points to a location that will store a pointer to the selected + * menu item. If no item is selected or there is an error, no value will be + * written at the location it points to. + * + * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no + * default has been set and the menu is set to not prompt or the timeout + * expires, or -EINTR if the user exits the menu via ^c. + */ +int menu_get_choice(struct menu *m, void **choice) +{ + if (!m || !choice) + return -EINVAL; + + if (!m->prompt || m->item_cnt == 1) + return menu_default_choice(m, choice); + + return menu_interactive_choice(m, choice); +} + +/* + * menu_item_add() - Adds or replaces a menu item. Note that this replaces the + * data of an item if it already exists, but doesn't change the order of the + * item. + * + * m - Points to a menu created by menu_create(). + * + * item_key - Points to a string that will uniquely identify the item. The + * string will be copied to internal storage, and is safe to discard after + * passing to menu_item_add. + * + * item_data - An opaque pointer associated with an item. It is never + * dereferenced internally, but will be passed to the item_data_print, and + * will be returned from menu_get_choice if the menu item is selected. + * + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is + * insufficient memory to add the menu item. + */ +int menu_item_add(struct menu *m, char *item_key, void *item_data) +{ + struct menu_item *item; + + if (!m) + return -EINVAL; + + item = menu_item_by_key(m, item_key); + + if (item) { + item->data = item_data; + return 1; + } + + item = malloc(sizeof *item); + if (!item) + return -ENOMEM; + + item->key = strdup(item_key); + + if (!item->key) { + free(item); + return -ENOMEM; + } + + item->data = item_data; + + list_add_tail(&item->list, &m->items); + m->item_cnt++; + + return 1; +} + +/* + * menu_create() - Creates a menu handle with default settings + * + * title - If not NULL, points to a string that will be displayed before the + * list of menu items. It will be copied to internal storage, and is safe to + * discard after passing to menu_create(). + * + * timeout - A delay in seconds to wait for user input. If 0, timeout is + * disabled, and the default choice will be returned unless prompt is 1. + * + * prompt - If 0, don't ask for user input unless there is an interrupted + * timeout. If 1, the user will be prompted for input regardless of the value + * of timeout. + * + * display_statusline - If not NULL, will be called to show a statusline when + * the menu is displayed. + * + * item_data_print - If not NULL, will be called for each item when the menu + * is displayed, with the pointer to the item's data passed as the argument. + * If NULL, each item's key will be printed instead. Since an item's key is + * what must be entered to select an item, the item_data_print function should + * make it obvious what the key for each entry is. + * + * item_choice - If not NULL, will be called when asking the user to choose an + * item. Returns a key string corresponding to the chosen item or NULL if + * no item has been selected. + * + * item_choice_data - Will be passed as the argument to the item_choice function + * + * Returns a pointer to the menu if successful, or NULL if there is + * insufficient memory available to create the menu. + */ +struct menu *menu_create(char *title, int timeout, int prompt, + void (*display_statusline)(struct menu *), + void (*item_data_print)(void *), + char *(*item_choice)(void *), + void *item_choice_data) +{ + struct menu *m; + + m = malloc(sizeof *m); + + if (!m) + return NULL; + + m->default_item = NULL; + m->prompt = prompt; + m->timeout = timeout; + m->display_statusline = display_statusline; + m->item_data_print = item_data_print; + m->item_choice = item_choice; + m->item_choice_data = item_choice_data; + m->item_cnt = 0; + + if (title) { + m->title = strdup(title); + if (!m->title) { + free(m); + return NULL; + } + } else + m->title = NULL; + + + INIT_LIST_HEAD(&m->items); + + return m; +} + +/* + * menu_destroy() - frees the memory used by a menu and its items. + * + * m - Points to a menu created by menu_create(). + * + * Returns 1 if successful, or -EINVAL if m is NULL. + */ +int menu_destroy(struct menu *m) +{ + if (!m) + return -EINVAL; + + menu_items_iter(m, menu_item_destroy, NULL); + + if (m->title) + free(m->title); + + free(m); + + return 1; +} diff --git a/roms/u-boot/common/miiphyutil.c b/roms/u-boot/common/miiphyutil.c new file mode 100644 index 000000000..7d4d15ed9 --- /dev/null +++ b/roms/u-boot/common/miiphyutil.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + */ + +/* + * This provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <miiphy.h> +#include <phy.h> +#include <linux/delay.h> + +#include <asm/types.h> +#include <linux/list.h> +#include <malloc.h> +#include <net.h> + +/* local debug macro */ +#undef MII_DEBUG + +#undef debug +#ifdef MII_DEBUG +#define debug(fmt, args...) printf(fmt, ##args) +#else +#define debug(fmt, args...) +#endif /* MII_DEBUG */ + +static struct list_head mii_devs; +static struct mii_dev *current_mii; + +/* + * Lookup the mii_dev struct by the registered device name. + */ +struct mii_dev *miiphy_get_dev_by_name(const char *devname) +{ + struct list_head *entry; + struct mii_dev *dev; + + if (!devname) { + printf("NULL device name!\n"); + return NULL; + } + + list_for_each(entry, &mii_devs) { + dev = list_entry(entry, struct mii_dev, link); + if (strcmp(dev->name, devname) == 0) + return dev; + } + + return NULL; +} + +/***************************************************************************** + * + * Initialize global data. Need to be called before any other miiphy routine. + */ +void miiphy_init(void) +{ + INIT_LIST_HEAD(&mii_devs); + current_mii = NULL; +} + +struct mii_dev *mdio_alloc(void) +{ + struct mii_dev *bus; + + bus = malloc(sizeof(*bus)); + if (!bus) + return bus; + + memset(bus, 0, sizeof(*bus)); + + /* initalize mii_dev struct fields */ + INIT_LIST_HEAD(&bus->link); + + return bus; +} + +void mdio_free(struct mii_dev *bus) +{ + free(bus); +} + +int mdio_register(struct mii_dev *bus) +{ + if (!bus || !bus->read || !bus->write) + return -1; + + /* check if we have unique name */ + if (miiphy_get_dev_by_name(bus->name)) { + printf("mdio_register: non unique device name '%s'\n", + bus->name); + return -1; + } + + /* add it to the list */ + list_add_tail(&bus->link, &mii_devs); + + if (!current_mii) + current_mii = bus; + + return 0; +} + +int mdio_register_seq(struct mii_dev *bus, int seq) +{ + int ret; + + /* Setup a unique name for each mdio bus */ + ret = snprintf(bus->name, MDIO_NAME_LEN, "eth%d", seq); + if (ret < 0) + return ret; + + return mdio_register(bus); +} + +int mdio_unregister(struct mii_dev *bus) +{ + if (!bus) + return 0; + + /* delete it from the list */ + list_del(&bus->link); + + if (current_mii == bus) + current_mii = NULL; + + return 0; +} + +void mdio_list_devices(void) +{ + struct list_head *entry; + + list_for_each(entry, &mii_devs) { + int i; + struct mii_dev *bus = list_entry(entry, struct mii_dev, link); + + printf("%s:\n", bus->name); + + for (i = 0; i < PHY_MAX_ADDR; i++) { + struct phy_device *phydev = bus->phymap[i]; + + if (phydev) { + printf("%x - %s", i, phydev->drv->name); + + if (phydev->dev) + printf(" <--> %s\n", phydev->dev->name); + else + printf("\n"); + } + } + } +} + +int miiphy_set_current_dev(const char *devname) +{ + struct mii_dev *dev; + + dev = miiphy_get_dev_by_name(devname); + if (dev) { + current_mii = dev; + return 0; + } + + printf("No such device: %s\n", devname); + + return 1; +} + +struct mii_dev *mdio_get_current_dev(void) +{ + return current_mii; +} + +struct list_head *mdio_get_list_head(void) +{ + return &mii_devs; +} + +struct phy_device *mdio_phydev_for_ethname(const char *ethname) +{ + struct list_head *entry; + struct mii_dev *bus; + + list_for_each(entry, &mii_devs) { + int i; + bus = list_entry(entry, struct mii_dev, link); + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (!bus->phymap[i] || !bus->phymap[i]->dev) + continue; + + if (strcmp(bus->phymap[i]->dev->name, ethname) == 0) + return bus->phymap[i]; + } + } + + printf("%s is not a known ethernet\n", ethname); + return NULL; +} + +const char *miiphy_get_current_dev(void) +{ + if (current_mii) + return current_mii->name; + + return NULL; +} + +static struct mii_dev *miiphy_get_active_dev(const char *devname) +{ + /* If the current mii is the one we want, return it */ + if (current_mii) + if (strcmp(current_mii->name, devname) == 0) + return current_mii; + + /* Otherwise, set the active one to the one we want */ + if (miiphy_set_current_dev(devname)) + return NULL; + else + return current_mii; +} + +/***************************************************************************** + * + * Read to variable <value> from the PHY attached to device <devname>, + * use PHY address <addr> and register <reg>. + * + * This API is deprecated. Use phy_read on a phy_device found via phy_connect + * + * Returns: + * 0 on success + */ +int miiphy_read(const char *devname, unsigned char addr, unsigned char reg, + unsigned short *value) +{ + struct mii_dev *bus; + int ret; + + bus = miiphy_get_active_dev(devname); + if (!bus) + return 1; + + ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg); + if (ret < 0) + return 1; + + *value = (unsigned short)ret; + return 0; +} + +/***************************************************************************** + * + * Write <value> to the PHY attached to device <devname>, + * use PHY address <addr> and register <reg>. + * + * This API is deprecated. Use phy_write on a phy_device found by phy_connect + * + * Returns: + * 0 on success + */ +int miiphy_write(const char *devname, unsigned char addr, unsigned char reg, + unsigned short value) +{ + struct mii_dev *bus; + + bus = miiphy_get_active_dev(devname); + if (bus) + return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value); + + return 1; +} + +/***************************************************************************** + * + * Print out list of registered MII capable devices. + */ +void miiphy_listdev(void) +{ + struct list_head *entry; + struct mii_dev *dev; + + puts("MII devices: "); + list_for_each(entry, &mii_devs) { + dev = list_entry(entry, struct mii_dev, link); + printf("'%s' ", dev->name); + } + puts("\n"); + + if (current_mii) + printf("Current device: '%s'\n", current_mii->name); +} + +/***************************************************************************** + * + * Read the OUI, manufacture's model number, and revision number. + * + * OUI: 22 bits (unsigned int) + * Model: 6 bits (unsigned char) + * Revision: 4 bits (unsigned char) + * + * This API is deprecated. + * + * Returns: + * 0 on success + */ +int miiphy_info(const char *devname, unsigned char addr, unsigned int *oui, + unsigned char *model, unsigned char *rev) +{ + unsigned int reg = 0; + unsigned short tmp; + + if (miiphy_read(devname, addr, MII_PHYSID2, &tmp) != 0) { + debug("PHY ID register 2 read failed\n"); + return -1; + } + reg = tmp; + + debug("MII_PHYSID2 @ 0x%x = 0x%04x\n", addr, reg); + + if (reg == 0xFFFF) { + /* No physical device present at this address */ + return -1; + } + + if (miiphy_read(devname, addr, MII_PHYSID1, &tmp) != 0) { + debug("PHY ID register 1 read failed\n"); + return -1; + } + reg |= tmp << 16; + debug("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg); + + *oui = (reg >> 10); + *model = (unsigned char)((reg >> 4) & 0x0000003F); + *rev = (unsigned char)(reg & 0x0000000F); + return 0; +} + +#ifndef CONFIG_PHYLIB +/***************************************************************************** + * + * Reset the PHY. + * + * This API is deprecated. Use PHYLIB. + * + * Returns: + * 0 on success + */ +int miiphy_reset(const char *devname, unsigned char addr) +{ + unsigned short reg; + int timeout = 500; + + if (miiphy_read(devname, addr, MII_BMCR, ®) != 0) { + debug("PHY status read failed\n"); + return -1; + } + if (miiphy_write(devname, addr, MII_BMCR, reg | BMCR_RESET) != 0) { + debug("PHY reset failed\n"); + return -1; + } +#ifdef CONFIG_PHY_RESET_DELAY + udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */ +#endif + /* + * Poll the control register for the reset bit to go to 0 (it is + * auto-clearing). This should happen within 0.5 seconds per the + * IEEE spec. + */ + reg = 0x8000; + while (((reg & 0x8000) != 0) && timeout--) { + if (miiphy_read(devname, addr, MII_BMCR, ®) != 0) { + debug("PHY status read failed\n"); + return -1; + } + udelay(1000); + } + if ((reg & 0x8000) == 0) { + return 0; + } else { + puts("PHY reset timed out\n"); + return -1; + } + return 0; +} +#endif /* !PHYLIB */ + +/***************************************************************************** + * + * Determine the ethernet speed (10/100/1000). Return 10 on error. + */ +int miiphy_speed(const char *devname, unsigned char addr) +{ + u16 bmcr, anlpar, adv; + +#if defined(CONFIG_PHY_GIGE) + u16 btsr; + + /* + * Check for 1000BASE-X. If it is supported, then assume that the speed + * is 1000. + */ + if (miiphy_is_1000base_x(devname, addr)) + return _1000BASET; + + /* + * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set. + */ + /* Check for 1000BASE-T. */ + if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) { + printf("PHY 1000BT status"); + goto miiphy_read_failed; + } + if (btsr != 0xFFFF && + (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD))) + return _1000BASET; +#endif /* CONFIG_PHY_GIGE */ + + /* Check Basic Management Control Register first. */ + if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) { + printf("PHY speed"); + goto miiphy_read_failed; + } + /* Check if auto-negotiation is on. */ + if (bmcr & BMCR_ANENABLE) { + /* Get auto-negotiation results. */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + printf("PHY AN speed"); + goto miiphy_read_failed; + } + + if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) { + puts("PHY AN adv speed"); + goto miiphy_read_failed; + } + return ((anlpar & adv) & LPA_100) ? _100BASET : _10BASET; + } + /* Get speed from basic control settings. */ + return (bmcr & BMCR_SPEED100) ? _100BASET : _10BASET; + +miiphy_read_failed: + printf(" read failed, assuming 10BASE-T\n"); + return _10BASET; +} + +/***************************************************************************** + * + * Determine full/half duplex. Return half on error. + */ +int miiphy_duplex(const char *devname, unsigned char addr) +{ + u16 bmcr, anlpar, adv; + +#if defined(CONFIG_PHY_GIGE) + u16 btsr; + + /* Check for 1000BASE-X. */ + if (miiphy_is_1000base_x(devname, addr)) { + /* 1000BASE-X */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + printf("1000BASE-X PHY AN duplex"); + goto miiphy_read_failed; + } + } + /* + * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set. + */ + /* Check for 1000BASE-T. */ + if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) { + printf("PHY 1000BT status"); + goto miiphy_read_failed; + } + if (btsr != 0xFFFF) { + if (btsr & PHY_1000BTSR_1000FD) { + return FULL; + } else if (btsr & PHY_1000BTSR_1000HD) { + return HALF; + } + } +#endif /* CONFIG_PHY_GIGE */ + + /* Check Basic Management Control Register first. */ + if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) { + puts("PHY duplex"); + goto miiphy_read_failed; + } + /* Check if auto-negotiation is on. */ + if (bmcr & BMCR_ANENABLE) { + /* Get auto-negotiation results. */ + if (miiphy_read(devname, addr, MII_LPA, &anlpar)) { + puts("PHY AN duplex"); + goto miiphy_read_failed; + } + + if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) { + puts("PHY AN adv duplex"); + goto miiphy_read_failed; + } + return ((anlpar & adv) & (LPA_10FULL | LPA_100FULL)) ? + FULL : HALF; + } + /* Get speed from basic control settings. */ + return (bmcr & BMCR_FULLDPLX) ? FULL : HALF; + +miiphy_read_failed: + printf(" read failed, assuming half duplex\n"); + return HALF; +} + +/***************************************************************************** + * + * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/ + * 1000BASE-T, or on error. + */ +int miiphy_is_1000base_x(const char *devname, unsigned char addr) +{ +#if defined(CONFIG_PHY_GIGE) + u16 exsr; + + if (miiphy_read(devname, addr, MII_ESTATUS, &exsr)) { + printf("PHY extended status read failed, assuming no " + "1000BASE-X\n"); + return 0; + } + return 0 != (exsr & (ESTATUS_1000XF | ESTATUS_1000XH)); +#else + return 0; +#endif +} + +#ifdef CONFIG_SYS_FAULT_ECHO_LINK_DOWN +/***************************************************************************** + * + * Determine link status + */ +int miiphy_link(const char *devname, unsigned char addr) +{ + unsigned short reg; + + /* dummy read; needed to latch some phys */ + (void)miiphy_read(devname, addr, MII_BMSR, ®); + if (miiphy_read(devname, addr, MII_BMSR, ®)) { + puts("MII_BMSR read failed, assuming no link\n"); + return 0; + } + + /* Determine if a link is active */ + if ((reg & BMSR_LSTATUS) != 0) { + return 1; + } else { + return 0; + } +} +#endif diff --git a/roms/u-boot/common/qfw.c b/roms/u-boot/common/qfw.c new file mode 100644 index 000000000..90cbb8c5d --- /dev/null +++ b/roms/u-boot/common/qfw.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> + */ + +#include <dm.h> +#include <dm/uclass.h> +#include <qfw.h> +#include <stdlib.h> + +int qfw_get_dev(struct udevice **devp) +{ + return uclass_first_device_err(UCLASS_QFW, devp); +} + +int qfw_online_cpus(struct udevice *dev) +{ + u16 nb_cpus; + + qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus); + + return le16_to_cpu(nb_cpus); +} + +int qfw_read_firmware_list(struct udevice *dev) +{ + int i; + u32 count; + struct fw_file *file; + struct list_head *entry; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + /* don't read it twice */ + if (!list_empty(&qdev->fw_list)) + return 0; + + qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count); + if (!count) + return 0; + + count = be32_to_cpu(count); + for (i = 0; i < count; i++) { + file = malloc(sizeof(*file)); + if (!file) { + printf("error: allocating resource\n"); + goto err; + } + qfw_read_entry(dev, FW_CFG_INVALID, + sizeof(struct fw_cfg_file), &file->cfg); + file->addr = 0; + list_add_tail(&file->list, &qdev->fw_list); + } + + return 0; + +err: + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + free(file); + } + + return -ENOMEM; +} + +struct fw_file *qfw_find_file(struct udevice *dev, const char *name) +{ + struct list_head *entry; + struct fw_file *file; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + if (!strcmp(file->cfg.name, name)) + return file; + } + + return NULL; +} + +struct fw_file *qfw_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter) +{ + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + iter->entry = qdev->fw_list.next; + iter->end = &qdev->fw_list; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter) +{ + iter->entry = ((struct list_head *)iter->entry)->next; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +bool qfw_file_iter_end(struct fw_cfg_file_iter *iter) +{ + return iter->entry == iter->end; +} diff --git a/roms/u-boot/common/s_record.c b/roms/u-boot/common/s_record.c new file mode 100644 index 000000000..2b7651fcf --- /dev/null +++ b/roms/u-boot/common/s_record.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <s_record.h> + +static int hex1_bin (char c); +static int hex2_bin (char *s); + +int srec_decode (char *input, int *count, ulong *addr, char *data) +{ + int i; + int v; /* conversion buffer */ + int srec_type; /* S-Record type */ + unsigned char chksum; /* buffer for checksum */ + + chksum = 0; + + /* skip anything before 'S', and the 'S' itself. + * Return error if not found + */ + + for (; *input; ++input) { + if (*input == 'S') { /* skip 'S' */ + ++input; + break; + } + } + if (*input == '\0') { /* no more data? */ + return (SREC_EMPTY); + } + + v = *input++; /* record type */ + + if ((*count = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + chksum += *count; + input += 2; + + switch (v) { /* record type */ + + case '0': /* start record */ + srec_type = SREC_START; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '1': + srec_type = SREC_DATA2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '2': + srec_type = SREC_DATA3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '3': /* data record with a */ + srec_type = SREC_DATA4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; +/*** case '4' ***/ + case '5': /* count record, addr field contains */ + srec_type = SREC_COUNT; /* a 2 byte record counter */ + *count = 0; /* no data */ + break; +/*** case '6' -- not used ***/ + case '7': /* end record with a */ + srec_type = SREC_END4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; + case '8': /* end record with a */ + srec_type = SREC_END3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '9': /* end record with a */ + srec_type = SREC_END2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + default: + return (SREC_E_BADTYPE); + } + + /* read address field */ + *addr = 0; + + switch (v) { + case '3': /* 4 byte addr field */ + case '7': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '2': /* 3 byte addr field */ + case '8': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '0': /* 2 byte addr field */ + case '1': + case '5': + case '9': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + break; + default: + return (SREC_E_BADTYPE); + } + + /* convert data and calculate checksum */ + for (i=0; i < *count; ++i) { + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + data[i] = v; + chksum += v; + input += 2; + } + + /* read anc check checksum */ + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + if ((unsigned char)v != (unsigned char)~chksum) { + return (SREC_E_BADCHKS); + } + + return (srec_type); +} + +static int hex1_bin (char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c + 10 - 'a'); + if (c >= 'A' && c <= 'F') + return (c + 10 - 'A'); + return (-1); +} + +static int hex2_bin (char *s) +{ + int i, j; + + if ((i = hex1_bin(*s++)) < 0) { + return (-1); + } + if ((j = hex1_bin(*s)) < 0) { + return (-1); + } + + return ((i<<4) + j); +} diff --git a/roms/u-boot/common/scp03.c b/roms/u-boot/common/scp03.c new file mode 100644 index 000000000..09ef7b5ba --- /dev/null +++ b/roms/u-boot/common/scp03.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021, Foundries.IO + * + */ + +#include <common.h> +#include <scp03.h> +#include <tee.h> +#include <tee/optee_ta_scp03.h> + +static int scp03_enable(bool provision) +{ + const struct tee_optee_ta_uuid uuid = PTA_SCP03_UUID; + struct tee_open_session_arg session; + struct tee_invoke_arg invoke; + struct tee_param param; + struct udevice *tee = NULL; + + tee = tee_find_device(tee, NULL, NULL, NULL); + if (!tee) + return -ENODEV; + + memset(&session, 0, sizeof(session)); + tee_optee_ta_uuid_to_octets(session.uuid, &uuid); + if (tee_open_session(tee, &session, 0, NULL)) + return -ENXIO; + + memset(¶m, 0, sizeof(param)); + param.attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param.u.value.a = provision; + + memset(&invoke, 0, sizeof(invoke)); + invoke.func = PTA_CMD_ENABLE_SCP03; + invoke.session = session.session; + + if (tee_invoke_func(tee, &invoke, 1, ¶m)) + return -EIO; + + tee_close_session(tee, session.session); + + return 0; +} + +int tee_enable_scp03(void) +{ + return scp03_enable(false); +} + +int tee_provision_scp03(void) +{ + return scp03_enable(true); +} diff --git a/roms/u-boot/common/spl/Kconfig b/roms/u-boot/common/spl/Kconfig new file mode 100644 index 000000000..fa80524cf --- /dev/null +++ b/roms/u-boot/common/spl/Kconfig @@ -0,0 +1,1639 @@ +menu "SPL / TPL" + +config SUPPORT_SPL + bool + +config SUPPORT_TPL + bool + +config SPL_DFU_NO_RESET + bool + +config SPL + bool + depends on SUPPORT_SPL + prompt "Enable SPL" + help + If you want to build SPL as well as the normal image, say Y. + +config SPL_FRAMEWORK + bool "Support SPL based upon the common SPL framework" + depends on SPL + default y + help + Enable the SPL framework under common/spl/. This framework + supports MMC, NAND and YMODEM and other methods loading of U-Boot + and the Linux Kernel. If unsure, say Y. + +config SPL_FRAMEWORK_BOARD_INIT_F + bool "Define a generic function board_init_f" + depends on SPL_FRAMEWORK + help + Define a generic function board_init_f that: + - initialize the spl (spl_early_init) + - initialize the serial (preloader_console_init) + Unless you want to provide your own board_init_f, you should say Y. + +config SPL_SIZE_LIMIT + hex "Maximum size of SPL image" + depends on SPL + default 0x11000 if ARCH_MX6 && !MX6_OCRAM_256KB + default 0x31000 if ARCH_MX6 && MX6_OCRAM_256KB + default 0x0 + help + Specifies the maximum length of the U-Boot SPL image. + If this value is zero, it is ignored. + +config SPL_SIZE_LIMIT_SUBTRACT_GD + bool "SPL image size check: provide space for global data" + depends on SPL_SIZE_LIMIT > 0 + help + If enabled, aligned size of global data is reserved in + SPL_SIZE_LIMIT check to ensure such an image does not overflow SRAM + if SPL_SIZE_LIMIT describes the size of SRAM available for SPL when + pre-reloc global data is put into this SRAM, too. + +config SPL_SIZE_LIMIT_SUBTRACT_MALLOC + bool "SPL image size check: provide space for malloc() pool before relocation" + depends on SPL_SIZE_LIMIT > 0 + help + If enabled, SPL_SYS_MALLOC_F_LEN is reserved in SPL_SIZE_LIMIT check + to ensure such an image does not overflow SRAM if SPL_SIZE_LIMIT + describes the size of SRAM available for SPL when pre-reloc malloc + pool is put into this SRAM, too. + +config SPL_SIZE_LIMIT_PROVIDE_STACK + hex "SPL image size check: provide stack space before relocation" + depends on SPL_SIZE_LIMIT > 0 + default 0 + help + If set, this size is reserved in SPL_SIZE_LIMIT check to ensure such + an image does not overflow SRAM if SPL_SIZE_LIMIT describes the size + of SRAM available for SPL when the stack required before reolcation + uses this SRAM, too. + +config SPL_SYS_STACK_F_CHECK_BYTE + hex + default 0xaa + help + Constant used to check the stack + +config SPL_SYS_REPORT_STACK_F_USAGE + depends on SPL_SIZE_LIMIT_PROVIDE_STACK != 0 + bool "Check and report stack usage in SPL before relocation" + help + If this option is enabled, the initial SPL stack is filled with 0xaa + very early, up to the size configured with + SPL_SIZE_LIMIT_PROVIDE_STACK. + Later when SPL is done using this initial stack and switches to a + stack in DRAM, the actually used size of this initial stack is + reported by examining the memory and searching for the lowest + occurrence of non 0xaa bytes. + This default implementation works for stacks growing down only. + +menu "PowerPC and LayerScape SPL Boot options" + +config SPL_NAND_BOOT + bool "Load SPL from NAND flash" + depends on PPC && (SUPPORT_SPL && !SPL_FRAMEWORK) + +config SPL_MMC_BOOT + bool "Load SPL from SD Card / eMMC" + depends on PPC && (SUPPORT_SPL && !SPL_FRAMEWORK) + +config SPL_SPI_BOOT + bool "Load SPL from SPI flash" + depends on PPC && (SUPPORT_SPL && !SPL_FRAMEWORK) + +config SPL_FSL_PBL + bool "Create SPL in Freescale PBI format" + depends on (PPC || ARCH_LS1021A || ARCH_LS1043A || ARCH_LS1046A) && \ + SUPPORT_SPL + help + Create boot binary having SPL binary in PBI format concatenated with + u-boot binary. + +endmenu + +config HANDOFF + bool "Pass hand-off information from SPL to U-Boot proper" + depends on SPL && BLOBLIST + help + It is useful to be able to pass information from SPL to U-Boot + proper to preserve state that is known in SPL and is needed in U-Boot. + Enable this to locate the handoff information in U-Boot proper, early + in boot. It is available in gd->handoff. The state state is set up + in SPL (or TPL if that is being used). + +if SPL + +config SPL_HANDOFF + bool "Pass hand-off information from SPL to U-Boot proper" + depends on HANDOFF && SPL_BLOBLIST + default y + help + This option enables SPL to write handoff information. This can be + used to pass information like the size of SDRAM from SPL to U-Boot + proper. Also SPL can receive information from TPL in the same place + if that is enabled. + +config SPL_LDSCRIPT + string "Linker script for the SPL stage" + default "arch/\$(ARCH)/cpu/u-boot-spl.lds" + help + The SPL stage will usually require a different linker-script + (as it runs from a different memory region) than the regular + U-Boot stage. Set this to the path of the linker-script to + be used for SPL. + +config SPL_TEXT_BASE + hex "SPL Text Base" + default ISW_ENTRY_ADDR if AM43XX || AM33XX || OMAP54XX || ARCH_KEYSTONE + default 0x10060 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN9I + default 0x20060 if SUN50I_GEN_H6 + default 0x00060 if ARCH_SUNXI + default 0xfffc0000 if ARCH_ZYNQMP + default 0x0 + help + The address in memory that SPL will be running from. + +config SPL_BOARD_INIT + bool "Call board-specific initialization in SPL" + help + If this option is enabled, U-Boot will call the function + spl_board_init() from board_init_r(). This function should be + provided by the board. + +config SPL_BOOTROM_SUPPORT + bool "Support returning to the BOOTROM" + help + Some platforms (e.g. the Rockchip RK3368) provide support in their + ROM for loading the next boot-stage after performing basic setup + from the SPL stage. + + Enable this option, to return to the BOOTROM through the + BOOT_DEVICE_BOOTROM (or fall-through to the next boot device in the + boot device list, if not implemented for a given board) + +config SPL_BOOTCOUNT_LIMIT + bool "Support bootcount in SPL" + depends on SPL_ENV_SUPPORT && !TPL_BOOTCOUNT_LIMIT + help + On some boards, which use 'falcon' mode, it is necessary to check + and increment the number of boot attempts. Such boards do not + use proper U-Boot for normal boot flow and hence needs those + adjustments to be done in the SPL. + +config SPL_RAW_IMAGE_SUPPORT + bool "Support SPL loading and booting of RAW images" + default n if (ARCH_MX6 && (SPL_MMC_SUPPORT || SPL_SATA_SUPPORT)) + default y if !TI_SECURE_DEVICE + help + SPL will support loading and booting a RAW image when this option + is y. If this is not set, SPL will move on to other available + boot media to find a suitable image. + +config SPL_LEGACY_IMAGE_SUPPORT + bool "Support SPL loading and booting of Legacy images" + default y if !TI_SECURE_DEVICE && !SPL_LOAD_FIT + help + SPL will support loading and booting Legacy images when this option + is y. If this is not set, SPL will move on to other available + boot media to find a suitable image. + +config SPL_LEGACY_IMAGE_CRC_CHECK + bool "Check CRC of Legacy images" + depends on SPL_LEGACY_IMAGE_SUPPORT + select SPL_CRC32_SUPPORT + help + Enable this to check the CRC of Legacy images. While this increases + reliability, it affects both code size and boot duration. + If disabled, Legacy images are booted if the image magic and size + are correct, without further integrity checks. + +config SPL_SYS_MALLOC_SIMPLE + bool + prompt "Only use malloc_simple functions in the SPL" + help + Say Y here to only use the *_simple malloc functions from + malloc_simple.c, rather then using the versions from dlmalloc.c; + this will make the SPL binary smaller at the cost of more heap + usage as the *_simple malloc functions do not re-use free-ed mem. + +config TPL_SYS_MALLOC_SIMPLE + bool + prompt "Only use malloc_simple functions in the TPL" + depends on TPL + help + Say Y here to only use the *_simple malloc functions from + malloc_simple.c, rather then using the versions from dlmalloc.c; + this will make the TPL binary smaller at the cost of more heap + usage as the *_simple malloc functions do not re-use free-ed mem. + +config SPL_STACK_R + bool "Enable SDRAM location for SPL stack" + help + SPL starts off execution in SRAM and thus typically has only a small + stack available. Since SPL sets up DRAM while in its board_init_f() + function, it is possible for the stack to move there before + board_init_r() is reached. This option enables a special SDRAM + location for the SPL stack. U-Boot SPL switches to this after + board_init_f() completes, and before board_init_r() starts. + +config SPL_STACK_R_ADDR + depends on SPL_STACK_R + hex "SDRAM location for SPL stack" + default 0x82000000 if ARCH_OMAP2PLUS + help + Specify the address in SDRAM for the SPL stack. This will be set up + before board_init_r() is called. + +config SPL_STACK_R_MALLOC_SIMPLE_LEN + depends on SPL_STACK_R && SPL_SYS_MALLOC_SIMPLE + hex "Size of malloc_simple heap after switching to DRAM SPL stack" + default 0x100000 + help + Specify the amount of the stack to use as memory pool for + malloc_simple after switching the stack to DRAM. This may be set + to give board_init_r() a larger heap then the initial heap in + SRAM which is limited to SYS_MALLOC_F_LEN bytes. + +config SPL_SEPARATE_BSS + bool "BSS section is in a different memory region from text" + help + Some platforms need a large BSS region in SPL and can provide this + because RAM is already set up. In this case BSS can be moved to RAM. + This option should then be enabled so that the correct device tree + location is used. Normally we put the device tree at the end of BSS + but with this option enabled, it goes at _image_binary_end. + +config SPL_READ_ONLY + bool + depends on SPL_OF_PLATDATA + # Bind cannot be supported because the udevice structs are in read-only + # memory so we cannot update the linked lists. + select SPL_OF_PLATDATA_NO_BIND + select SPL_OF_PLATDATA_RT + help + Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only + section of memory. This means that of-platdata must make a copy (in + writeable memory) of anything it wants to modify, such as + device-private data. + +config SPL_BANNER_PRINT + bool "Enable output of the SPL banner 'U-Boot SPL ...'" + default y + help + If this option is enabled, SPL will print the banner with version + info. Disabling this option could be useful to reduce SPL boot time + (e.g. approx. 6 ms faster, when output on i.MX6 with 115200 baud). + +config TPL_BANNER_PRINT + bool "Enable output of the TPL banner 'U-Boot TPL ...'" + depends on TPL + default y + help + If this option is enabled, TPL will print the banner with version + info. Disabling this option could be useful to reduce TPL boot time + (e.g. approx. 6 ms faster, when output on i.MX6 with 115200 baud). + +config SPL_EARLY_BSS + depends on ARM && !ARM64 + bool "Allows initializing BSS early before entering board_init_f" + help + On some platform we have sufficient memory available early on to + allow setting up and using a basic BSS prior to entering + board_init_f. Activating this option will also de-activate the + clearing of BSS during the SPL relocation process, thus allowing + to carry state from board_init_f to board_init_r by way of BSS. + +config SPL_DISPLAY_PRINT + bool "Display a board-specific message in SPL" + help + If this option is enabled, U-Boot will call the function + spl_display_print() immediately after displaying the SPL console + banner ("U-Boot SPL ..."). This function should be provided by + the board. + +config SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR + bool "MMC raw mode: by sector" + default y if ARCH_SUNXI || ARCH_DAVINCI || ARCH_UNIPHIER || \ + ARCH_MX6 || ARCH_MX7 || \ + ARCH_ROCKCHIP || ARCH_MVEBU || ARCH_SOCFPGA || \ + ARCH_AT91 || ARCH_ZYNQ || ARCH_KEYSTONE || OMAP34XX || \ + OMAP44XX || OMAP54XX || AM33XX || AM43XX || \ + TARGET_SIFIVE_UNLEASHED || TARGET_SIFIVE_UNMATCHED + help + Use sector number for specifying U-Boot location on MMC/SD in + raw mode. + +config SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR + hex "Address on the MMC to load U-Boot from" + depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR + default 0x40 if ARCH_SUNXI + default 0x75 if ARCH_DAVINCI + default 0x8a if ARCH_MX6 || ARCH_MX7 + default 0x100 if ARCH_UNIPHIER + default 0x140 if ARCH_MVEBU + default 0x200 if ARCH_SOCFPGA || ARCH_AT91 + default 0x300 if ARCH_ZYNQ || ARCH_KEYSTONE || OMAP34XX || OMAP44XX || \ + OMAP54XX || AM33XX || AM43XX || ARCH_K3 + default 0x4000 if ARCH_ROCKCHIP + default 0x822 if TARGET_SIFIVE_UNLEASHED || TARGET_SIFIVE_UNMATCHED + help + Address on the MMC to load U-Boot from, when the MMC is being used + in raw mode. Units: MMC sectors (1 sector = 512 bytes). + +config SYS_MMCSD_RAW_MODE_U_BOOT_DATA_PART_OFFSET + hex "U-Boot main hardware partition image offset" + depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR + default 0x10 if ARCH_SUNXI + default 0x0 + help + On some platforms SPL location depends on hardware partition. The ROM + code skips the MBR sector when loading SPL from main hardware data + partition. This adds offset to the main U-Boot image. Set this symbol + to the number of skipped sectors. + + If unsure, leave the default. + +config SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION + bool "MMC Raw mode: by partition" + help + Use a partition for loading U-Boot when using MMC/SD in raw mode. + +config SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION + hex "Partition to use to load U-Boot from" + depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION + default 1 + help + Partition on the MMC to load U-Boot from when the MMC is being + used in raw mode + +config SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION_TYPE + bool "MMC raw mode: by partition type" + depends on DOS_PARTITION && SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION + help + Use partition type for specifying U-Boot partition on MMC/SD in + raw mode. U-Boot will be loaded from the first partition of this + type to be found. + +config SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION_TYPE + hex "Partition Type on the MMC to load U-Boot from" + depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION_TYPE + help + Partition Type on the MMC to load U-Boot from, when the MMC is being + used in raw mode. + +config SUPPORT_EMMC_BOOT_OVERRIDE_PART_CONFIG + bool "Override eMMC EXT_CSC_PART_CONFIG by user defined partition" + depends on SUPPORT_EMMC_BOOT + help + eMMC boot partition is normally configured by the bits of the EXT_CSD + register (EXT_CSC_PART_CONFIG), BOOT_PARTITION_ENABLE field. In some + cases it might be required in SPL to load the image from different + partition than the partition selected by EXT_CSC_PART_CONFIG register. + Enable this option if you intend to use an eMMC boot partition other + then selected via EXT_CSC_PART_CONFIG register and specify the custom + partition number by the CONFIG_SYS_MMCSD_RAW_MODE_EMMC_BOOT_PARTITION + option. + +config SYS_MMCSD_RAW_MODE_EMMC_BOOT_PARTITION + int "Number of the eMMC boot partition to use" + depends on SUPPORT_EMMC_BOOT_OVERRIDE_PART_CONFIG + default 1 + help + eMMC boot partition number to use when the eMMC in raw mode and + the eMMC EXT_CSC_PART_CONFIG selection should be overridden in SPL + by user defined partition number. + +config SPL_CRC32_SUPPORT + bool "Support CRC32" + default y if SPL_LEGACY_IMAGE_SUPPORT + help + Enable this to support CRC32 in uImages or FIT images within SPL. + This is a 32-bit checksum value that can be used to verify images. + For FIT images, this is the least secure type of checksum, suitable + for detected accidental image corruption. For secure applications you + should consider SHA1 or SHA256. + +config SPL_MD5_SUPPORT + bool "Support MD5" + depends on SPL_FIT + help + Enable this to support MD5 in FIT images within SPL. An MD5 + checksum is a 128-bit hash value used to check that the image + contents have not been corrupted. Note that MD5 is not considered + secure as it is possible (with a brute-force attack) to adjust the + image while still retaining the same MD5 hash value. For secure + applications where images may be changed maliciously, you should + consider SHA256 or SHA384. + +config SPL_SHA1_SUPPORT + bool "Support SHA1" + depends on SPL_FIT + select SHA1 + help + Enable this to support SHA1 in FIT images within SPL. A SHA1 + checksum is a 160-bit (20-byte) hash value used to check that the + image contents have not been corrupted or maliciously altered. + While SHA1 is fairly secure it is coming to the end of its life + due to the expanding computing power available to brute-force + attacks. For more security, consider SHA256 or SHA384. + +config SPL_SHA256_SUPPORT + bool "Support SHA256" + depends on SPL_FIT + select SHA256 + help + Enable this to support SHA256 in FIT images within SPL. A SHA256 + checksum is a 256-bit (32-byte) hash value used to check that the + image contents have not been corrupted. + +config SPL_SHA384_SUPPORT + bool "Support SHA384" + depends on SPL_FIT + select SHA384 + select SHA512_ALGO + help + Enable this to support SHA384 in FIT images within SPL. A SHA384 + checksum is a 384-bit (48-byte) hash value used to check that the + image contents have not been corrupted. Use this for the highest + security. + +config SPL_SHA512_SUPPORT + bool "Support SHA512" + depends on SPL_FIT + select SHA512 + select SHA512_ALGO + help + Enable this to support SHA512 in FIT images within SPL. A SHA512 + checksum is a 512-bit (64-byte) hash value used to check that the + image contents have not been corrupted. + +config SPL_FIT_IMAGE_TINY + bool "Remove functionality from SPL FIT loading to reduce size" + depends on SPL_FIT + default y if MACH_SUN50I || MACH_SUN50I_H5 || SUN50I_GEN_H6 + default y if ARCH_IMX8M + help + Enable this to reduce the size of the FIT image loading code + in SPL, if space for the SPL binary is very tight. + + This skips the recording of each loaded payload + (i.e. loadable) into the FDT (modifying the loaded FDT to + ensure this information is available to the next image + invoked). + +config SPL_CACHE_SUPPORT + bool "Support CACHE drivers" + help + Enable CACHE drivers in SPL. These drivers can keep data so that + future requests for that data can be served faster. Enable this option + to build the drivers in drivers/cache as part of an SPL build. + +config SPL_CPU + bool "Support CPU drivers" + help + Enable this to support CPU drivers in SPL. These drivers can set + up CPUs and provide information about them such as the model and + name. This can be useful in SPL since setting up the CPUs earlier + may improve boot performance. Enable this option to build the + drivers in drivers/cpu as part of an SPL build. + +config SPL_CRYPTO_SUPPORT + bool "Support crypto drivers" + help + Enable crypto drivers in SPL. These drivers can be used to + accelerate secure boot processing in secure applications. Enable + this option to build the drivers in drivers/crypto as part of an + SPL build. + +config SPL_HASH_SUPPORT + bool "Support hashing drivers" + select SHA1 + select SHA256 + help + Enable hashing drivers in SPL. These drivers can be used to + accelerate secure boot processing in secure applications. Enable + this option to build system-specific drivers for hash acceleration + as part of an SPL build. + +config TPL_HASH_SUPPORT + bool "Support hashing drivers in TPL" + depends on TPL + select SHA1 + select SHA256 + help + Enable hashing drivers in SPL. These drivers can be used to + accelerate secure boot processing in secure applications. Enable + this option to build system-specific drivers for hash acceleration + as part of an SPL build. + +config SPL_DMA + bool "Support DMA drivers" + help + Enable DMA (direct-memory-access) drivers in SPL. These drivers + can be used to handle memory-to-peripheral data transfer without + the CPU moving the data. Enable this option to build the drivers + in drivers/dma as part of an SPL build. + +config SPL_DRIVERS_MISC_SUPPORT + bool "Support misc drivers" + help + Enable miscellaneous drivers in SPL. These drivers perform various + tasks that don't fall nicely into other categories, Enable this + option to build the drivers in drivers/misc as part of an SPL + build, for those that support building in SPL (not all drivers do). + +config SPL_ENV_SUPPORT + bool "Support an environment" + help + Enable environment support in SPL. The U-Boot environment provides + a number of settings (essentially name/value pairs) which can + control many aspects of U-Boot's operation. Normally this is not + needed in SPL as it has a much simpler task with less + configuration. But some boards use this to support 'Falcon' boot + on EXT2 and FAT, where SPL boots directly into Linux without + starting U-Boot first. Enabling this option will make env_get() + and env_set() available in SPL. + +config SPL_SAVEENV + bool "Support save environment" + depends on SPL_ENV_SUPPORT + select SPL_MMC_WRITE if ENV_IS_IN_MMC + help + Enable save environment support in SPL after setenv. By default + the saveenv option is not provided in SPL, but some boards need + this support in 'Falcon' boot, where SPL need to boot from + different images based on environment variable set by OS. For + example OS may set "reboot_image" environment variable to + "recovery" inorder to boot recovery image by SPL. The SPL read + "reboot_image" and act accordingly and change the reboot_image + to default mode using setenv and save the environment. + +config SPL_ETH_SUPPORT + bool "Support Ethernet" + depends on SPL_ENV_SUPPORT + help + Enable access to the network subsystem and associated Ethernet + drivers in SPL. This permits SPL to load U-Boot over an Ethernet + link rather than from an on-board peripheral. Environment support + is required since the network stack uses a number of environment + variables. See also SPL_NET_SUPPORT. + +config SPL_FS_EXT4 + bool "Support EXT filesystems" + help + Enable support for EXT2/3/4 filesystems with SPL. This permits + U-Boot (or Linux in Falcon mode) to be loaded from an EXT + filesystem from within SPL. Support for the underlying block + device (e.g. MMC or USB) must be enabled separately. + +config SPL_FS_SQUASHFS + bool "Support SquashFS filesystems" + select FS_SQUASHFS + help + Enable support for SquashFS filesystems with SPL. This permits + U-Boot (or Linux in Falcon mode) to be loaded from a SquashFS + filesystem from within SPL. Support for the underlying block + device (e.g. MMC or USB) must be enabled separately. + +config SPL_FS_FAT + bool "Support FAT filesystems" + select FS_FAT + help + Enable support for FAT and VFAT filesystems with SPL. This + permits U-Boot (or Linux in Falcon mode) to be loaded from a FAT + filesystem from within SPL. Support for the underlying block + device (e.g. MMC or USB) must be enabled separately. + +config SPL_FAT_WRITE + bool "Support write for FAT filesystems" + help + Enable write support for FAT and VFAT filesystems with SPL. + Support for the underlying block device (e.g. MMC or USB) must be + enabled separately. + +config SPL_FPGA + bool "Support FPGAs" + help + Enable support for FPGAs in SPL. Field-programmable Gate Arrays + provide software-configurable hardware which is typically used to + implement peripherals (such as UARTs, LCD displays, MMC) or + accelerate custom processing functions, such as image processing + or machine learning. Sometimes it is useful to program the FPGA + as early as possible during boot, and this option can enable that + within SPL. + +config SPL_GPIO_SUPPORT + bool "Support GPIO in SPL" + help + Enable support for GPIOs (General-purpose Input/Output) in SPL. + GPIOs allow U-Boot to read the state of an input line (high or + low) and set the state of an output line. This can be used to + drive LEDs, control power to various system parts and read user + input. GPIOs can be useful in SPL to enable a 'sign-of-life' LED, + for example. Enable this option to build the drivers in + drivers/gpio as part of an SPL build. + +config SPL_I2C_SUPPORT + bool "Support I2C" + help + Enable support for the I2C (Inter-Integrated Circuit) bus in SPL. + I2C works with a clock and data line which can be driven by a + one or more masters or slaves. It is a fairly complex bus but is + widely used as it only needs two lines for communication. Speeds of + 400kbps are typical but up to 3.4Mbps is supported by some + hardware. I2C can be useful in SPL to configure power management + ICs (PMICs) before raising the CPU clock speed, for example. + Enable this option to build the drivers in drivers/i2c as part of + an SPL build. + +config SPL_LIBCOMMON_SUPPORT + bool "Support common libraries" + help + Enable support for common U-Boot libraries within SPL. These + libraries include common code to deal with U-Boot images, + environment and USB, for example. This option is enabled on many + boards. Enable this option to build the code in common/ as part of + an SPL build. + +config SPL_LIBDISK_SUPPORT + bool "Support disk partitions" + select PARTITIONS + help + Enable support for disk partitions within SPL. 'Disk' is something + of a misnomer as it includes non-spinning media such as flash (as + used in MMC and USB sticks). Partitions provide a way for a disk + to be split up into separate regions, with a partition table placed + at the start or end which describes the location and size of each + 'partition'. These partitions are typically uses as individual block + devices, typically with an EXT2 or FAT filesystem in each. This + option enables whatever partition support has been enabled in + U-Boot to also be used in SPL. It brings in the code in disk/. + +config SPL_LIBGENERIC_SUPPORT + bool "Support generic libraries" + help + Enable support for generic U-Boot libraries within SPL. These + libraries include generic code to deal with device tree, hashing, + printf(), compression and the like. This option is enabled on many + boards. Enable this option to build the code in lib/ as part of an + SPL build. + +config SPL_DM_MAILBOX + bool "Support Mailbox" + help + Enable support for Mailbox within SPL. This enable the inter + processor communication protocols tobe used within SPL. Enable + this option to build the drivers in drivers/mailbox as part of + SPL build. + +config SPL_MMC_SUPPORT + bool "Support MMC" + depends on MMC + select HAVE_BLOCK_DEVICE + help + Enable support for MMC (Multimedia Card) within SPL. This enables + the MMC protocol implementation and allows any enabled drivers to + be used within SPL. MMC can be used with or without disk partition + support depending on the application (SPL_LIBDISK_SUPPORT). Enable + this option to build the drivers in drivers/mmc as part of an SPL + build. + +config SYS_MMCSD_FS_BOOT_PARTITION + int "MMC Boot Partition" + default 1 + help + Partition on the MMC to load U-Boot from when the MMC is being + used in fs mode + +config SPL_MMC_TINY + bool "Tiny MMC framework in SPL" + depends on SPL_MMC_SUPPORT + default n + help + Enable MMC framework tinification support. This option is useful if + if your SPL is extremely size constrained. Heed the warning, enable + this option if and only if you know exactly what you are doing, if + you are reading this help text, you most likely have no idea :-) + + The MMC framework is reduced to bare minimum to be useful. No malloc + support is needed for the MMC framework operation with this option + enabled. The framework supports exactly one MMC device and exactly + one MMC driver. The MMC driver can be adjusted to avoid any malloc + operations too, which can remove the need for malloc support in SPL + and thus further reduce footprint. + +config SPL_MMC_WRITE + bool "MMC/SD/SDIO card support for write operations in SPL" + depends on SPL_MMC_SUPPORT + default n + help + Enable write access to MMC and SD Cards in SPL + + +config SPL_MPC8XXX_INIT_DDR_SUPPORT + bool "Support MPC8XXX DDR init" + help + Enable support for DDR-SDRAM (double-data-rate synchronous dynamic + random-access memory) on the MPC8XXX family within SPL. This + allows DRAM to be set up before loading U-Boot into that DRAM, + where it can run. + +config SPL_MTD_SUPPORT + bool "Support MTD drivers" + help + Enable support for MTD (Memory Technology Device) within SPL. MTD + provides a block interface over raw NAND and can also be used with + SPI flash. This allows SPL to load U-Boot from supported MTD + devices. See SPL_NAND_SUPPORT and SPL_ONENAND_SUPPORT for how + to enable specific MTD drivers. + +config SPL_MUSB_NEW_SUPPORT + bool "Support new Mentor Graphics USB" + help + Enable support for Mentor Graphics USB in SPL. This is a new + driver used by some boards. Enable this option to build + the drivers in drivers/usb/musb-new as part of an SPL build. The + old drivers are in drivers/usb/musb. + +config SPL_NAND_SUPPORT + bool "Support NAND flash" + help + Enable support for NAND (Negative AND) flash in SPL. NAND flash + can be used to allow SPL to load U-Boot from supported devices. + This enables the drivers in drivers/mtd/nand/raw as part of an SPL + build. + +config SPL_NAND_DRIVERS + bool "Use standard NAND driver" + help + SPL uses normal NAND drivers, not minimal drivers. + +config SPL_NAND_ECC + bool "Include standard software ECC in the SPL" + +config SPL_NAND_SIMPLE + bool "Support simple NAND drivers in SPL" + help + Support for NAND boot using simple NAND drivers that + expose the cmd_ctrl() interface. + +config SPL_NAND_BASE + depends on SPL_NAND_DRIVERS + bool "Use Base NAND Driver" + help + Include nand_base.c in the SPL. + +config SPL_NAND_IDENT + depends on SPL_NAND_BASE + bool "Use chip ID to identify NAND flash" + help + SPL uses the chip ID list to identify the NAND flash. + +config SPL_UBI + bool "Support UBI" + help + Enable support for loading payloads from UBI. See + README.ubispl for more info. + +if SPL_DM +config SPL_CACHE + depends on CACHE + bool "Support cache drivers in SPL" + help + Enable support for cache drivers in SPL. + +config SPL_DM_SPI + bool "Support SPI DM drivers in SPL" + help + Enable support for SPI DM drivers in SPL. + +config SPL_DM_SPI_FLASH + bool "Support SPI DM FLASH drivers in SPL" + help + Enable support for SPI DM flash drivers in SPL. + +endif +if SPL_UBI +config SPL_UBI_LOAD_BY_VOLNAME + bool "Support loading volumes by name" + help + This enables support for loading UBI volumes by name. When this + is set, CONFIG_SPL_UBI_LOAD_MONITOR_VOLNAME can be used to + configure the volume name from which to load U-Boot. + +config SPL_UBI_MAX_VOL_LEBS + int "Maximum number of LEBs per volume" + depends on SPL_UBI + help + The maximum number of logical eraseblocks which a static volume + to load can contain. Used for sizing the scan data structure. + +config SPL_UBI_MAX_PEB_SIZE + int "Maximum PEB size" + depends on SPL_UBI + help + The maximum physical erase block size. + +config SPL_UBI_MAX_PEBS + int "Maximum number of PEBs" + depends on SPL_UBI + help + The maximum physical erase block size. If not overridden by + board code, this value will be used as the actual number of PEBs. + +config SPL_UBI_PEB_OFFSET + int "Offset to first UBI PEB" + depends on SPL_UBI + help + The offset in number of PEBs from the start of flash to the first + PEB part of the UBI image. + +config SPL_UBI_VID_OFFSET + int "Offset to VID header" + depends on SPL_UBI + +config SPL_UBI_LEB_START + int "Offset to LEB in PEB" + depends on SPL_UBI + help + The offset in bytes to the LEB within a PEB. + +config SPL_UBI_INFO_ADDR + hex "Address to place UBI scan info" + depends on SPL_UBI + help + Address for ubispl to place the scan info. Read README.ubispl to + determine the required size + +config SPL_UBI_VOL_IDS + int "Maximum volume id" + depends on SPL_UBI + help + The maximum volume id which can be loaded. Used for sizing the + scan data structure. + +config SPL_UBI_LOAD_MONITOR_ID + int "id of U-Boot volume" + depends on SPL_UBI + help + The UBI volume id from which to load U-Boot + +config SPL_UBI_LOAD_MONITOR_VOLNAME + string "volume name of U-Boot volume" + depends on SPL_UBI_LOAD_BY_VOLNAME + help + The UBI volume name from which to load U-Boot + +config SPL_UBI_LOAD_KERNEL_ID + int "id of kernel volume" + depends on SPL_OS_BOOT && SPL_UBI + help + The UBI volume id from which to load the kernel + +config SPL_UBI_LOAD_ARGS_ID + int "id of kernel args volume" + depends on SPL_OS_BOOT && SPL_UBI + help + The UBI volume id from which to load the device tree + +config UBI_SPL_SILENCE_MSG + bool "silence UBI SPL messages" + default n + help + Disable messages from UBI SPL. This leaves warnings + and errors enabled. + +endif # if SPL_UBI + +config SPL_NET_SUPPORT + bool "Support networking" + help + Enable support for network devices (such as Ethernet) in SPL. + This permits SPL to load U-Boot over a network link rather than + from an on-board peripheral. Environment support is required since + the network stack uses a number of environment variables. See also + SPL_ETH_SUPPORT. + +if SPL_NET_SUPPORT +config SPL_NET_VCI_STRING + string "BOOTP Vendor Class Identifier string sent by SPL" + help + As defined by RFC 2132 the vendor class identifier field can be + sent by the client to identify the vendor type and configuration + of a client. This is often used in practice to allow for the DHCP + server to specify different files to load depending on if the ROM, + SPL or U-Boot itself makes the request +endif # if SPL_NET_SUPPORT + +config SPL_NO_CPU_SUPPORT + bool "Drop CPU code in SPL" + help + This is specific to the ARM926EJ-S CPU. It disables the standard + start.S start-up code, presumably so that a replacement can be + used on that CPU. You should not enable it unless you know what + you are doing. + +config SPL_NOR_SUPPORT + bool "Support NOR flash" + help + Enable support for loading U-Boot from memory-mapped NOR (Negative + OR) flash in SPL. NOR flash is slow to write but fast to read, and + a memory-mapped device makes it very easy to access. Loading from + NOR is typically achieved with just a memcpy(). + +config SPL_XIP_SUPPORT + bool "Support XIP" + depends on SPL + help + Enable support for execute in place of U-Boot or kernel image. There + is no need to copy image from flash to ram if flash supports execute + in place. Its very useful in systems having enough flash but not + enough ram to load the image. + +config SPL_ONENAND_SUPPORT + bool "Support OneNAND flash" + help + Enable support for OneNAND (Negative AND) flash in SPL. OneNAND is + a type of NAND flash and therefore can be used to allow SPL to + load U-Boot from supported devices. This enables the drivers in + drivers/mtd/onenand as part of an SPL build. + +config SPL_OS_BOOT + bool "Activate Falcon Mode" + depends on !TI_SECURE_DEVICE + default n + help + Enable booting directly to an OS from SPL. + for more info read doc/README.falcon + +if SPL_OS_BOOT +config SYS_OS_BASE + hex "addr, where OS is found" + depends on SPL_NOR_SUPPORT + help + Specify the address, where the OS image is found, which + gets booted. + +endif # SPL_OS_BOOT + +config SPL_PAYLOAD + string "SPL payload" + default "tpl/u-boot-with-tpl.bin" if TPL + default "u-boot.bin" + help + Payload for SPL boot. For backward compatibility, default to + u-boot.bin, i.e. RAW image without any header. In case of + TPL, tpl/u-boot-with-tpl.bin. For new boards, suggest to + use u-boot.img. + +config SPL_PCI + bool "Support PCI drivers" + help + Enable support for PCI in SPL. For platforms that need PCI to boot, + or must perform some init using PCI in SPL, this provides the + necessary driver support. This enables the drivers in drivers/pci + as part of an SPL build. + +config SPL_PCH_SUPPORT + bool "Support PCH drivers" + help + Enable support for PCH (Platform Controller Hub) devices in SPL. + These are used to set up GPIOs and the SPI peripheral early in + boot. This enables the drivers in drivers/pch as part of an SPL + build. + +config SPL_POST_MEM_SUPPORT + bool "Support POST drivers" + help + Enable support for POST (Power-on Self Test) in SPL. POST is a + procedure that checks that the hardware (CPU or board) appears to + be functionally correctly. It is a sanity check that can be + performed before booting. This enables the drivers in post/drivers + as part of an SPL build. + +config SPL_DM_RESET + bool "Support reset drivers" + depends on SPL + help + Enable support for reset control in SPL. + That can be useful in SPL to handle IP reset in driver, as in U-Boot, + by using the generic reset API provided by driver model. + This enables the drivers in drivers/reset as part of an SPL build. + +config SPL_POWER_SUPPORT + bool "Support power drivers" + help + Enable support for power control in SPL. This includes support + for PMICs (Power-management Integrated Circuits) and some of the + features provided by PMICs. In particular, voltage regulators can + be used to enable/disable power and vary its voltage. That can be + useful in SPL to turn on boot peripherals and adjust CPU voltage + so that the clock speed can be increased. This enables the drivers + in drivers/power, drivers/power/pmic and drivers/power/regulator + as part of an SPL build. + +config SPL_POWER_DOMAIN + bool "Support power domain drivers" + help + Enable support for power domain control in SPL. Many SoCs allow + power to be applied to or removed from portions of the SoC (power + domains). This may be used to save power. This API provides the + means to control such power management hardware. This enables + the drivers in drivers/power/domain as part of a SPL build. + +config SPL_RAM_SUPPORT + bool "Support booting from RAM" + default y if MICROBLAZE || ARCH_SOCFPGA || ARCH_TEGRA || ARCH_ZYNQ + help + Enable booting of an image in RAM. The image can be preloaded or + it can be loaded by SPL directly into RAM (e.g. using USB). + +config SPL_RAM_DEVICE + bool "Support booting from preloaded image in RAM" + depends on SPL_RAM_SUPPORT + default y if MICROBLAZE || ARCH_SOCFPGA || ARCH_TEGRA || ARCH_ZYNQ + help + Enable booting of an image already loaded in RAM. The image has to + be already in memory when SPL takes over, e.g. loaded by the boot + ROM. + +config SPL_REMOTEPROC + bool "Support REMOTEPROCS" + help + Enable support for REMOTEPROCs in SPL. This permits to load + a remote processor firmware in SPL. + +config SPL_RTC_SUPPORT + bool "Support RTC drivers" + help + Enable RTC (Real-time Clock) support in SPL. This includes support + for reading and setting the time. Some RTC devices also have some + non-volatile (battery-backed) memory which is accessible if + needed. This enables the drivers in drivers/rtc as part of an SPL + build. + +config SPL_SATA_SUPPORT + bool "Support loading from SATA" + help + Enable support for SATA (Serial AT attachment) in SPL. This allows + use of SATA devices such as hard drives and flash drivers for + loading U-Boot. SATA is used in higher-end embedded systems and + can provide higher performance than MMC , at somewhat higher + expense and power consumption. This enables loading from SATA + using a configured device. + +config SPL_SATA_RAW_U_BOOT_USE_SECTOR + bool "SATA raw mode: by sector" + depends on SPL_SATA_SUPPORT + help + Use sector number for specifying U-Boot location on SATA disk in + raw mode. + +config SPL_SATA_RAW_U_BOOT_SECTOR + hex "Sector on the SATA disk to load U-Boot from" + depends on SPL_SATA_RAW_U_BOOT_USE_SECTOR + help + Sector on the SATA disk to load U-Boot from, when the SATA disk is being + used in raw mode. Units: SATA disk sectors (1 sector = 512 bytes). + +config SPL_SERIAL_SUPPORT + bool "Support serial" + select SPL_PRINTF + select SPL_STRTO + help + Enable support for serial in SPL. This allows use of a serial UART + for displaying messages while SPL is running. It also brings in + printf() and panic() functions. This should normally be enabled + unless there are space reasons not to. Even then, consider + enabling SPL_USE_TINY_PRINTF which is a small printf() version. + +config SPL_SPI_SUPPORT + bool "Support SPI drivers" + help + Enable support for using SPI in SPL. This is used for connecting + to SPI flash for loading U-Boot. See SPL_SPI_FLASH_SUPPORT for + more details on that. The SPI driver provides the transport for + data between the SPI flash and the CPU. This option can be used to + enable SPI drivers that are needed for other purposes also, such + as a SPI PMIC. + +config SPL_SPI_FLASH_SUPPORT + bool "Support SPI flash drivers" + depends on SPL_SPI_SUPPORT + help + Enable support for using SPI flash in SPL, and loading U-Boot from + SPI flash. SPI flash (Serial Peripheral Bus flash) is named after + the SPI bus that is used to connect it to a system. It is a simple + but fast bidirectional 4-wire bus (clock, chip select and two data + lines). This enables the drivers in drivers/mtd/spi as part of an + SPL build. This normally requires SPL_SPI_SUPPORT. + +if SPL_SPI_FLASH_SUPPORT + +config SPL_SPI_FLASH_TINY + bool "Enable low footprint SPL SPI Flash support" + depends on !SPI_FLASH_BAR + default y if SPI_FLASH + help + Enable lightweight SPL SPI Flash support that supports just reading + data/images from flash. No support to write/erase flash. Enable + this if you have SPL size limitations and don't need full + fledged SPI flash support. + +config SPL_SPI_FLASH_SFDP_SUPPORT + bool "SFDP table parsing support for SPI NOR flashes" + depends on !SPI_FLASH_BAR && !SPL_SPI_FLASH_TINY + help + Enable support for parsing and auto discovery of parameters for + SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP) + tables as per JESD216 standard in SPL. + +config SPL_SPI_FLASH_MTD + bool "Support for SPI flash MTD drivers in SPL" + help + Enable support for SPI flash MTD drivers in SPL. + +config SPL_SPI_LOAD + bool "Support loading from SPI flash" + help + Enable support for loading next stage, U-Boot or otherwise, from + SPI NOR in U-Boot SPL. + +endif # SPL_SPI_FLASH_SUPPORT + +config SYS_SPI_U_BOOT_OFFS + hex "address of u-boot payload in SPI flash" + default 0x8000 if ARCH_SUNXI + default 0x0 + depends on SPL_SPI_LOAD || SPL_SPI_SUNXI + help + Address within SPI-Flash from where the u-boot payload is fetched + from. + +config SPL_THERMAL + bool "Driver support for thermal devices" + help + Enable support for temperature-sensing devices. Some SoCs have on-chip + temperature sensors to permit warnings, speed throttling or even + automatic power-off when the temperature gets too high or low. Other + devices may be discrete but connected on a suitable bus. + +config SPL_USB_HOST_SUPPORT + bool "Support USB host drivers" + select HAVE_BLOCK_DEVICE + help + Enable access to USB (Universal Serial Bus) host devices so that + SPL can load U-Boot from a connected USB peripheral, such as a USB + flash stick. While USB takes a little longer to start up than most + buses, it is very flexible since many different types of storage + device can be attached. This option enables the drivers in + drivers/usb/host as part of an SPL build. + +config SPL_USB_STORAGE + bool "Support loading from USB" + depends on SPL_USB_HOST_SUPPORT && !(BLK && !DM_USB) + help + Enable support for USB devices in SPL. This allows use of USB + devices such as hard drives and flash drivers for loading U-Boot. + The actual drivers are enabled separately using the normal U-Boot + config options. This enables loading from USB using a configured + device. + +config SPL_USB_GADGET + bool "Suppport USB Gadget drivers" + help + Enable USB Gadget API which allows to enable USB device functions + in SPL. + +if SPL_USB_GADGET + +config SPL_USB_ETHER + bool "Support USB Ethernet drivers" + help + Enable access to the USB network subsystem and associated + drivers in SPL. This permits SPL to load U-Boot over a + USB-connected Ethernet link (such as a USB Ethernet dongle) rather + than from an onboard peripheral. Environment support is required + since the network stack uses a number of environment variables. + See also SPL_NET_SUPPORT and SPL_ETH_SUPPORT. + +config SPL_DFU + bool "Support DFU (Device Firmware Upgrade)" + select SPL_HASH_SUPPORT + select SPL_DFU_NO_RESET + depends on SPL_RAM_SUPPORT + help + This feature enables the DFU (Device Firmware Upgrade) in SPL with + RAM memory device support. The ROM code will load and execute + the SPL built with dfu. The user can load binaries (u-boot/kernel) to + selected device partition from host-pc using dfu-utils. + This feature is useful to flash the binaries to factory or bare-metal + boards using USB interface. + +choice + bool "DFU device selection" + depends on SPL_DFU + +config SPL_DFU_RAM + bool "RAM device" + depends on SPL_DFU && SPL_RAM_SUPPORT + help + select RAM/DDR memory device for loading binary images + (u-boot/kernel) to the selected device partition using + DFU and execute the u-boot/kernel from RAM. + +endchoice + +config SPL_USB_SDP_SUPPORT + bool "Support SDP (Serial Download Protocol)" + depends on SPL_SERIAL_SUPPORT + help + Enable Serial Download Protocol (SDP) device support in SPL. This + allows to download images into memory and execute (jump to) them + using the same protocol as implemented by the i.MX family's boot ROM. + +config SPL_SDP_USB_DEV + int "SDP USB controller index" + default 0 + depends on SPL_USB_SDP_SUPPORT + help + Some boards have USB controller other than 0. Define this option + so it can be used in compiled environment. +endif + +config SPL_WATCHDOG_SUPPORT + bool "Support watchdog drivers" + imply SPL_WDT if !HW_WATCHDOG + help + Enable support for watchdog drivers in SPL. A watchdog is + typically a hardware peripheral which can reset the system when it + detects no activity for a while (such as a software crash). This + enables the drivers in drivers/watchdog as part of an SPL build. + +config SPL_YMODEM_SUPPORT + bool "Support loading using Ymodem" + depends on SPL_SERIAL_SUPPORT + help + While loading from serial is slow it can be a useful backup when + there is no other option. The Ymodem protocol provides a reliable + means of transmitting U-Boot over a serial line for using in SPL, + with a checksum to ensure correctness. + +config SPL_ATF + bool "Support ARM Trusted Firmware" + depends on ARM64 && SPL_FIT + help + ATF(ARM Trusted Firmware) is a component for ARM AArch64 which + is loaded by SPL (which is considered as BL2 in ATF terminology). + More detail at: https://github.com/ARM-software/arm-trusted-firmware + +config SPL_ATF_LOAD_IMAGE_V2 + bool "Use the new LOAD_IMAGE_V2 parameter passing" + depends on SPL_ATF + help + Some platforms use the newer LOAD_IMAGE_V2 parameter passing. + + If you want to load a bl31 image from the SPL and need the new + method, say Y. + +config SPL_ATF_NO_PLATFORM_PARAM + bool "Pass no platform parameter" + depends on SPL_ATF + help + While we expect to call a pointer to a valid FDT (or NULL) + as the platform parameter to an ATF, some ATF versions are + not U-Boot aware and have an insufficiently robust parameter + validation to gracefully reject a FDT being passed. + + If this option is enabled, the spl_atf os-type handler will + always pass NULL for the platform parameter. + + If your ATF is affected, say Y. + +config SPL_AM33XX_ENABLE_RTC32K_OSC + bool "Enable the RTC32K OSC on AM33xx based platforms" + default y if AM33XX + help + Enable access to the AM33xx RTC and select the external 32kHz clock + source. + +config SPL_OPTEE + bool "Support OP-TEE Trusted OS" + depends on ARM + help + OP-TEE is an open source Trusted OS which is loaded by SPL. + More detail at: https://github.com/OP-TEE/optee_os + +config SPL_OPENSBI + bool "Support RISC-V OpenSBI" + depends on RISCV && SPL_RISCV_MMODE && RISCV_SMODE + help + OpenSBI is an open-source implementation of the RISC-V Supervisor Binary + Interface (SBI) specification. U-Boot supports the OpenSBI FW_DYNAMIC + firmware. It is loaded and started by U-Boot SPL. + + More details are available at https://github.com/riscv/opensbi and + https://github.com/riscv/riscv-sbi-doc + +config SPL_OPENSBI_LOAD_ADDR + hex "OpenSBI load address" + depends on SPL_OPENSBI + help + Load address of the OpenSBI binary. + +config TPL + bool + depends on SUPPORT_TPL + prompt "Enable TPL" + help + If you want to build TPL as well as the normal image and SPL, say Y. + +if TPL + +config TPL_SIZE_LIMIT + hex "Maximum size of TPL image" + depends on TPL + default 0x0 + help + Specifies the maximum length of the U-Boot TPL image. + If this value is zero, it is ignored. + +config TPL_FRAMEWORK + bool "Support TPL based upon the common SPL framework" + default y if SPL_FRAMEWORK + help + Enable the SPL framework under common/spl/ for TPL builds. + This framework supports MMC, NAND and YMODEM and other methods + loading of U-Boot's SPL stage. If unsure, say Y. + +config TPL_HANDOFF + bool "Pass hand-off information from TPL to SPL and U-Boot proper" + depends on HANDOFF && TPL_BLOBLIST + default y + help + This option enables TPL to write handoff information. This can be + used to pass information like the size of SDRAM from TPL to U-Boot + proper. The information is also available to SPL if it is useful + there. + +config TPL_BOARD_INIT + bool "Call board-specific initialization in TPL" + help + If this option is enabled, U-Boot will call the function + spl_board_init() from board_init_r(). This function should be + provided by the board. + +config TPL_BOOTCOUNT_LIMIT + bool "Support bootcount in TPL" + depends on TPL_ENV_SUPPORT + help + If this option is enabled, the TPL will support bootcount. + For example, it may be useful to choose the device to boot. + +config TPL_LDSCRIPT + string "Linker script for the TPL stage" + depends on TPL + default "arch/arm/cpu/armv8/u-boot-spl.lds" if ARM64 + default "arch/\$(ARCH)/cpu/u-boot-spl.lds" + help + The TPL stage will usually require a different linker-script + (as it runs from a different memory region) than the regular + U-Boot stage. Set this to the path of the linker-script to + be used for TPL. + + May be left empty to trigger the Makefile infrastructure to + fall back to the linker-script used for the SPL stage. + +config TPL_NEEDS_SEPARATE_TEXT_BASE + bool "TPL needs a separate text-base" + default n + depends on TPL + help + Enable, if the TPL stage should not inherit its text-base + from the SPL stage. When enabled, a base address for the + .text sections of the TPL stage has to be set below. + +config TPL_NEEDS_SEPARATE_STACK + bool "TPL needs a separate initial stack-pointer" + default n + depends on TPL + help + Enable, if the TPL stage should not inherit its initial + stack-pointer from the settings for the SPL stage. + +config TPL_TEXT_BASE + hex "Base address for the .text section of the TPL stage" + depends on TPL_NEEDS_SEPARATE_TEXT_BASE + help + The base address for the .text section of the TPL stage. + +config TPL_MAX_SIZE + int "Maximum size (in bytes) for the TPL stage" + default 0 + depends on TPL + help + The maximum size (in bytes) of the TPL stage. + +config TPL_STACK + hex "Address of the initial stack-pointer for the TPL stage" + depends on TPL_NEEDS_SEPARATE_STACK + help + The address of the initial stack-pointer for the TPL stage. + Usually this will be the (aligned) top-of-stack. + +config TPL_READ_ONLY + bool + depends on TPL_OF_PLATDATA + select TPL_OF_PLATDATA_NO_BIND + select TPL_OF_PLATDATA_RT + help + Some platforms (e.g. x86 Apollo Lake) load SPL into a read-only + section of memory. This means that of-platdata must make a copy (in + writeable memory) of anything it wants to modify, such as + device-private data. + +config TPL_BOOTROM_SUPPORT + bool "Support returning to the BOOTROM (from TPL)" + help + Some platforms (e.g. the Rockchip RK3368) provide support in their + ROM for loading the next boot-stage after performing basic setup + from the TPL stage. + + Enable this option, to return to the BOOTROM through the + BOOT_DEVICE_BOOTROM (or fall-through to the next boot device in the + boot device list, if not implemented for a given board) + +config TPL_DRIVERS_MISC_SUPPORT + bool "Support misc drivers in TPL" + help + Enable miscellaneous drivers in TPL. These drivers perform various + tasks that don't fall nicely into other categories, Enable this + option to build the drivers in drivers/misc as part of an TPL + build, for those that support building in TPL (not all drivers do). + +config TPL_ENV_SUPPORT + bool "Support an environment" + help + Enable environment support in TPL. See SPL_ENV_SUPPORT for details. + +config TPL_GPIO_SUPPORT + bool "Support GPIO in TPL" + help + Enable support for GPIOs (General-purpose Input/Output) in TPL. + GPIOs allow U-Boot to read the state of an input line (high or + low) and set the state of an output line. This can be used to + drive LEDs, control power to various system parts and read user + input. GPIOs can be useful in TPL to enable a 'sign-of-life' LED, + for example. Enable this option to build the drivers in + drivers/gpio as part of an TPL build. + +config TPL_I2C_SUPPORT + bool "Support I2C" + help + Enable support for the I2C bus in TPL. See SPL_I2C_SUPPORT for + details. + +config TPL_LIBCOMMON_SUPPORT + bool "Support common libraries" + help + Enable support for common U-Boot libraries within TPL. See + SPL_LIBCOMMON_SUPPORT for details. + +config TPL_LIBGENERIC_SUPPORT + bool "Support generic libraries" + help + Enable support for generic U-Boot libraries within TPL. See + SPL_LIBGENERIC_SUPPORT for details. + +config TPL_MPC8XXX_INIT_DDR_SUPPORT + bool "Support MPC8XXX DDR init" + help + Enable support for DDR-SDRAM on the MPC8XXX family within TPL. See + SPL_MPC8XXX_INIT_DDR_SUPPORT for details. + +config TPL_MMC_SUPPORT + bool "Support MMC" + depends on MMC + help + Enable support for MMC within TPL. See SPL_MMC_SUPPORT for details. + +config TPL_NAND_SUPPORT + bool "Support NAND flash" + help + Enable support for NAND in TPL. See SPL_NAND_SUPPORT for details. + +config TPL_PCI + bool "Support PCI drivers" + help + Enable support for PCI in TPL. For platforms that need PCI to boot, + or must perform some init using PCI in SPL, this provides the + necessary driver support. This enables the drivers in drivers/pci + as part of a TPL build. + +config TPL_PCH_SUPPORT + bool "Support PCH drivers" + help + Enable support for PCH (Platform Controller Hub) devices in TPL. + These are used to set up GPIOs and the SPI peripheral early in + boot. This enables the drivers in drivers/pch as part of a TPL + build. + +config TPL_RAM_SUPPORT + bool "Support booting from RAM" + help + Enable booting of an image in RAM. The image can be preloaded or + it can be loaded by TPL directly into RAM (e.g. using USB). + +config TPL_RAM_DEVICE + bool "Support booting from preloaded image in RAM" + depends on TPL_RAM_SUPPORT + help + Enable booting of an image already loaded in RAM. The image has to + be already in memory when TPL takes over, e.g. loaded by the boot + ROM. + +config TPL_RTC_SUPPORT + bool "Support RTC drivers" + help + Enable RTC (Real-time Clock) support in TPL. This includes support + for reading and setting the time. Some RTC devices also have some + non-volatile (battery-backed) memory which is accessible if + needed. This enables the drivers in drivers/rtc as part of an TPL + build. + +config TPL_SERIAL_SUPPORT + bool "Support serial" + select TPL_PRINTF + select TPL_STRTO + help + Enable support for serial in TPL. See SPL_SERIAL_SUPPORT for + details. + +config TPL_SPI_FLASH_SUPPORT + bool "Support SPI flash drivers" + help + Enable support for using SPI flash in TPL. See SPL_SPI_FLASH_SUPPORT + for details. + +config TPL_SPI_FLASH_TINY + bool "Enable low footprint TPL SPI Flash support" + depends on TPL_SPI_FLASH_SUPPORT && !SPI_FLASH_BAR + default y if SPI_FLASH + help + Enable lightweight TPL SPI Flash support that supports just reading + data/images from flash. No support to write/erase flash. Enable + this if you have TPL size limitations and don't need full-fledged + SPI flash support. + +config TPL_SPI_LOAD + bool "Support loading from SPI flash" + depends on TPL_SPI_FLASH_SUPPORT + help + Enable support for loading next stage, U-Boot or otherwise, from + SPI NOR in U-Boot TPL. + +config TPL_SPI_SUPPORT + bool "Support SPI drivers" + help + Enable support for using SPI in TPL. See SPL_SPI_SUPPORT for + details. + +config TPL_DM_SPI + bool "Support SPI DM drivers in TPL" + help + Enable support for SPI DM drivers in TPL. + +config TPL_DM_SPI_FLASH + bool "Support SPI DM FLASH drivers in TPL" + help + Enable support for SPI DM flash drivers in TPL. + +config TPL_YMODEM_SUPPORT + bool "Support loading using Ymodem" + depends on TPL_SERIAL_SUPPORT + help + While loading from serial is slow it can be a useful backup when + there is no other option. The Ymodem protocol provides a reliable + means of transmitting U-Boot over a serial line for using in TPL, + with a checksum to ensure correctness. + +endif # TPL + +config SPL_AT91_MCK_BYPASS + bool "Use external clock signal as a source of main clock for AT91 platforms" + depends on ARCH_AT91 + default n + help + Use external 8 to 24 Mhz clock signal as source of main clock instead + of an external crystal oscillator. + This option disables the internal driving on the XOUT pin. + The external source has to provide a stable clock on the XIN pin. + If this option is disabled, the SoC expects a crystal oscillator + that needs driving on both XIN and XOUT lines. + +endif # SPL +endmenu diff --git a/roms/u-boot/common/spl/Makefile b/roms/u-boot/common/spl/Makefile new file mode 100644 index 000000000..c576a7812 --- /dev/null +++ b/roms/u-boot/common/spl/Makefile @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2012 +# Texas Instruments Incorporated - http://www.ti.com/ +# Aneesh V <aneesh@ti.com> +# Based on common/Makefile. +# + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_$(SPL_TPL_)FRAMEWORK) += spl.o +obj-$(CONFIG_$(SPL_TPL_)BOOTROM_SUPPORT) += spl_bootrom.o +obj-$(CONFIG_$(SPL_TPL_)LOAD_FIT) += spl_fit.o +obj-$(CONFIG_$(SPL_TPL_)LEGACY_IMAGE_SUPPORT) += spl_legacy.o +obj-$(CONFIG_$(SPL_TPL_)NOR_SUPPORT) += spl_nor.o +obj-$(CONFIG_$(SPL_TPL_)XIP_SUPPORT) += spl_xip.o +obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += spl_ymodem.o +ifndef CONFIG_SPL_UBI +obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += spl_nand.o +obj-$(CONFIG_$(SPL_TPL_)ONENAND_SUPPORT) += spl_onenand.o +endif +obj-$(CONFIG_$(SPL_TPL_)UBI) += spl_ubi.o +obj-$(CONFIG_$(SPL_TPL_)NET_SUPPORT) += spl_net.o +obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += spl_mmc.o +obj-$(CONFIG_$(SPL_TPL_)ATF) += spl_atf.o +obj-$(CONFIG_$(SPL_TPL_)OPTEE) += spl_optee.o +obj-$(CONFIG_$(SPL_TPL_)OPENSBI) += spl_opensbi.o +obj-$(CONFIG_$(SPL_TPL_)USB_STORAGE) += spl_usb.o +obj-$(CONFIG_$(SPL_TPL_)FS_FAT) += spl_fat.o +obj-$(CONFIG_$(SPL_TPL_)FS_EXT4) += spl_ext.o +obj-$(CONFIG_$(SPL_TPL_)SATA_SUPPORT) += spl_sata.o +obj-$(CONFIG_$(SPL_TPL_)DFU) += spl_dfu.o +obj-$(CONFIG_$(SPL_TPL_)SPI_LOAD) += spl_spi.o +obj-$(CONFIG_$(SPL_TPL_)RAM_SUPPORT) += spl_ram.o +obj-$(CONFIG_$(SPL_TPL_)USB_SDP_SUPPORT) += spl_sdp.o +endif diff --git a/roms/u-boot/common/spl/spl.c b/roms/u-boot/common/spl/spl.c new file mode 100644 index 000000000..a0a608fd7 --- /dev/null +++ b/roms/u-boot/common/spl/spl.c @@ -0,0 +1,895 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Aneesh V <aneesh@ti.com> + */ + +#include <common.h> +#include <bloblist.h> +#include <binman_sym.h> +#include <bootstage.h> +#include <dm.h> +#include <handoff.h> +#include <hang.h> +#include <init.h> +#include <irq_func.h> +#include <log.h> +#include <mapmem.h> +#include <serial.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/u-boot.h> +#include <nand.h> +#include <fat.h> +#include <u-boot/crc.h> +#include <version.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <dm/root.h> +#include <linux/compiler.h> +#include <fdt_support.h> +#include <bootcount.h> +#include <wdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_UBOOT_START +#define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE +#endif +#ifndef CONFIG_SYS_MONITOR_LEN +/* Unknown U-Boot size, let's assume it will not be more than 200 KB */ +#define CONFIG_SYS_MONITOR_LEN (200 * 1024) +#endif + +u32 *boot_params_ptr = NULL; + +/* See spl.h for information about this */ +binman_sym_declare(ulong, u_boot_any, image_pos); +binman_sym_declare(ulong, u_boot_any, size); + +#ifdef CONFIG_TPL +binman_sym_declare(ulong, spl, image_pos); +binman_sym_declare(ulong, spl, size); +#endif + +/* Define board data structure */ +static struct bd_info bdata __attribute__ ((section(".data"))); + +/* + * Board-specific Platform code can reimplement show_boot_progress () if needed + */ +__weak void show_boot_progress(int val) {} + +#if defined(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF) || \ + defined(CONFIG_SPL_ATF) +/* weak, default platform-specific function to initialize dram banks */ +__weak int dram_init_banksize(void) +{ + return 0; +} +#endif + +/* + * Default function to determine if u-boot or the OS should + * be started. This implementation always returns 1. + * + * Please implement your own board specific funcion to do this. + * + * RETURN + * 0 to not start u-boot + * positive if u-boot should start + */ +#ifdef CONFIG_SPL_OS_BOOT +__weak int spl_start_uboot(void) +{ + puts(SPL_TPL_PROMPT + "Please implement spl_start_uboot() for your board\n"); + puts(SPL_TPL_PROMPT "Direct Linux boot not active!\n"); + return 1; +} + +/* + * Weak default function for arch specific zImage check. Return zero + * and fill start and end address if image is recognized. + */ +int __weak bootz_setup(ulong image, ulong *start, ulong *end) +{ + return 1; +} +#endif + +/* Weak default function for arch/board-specific fixups to the spl_image_info */ +void __weak spl_perform_fixups(struct spl_image_info *spl_image) +{ +} + +void spl_fixup_fdt(void *fdt_blob) +{ +#if defined(CONFIG_SPL_OF_LIBFDT) + int err; + + if (!fdt_blob) + return; + + err = fdt_check_header(fdt_blob); + if (err < 0) { + printf("fdt_root: %s\n", fdt_strerror(err)); + return; + } + + /* fixup the memory dt node */ + err = fdt_shrink_to_minimum(fdt_blob, 0); + if (err == 0) { + printf(SPL_TPL_PROMPT "fdt_shrink_to_minimum err - %d\n", err); + return; + } + + err = arch_fixup_fdt(fdt_blob); + if (err) { + printf(SPL_TPL_PROMPT "arch_fixup_fdt err - %d\n", err); + return; + } +#endif +} + +ulong spl_get_image_pos(void) +{ + return spl_phase() == PHASE_TPL ? + binman_sym(ulong, spl, image_pos) : + binman_sym(ulong, u_boot_any, image_pos); +} + +ulong spl_get_image_size(void) +{ + return spl_phase() == PHASE_TPL ? + binman_sym(ulong, spl, size) : + binman_sym(ulong, u_boot_any, size); +} + +ulong spl_get_image_text_base(void) +{ + return spl_phase() == PHASE_TPL ? CONFIG_SPL_TEXT_BASE : + CONFIG_SYS_TEXT_BASE; +} + +/* + * Weak default function for board specific cleanup/preparation before + * Linux boot. Some boards/platforms might not need it, so just provide + * an empty stub here. + */ +__weak void spl_board_prepare_for_linux(void) +{ + /* Nothing to do! */ +} + +__weak void spl_board_prepare_for_boot(void) +{ + /* Nothing to do! */ +} + +__weak struct image_header *spl_get_load_buffer(ssize_t offset, size_t size) +{ + return map_sysmem(CONFIG_SYS_TEXT_BASE + offset, 0); +} + +void spl_set_header_raw_uboot(struct spl_image_info *spl_image) +{ + ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos); + + spl_image->size = CONFIG_SYS_MONITOR_LEN; + + /* + * Binman error cases: address of the end of the previous region or the + * start of the image's entry area (usually 0) if there is no previous + * region. + */ + if (u_boot_pos && u_boot_pos != BINMAN_SYM_MISSING) { + /* Binman does not support separated entry addresses */ + spl_image->entry_point = u_boot_pos; + spl_image->load_addr = u_boot_pos; + } else { + spl_image->entry_point = CONFIG_SYS_UBOOT_START; + spl_image->load_addr = CONFIG_SYS_TEXT_BASE; + } + spl_image->os = IH_OS_U_BOOT; + spl_image->name = "U-Boot"; +} + +#ifdef CONFIG_SPL_LOAD_FIT_FULL +/* Parse and load full fitImage in SPL */ +static int spl_load_fit_image(struct spl_image_info *spl_image, + const struct image_header *header) +{ + bootm_headers_t images; + const char *fit_uname_config = NULL; + uintptr_t fdt_hack; + const char *uname; + ulong fw_data = 0, dt_data = 0, img_data = 0; + ulong fw_len = 0, dt_len = 0, img_len = 0; + int idx, conf_noffset; + int ret; + +#ifdef CONFIG_SPL_FIT_SIGNATURE + images.verify = 1; +#endif + ret = fit_image_load(&images, (ulong)header, + NULL, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_STANDALONE, -1, + FIT_LOAD_OPTIONAL, &fw_data, &fw_len); + if (ret >= 0) { + printf("DEPRECATED: 'standalone = ' property."); + printf("Please use either 'firmware =' or 'kernel ='\n"); + } else { + ret = fit_image_load(&images, (ulong)header, NULL, + &fit_uname_config, IH_ARCH_DEFAULT, + IH_TYPE_FIRMWARE, -1, FIT_LOAD_OPTIONAL, + &fw_data, &fw_len); + } + + if (ret < 0) { + ret = fit_image_load(&images, (ulong)header, NULL, + &fit_uname_config, IH_ARCH_DEFAULT, + IH_TYPE_KERNEL, -1, FIT_LOAD_OPTIONAL, + &fw_data, &fw_len); + } + + if (ret < 0) + return ret; + + spl_image->size = fw_len; + spl_image->entry_point = fw_data; + spl_image->load_addr = fw_data; + if (fit_image_get_os(header, ret, &spl_image->os)) + spl_image->os = IH_OS_INVALID; + spl_image->name = genimg_get_os_name(spl_image->os); + + debug(SPL_TPL_PROMPT "payload image: %32s load addr: 0x%lx size: %d\n", + spl_image->name, spl_image->load_addr, spl_image->size); + +#ifdef CONFIG_SPL_FIT_SIGNATURE + images.verify = 1; +#endif + ret = fit_image_load(&images, (ulong)header, NULL, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_FLATDT, -1, + FIT_LOAD_OPTIONAL, &dt_data, &dt_len); + if (ret >= 0) { + spl_image->fdt_addr = (void *)dt_data; + + if (spl_image->os == IH_OS_U_BOOT) { + /* HACK: U-boot expects FDT at a specific address */ + fdt_hack = spl_image->load_addr + spl_image->size; + fdt_hack = (fdt_hack + 3) & ~3; + debug("Relocating FDT to %p\n", spl_image->fdt_addr); + memcpy((void *)fdt_hack, spl_image->fdt_addr, dt_len); + } + } + + conf_noffset = fit_conf_get_node((const void *)header, + fit_uname_config); + if (conf_noffset <= 0) + return 0; + + for (idx = 0; + uname = fdt_stringlist_get((const void *)header, conf_noffset, + FIT_LOADABLE_PROP, idx, + NULL), uname; + idx++) + { +#ifdef CONFIG_SPL_FIT_SIGNATURE + images.verify = 1; +#endif + ret = fit_image_load(&images, (ulong)header, + &uname, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_LOADABLE, -1, + FIT_LOAD_OPTIONAL_NON_ZERO, + &img_data, &img_len); + if (ret < 0) + return ret; + } + + return 0; +} +#endif + +__weak int spl_parse_legacy_header(struct spl_image_info *spl_image, + const struct image_header *header) +{ + /* LEGACY image not supported */ + debug("Legacy boot image support not enabled, proceeding to other boot methods\n"); + return -EINVAL; +} + +int spl_parse_image_header(struct spl_image_info *spl_image, + const struct image_header *header) +{ +#ifdef CONFIG_SPL_LOAD_FIT_FULL + int ret = spl_load_fit_image(spl_image, header); + + if (!ret) + return ret; +#endif + if (image_get_magic(header) == IH_MAGIC) { + int ret; + + ret = spl_parse_legacy_header(spl_image, header); + if (ret) + return ret; + } else { +#ifdef CONFIG_SPL_PANIC_ON_RAW_IMAGE + /* + * CONFIG_SPL_PANIC_ON_RAW_IMAGE is defined when the + * code which loads images in SPL cannot guarantee that + * absolutely all read errors will be reported. + * An example is the LPC32XX MLC NAND driver, which + * will consider that a completely unreadable NAND block + * is bad, and thus should be skipped silently. + */ + panic("** no mkimage signature but raw image not supported"); +#endif + +#ifdef CONFIG_SPL_OS_BOOT + ulong start, end; + + if (!bootz_setup((ulong)header, &start, &end)) { + spl_image->name = "Linux"; + spl_image->os = IH_OS_LINUX; + spl_image->load_addr = CONFIG_SYS_LOAD_ADDR; + spl_image->entry_point = CONFIG_SYS_LOAD_ADDR; + spl_image->size = end - start; + debug(SPL_TPL_PROMPT + "payload zImage, load addr: 0x%lx size: %d\n", + spl_image->load_addr, spl_image->size); + return 0; + } +#endif + +#ifdef CONFIG_SPL_RAW_IMAGE_SUPPORT + /* Signature not found - assume u-boot.bin */ + debug("mkimage signature not found - ih_magic = %x\n", + header->ih_magic); + spl_set_header_raw_uboot(spl_image); +#else + /* RAW image not supported, proceed to other boot methods. */ + debug("Raw boot image support not enabled, proceeding to other boot methods\n"); + return -EINVAL; +#endif + } + + return 0; +} + +__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) +{ + typedef void __noreturn (*image_entry_noargs_t)(void); + + image_entry_noargs_t image_entry = + (image_entry_noargs_t)spl_image->entry_point; + + debug("image entry point: 0x%lx\n", spl_image->entry_point); + image_entry(); +} + +#if CONFIG_IS_ENABLED(HANDOFF) +/** + * Set up the SPL hand-off information + * + * This is initially empty (zero) but can be written by + */ +static int setup_spl_handoff(void) +{ + struct spl_handoff *ho; + + ho = bloblist_ensure(BLOBLISTT_SPL_HANDOFF, sizeof(struct spl_handoff)); + if (!ho) + return -ENOENT; + + return 0; +} + +__weak int handoff_arch_save(struct spl_handoff *ho) +{ + return 0; +} + +static int write_spl_handoff(void) +{ + struct spl_handoff *ho; + int ret; + + ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(struct spl_handoff)); + if (!ho) + return -ENOENT; + handoff_save_dram(ho); + ret = handoff_arch_save(ho); + if (ret) + return ret; + debug(SPL_TPL_PROMPT "Wrote SPL handoff\n"); + + return 0; +} +#else +static inline int setup_spl_handoff(void) { return 0; } +static inline int write_spl_handoff(void) { return 0; } + +#endif /* HANDOFF */ + +/** + * get_bootstage_id() - Get the bootstage ID to emit + * + * @start: true if this is for starting SPL, false for ending it + * @return bootstage ID to use + */ +static enum bootstage_id get_bootstage_id(bool start) +{ + enum u_boot_phase phase = spl_phase(); + + if (IS_ENABLED(CONFIG_TPL_BUILD) && phase == PHASE_TPL) + return start ? BOOTSTAGE_ID_START_TPL : BOOTSTAGE_ID_END_TPL; + else + return start ? BOOTSTAGE_ID_START_SPL : BOOTSTAGE_ID_END_SPL; +} + +static int spl_common_init(bool setup_malloc) +{ + int ret; + +#if CONFIG_VAL(SYS_MALLOC_F_LEN) + if (setup_malloc) { +#ifdef CONFIG_MALLOC_F_ADDR + gd->malloc_base = CONFIG_MALLOC_F_ADDR; +#endif + gd->malloc_limit = CONFIG_VAL(SYS_MALLOC_F_LEN); + gd->malloc_ptr = 0; + } +#endif + ret = bootstage_init(u_boot_first_phase()); + if (ret) { + debug("%s: Failed to set up bootstage: ret=%d\n", __func__, + ret); + return ret; + } +#ifdef CONFIG_BOOTSTAGE_STASH + if (!u_boot_first_phase()) { + const void *stash = map_sysmem(CONFIG_BOOTSTAGE_STASH_ADDR, + CONFIG_BOOTSTAGE_STASH_SIZE); + + ret = bootstage_unstash(stash, CONFIG_BOOTSTAGE_STASH_SIZE); + if (ret) + debug("%s: Failed to unstash bootstage: ret=%d\n", + __func__, ret); + } +#endif /* CONFIG_BOOTSTAGE_STASH */ + bootstage_mark_name(get_bootstage_id(true), + spl_phase_name(spl_phase())); +#if CONFIG_IS_ENABLED(LOG) + ret = log_init(); + if (ret) { + debug("%s: Failed to set up logging\n", __func__); + return ret; + } +#endif + if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { + ret = fdtdec_setup(); + if (ret) { + debug("fdtdec_setup() returned error %d\n", ret); + return ret; + } + } + if (CONFIG_IS_ENABLED(DM)) { + bootstage_start(BOOTSTAGE_ID_ACCUM_DM_SPL, + spl_phase() == PHASE_TPL ? "dm tpl" : "dm_spl"); + /* With CONFIG_SPL_OF_PLATDATA, bring in all devices */ + ret = dm_init_and_scan(!CONFIG_IS_ENABLED(OF_PLATDATA)); + bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_SPL); + if (ret) { + debug("dm_init_and_scan() returned error %d\n", ret); + return ret; + } + } + + return 0; +} + +void spl_set_bd(void) +{ + /* + * NOTE: On some platforms (e.g. x86) bdata may be in flash and not + * writeable. + */ + if (!gd->bd) + gd->bd = &bdata; +} + +int spl_early_init(void) +{ + int ret; + + debug("%s\n", __func__); + + ret = spl_common_init(true); + if (ret) + return ret; + gd->flags |= GD_FLG_SPL_EARLY_INIT; + + return 0; +} + +int spl_init(void) +{ + int ret; + bool setup_malloc = !(IS_ENABLED(CONFIG_SPL_STACK_R) && + IS_ENABLED(CONFIG_SPL_SYS_MALLOC_SIMPLE)); + + debug("%s\n", __func__); + + if (!(gd->flags & GD_FLG_SPL_EARLY_INIT)) { + ret = spl_common_init(setup_malloc); + if (ret) + return ret; + } + gd->flags |= GD_FLG_SPL_INIT; + + return 0; +} + +#ifndef BOOT_DEVICE_NONE +#define BOOT_DEVICE_NONE 0xdeadbeef +#endif + +__weak void board_boot_order(u32 *spl_boot_list) +{ + spl_boot_list[0] = spl_boot_device(); +} + +static struct spl_image_loader *spl_ll_find_loader(uint boot_device) +{ + struct spl_image_loader *drv = + ll_entry_start(struct spl_image_loader, spl_image_loader); + const int n_ents = + ll_entry_count(struct spl_image_loader, spl_image_loader); + struct spl_image_loader *entry; + + for (entry = drv; entry != drv + n_ents; entry++) { + if (boot_device == entry->boot_device) + return entry; + } + + /* Not found */ + return NULL; +} + +static int spl_load_image(struct spl_image_info *spl_image, + struct spl_image_loader *loader) +{ + int ret; + struct spl_boot_device bootdev; + + bootdev.boot_device = loader->boot_device; + bootdev.boot_device_name = NULL; + + ret = loader->load_image(spl_image, &bootdev); +#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK + if (!ret && spl_image->dcrc_length) { + /* check data crc */ + ulong dcrc = crc32_wd(0, (unsigned char *)spl_image->dcrc_data, + spl_image->dcrc_length, CHUNKSZ_CRC32); + if (dcrc != spl_image->dcrc) { + puts("SPL: Image data CRC check failed!\n"); + ret = -EINVAL; + } + } +#endif + return ret; +} + +/** + * boot_from_devices() - Try loading a booting U-Boot from a list of devices + * + * @spl_image: Place to put the image details if successful + * @spl_boot_list: List of boot devices to try + * @count: Number of elements in spl_boot_list + * @return 0 if OK, -ve on error + */ +static int boot_from_devices(struct spl_image_info *spl_image, + u32 spl_boot_list[], int count) +{ + int i; + + for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) { + struct spl_image_loader *loader; + + loader = spl_ll_find_loader(spl_boot_list[i]); +#if defined(CONFIG_SPL_SERIAL_SUPPORT) \ + && defined(CONFIG_SPL_LIBCOMMON_SUPPORT) \ + && !defined(CONFIG_SILENT_CONSOLE) + if (loader) + printf("Trying to boot from %s\n", loader->name); + else + puts(SPL_TPL_PROMPT "Unsupported Boot Device!\n"); +#endif + if (loader && !spl_load_image(spl_image, loader)) { + spl_image->boot_device = spl_boot_list[i]; + return 0; + } + } + + return -ENODEV; +} + +#if defined(CONFIG_SPL_FRAMEWORK_BOARD_INIT_F) +void board_init_f(ulong dummy) +{ + if (CONFIG_IS_ENABLED(OF_CONTROL)) { + int ret; + + ret = spl_early_init(); + if (ret) { + debug("spl_early_init() failed: %d\n", ret); + hang(); + } + } + + preloader_console_init(); +} +#endif + +void board_init_r(gd_t *dummy1, ulong dummy2) +{ + u32 spl_boot_list[] = { + BOOT_DEVICE_NONE, + BOOT_DEVICE_NONE, + BOOT_DEVICE_NONE, + BOOT_DEVICE_NONE, + BOOT_DEVICE_NONE, + }; + struct spl_image_info spl_image; + int ret; + + debug(">>" SPL_TPL_PROMPT "board_init_r()\n"); + + spl_set_bd(); + +#if defined(CONFIG_SYS_SPL_MALLOC_START) + mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, + CONFIG_SYS_SPL_MALLOC_SIZE); + gd->flags |= GD_FLG_FULL_MALLOC_INIT; +#endif + if (!(gd->flags & GD_FLG_SPL_INIT)) { + if (spl_init()) + hang(); + } +#if !defined(CONFIG_PPC) && !defined(CONFIG_ARCH_MX6) + /* + * timer_init() does not exist on PPC systems. The timer is initialized + * and enabled (decrementer) in interrupt_init() here. + */ + timer_init(); +#endif + if (CONFIG_IS_ENABLED(BLOBLIST)) { + ret = bloblist_init(); + if (ret) { + debug("%s: Failed to set up bloblist: ret=%d\n", + __func__, ret); + puts(SPL_TPL_PROMPT "Cannot set up bloblist\n"); + hang(); + } + } + if (CONFIG_IS_ENABLED(HANDOFF)) { + int ret; + + ret = setup_spl_handoff(); + if (ret) { + puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n"); + hang(); + } + } + +#if CONFIG_IS_ENABLED(BOARD_INIT) + spl_board_init(); +#endif + +#if defined(CONFIG_SPL_WATCHDOG_SUPPORT) && CONFIG_IS_ENABLED(WDT) + initr_watchdog(); +#endif + + if (IS_ENABLED(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF) || + IS_ENABLED(CONFIG_SPL_ATF)) + dram_init_banksize(); + + bootcount_inc(); + + memset(&spl_image, '\0', sizeof(spl_image)); +#ifdef CONFIG_SYS_SPL_ARGS_ADDR + spl_image.arg = (void *)CONFIG_SYS_SPL_ARGS_ADDR; +#endif + spl_image.boot_device = BOOT_DEVICE_NONE; + board_boot_order(spl_boot_list); + + if (boot_from_devices(&spl_image, spl_boot_list, + ARRAY_SIZE(spl_boot_list))) { + puts(SPL_TPL_PROMPT "failed to boot from all boot devices\n"); + hang(); + } + + spl_perform_fixups(&spl_image); + if (CONFIG_IS_ENABLED(HANDOFF)) { + ret = write_spl_handoff(); + if (ret) + printf(SPL_TPL_PROMPT + "SPL hand-off write failed (err=%d)\n", ret); + } + if (CONFIG_IS_ENABLED(BLOBLIST)) { + ret = bloblist_finish(); + if (ret) + printf("Warning: Failed to finish bloblist (ret=%d)\n", + ret); + } + +#ifdef CONFIG_CPU_V7M + spl_image.entry_point |= 0x1; +#endif + switch (spl_image.os) { + case IH_OS_U_BOOT: + debug("Jumping to %s...\n", spl_phase_name(spl_next_phase())); + break; +#if CONFIG_IS_ENABLED(ATF) + case IH_OS_ARM_TRUSTED_FIRMWARE: + debug("Jumping to U-Boot via ARM Trusted Firmware\n"); + spl_fixup_fdt(spl_image.fdt_addr); + spl_invoke_atf(&spl_image); + break; +#endif +#if CONFIG_IS_ENABLED(OPTEE) + case IH_OS_TEE: + debug("Jumping to U-Boot via OP-TEE\n"); + spl_optee_entry(NULL, NULL, spl_image.fdt_addr, + (void *)spl_image.entry_point); + break; +#endif +#if CONFIG_IS_ENABLED(OPENSBI) + case IH_OS_OPENSBI: + debug("Jumping to U-Boot via RISC-V OpenSBI\n"); + spl_invoke_opensbi(&spl_image); + break; +#endif +#ifdef CONFIG_SPL_OS_BOOT + case IH_OS_LINUX: + debug("Jumping to Linux\n"); +#if defined(CONFIG_SYS_SPL_ARGS_ADDR) + spl_fixup_fdt((void *)CONFIG_SYS_SPL_ARGS_ADDR); +#endif + spl_board_prepare_for_linux(); + jump_to_image_linux(&spl_image); +#endif + default: + debug("Unsupported OS image.. Jumping nevertheless..\n"); + } +#if CONFIG_VAL(SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE) + debug("SPL malloc() used 0x%lx bytes (%ld KB)\n", gd->malloc_ptr, + gd->malloc_ptr / 1024); +#endif + bootstage_mark_name(get_bootstage_id(false), "end phase"); +#ifdef CONFIG_BOOTSTAGE_STASH + ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR, + CONFIG_BOOTSTAGE_STASH_SIZE); + if (ret) + debug("Failed to stash bootstage: err=%d\n", ret); +#endif + + spl_board_prepare_for_boot(); + jump_to_image_no_args(&spl_image); +} + +/* + * This requires UART clocks to be enabled. In order for this to work the + * caller must ensure that the gd pointer is valid. + */ +void preloader_console_init(void) +{ +#ifdef CONFIG_SPL_SERIAL_SUPPORT + gd->baudrate = CONFIG_BAUDRATE; + + serial_init(); /* serial communications setup */ + + gd->have_console = 1; + +#if CONFIG_IS_ENABLED(BANNER_PRINT) + puts("\nU-Boot " SPL_TPL_NAME " " PLAIN_VERSION " (" U_BOOT_DATE " - " + U_BOOT_TIME " " U_BOOT_TZ ")\n"); +#endif +#ifdef CONFIG_SPL_DISPLAY_PRINT + spl_display_print(); +#endif +#endif +} + +/** + * This function is called before the stack is changed from initial stack to + * relocated stack. It tries to dump the stack size used + */ +__weak void spl_relocate_stack_check(void) +{ +#if CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE) + ulong init_sp = gd->start_addr_sp; + ulong stack_bottom = init_sp - CONFIG_VAL(SIZE_LIMIT_PROVIDE_STACK); + u8 *ptr = (u8 *)stack_bottom; + ulong i; + + for (i = 0; i < CONFIG_VAL(SIZE_LIMIT_PROVIDE_STACK); i++) { + if (*ptr != CONFIG_VAL(SYS_STACK_F_CHECK_BYTE)) + break; + ptr++; + } + printf("SPL initial stack usage: %lu bytes\n", + CONFIG_VAL(SIZE_LIMIT_PROVIDE_STACK) - i); +#endif +} + +/** + * spl_relocate_stack_gd() - Relocate stack ready for board_init_r() execution + * + * Sometimes board_init_f() runs with a stack in SRAM but we want to use SDRAM + * for the main board_init_r() execution. This is typically because we need + * more stack space for things like the MMC sub-system. + * + * This function calculates the stack position, copies the global_data into + * place, sets the new gd (except for ARM, for which setting GD within a C + * function may not always work) and returns the new stack position. The + * caller is responsible for setting up the sp register and, in the case + * of ARM, setting up gd. + * + * All of this is done using the same layout and alignments as done in + * board_init_f_init_reserve() / board_init_f_alloc_reserve(). + * + * @return new stack location, or 0 to use the same stack + */ +ulong spl_relocate_stack_gd(void) +{ +#ifdef CONFIG_SPL_STACK_R + gd_t *new_gd; + ulong ptr = CONFIG_SPL_STACK_R_ADDR; + + if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE)) + spl_relocate_stack_check(); + +#if defined(CONFIG_SPL_SYS_MALLOC_SIMPLE) && CONFIG_VAL(SYS_MALLOC_F_LEN) + if (CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN) { + debug("SPL malloc() before relocation used 0x%lx bytes (%ld KB)\n", + gd->malloc_ptr, gd->malloc_ptr / 1024); + ptr -= CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN; + gd->malloc_base = ptr; + gd->malloc_limit = CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN; + gd->malloc_ptr = 0; + } +#endif + /* Get stack position: use 8-byte alignment for ABI compliance */ + ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16); + new_gd = (gd_t *)ptr; + memcpy(new_gd, (void *)gd, sizeof(gd_t)); +#if CONFIG_IS_ENABLED(DM) + dm_fixup_for_gd_move(new_gd); +#endif +#if !defined(CONFIG_ARM) && !defined(CONFIG_RISCV) + gd = new_gd; +#endif + return ptr; +#else + return 0; +#endif +} + +#if defined(CONFIG_BOOTCOUNT_LIMIT) && \ + ((!defined(CONFIG_TPL_BUILD) && !defined(CONFIG_SPL_BOOTCOUNT_LIMIT)) || \ + (defined(CONFIG_TPL_BUILD) && !defined(CONFIG_TPL_BOOTCOUNT_LIMIT))) +void bootcount_store(ulong a) +{ +} + +ulong bootcount_load(void) +{ + return 0; +} +#endif diff --git a/roms/u-boot/common/spl/spl_atf.c b/roms/u-boot/common/spl/spl_atf.c new file mode 100644 index 000000000..e1b68dd56 --- /dev/null +++ b/roms/u-boot/common/spl/spl_atf.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Reference to the ARM TF Project, + * plat/arm/common/arm_bl2_setup.c + * Portions copyright (c) 2013-2016, ARM Limited and Contributors. All rights + * reserved. + * Copyright (C) 2016 Rockchip Electronic Co.,Ltd + * Written by Kever Yang <kever.yang@rock-chips.com> + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#include <atf_common.h> +#include <cpu_func.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <asm/cache.h> + +/* Holds all the structures we need for bl31 parameter passing */ +struct bl2_to_bl31_params_mem { + struct bl31_params bl31_params; + struct atf_image_info bl31_image_info; + struct atf_image_info bl32_image_info; + struct atf_image_info bl33_image_info; + struct entry_point_info bl33_ep_info; + struct entry_point_info bl32_ep_info; + struct entry_point_info bl31_ep_info; +}; + +struct bl2_to_bl31_params_mem_v2 { + struct bl_params bl_params; + struct bl_params_node bl31_params_node; + struct bl_params_node bl32_params_node; + struct bl_params_node bl33_params_node; + struct atf_image_info bl31_image_info; + struct atf_image_info bl32_image_info; + struct atf_image_info bl33_image_info; + struct entry_point_info bl33_ep_info; + struct entry_point_info bl32_ep_info; + struct entry_point_info bl31_ep_info; +}; + +struct bl31_params *bl2_plat_get_bl31_params_default(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + static struct bl2_to_bl31_params_mem bl31_params_mem; + struct bl31_params *bl2_to_bl31_params; + struct entry_point_info *bl32_ep_info; + struct entry_point_info *bl33_ep_info; + + /* + * Initialise the memory for all the arguments that needs to + * be passed to BL31 + */ + memset(&bl31_params_mem, 0, sizeof(struct bl2_to_bl31_params_mem)); + + /* Assign memory for TF related information */ + bl2_to_bl31_params = &bl31_params_mem.bl31_params; + SET_PARAM_HEAD(bl2_to_bl31_params, ATF_PARAM_BL31, ATF_VERSION_1, 0); + + /* Fill BL31 related information */ + bl2_to_bl31_params->bl31_image_info = &bl31_params_mem.bl31_image_info; + SET_PARAM_HEAD(bl2_to_bl31_params->bl31_image_info, + ATF_PARAM_IMAGE_BINARY, ATF_VERSION_1, 0); + + /* Fill BL32 related information */ + bl2_to_bl31_params->bl32_ep_info = &bl31_params_mem.bl32_ep_info; + bl32_ep_info = &bl31_params_mem.bl32_ep_info; + SET_PARAM_HEAD(bl32_ep_info, ATF_PARAM_EP, ATF_VERSION_1, + ATF_EP_SECURE); + + /* secure payload is optional, so set pc to 0 if absent */ + bl32_ep_info->args.arg3 = fdt_addr; + bl32_ep_info->pc = bl32_entry ? bl32_entry : 0; + bl32_ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, + DISABLE_ALL_EXECPTIONS); + + bl2_to_bl31_params->bl32_image_info = &bl31_params_mem.bl32_image_info; + SET_PARAM_HEAD(bl2_to_bl31_params->bl32_image_info, + ATF_PARAM_IMAGE_BINARY, ATF_VERSION_1, 0); + + /* Fill BL33 related information */ + bl2_to_bl31_params->bl33_ep_info = &bl31_params_mem.bl33_ep_info; + bl33_ep_info = &bl31_params_mem.bl33_ep_info; + SET_PARAM_HEAD(bl33_ep_info, ATF_PARAM_EP, ATF_VERSION_1, + ATF_EP_NON_SECURE); + + /* BL33 expects to receive the primary CPU MPID (through x0) */ + bl33_ep_info->args.arg0 = 0xffff & read_mpidr(); + bl33_ep_info->pc = bl33_entry; + bl33_ep_info->spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXECPTIONS); + + bl2_to_bl31_params->bl33_image_info = &bl31_params_mem.bl33_image_info; + SET_PARAM_HEAD(bl2_to_bl31_params->bl33_image_info, + ATF_PARAM_IMAGE_BINARY, ATF_VERSION_1, 0); + + return bl2_to_bl31_params; +} + +__weak struct bl31_params *bl2_plat_get_bl31_params(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + return bl2_plat_get_bl31_params_default(bl32_entry, bl33_entry, + fdt_addr); +} + +struct bl_params *bl2_plat_get_bl31_params_v2_default(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + static struct bl2_to_bl31_params_mem_v2 bl31_params_mem; + struct bl_params *bl_params; + struct bl_params_node *bl_params_node; + + /* + * Initialise the memory for all the arguments that needs to + * be passed to BL31 + */ + memset(&bl31_params_mem, 0, sizeof(bl31_params_mem)); + + /* Assign memory for TF related information */ + bl_params = &bl31_params_mem.bl_params; + SET_PARAM_HEAD(bl_params, ATF_PARAM_BL_PARAMS, ATF_VERSION_2, 0); + bl_params->head = &bl31_params_mem.bl31_params_node; + + /* Fill BL31 related information */ + bl_params_node = &bl31_params_mem.bl31_params_node; + bl_params_node->image_id = ATF_BL31_IMAGE_ID; + bl_params_node->image_info = &bl31_params_mem.bl31_image_info; + bl_params_node->ep_info = &bl31_params_mem.bl31_ep_info; + bl_params_node->next_params_info = &bl31_params_mem.bl32_params_node; + SET_PARAM_HEAD(bl_params_node->image_info, ATF_PARAM_IMAGE_BINARY, + ATF_VERSION_2, 0); + + /* Fill BL32 related information */ + bl_params_node = &bl31_params_mem.bl32_params_node; + bl_params_node->image_id = ATF_BL32_IMAGE_ID; + bl_params_node->image_info = &bl31_params_mem.bl32_image_info; + bl_params_node->ep_info = &bl31_params_mem.bl32_ep_info; + bl_params_node->next_params_info = &bl31_params_mem.bl33_params_node; + SET_PARAM_HEAD(bl_params_node->ep_info, ATF_PARAM_EP, + ATF_VERSION_2, ATF_EP_SECURE); + + /* secure payload is optional, so set pc to 0 if absent */ + bl_params_node->ep_info->args.arg3 = fdt_addr; + bl_params_node->ep_info->pc = bl32_entry ? bl32_entry : 0; + bl_params_node->ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, + DISABLE_ALL_EXECPTIONS); + SET_PARAM_HEAD(bl_params_node->image_info, ATF_PARAM_IMAGE_BINARY, + ATF_VERSION_2, 0); + + /* Fill BL33 related information */ + bl_params_node = &bl31_params_mem.bl33_params_node; + bl_params_node->image_id = ATF_BL33_IMAGE_ID; + bl_params_node->image_info = &bl31_params_mem.bl33_image_info; + bl_params_node->ep_info = &bl31_params_mem.bl33_ep_info; + bl_params_node->next_params_info = NULL; + SET_PARAM_HEAD(bl_params_node->ep_info, ATF_PARAM_EP, + ATF_VERSION_2, ATF_EP_NON_SECURE); + + /* BL33 expects to receive the primary CPU MPID (through x0) */ + bl_params_node->ep_info->args.arg0 = 0xffff & read_mpidr(); + bl_params_node->ep_info->pc = bl33_entry; + bl_params_node->ep_info->spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXECPTIONS); + SET_PARAM_HEAD(bl_params_node->image_info, ATF_PARAM_IMAGE_BINARY, + ATF_VERSION_2, 0); + + return bl_params; +} + +__weak struct bl_params *bl2_plat_get_bl31_params_v2(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + return bl2_plat_get_bl31_params_v2_default(bl32_entry, bl33_entry, + fdt_addr); +} + +static inline void raw_write_daif(unsigned int daif) +{ + __asm__ __volatile__("msr DAIF, %0\n\t" : : "r" (daif) : "memory"); +} + +typedef void (*atf_entry_t)(struct bl31_params *params, void *plat_params); + +static void bl31_entry(uintptr_t bl31_entry, uintptr_t bl32_entry, + uintptr_t bl33_entry, uintptr_t fdt_addr) +{ + atf_entry_t atf_entry = (atf_entry_t)bl31_entry; + void *bl31_params; + + if (CONFIG_IS_ENABLED(ATF_LOAD_IMAGE_V2)) + bl31_params = bl2_plat_get_bl31_params_v2(bl32_entry, + bl33_entry, + fdt_addr); + else + bl31_params = bl2_plat_get_bl31_params(bl32_entry, bl33_entry, + fdt_addr); + + raw_write_daif(SPSR_EXCEPTION_MASK); + dcache_disable(); + + atf_entry(bl31_params, (void *)fdt_addr); +} + +static int spl_fit_images_find(void *blob, int os) +{ + int parent, node, ndepth = 0; + const void *data; + + if (!blob) + return -FDT_ERR_BADMAGIC; + + parent = fdt_path_offset(blob, "/fit-images"); + if (parent < 0) + return -FDT_ERR_NOTFOUND; + + for (node = fdt_next_node(blob, parent, &ndepth); + (node >= 0) && (ndepth > 0); + node = fdt_next_node(blob, node, &ndepth)) { + if (ndepth != 1) + continue; + + data = fdt_getprop(blob, node, FIT_OS_PROP, NULL); + if (!data) + continue; + + if (genimg_get_os_id(data) == os) + return node; + }; + + return -FDT_ERR_NOTFOUND; +} + +uintptr_t spl_fit_images_get_entry(void *blob, int node) +{ + ulong val; + int ret; + + ret = fit_image_get_entry(blob, node, &val); + if (ret) + ret = fit_image_get_load(blob, node, &val); + + debug("%s: entry point 0x%lx\n", __func__, val); + return val; +} + +void spl_invoke_atf(struct spl_image_info *spl_image) +{ + uintptr_t bl32_entry = 0; + uintptr_t bl33_entry = CONFIG_SYS_TEXT_BASE; + void *blob = spl_image->fdt_addr; + uintptr_t platform_param = (uintptr_t)blob; + int node; + + /* + * Find the OP-TEE binary (in /fit-images) load address or + * entry point (if different) and pass it as the BL3-2 entry + * point, this is optional. + */ + node = spl_fit_images_find(blob, IH_OS_TEE); + if (node >= 0) + bl32_entry = spl_fit_images_get_entry(blob, node); + + /* + * Find the U-Boot binary (in /fit-images) load addreess or + * entry point (if different) and pass it as the BL3-3 entry + * point. + * This will need to be extended to support Falcon mode. + */ + + node = spl_fit_images_find(blob, IH_OS_U_BOOT); + if (node >= 0) + bl33_entry = spl_fit_images_get_entry(blob, node); + + /* + * If ATF_NO_PLATFORM_PARAM is set, we override the platform + * parameter and always pass 0. This is a workaround for + * older ATF versions that have insufficiently robust (or + * overzealous) argument validation. + */ + if (CONFIG_IS_ENABLED(ATF_NO_PLATFORM_PARAM)) + platform_param = 0; + + /* + * We don't provide a BL3-2 entry yet, but this will be possible + * using similar logic. + */ + bl31_entry(spl_image->entry_point, bl32_entry, + bl33_entry, platform_param); +} diff --git a/roms/u-boot/common/spl/spl_bootrom.c b/roms/u-boot/common/spl/spl_bootrom.c new file mode 100644 index 000000000..0eefd39a5 --- /dev/null +++ b/roms/u-boot/common/spl/spl_bootrom.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmH + */ + +#include <common.h> +#include <spl.h> + +__weak int board_return_to_bootrom(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + return 0; +} + +static int spl_return_to_bootrom(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + /* + * If the board implements a way to return to its ROM (with + * the expectation that the next stage of will be booted by + * the ROM), it will implement board_return_to_bootrom() and + * should not return from it. + */ + return board_return_to_bootrom(spl_image, bootdev); +} + +SPL_LOAD_IMAGE_METHOD("BOOTROM", 0, BOOT_DEVICE_BOOTROM, spl_return_to_bootrom); diff --git a/roms/u-boot/common/spl/spl_dfu.c b/roms/u-boot/common/spl/spl_dfu.c new file mode 100644 index 000000000..5728d43ad --- /dev/null +++ b/roms/u-boot/common/spl/spl_dfu.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * Texas Instruments, <www.ti.com> + * + * Ravi B <ravibabu@ti.com> + */ +#include <common.h> +#include <env.h> +#include <spl.h> +#include <linux/compiler.h> +#include <errno.h> +#include <watchdog.h> +#include <console.h> +#include <g_dnl.h> +#include <usb.h> +#include <dfu.h> + +static int run_dfu(int usb_index, char *interface, char *devstring) +{ + int ret; + + ret = dfu_init_env_entities(interface, devstring); + if (ret) { + dfu_free_entities(); + goto exit; + } + + run_usb_dnl_gadget(usb_index, "usb_dnl_dfu"); +exit: + dfu_free_entities(); + return ret; +} + +int spl_dfu_cmd(int usbctrl, char *dfu_alt_info, char *interface, char *devstr) +{ + char *str_env; + int ret; + + /* set default environment */ + env_set_default(NULL, 0); + str_env = env_get(dfu_alt_info); + if (!str_env) { + pr_err("\"%s\" env variable not defined!\n", dfu_alt_info); + return -EINVAL; + } + + ret = env_set("dfu_alt_info", str_env); + if (ret) { + pr_err("unable to set env variable \"dfu_alt_info\"!\n"); + return -EINVAL; + } + + /* invoke dfu command */ + return run_dfu(usbctrl, interface, devstr); +} diff --git a/roms/u-boot/common/spl/spl_ext.c b/roms/u-boot/common/spl/spl_ext.c new file mode 100644 index 000000000..d73f06276 --- /dev/null +++ b/roms/u-boot/common/spl/spl_ext.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <env.h> +#include <part.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <ext4fs.h> +#include <errno.h> +#include <image.h> + +int spl_load_image_ext(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition, + const char *filename) +{ + s32 err; + struct image_header *header; + loff_t filelen, actlen; + struct disk_partition part_info = {}; + + header = spl_get_load_buffer(-sizeof(*header), sizeof(*header)); + + if (part_get_info(block_dev, partition, &part_info)) { + printf("spl: no partition table found\n"); + return -1; + } + + ext4fs_set_blk_dev(block_dev, &part_info); + + err = ext4fs_mount(0); + if (!err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: ext4fs mount err - %d\n", __func__, err); +#endif + return -1; + } + + err = ext4fs_open(filename, &filelen); + if (err < 0) { + puts("spl: ext4fs_open failed\n"); + goto end; + } + err = ext4fs_read((char *)header, 0, sizeof(struct image_header), &actlen); + if (err < 0) { + puts("spl: ext4fs_read failed\n"); + goto end; + } + + err = spl_parse_image_header(spl_image, header); + if (err < 0) { + puts("spl: ext: failed to parse image header\n"); + goto end; + } + + err = ext4fs_read((char *)spl_image->load_addr, 0, filelen, &actlen); + +end: +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + if (err < 0) + printf("%s: error reading image %s, err - %d\n", + __func__, filename, err); +#endif + + return err < 0; +} + +#ifdef CONFIG_SPL_OS_BOOT +int spl_load_image_ext_os(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition) +{ + int err; + __maybe_unused loff_t filelen, actlen; + struct disk_partition part_info = {}; + __maybe_unused char *file; + + if (part_get_info(block_dev, partition, &part_info)) { + printf("spl: no partition table found\n"); + return -1; + } + + ext4fs_set_blk_dev(block_dev, &part_info); + + err = ext4fs_mount(0); + if (!err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: ext4fs mount err - %d\n", __func__, err); +#endif + return -1; + } +#if defined(CONFIG_SPL_ENV_SUPPORT) + file = env_get("falcon_args_file"); + if (file) { + err = ext4fs_open(file, &filelen); + if (err < 0) { + puts("spl: ext4fs_open failed\n"); + goto defaults; + } + err = ext4fs_read((void *)CONFIG_SYS_SPL_ARGS_ADDR, 0, filelen, &actlen); + if (err < 0) { + printf("spl: error reading image %s, err - %d, falling back to default\n", + file, err); + goto defaults; + } + file = env_get("falcon_image_file"); + if (file) { + err = spl_load_image_ext(spl_image, block_dev, + partition, file); + if (err != 0) { + puts("spl: falling back to default\n"); + goto defaults; + } + + return 0; + } else { + puts("spl: falcon_image_file not set in environment, falling back to default\n"); + } + } else { + puts("spl: falcon_args_file not set in environment, falling back to default\n"); + } + +defaults: +#endif + + err = ext4fs_open(CONFIG_SPL_FS_LOAD_ARGS_NAME, &filelen); + if (err < 0) + puts("spl: ext4fs_open failed\n"); + + err = ext4fs_read((void *)CONFIG_SYS_SPL_ARGS_ADDR, 0, filelen, &actlen); + if (err < 0) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: error reading image %s, err - %d\n", + __func__, CONFIG_SPL_FS_LOAD_ARGS_NAME, err); +#endif + return -1; + } + + return spl_load_image_ext(spl_image, block_dev, partition, + CONFIG_SPL_FS_LOAD_KERNEL_NAME); +} +#else +int spl_load_image_ext_os(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition) +{ + return -ENOSYS; +} +#endif diff --git a/roms/u-boot/common/spl/spl_fat.c b/roms/u-boot/common/spl/spl_fat.c new file mode 100644 index 000000000..c2eb09736 --- /dev/null +++ b/roms/u-boot/common/spl/spl_fat.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * FAT Image Functions copied from spl_mmc.c + */ + +#include <common.h> +#include <env.h> +#include <log.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <fat.h> +#include <errno.h> +#include <image.h> +#include <linux/libfdt.h> + +static int fat_registered; + +static int spl_register_fat_device(struct blk_desc *block_dev, int partition) +{ + int err = 0; + + if (fat_registered) + return err; + + err = fat_register_device(block_dev, partition); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: fat register err - %d\n", __func__, err); +#endif + return err; + } + + fat_registered = 1; + + return err; +} + +static ulong spl_fit_read(struct spl_load_info *load, ulong file_offset, + ulong size, void *buf) +{ + loff_t actread; + int ret; + char *filename = (char *)load->filename; + + ret = fat_read_file(filename, buf, file_offset, size, &actread); + if (ret) + return ret; + + return actread; +} + +int spl_load_image_fat(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition, + const char *filename) +{ + int err; + struct image_header *header; + + err = spl_register_fat_device(block_dev, partition); + if (err) + goto end; + + header = spl_get_load_buffer(-sizeof(*header), sizeof(*header)); + + err = file_fat_read(filename, header, sizeof(struct image_header)); + if (err <= 0) + goto end; + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) && + image_get_magic(header) == FDT_MAGIC) { + err = file_fat_read(filename, (void *)CONFIG_SYS_LOAD_ADDR, 0); + if (err <= 0) + goto end; + err = spl_parse_image_header(spl_image, + (struct image_header *)CONFIG_SYS_LOAD_ADDR); + if (err == -EAGAIN) + return err; + if (err == 0) + err = 1; + } else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.read = spl_fit_read; + load.bl_len = 1; + load.filename = (void *)filename; + load.priv = NULL; + + return spl_load_simple_fit(spl_image, &load, 0, header); + } else { + err = spl_parse_image_header(spl_image, header); + if (err) + goto end; + + err = file_fat_read(filename, + (u8 *)(uintptr_t)spl_image->load_addr, 0); + } + +end: +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + if (err <= 0) + printf("%s: error reading image %s, err - %d\n", + __func__, filename, err); +#endif + + return (err <= 0); +} + +#ifdef CONFIG_SPL_OS_BOOT +int spl_load_image_fat_os(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition) +{ + int err; + __maybe_unused char *file; + + err = spl_register_fat_device(block_dev, partition); + if (err) + return err; + +#if defined(CONFIG_SPL_ENV_SUPPORT) && defined(CONFIG_SPL_OS_BOOT) + file = env_get("falcon_args_file"); + if (file) { + err = file_fat_read(file, (void *)CONFIG_SYS_SPL_ARGS_ADDR, 0); + if (err <= 0) { + printf("spl: error reading image %s, err - %d, falling back to default\n", + file, err); + goto defaults; + } + file = env_get("falcon_image_file"); + if (file) { + err = spl_load_image_fat(spl_image, block_dev, + partition, file); + if (err != 0) { + puts("spl: falling back to default\n"); + goto defaults; + } + + return 0; + } else + puts("spl: falcon_image_file not set in environment, falling back to default\n"); + } else + puts("spl: falcon_args_file not set in environment, falling back to default\n"); + +defaults: +#endif + + err = file_fat_read(CONFIG_SPL_FS_LOAD_ARGS_NAME, + (void *)CONFIG_SYS_SPL_ARGS_ADDR, 0); + if (err <= 0) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: error reading image %s, err - %d\n", + __func__, CONFIG_SPL_FS_LOAD_ARGS_NAME, err); +#endif + return -1; + } + + return spl_load_image_fat(spl_image, block_dev, partition, + CONFIG_SPL_FS_LOAD_KERNEL_NAME); +} +#else +int spl_load_image_fat_os(struct spl_image_info *spl_image, + struct blk_desc *block_dev, int partition) +{ + return -ENOSYS; +} +#endif diff --git a/roms/u-boot/common/spl/spl_fit.c b/roms/u-boot/common/spl/spl_fit.c new file mode 100644 index 000000000..caddf5119 --- /dev/null +++ b/roms/u-boot/common/spl/spl_fit.c @@ -0,0 +1,810 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <errno.h> +#include <fpga.h> +#include <gzip.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <spl.h> +#include <sysinfo.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ +#define CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ (64 * 1024) +#endif + +#ifndef CONFIG_SYS_BOOTM_LEN +#define CONFIG_SYS_BOOTM_LEN (64 << 20) +#endif + +struct spl_fit_info { + const void *fit; /* Pointer to a valid FIT blob */ + size_t ext_data_offset; /* Offset to FIT external data (end of FIT) */ + int images_node; /* FDT offset to "/images" node */ + int conf_node; /* FDT offset to selected configuration node */ +}; + +__weak void board_spl_fit_post_load(const void *fit) +{ +} + +__weak ulong board_spl_fit_size_align(ulong size) +{ + return size; +} + +static int find_node_from_desc(const void *fit, int node, const char *str) +{ + int child; + + if (node < 0) + return -EINVAL; + + /* iterate the FIT nodes and find a matching description */ + for (child = fdt_first_subnode(fit, node); child >= 0; + child = fdt_next_subnode(fit, child)) { + int len; + const char *desc = fdt_getprop(fit, child, "description", &len); + + if (!desc) + continue; + + if (!strcmp(desc, str)) + return child; + } + + return -ENOENT; +} + +/** + * spl_fit_get_image_name(): By using the matching configuration subnode, + * retrieve the name of an image, specified by a property name and an index + * into that. + * @fit: Pointer to the FDT blob. + * @images: Offset of the /images subnode. + * @type: Name of the property within the configuration subnode. + * @index: Index into the list of strings in this property. + * @outname: Name of the image + * + * Return: 0 on success, or a negative error number + */ +static int spl_fit_get_image_name(const struct spl_fit_info *ctx, + const char *type, int index, + const char **outname) +{ + struct udevice *sysinfo; + const char *name, *str; + __maybe_unused int node; + int len, i; + bool found = true; + + name = fdt_getprop(ctx->fit, ctx->conf_node, type, &len); + if (!name) { + debug("cannot find property '%s': %d\n", type, len); + return -EINVAL; + } + + str = name; + for (i = 0; i < index; i++) { + str = strchr(str, '\0') + 1; + if (!str || (str - name >= len)) { + found = false; + break; + } + } + + if (!found && CONFIG_IS_ENABLED(SYSINFO) && !sysinfo_get(&sysinfo)) { + int rc; + /* + * no string in the property for this index. Check if the + * sysinfo-level code can supply one. + */ + rc = sysinfo_detect(sysinfo); + if (rc) + return rc; + + rc = sysinfo_get_fit_loadable(sysinfo, index - i - 1, type, + &str); + if (rc && rc != -ENOENT) + return rc; + + if (!rc) { + /* + * The sysinfo provided a name for a loadable. + * Try to match it against the description properties + * first. If no matching node is found, use it as a + * node name. + */ + int node; + int images = fdt_path_offset(ctx->fit, FIT_IMAGES_PATH); + + node = find_node_from_desc(ctx->fit, images, str); + if (node > 0) + str = fdt_get_name(ctx->fit, node, NULL); + + found = true; + } + } + + if (!found) { + debug("no string for index %d\n", index); + return -E2BIG; + } + + *outname = str; + return 0; +} + +/** + * spl_fit_get_image_node(): By using the matching configuration subnode, + * retrieve the name of an image, specified by a property name and an index + * into that. + * @fit: Pointer to the FDT blob. + * @images: Offset of the /images subnode. + * @type: Name of the property within the configuration subnode. + * @index: Index into the list of strings in this property. + * + * Return: the node offset of the respective image node or a negative + * error number. + */ +static int spl_fit_get_image_node(const struct spl_fit_info *ctx, + const char *type, int index) +{ + const char *str; + int err; + int node; + + err = spl_fit_get_image_name(ctx, type, index, &str); + if (err) + return err; + + debug("%s: '%s'\n", type, str); + + node = fdt_subnode_offset(ctx->fit, ctx->images_node, str); + if (node < 0) { + pr_err("cannot find image node '%s': %d\n", str, node); + return -EINVAL; + } + + return node; +} + +static int get_aligned_image_offset(struct spl_load_info *info, int offset) +{ + /* + * If it is a FS read, get the first address before offset which is + * aligned to ARCH_DMA_MINALIGN. If it is raw read return the + * block number to which offset belongs. + */ + if (info->filename) + return offset & ~(ARCH_DMA_MINALIGN - 1); + + return offset / info->bl_len; +} + +static int get_aligned_image_overhead(struct spl_load_info *info, int offset) +{ + /* + * If it is a FS read, get the difference between the offset and + * the first address before offset which is aligned to + * ARCH_DMA_MINALIGN. If it is raw read return the offset within the + * block. + */ + if (info->filename) + return offset & (ARCH_DMA_MINALIGN - 1); + + return offset % info->bl_len; +} + +static int get_aligned_image_size(struct spl_load_info *info, int data_size, + int offset) +{ + data_size = data_size + get_aligned_image_overhead(info, offset); + + if (info->filename) + return data_size; + + return (data_size + info->bl_len - 1) / info->bl_len; +} + +/** + * spl_load_fit_image(): load the image described in a certain FIT node + * @info: points to information about the device to load data from + * @sector: the start sector of the FIT image on the device + * @ctx: points to the FIT context structure + * @node: offset of the DT node describing the image to load (relative + * to @fit) + * @image_info: will be filled with information about the loaded image + * If the FIT node does not contain a "load" (address) property, + * the image gets loaded to the address pointed to by the + * load_addr member in this struct, if load_addr is not 0 + * + * Return: 0 on success or a negative error number. + */ +static int spl_load_fit_image(struct spl_load_info *info, ulong sector, + const struct spl_fit_info *ctx, int node, + struct spl_image_info *image_info) +{ + int offset; + size_t length; + int len; + ulong size; + ulong load_addr; + void *load_ptr; + void *src; + ulong overhead; + int nr_sectors; + uint8_t image_comp = -1, type = -1; + const void *data; + const void *fit = ctx->fit; + bool external_data = false; + + if (IS_ENABLED(CONFIG_SPL_FPGA) || + (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP))) { + if (fit_image_get_type(fit, node, &type)) + puts("Cannot get image type.\n"); + else + debug("%s ", genimg_get_type_name(type)); + } + + if (IS_ENABLED(CONFIG_SPL_GZIP)) { + fit_image_get_comp(fit, node, &image_comp); + debug("%s ", genimg_get_comp_name(image_comp)); + } + + if (fit_image_get_load(fit, node, &load_addr)) { + if (!image_info->load_addr) { + printf("Can't load %s: No load address and no buffer\n", + fit_get_name(fit, node, NULL)); + return -ENOBUFS; + } + load_addr = image_info->load_addr; + } + + if (!fit_image_get_data_position(fit, node, &offset)) { + external_data = true; + } else if (!fit_image_get_data_offset(fit, node, &offset)) { + offset += ctx->ext_data_offset; + external_data = true; + } + + if (external_data) { + void *src_ptr; + + /* External data */ + if (fit_image_get_data_size(fit, node, &len)) + return -ENOENT; + + src_ptr = map_sysmem(ALIGN(load_addr, ARCH_DMA_MINALIGN), len); + length = len; + + overhead = get_aligned_image_overhead(info, offset); + nr_sectors = get_aligned_image_size(info, length, offset); + + if (info->read(info, + sector + get_aligned_image_offset(info, offset), + nr_sectors, src_ptr) != nr_sectors) + return -EIO; + + debug("External data: dst=%p, offset=%x, size=%lx\n", + src_ptr, offset, (unsigned long)length); + src = src_ptr + overhead; + } else { + /* Embedded data */ + if (fit_image_get_data(fit, node, &data, &length)) { + puts("Cannot get image data/size\n"); + return -ENOENT; + } + debug("Embedded data: dst=%lx, size=%lx\n", load_addr, + (unsigned long)length); + src = (void *)data; /* cast away const */ + } + + if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) { + printf("## Checking hash(es) for Image %s ... ", + fit_get_name(fit, node, NULL)); + if (!fit_image_verify_with_data(fit, node, src, length)) + return -EPERM; + puts("OK\n"); + } + + if (CONFIG_IS_ENABLED(FIT_IMAGE_POST_PROCESS)) + board_fit_image_post_process(&src, &length); + + load_ptr = map_sysmem(load_addr, length); + if (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) { + size = length; + if (gunzip(load_ptr, CONFIG_SYS_BOOTM_LEN, src, &size)) { + puts("Uncompressing error\n"); + return -EIO; + } + length = size; + } else { + memcpy(load_ptr, src, length); + } + + if (image_info) { + ulong entry_point; + + image_info->load_addr = load_addr; + image_info->size = length; + + if (!fit_image_get_entry(fit, node, &entry_point)) + image_info->entry_point = entry_point; + else + image_info->entry_point = FDT_ERROR; + } + + return 0; +} + +static bool os_takes_devicetree(uint8_t os) +{ + switch (os) { + case IH_OS_U_BOOT: + return true; + case IH_OS_LINUX: + return IS_ENABLED(CONFIG_SPL_OS_BOOT); + default: + return false; + } +} + +static int spl_fit_append_fdt(struct spl_image_info *spl_image, + struct spl_load_info *info, ulong sector, + const struct spl_fit_info *ctx) +{ + struct spl_image_info image_info; + int node, ret = 0, index = 0; + + /* + * Use the address following the image as target address for the + * device tree. + */ + image_info.load_addr = spl_image->load_addr + spl_image->size; + + /* Figure out which device tree the board wants to use */ + node = spl_fit_get_image_node(ctx, FIT_FDT_PROP, index++); + if (node < 0) { + debug("%s: cannot find FDT node\n", __func__); + + /* + * U-Boot did not find a device tree inside the FIT image. Use + * the U-Boot device tree instead. + */ + if (gd->fdt_blob) + memcpy((void *)image_info.load_addr, gd->fdt_blob, + fdt_totalsize(gd->fdt_blob)); + else + return node; + } else { + ret = spl_load_fit_image(info, sector, ctx, node, + &image_info); + if (ret < 0) + return ret; + } + + /* Make the load-address of the FDT available for the SPL framework */ + spl_image->fdt_addr = map_sysmem(image_info.load_addr, 0); + if (CONFIG_IS_ENABLED(FIT_IMAGE_TINY)) + return 0; + + if (CONFIG_IS_ENABLED(LOAD_FIT_APPLY_OVERLAY)) { + void *tmpbuffer = NULL; + + for (; ; index++) { + node = spl_fit_get_image_node(ctx, FIT_FDT_PROP, index); + if (node == -E2BIG) { + debug("%s: No additional FDT node\n", __func__); + break; + } else if (node < 0) { + debug("%s: unable to find FDT node %d\n", + __func__, index); + continue; + } + + if (!tmpbuffer) { + /* + * allocate memory to store the DT overlay + * before it is applied. It may not be used + * depending on how the overlay is stored, so + * don't fail yet if the allocation failed. + */ + tmpbuffer = malloc(CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ); + if (!tmpbuffer) + debug("%s: unable to allocate space for overlays\n", + __func__); + } + image_info.load_addr = (ulong)tmpbuffer; + ret = spl_load_fit_image(info, sector, ctx, + node, &image_info); + if (ret < 0) + break; + + /* Make room in FDT for changes from the overlay */ + ret = fdt_increase_size(spl_image->fdt_addr, + image_info.size); + if (ret < 0) + break; + + ret = fdt_overlay_apply_verbose(spl_image->fdt_addr, + (void *)image_info.load_addr); + if (ret) { + pr_err("failed to apply DT overlay %s\n", + fit_get_name(ctx->fit, node, NULL)); + break; + } + + debug("%s: DT overlay %s applied\n", __func__, + fit_get_name(ctx->fit, node, NULL)); + } + free(tmpbuffer); + if (ret) + return ret; + } + /* Try to make space, so we can inject details on the loadables */ + ret = fdt_shrink_to_minimum(spl_image->fdt_addr, 8192); + if (ret < 0) + return ret; + + return ret; +} + +static int spl_fit_record_loadable(const struct spl_fit_info *ctx, int index, + void *blob, struct spl_image_info *image) +{ + int ret = 0; + const char *name; + int node; + + if (CONFIG_IS_ENABLED(FIT_IMAGE_TINY)) + return 0; + + ret = spl_fit_get_image_name(ctx, "loadables", index, &name); + if (ret < 0) + return ret; + + node = spl_fit_get_image_node(ctx, "loadables", index); + + ret = fdt_record_loadable(blob, index, name, image->load_addr, + image->size, image->entry_point, + fdt_getprop(ctx->fit, node, "type", NULL), + fdt_getprop(ctx->fit, node, "os", NULL)); + return ret; +} + +static int spl_fit_image_is_fpga(const void *fit, int node) +{ + const char *type; + + if (!IS_ENABLED(CONFIG_SPL_FPGA)) + return 0; + + type = fdt_getprop(fit, node, FIT_TYPE_PROP, NULL); + if (!type) + return 0; + + return !strcmp(type, "fpga"); +} + +static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os) +{ + if (!CONFIG_IS_ENABLED(FIT_IMAGE_TINY) || CONFIG_IS_ENABLED(OS_BOOT)) + return fit_image_get_os(fit, noffset, os); + + const char *name = fdt_getprop(fit, noffset, FIT_OS_PROP, NULL); + if (!name) + return -ENOENT; + + /* + * We don't care what the type of the image actually is, + * only whether or not it is U-Boot. This saves some + * space by omitting the large table of OS types. + */ + if (!strcmp(name, "u-boot")) + *os = IH_OS_U_BOOT; + else + *os = IH_OS_INVALID; + + return 0; +} + +/* + * The purpose of the FIT load buffer is to provide a memory location that is + * independent of the load address of any FIT component. + */ +static void *spl_get_fit_load_buffer(size_t size) +{ + void *buf; + + buf = malloc(size); + if (!buf) { + pr_err("Could not get FIT buffer of %lu bytes\n", (ulong)size); + pr_err("\tcheck CONFIG_SYS_SPL_MALLOC_SIZE\n"); + buf = spl_get_load_buffer(0, size); + } + return buf; +} + +/* + * Weak default function to allow customizing SPL fit loading for load-only + * use cases by allowing to skip the parsing/processing of the FIT contents + * (so that this can be done separately in a more customized fashion) + */ +__weak bool spl_load_simple_fit_skip_processing(void) +{ + return false; +} + +static void warn_deprecated(const char *msg) +{ + printf("DEPRECATED: %s\n", msg); + printf("\tSee doc/uImage.FIT/source_file_format.txt\n"); +} + +static int spl_fit_upload_fpga(struct spl_fit_info *ctx, int node, + struct spl_image_info *fpga_image) +{ + const char *compatible; + int ret; + + debug("FPGA bitstream at: %x, size: %x\n", + (u32)fpga_image->load_addr, fpga_image->size); + + compatible = fdt_getprop(ctx->fit, node, "compatible", NULL); + if (!compatible) + warn_deprecated("'fpga' image without 'compatible' property"); + else if (strcmp(compatible, "u-boot,fpga-legacy")) + printf("Ignoring compatible = %s property\n", compatible); + + ret = fpga_load(0, (void *)fpga_image->load_addr, fpga_image->size, + BIT_FULL); + if (ret) { + printf("%s: Cannot load the image to the FPGA\n", __func__); + return ret; + } + + puts("FPGA image loaded from FIT\n"); + + return 0; +} + +static int spl_fit_load_fpga(struct spl_fit_info *ctx, + struct spl_load_info *info, ulong sector) +{ + int node, ret; + + struct spl_image_info fpga_image = { + .load_addr = 0, + }; + + node = spl_fit_get_image_node(ctx, "fpga", 0); + if (node < 0) + return node; + + warn_deprecated("'fpga' property in config node. Use 'loadables'"); + + /* Load the image and set up the fpga_image structure */ + ret = spl_load_fit_image(info, sector, ctx, node, &fpga_image); + if (ret) { + printf("%s: Cannot load the FPGA: %i\n", __func__, ret); + return ret; + } + + return spl_fit_upload_fpga(ctx, node, &fpga_image); +} + +static int spl_simple_fit_read(struct spl_fit_info *ctx, + struct spl_load_info *info, ulong sector, + const void *fit_header) +{ + unsigned long count, size; + int sectors; + void *buf; + + /* + * For FIT with external data, figure out where the external images + * start. This is the base for the data-offset properties in each + * image. + */ + size = ALIGN(fdt_totalsize(fit_header), 4); + size = board_spl_fit_size_align(size); + ctx->ext_data_offset = ALIGN(size, 4); + + /* + * So far we only have one block of data from the FIT. Read the entire + * thing, including that first block. + * + * For FIT with data embedded, data is loaded as part of FIT image. + * For FIT with external data, data is not loaded in this step. + */ + sectors = get_aligned_image_size(info, size, 0); + buf = spl_get_fit_load_buffer(sectors * info->bl_len); + + count = info->read(info, sector, sectors, buf); + ctx->fit = buf; + debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n", + sector, sectors, buf, count, size); + + return (count == 0) ? -EIO : 0; +} + +static int spl_simple_fit_parse(struct spl_fit_info *ctx) +{ + /* Find the correct subnode under "/configurations" */ + ctx->conf_node = fit_find_config_node(ctx->fit); + if (ctx->conf_node < 0) + return -EINVAL; + + if (IS_ENABLED(CONFIG_SPL_FIT_SIGNATURE)) { + printf("## Checking hash(es) for config %s ... ", + fit_get_name(ctx->fit, ctx->conf_node, NULL)); + if (fit_config_verify(ctx->fit, ctx->conf_node)) + return -EPERM; + puts("OK\n"); + } + + /* find the node holding the images information */ + ctx->images_node = fdt_path_offset(ctx->fit, FIT_IMAGES_PATH); + if (ctx->images_node < 0) { + debug("%s: Cannot find /images node: %d\n", __func__, + ctx->images_node); + return -EINVAL; + } + + return 0; +} + +int spl_load_simple_fit(struct spl_image_info *spl_image, + struct spl_load_info *info, ulong sector, void *fit) +{ + struct spl_image_info image_info; + struct spl_fit_info ctx; + int node = -1; + int ret; + int index = 0; + int firmware_node; + + ret = spl_simple_fit_read(&ctx, info, sector, fit); + if (ret < 0) + return ret; + + /* skip further processing if requested to enable load-only use cases */ + if (spl_load_simple_fit_skip_processing()) + return 0; + + ret = spl_simple_fit_parse(&ctx); + if (ret < 0) + return ret; + + if (IS_ENABLED(CONFIG_SPL_FPGA)) + spl_fit_load_fpga(&ctx, info, sector); + + /* + * Find the U-Boot image using the following search order: + * - start at 'firmware' (e.g. an ARM Trusted Firmware) + * - fall back 'kernel' (e.g. a Falcon-mode OS boot + * - fall back to using the first 'loadables' entry + */ + if (node < 0) + node = spl_fit_get_image_node(&ctx, FIT_FIRMWARE_PROP, 0); + + if (node < 0 && IS_ENABLED(CONFIG_SPL_OS_BOOT)) + node = spl_fit_get_image_node(&ctx, FIT_KERNEL_PROP, 0); + + if (node < 0) { + debug("could not find firmware image, trying loadables...\n"); + node = spl_fit_get_image_node(&ctx, "loadables", 0); + /* + * If we pick the U-Boot image from "loadables", start at + * the second image when later loading additional images. + */ + index = 1; + } + if (node < 0) { + debug("%s: Cannot find u-boot image node: %d\n", + __func__, node); + return -1; + } + + /* Load the image and set up the spl_image structure */ + ret = spl_load_fit_image(info, sector, &ctx, node, spl_image); + if (ret) + return ret; + + /* + * For backward compatibility, we treat the first node that is + * as a U-Boot image, if no OS-type has been declared. + */ + if (!spl_fit_image_get_os(ctx.fit, node, &spl_image->os)) + debug("Image OS is %s\n", genimg_get_os_name(spl_image->os)); + else if (!IS_ENABLED(CONFIG_SPL_OS_BOOT)) + spl_image->os = IH_OS_U_BOOT; + + /* + * Booting a next-stage U-Boot may require us to append the FDT. + * We allow this to fail, as the U-Boot image might embed its FDT. + */ + if (os_takes_devicetree(spl_image->os)) { + ret = spl_fit_append_fdt(spl_image, info, sector, &ctx); + if (ret < 0 && spl_image->os != IH_OS_U_BOOT) + return ret; + } + + firmware_node = node; + /* Now check if there are more images for us to load */ + for (; ; index++) { + uint8_t os_type = IH_OS_INVALID; + + node = spl_fit_get_image_node(&ctx, "loadables", index); + if (node < 0) + break; + + /* + * if the firmware is also a loadable, skip it because + * it already has been loaded. This is typically the case with + * u-boot.img generated by mkimage. + */ + if (firmware_node == node) + continue; + + image_info.load_addr = 0; + ret = spl_load_fit_image(info, sector, &ctx, node, &image_info); + if (ret < 0) { + printf("%s: can't load image loadables index %d (ret = %d)\n", + __func__, index, ret); + return ret; + } + + if (spl_fit_image_is_fpga(ctx.fit, node)) + spl_fit_upload_fpga(&ctx, node, &image_info); + + if (!spl_fit_image_get_os(ctx.fit, node, &os_type)) + debug("Loadable is %s\n", genimg_get_os_name(os_type)); + + if (os_takes_devicetree(os_type)) { + spl_fit_append_fdt(&image_info, info, sector, &ctx); + spl_image->fdt_addr = image_info.fdt_addr; + } + + /* + * If the "firmware" image did not provide an entry point, + * use the first valid entry point from the loadables. + */ + if (spl_image->entry_point == FDT_ERROR && + image_info.entry_point != FDT_ERROR) + spl_image->entry_point = image_info.entry_point; + + /* Record our loadables into the FDT */ + if (spl_image->fdt_addr) + spl_fit_record_loadable(&ctx, index, + spl_image->fdt_addr, + &image_info); + } + + /* + * If a platform does not provide CONFIG_SYS_UBOOT_START, U-Boot's + * Makefile will set it to 0 and it will end up as the entry point + * here. What it actually means is: use the load address. + */ + if (spl_image->entry_point == FDT_ERROR || spl_image->entry_point == 0) + spl_image->entry_point = spl_image->load_addr; + + spl_image->flags |= SPL_FIT_FOUND; + + if (IS_ENABLED(CONFIG_IMX_HAB)) + board_spl_fit_post_load(ctx.fit); + + return 0; +} diff --git a/roms/u-boot/common/spl/spl_legacy.c b/roms/u-boot/common/spl/spl_legacy.c new file mode 100644 index 000000000..82d032680 --- /dev/null +++ b/roms/u-boot/common/spl/spl_legacy.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <spl.h> + +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> + +#define LZMA_LEN (1 << 20) + +int spl_parse_legacy_header(struct spl_image_info *spl_image, + const struct image_header *header) +{ + u32 header_size = sizeof(struct image_header); + + /* check uImage header CRC */ + if (IS_ENABLED(CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK) && + !image_check_hcrc(header)) { + puts("SPL: Image header CRC check failed!\n"); + return -EINVAL; + } + + if (spl_image->flags & SPL_COPY_PAYLOAD_ONLY) { + /* + * On some system (e.g. powerpc), the load-address and + * entry-point is located at address 0. We can't load + * to 0-0x40. So skip header in this case. + */ + spl_image->load_addr = image_get_load(header); + spl_image->entry_point = image_get_ep(header); + spl_image->size = image_get_data_size(header); + } else { + spl_image->entry_point = image_get_ep(header); + /* Load including the header */ + spl_image->load_addr = image_get_load(header) - + header_size; + spl_image->size = image_get_data_size(header) + + header_size; + } + +#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK + /* store uImage data length and CRC to check later */ + spl_image->dcrc_data = image_get_load(header); + spl_image->dcrc_length = image_get_data_size(header); + spl_image->dcrc = image_get_dcrc(header); +#endif + + spl_image->os = image_get_os(header); + spl_image->name = image_get_name(header); + debug(SPL_TPL_PROMPT + "payload image: %32s load addr: 0x%lx size: %d\n", + spl_image->name, spl_image->load_addr, spl_image->size); + + return 0; +} + +/* + * This function is added explicitly to avoid code size increase, when + * no compression method is enabled. The compiler will optimize the + * following switch/case statement in spl_load_legacy_img() away due to + * Dead Code Elimination. + */ +static inline int spl_image_get_comp(const struct image_header *hdr) +{ + if (IS_ENABLED(CONFIG_SPL_LZMA)) + return image_get_comp(hdr); + + return IH_COMP_NONE; +} + +int spl_load_legacy_img(struct spl_image_info *spl_image, + struct spl_load_info *load, ulong header) +{ + __maybe_unused SizeT lzma_len; + __maybe_unused void *src; + struct image_header hdr; + ulong dataptr; + int ret; + + /* Read header into local struct */ + load->read(load, header, sizeof(hdr), &hdr); + + ret = spl_parse_image_header(spl_image, &hdr); + if (ret) + return ret; + + dataptr = header + sizeof(hdr); + + /* Read image */ + switch (spl_image_get_comp(&hdr)) { + case IH_COMP_NONE: + load->read(load, dataptr, spl_image->size, + (void *)(unsigned long)spl_image->load_addr); + break; + + case IH_COMP_LZMA: + lzma_len = LZMA_LEN; + + debug("LZMA: Decompressing %08lx to %08lx\n", + dataptr, spl_image->load_addr); + src = malloc(spl_image->size); + if (!src) { + printf("Unable to allocate %d bytes for LZMA\n", + spl_image->size); + return -ENOMEM; + } + + load->read(load, dataptr, spl_image->size, src); + ret = lzmaBuffToBuffDecompress((void *)spl_image->load_addr, + &lzma_len, src, spl_image->size); + if (ret) { + printf("LZMA decompression error: %d\n", ret); + return ret; + } + + spl_image->size = lzma_len; + break; + + default: + debug("Compression method %s is not supported\n", + genimg_get_comp_short_name(image_get_comp(&hdr))); + return -EINVAL; + } + + return 0; +} diff --git a/roms/u-boot/common/spl/spl_mmc.c b/roms/u-boot/common/spl/spl_mmc.c new file mode 100644 index 000000000..add2785b4 --- /dev/null +++ b/roms/u-boot/common/spl/spl_mmc.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Aneesh V <aneesh@ti.com> + */ +#include <common.h> +#include <dm.h> +#include <log.h> +#include <part.h> +#include <spl.h> +#include <linux/compiler.h> +#include <errno.h> +#include <asm/u-boot.h> +#include <errno.h> +#include <mmc.h> +#include <image.h> + +static int mmc_load_legacy(struct spl_image_info *spl_image, struct mmc *mmc, + ulong sector, struct image_header *header) +{ + u32 image_size_sectors; + unsigned long count; + int ret; + + ret = spl_parse_image_header(spl_image, header); + if (ret) + return ret; + + /* convert size to sectors - round up */ + image_size_sectors = (spl_image->size + mmc->read_bl_len - 1) / + mmc->read_bl_len; + + /* Read the header too to avoid extra memcpy */ + count = blk_dread(mmc_get_blk_desc(mmc), sector, image_size_sectors, + (void *)(ulong)spl_image->load_addr); + debug("read %x sectors to %lx\n", image_size_sectors, + spl_image->load_addr); + if (count != image_size_sectors) + return -EIO; + + return 0; +} + +static ulong h_spl_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + struct mmc *mmc = load->dev; + + return blk_dread(mmc_get_blk_desc(mmc), sector, count, buf); +} + +static __maybe_unused unsigned long spl_mmc_raw_uboot_offset(int part) +{ +#if IS_ENABLED(CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR) + if (part == 0) + return CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_DATA_PART_OFFSET; +#endif + + return 0; +} + +static __maybe_unused +int mmc_load_image_raw_sector(struct spl_image_info *spl_image, + struct mmc *mmc, unsigned long sector) +{ + unsigned long count; + struct image_header *header; + struct blk_desc *bd = mmc_get_blk_desc(mmc); + int ret = 0; + + header = spl_get_load_buffer(-sizeof(*header), bd->blksz); + + /* read image header to find the image size & load address */ + count = blk_dread(bd, sector, 1, header); + debug("hdr read sector %lx, count=%lu\n", sector, count); + if (count == 0) { + ret = -EIO; + goto end; + } + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.dev = mmc; + load.priv = NULL; + load.filename = NULL; + load.bl_len = mmc->read_bl_len; + load.read = h_spl_load_read; + ret = spl_load_simple_fit(spl_image, &load, sector, header); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + load.dev = mmc; + load.priv = NULL; + load.filename = NULL; + load.bl_len = mmc->read_bl_len; + load.read = h_spl_load_read; + + ret = spl_load_imx_container(spl_image, &load, sector); + } else { + ret = mmc_load_legacy(spl_image, mmc, sector, header); + } + +end: + if (ret) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("mmc_load_image_raw_sector: mmc block read error\n"); +#endif + return -1; + } + + return 0; +} + +static int spl_mmc_get_device_index(u32 boot_device) +{ + switch (boot_device) { + case BOOT_DEVICE_MMC1: + return 0; + case BOOT_DEVICE_MMC2: + case BOOT_DEVICE_MMC2_2: + return 1; + } + +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: unsupported mmc boot device.\n"); +#endif + + return -ENODEV; +} + +static int spl_mmc_find_device(struct mmc **mmcp, u32 boot_device) +{ + int err, mmc_dev; + + mmc_dev = spl_mmc_get_device_index(boot_device); + if (mmc_dev < 0) + return mmc_dev; + +#if CONFIG_IS_ENABLED(DM_MMC) + err = mmc_init_device(mmc_dev); +#else + err = mmc_initialize(NULL); +#endif /* DM_MMC */ + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: could not initialize mmc. error: %d\n", err); +#endif + return err; + } + *mmcp = find_mmc_device(mmc_dev); + err = *mmcp ? 0 : -ENODEV; + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: could not find mmc device %d. error: %d\n", + mmc_dev, err); +#endif + return err; + } + + return 0; +} + +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION +static int mmc_load_image_raw_partition(struct spl_image_info *spl_image, + struct mmc *mmc, int partition, + unsigned long sector) +{ + struct disk_partition info; + int err; + +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION_TYPE + int type_part; + /* Only support MBR so DOS_ENTRY_NUMBERS */ + for (type_part = 1; type_part <= DOS_ENTRY_NUMBERS; type_part++) { + err = part_get_info(mmc_get_blk_desc(mmc), type_part, &info); + if (err) + continue; + if (info.sys_ind == + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION_TYPE) { + partition = type_part; + break; + } + } +#endif + + err = part_get_info(mmc_get_blk_desc(mmc), partition, &info); + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("spl: partition error\n"); +#endif + return -1; + } + +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR + return mmc_load_image_raw_sector(spl_image, mmc, info.start + sector); +#else + return mmc_load_image_raw_sector(spl_image, mmc, info.start); +#endif +} +#endif + +#ifdef CONFIG_SPL_OS_BOOT +static int mmc_load_image_raw_os(struct spl_image_info *spl_image, + struct mmc *mmc) +{ + int ret; + +#if defined(CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR) + unsigned long count; + + count = blk_dread(mmc_get_blk_desc(mmc), + CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR, + CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS, + (void *) CONFIG_SYS_SPL_ARGS_ADDR); + if (count != CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("mmc_load_image_raw_os: mmc block read error\n"); +#endif + return -1; + } +#endif /* CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR */ + + ret = mmc_load_image_raw_sector(spl_image, mmc, + CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR); + if (ret) + return ret; + + if (spl_image->os != IH_OS_LINUX) { + puts("Expected Linux image is not found. Trying to start U-boot\n"); + return -ENOENT; + } + + return 0; +} +#else +int spl_start_uboot(void) +{ + return 1; +} +static int mmc_load_image_raw_os(struct spl_image_info *spl_image, + struct mmc *mmc) +{ + return -ENOSYS; +} +#endif + +#ifdef CONFIG_SYS_MMCSD_FS_BOOT_PARTITION +static int spl_mmc_do_fs_boot(struct spl_image_info *spl_image, struct mmc *mmc, + const char *filename) +{ + int err = -ENOSYS; + +#ifdef CONFIG_SPL_FS_FAT + if (!spl_start_uboot()) { + err = spl_load_image_fat_os(spl_image, mmc_get_blk_desc(mmc), + CONFIG_SYS_MMCSD_FS_BOOT_PARTITION); + if (!err) + return err; + } +#ifdef CONFIG_SPL_FS_LOAD_PAYLOAD_NAME + err = spl_load_image_fat(spl_image, mmc_get_blk_desc(mmc), + CONFIG_SYS_MMCSD_FS_BOOT_PARTITION, + filename); + if (!err) + return err; +#endif +#endif +#ifdef CONFIG_SPL_FS_EXT4 + if (!spl_start_uboot()) { + err = spl_load_image_ext_os(spl_image, mmc_get_blk_desc(mmc), + CONFIG_SYS_MMCSD_FS_BOOT_PARTITION); + if (!err) + return err; + } +#ifdef CONFIG_SPL_FS_LOAD_PAYLOAD_NAME + err = spl_load_image_ext(spl_image, mmc_get_blk_desc(mmc), + CONFIG_SYS_MMCSD_FS_BOOT_PARTITION, + filename); + if (!err) + return err; +#endif +#endif + +#if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4) + err = -ENOENT; +#endif + + return err; +} +#else +static int spl_mmc_do_fs_boot(struct spl_image_info *spl_image, struct mmc *mmc, + const char *filename) +{ + return -ENOSYS; +} +#endif + +u32 __weak spl_mmc_boot_mode(const u32 boot_device) +{ +#if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4) + return MMCSD_MODE_FS; +#elif defined(CONFIG_SUPPORT_EMMC_BOOT) + return MMCSD_MODE_EMMCBOOT; +#else + return MMCSD_MODE_RAW; +#endif +} + +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION +int __weak spl_mmc_boot_partition(const u32 boot_device) +{ + return CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION; +} +#endif + +unsigned long __weak spl_mmc_get_uboot_raw_sector(struct mmc *mmc, + unsigned long raw_sect) +{ + return raw_sect; +} + +int spl_mmc_load(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev, + const char *filename, + int raw_part, + unsigned long raw_sect) +{ + static struct mmc *mmc; + u32 boot_mode; + int err = 0; + __maybe_unused int part = 0; + + /* Perform peripheral init only once */ + if (!mmc) { + err = spl_mmc_find_device(&mmc, bootdev->boot_device); + if (err) + return err; + + err = mmc_init(mmc); + if (err) { + mmc = NULL; +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: mmc init failed with error: %d\n", err); +#endif + return err; + } + } + + boot_mode = spl_mmc_boot_mode(bootdev->boot_device); + err = -EINVAL; + switch (boot_mode) { + case MMCSD_MODE_EMMCBOOT: +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_EMMC_BOOT_PARTITION + part = CONFIG_SYS_MMCSD_RAW_MODE_EMMC_BOOT_PARTITION; +#else + /* + * We need to check what the partition is configured to. + * 1 and 2 match up to boot0 / boot1 and 7 is user data + * which is the first physical partition (0). + */ + part = (mmc->part_config >> 3) & PART_ACCESS_MASK; + + if (part == 7) + part = 0; +#endif + + if (CONFIG_IS_ENABLED(MMC_TINY)) + err = mmc_switch_part(mmc, part); + else + err = blk_dselect_hwpart(mmc_get_blk_desc(mmc), part); + + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + puts("spl: mmc partition switch failed\n"); +#endif + return err; + } + /* Fall through */ + case MMCSD_MODE_RAW: + debug("spl: mmc boot mode: raw\n"); + + if (!spl_start_uboot()) { + err = mmc_load_image_raw_os(spl_image, mmc); + if (!err) + return err; + } + + raw_sect = spl_mmc_get_uboot_raw_sector(mmc, raw_sect); + +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION + err = mmc_load_image_raw_partition(spl_image, mmc, raw_part, + raw_sect); + if (!err) + return err; +#endif +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR + err = mmc_load_image_raw_sector(spl_image, mmc, + raw_sect + spl_mmc_raw_uboot_offset(part)); + if (!err) + return err; +#endif + /* If RAW mode fails, try FS mode. */ + case MMCSD_MODE_FS: + debug("spl: mmc boot mode: fs\n"); + + err = spl_mmc_do_fs_boot(spl_image, mmc, filename); + if (!err) + return err; + + break; +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + default: + puts("spl: mmc: wrong boot mode\n"); +#endif + } + + return err; +} + +int spl_mmc_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + return spl_mmc_load(spl_image, bootdev, +#ifdef CONFIG_SPL_FS_LOAD_PAYLOAD_NAME + CONFIG_SPL_FS_LOAD_PAYLOAD_NAME, +#else + NULL, +#endif +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION + spl_mmc_boot_partition(bootdev->boot_device), +#else + 0, +#endif +#ifdef CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR + CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR); +#else + 0); +#endif +} + +SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image); +SPL_LOAD_IMAGE_METHOD("MMC2", 0, BOOT_DEVICE_MMC2, spl_mmc_load_image); +SPL_LOAD_IMAGE_METHOD("MMC2_2", 0, BOOT_DEVICE_MMC2_2, spl_mmc_load_image); diff --git a/roms/u-boot/common/spl/spl_nand.c b/roms/u-boot/common/spl/spl_nand.c new file mode 100644 index 000000000..59f4a84a3 --- /dev/null +++ b/roms/u-boot/common/spl/spl_nand.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + */ +#include <common.h> +#include <config.h> +#include <fdt_support.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <asm/io.h> +#include <nand.h> +#include <linux/libfdt_env.h> +#include <fdt.h> + +uint32_t __weak spl_nand_get_uboot_raw_page(void) +{ + return CONFIG_SYS_NAND_U_BOOT_OFFS; +} + +#if defined(CONFIG_SPL_NAND_RAW_ONLY) +static int spl_nand_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + nand_init(); + + printf("Loading U-Boot from 0x%08x (size 0x%08x) to 0x%08x\n", + CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, + CONFIG_SYS_NAND_U_BOOT_DST); + + nand_spl_load_image(spl_nand_get_uboot_raw_page(), + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + spl_set_header_raw_uboot(spl_image); + nand_deselect(); + + return 0; +} +#else + +static ulong spl_nand_fit_read(struct spl_load_info *load, ulong offs, + ulong size, void *dst) +{ + int err; +#ifdef CONFIG_SYS_NAND_BLOCK_SIZE + ulong sector; + + sector = *(int *)load->priv; + offs = sector + nand_spl_adjust_offset(sector, offs - sector); +#else + offs *= load->bl_len; + size *= load->bl_len; +#endif + err = nand_spl_load_image(offs, size, dst); + if (err) + return 0; + + return size / load->bl_len; +} + +struct mtd_info * __weak nand_get_mtd(void) +{ + return NULL; +} + +static int spl_nand_load_element(struct spl_image_info *spl_image, + int offset, struct image_header *header) +{ + struct mtd_info *mtd = nand_get_mtd(); + int bl_len = mtd ? mtd->writesize : 1; + int err; + + err = nand_spl_load_image(offset, sizeof(*header), (void *)header); + if (err) + return err; + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.dev = NULL; + load.priv = &offset; + load.filename = NULL; + load.bl_len = bl_len; + load.read = spl_nand_fit_read; + return spl_load_simple_fit(spl_image, &load, offset / bl_len, header); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + load.dev = NULL; + load.priv = NULL; + load.filename = NULL; + load.bl_len = bl_len; + load.read = spl_nand_fit_read; + return spl_load_imx_container(spl_image, &load, offset / bl_len); + } else { + err = spl_parse_image_header(spl_image, header); + if (err) + return err; + return nand_spl_load_image(offset, spl_image->size, + (void *)(ulong)spl_image->load_addr); + } +} + +static int spl_nand_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int err; + struct image_header *header; + int *src __attribute__((unused)); + int *dst __attribute__((unused)); + +#ifdef CONFIG_SPL_NAND_SOFTECC + debug("spl: nand - using sw ecc\n"); +#else + debug("spl: nand - using hw ecc\n"); +#endif + nand_init(); + + header = spl_get_load_buffer(0, sizeof(*header)); + +#ifdef CONFIG_SPL_OS_BOOT + if (!spl_start_uboot()) { + /* + * load parameter image + * load to temp position since nand_spl_load_image reads + * a whole block which is typically larger than + * CONFIG_CMD_SPL_WRITE_SIZE therefore may overwrite + * following sections like BSS + */ + nand_spl_load_image(CONFIG_CMD_SPL_NAND_OFS, + CONFIG_CMD_SPL_WRITE_SIZE, + (void *)CONFIG_SYS_TEXT_BASE); + /* copy to destintion */ + for (dst = (int *)CONFIG_SYS_SPL_ARGS_ADDR, + src = (int *)CONFIG_SYS_TEXT_BASE; + src < (int *)(CONFIG_SYS_TEXT_BASE + + CONFIG_CMD_SPL_WRITE_SIZE); + src++, dst++) { + writel(readl(src), dst); + } + + /* load linux */ + nand_spl_load_image(CONFIG_SYS_NAND_SPL_KERNEL_OFFS, + sizeof(*header), (void *)header); + err = spl_parse_image_header(spl_image, header); + if (err) + return err; + if (header->ih_os == IH_OS_LINUX) { + /* happy - was a linux */ + err = nand_spl_load_image( + CONFIG_SYS_NAND_SPL_KERNEL_OFFS, + spl_image->size, + (void *)spl_image->load_addr); + nand_deselect(); + return err; + } else { + puts("The Expected Linux image was not " + "found. Please check your NAND " + "configuration.\n"); + puts("Trying to start u-boot now...\n"); + } + } +#endif +#ifdef CONFIG_NAND_ENV_DST + spl_nand_load_element(spl_image, CONFIG_ENV_OFFSET, header); +#ifdef CONFIG_ENV_OFFSET_REDUND + spl_nand_load_element(spl_image, CONFIG_ENV_OFFSET_REDUND, header); +#endif +#endif + /* Load u-boot */ + err = spl_nand_load_element(spl_image, spl_nand_get_uboot_raw_page(), + header); +#ifdef CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND +#if CONFIG_SYS_NAND_U_BOOT_OFFS != CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND + if (err) + err = spl_nand_load_element(spl_image, + CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND, + header); +#endif +#endif + nand_deselect(); + return err; +} +#endif +/* Use priorty 1 so that Ubi can override this */ +SPL_LOAD_IMAGE_METHOD("NAND", 1, BOOT_DEVICE_NAND, spl_nand_load_image); diff --git a/roms/u-boot/common/spl/spl_net.c b/roms/u-boot/common/spl/spl_net.c new file mode 100644 index 000000000..e140a6306 --- /dev/null +++ b/roms/u-boot/common/spl/spl_net.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2012 + * Ilya Yanok <ilya.yanok@gmail.com> + */ +#include <common.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <net.h> +#include <linux/libfdt.h> + +#if defined(CONFIG_SPL_ETH_SUPPORT) || defined(CONFIG_SPL_USB_ETHER) +static ulong spl_net_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + debug("%s: sector %lx, count %lx, buf %lx\n", + __func__, sector, count, (ulong)buf); + memcpy(buf, (void *)(image_load_addr + sector), count); + return count; +} + +static int spl_net_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct image_header *header = (struct image_header *)image_load_addr; + int rv; + + env_init(); + env_relocate(); + env_set("autoload", "yes"); + rv = eth_initialize(); + if (rv == 0) { + printf("No Ethernet devices found\n"); + return -ENODEV; + } + if (bootdev->boot_device_name) + env_set("ethact", bootdev->boot_device_name); + rv = net_loop(BOOTP); + if (rv < 0) { + printf("Problem booting with BOOTP\n"); + return rv; + } + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.bl_len = 1; + load.read = spl_net_load_read; + rv = spl_load_simple_fit(spl_image, &load, 0, header); + } else { + debug("Legacy image\n"); + + rv = spl_parse_image_header(spl_image, header); + if (rv) + return rv; + + memcpy((void *)spl_image->load_addr, header, spl_image->size); + } + + return rv; +} +#endif + +#ifdef CONFIG_SPL_ETH_SUPPORT +int spl_net_load_image_cpgmac(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ +#ifdef CONFIG_SPL_ETH_DEVICE + bootdev->boot_device_name = CONFIG_SPL_ETH_DEVICE; +#endif + + return spl_net_load_image(spl_image, bootdev); +} +SPL_LOAD_IMAGE_METHOD("eth device", 0, BOOT_DEVICE_CPGMAC, + spl_net_load_image_cpgmac); +#endif + +#ifdef CONFIG_SPL_USB_ETHER +int spl_net_load_image_usb(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + bootdev->boot_device_name = "usb_ether"; +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + usb_ether_init(); +#endif + return spl_net_load_image(spl_image, bootdev); +} +SPL_LOAD_IMAGE_METHOD("USB eth", 0, BOOT_DEVICE_USBETH, spl_net_load_image_usb); +#endif diff --git a/roms/u-boot/common/spl/spl_nor.c b/roms/u-boot/common/spl/spl_nor.c new file mode 100644 index 000000000..5270401db --- /dev/null +++ b/roms/u-boot/common/spl/spl_nor.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <image.h> +#include <log.h> +#include <spl.h> + +static ulong spl_nor_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + debug("%s: sector %lx, count %lx, buf %p\n", + __func__, sector, count, buf); + memcpy(buf, (void *)sector, count); + + return count; +} + +unsigned long __weak spl_nor_get_uboot_base(void) +{ + return CONFIG_SYS_UBOOT_BASE; +} + +static int spl_nor_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + __maybe_unused const struct image_header *header; + __maybe_unused struct spl_load_info load; + + /* + * Loading of the payload to SDRAM is done with skipping of + * the mkimage header in this SPL NOR driver + */ + spl_image->flags |= SPL_COPY_PAYLOAD_ONLY; + +#ifdef CONFIG_SPL_OS_BOOT + if (!spl_start_uboot()) { + /* + * Load Linux from its location in NOR flash to its defined + * location in SDRAM + */ + header = (const struct image_header *)CONFIG_SYS_OS_BASE; +#ifdef CONFIG_SPL_LOAD_FIT + if (image_get_magic(header) == FDT_MAGIC) { + int ret; + + debug("Found FIT\n"); + load.bl_len = 1; + load.read = spl_nor_load_read; + + ret = spl_load_simple_fit(spl_image, &load, + CONFIG_SYS_OS_BASE, + (void *)header); + +#if defined CONFIG_SYS_SPL_ARGS_ADDR && defined CONFIG_CMD_SPL_NOR_OFS + memcpy((void *)CONFIG_SYS_SPL_ARGS_ADDR, + (void *)CONFIG_CMD_SPL_NOR_OFS, + CONFIG_CMD_SPL_WRITE_SIZE); +#endif + return ret; + } +#endif + if (image_get_os(header) == IH_OS_LINUX) { + /* happy - was a Linux */ + int ret; + + ret = spl_parse_image_header(spl_image, header); + if (ret) + return ret; + + memcpy((void *)spl_image->load_addr, + (void *)(CONFIG_SYS_OS_BASE + + sizeof(struct image_header)), + spl_image->size); +#ifdef CONFIG_SYS_FDT_BASE + spl_image->arg = (void *)CONFIG_SYS_FDT_BASE; +#endif + + return 0; + } else { + puts("The Expected Linux image was not found.\n" + "Please check your NOR configuration.\n" + "Trying to start u-boot now...\n"); + } + } +#endif + + /* + * Load real U-Boot from its location in NOR flash to its + * defined location in SDRAM + */ +#ifdef CONFIG_SPL_LOAD_FIT + header = (const struct image_header *)spl_nor_get_uboot_base(); + if (image_get_magic(header) == FDT_MAGIC) { + debug("Found FIT format U-Boot\n"); + load.bl_len = 1; + load.read = spl_nor_load_read; + return spl_load_simple_fit(spl_image, &load, + spl_nor_get_uboot_base(), + (void *)header); + } +#endif + if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + load.bl_len = 1; + load.read = spl_nor_load_read; + return spl_load_imx_container(spl_image, &load, + spl_nor_get_uboot_base()); + } + + /* Legacy image handling */ + if (IS_ENABLED(CONFIG_SPL_LEGACY_IMAGE_SUPPORT)) { + load.bl_len = 1; + load.read = spl_nor_load_read; + return spl_load_legacy_img(spl_image, &load, + spl_nor_get_uboot_base()); + } + + return 0; +} +SPL_LOAD_IMAGE_METHOD("NOR", 0, BOOT_DEVICE_NOR, spl_nor_load_image); diff --git a/roms/u-boot/common/spl/spl_onenand.c b/roms/u-boot/common/spl/spl_onenand.c new file mode 100644 index 000000000..93cbf47e8 --- /dev/null +++ b/roms/u-boot/common/spl/spl_onenand.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 + * ISEE 2007 SL - Enric Balletbo i Serra <eballetbo@iseebcn.com> + * + * Based on common/spl/spl_nand.c + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + */ +#include <common.h> +#include <config.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <asm/io.h> +#include <onenand_uboot.h> + +static int spl_onenand_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct image_header *header; + int ret; + + debug("spl: onenand\n"); + + header = spl_get_load_buffer(0, CONFIG_SYS_ONENAND_PAGE_SIZE); + /* Load u-boot */ + onenand_spl_load_image(CONFIG_SYS_ONENAND_U_BOOT_OFFS, + CONFIG_SYS_ONENAND_PAGE_SIZE, (void *)header); + ret = spl_parse_image_header(spl_image, header); + if (ret) + return ret; + onenand_spl_load_image(CONFIG_SYS_ONENAND_U_BOOT_OFFS, + spl_image->size, (void *)spl_image->load_addr); + + return 0; +} +/* Use priorty 1 so that Ubi can override this */ +SPL_LOAD_IMAGE_METHOD("OneNAND", 1, BOOT_DEVICE_ONENAND, + spl_onenand_load_image); diff --git a/roms/u-boot/common/spl/spl_opensbi.c b/roms/u-boot/common/spl/spl_opensbi.c new file mode 100644 index 000000000..1c0abf855 --- /dev/null +++ b/roms/u-boot/common/spl/spl_opensbi.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Fraunhofer AISEC, + * Lukas Auer <lukas.auer@aisec.fraunhofer.de> + * + * Based on common/spl/spl_atf.c + */ +#include <common.h> +#include <cpu_func.h> +#include <errno.h> +#include <hang.h> +#include <image.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/smp.h> +#include <opensbi.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct fw_dynamic_info opensbi_info; + +static int spl_opensbi_find_uboot_node(void *blob, int *uboot_node) +{ + int fit_images_node, node; + const char *fit_os; + + fit_images_node = fdt_path_offset(blob, "/fit-images"); + if (fit_images_node < 0) + return -ENODEV; + + fdt_for_each_subnode(node, blob, fit_images_node) { + fit_os = fdt_getprop(blob, node, FIT_OS_PROP, NULL); + if (!fit_os) + continue; + + if (genimg_get_os_id(fit_os) == IH_OS_U_BOOT) { + *uboot_node = node; + return 0; + } + } + + return -ENODEV; +} + +void spl_invoke_opensbi(struct spl_image_info *spl_image) +{ + int ret, uboot_node; + ulong uboot_entry; + void (*opensbi_entry)(ulong hartid, ulong dtb, ulong info); + + if (!spl_image->fdt_addr) { + pr_err("No device tree specified in SPL image\n"); + hang(); + } + + /* Find U-Boot image in /fit-images */ + ret = spl_opensbi_find_uboot_node(spl_image->fdt_addr, &uboot_node); + if (ret) { + pr_err("Can't find U-Boot node, %d\n", ret); + hang(); + } + + /* Get U-Boot entry point */ + ret = fit_image_get_entry(spl_image->fdt_addr, uboot_node, &uboot_entry); + if (ret) + ret = fit_image_get_load(spl_image->fdt_addr, uboot_node, &uboot_entry); + + /* Prepare obensbi_info object */ + opensbi_info.magic = FW_DYNAMIC_INFO_MAGIC_VALUE; + opensbi_info.version = FW_DYNAMIC_INFO_VERSION; + opensbi_info.next_addr = uboot_entry; + opensbi_info.next_mode = FW_DYNAMIC_INFO_NEXT_MODE_S; + opensbi_info.options = SBI_SCRATCH_NO_BOOT_PRINTS; + opensbi_info.boot_hart = gd->arch.boot_hart; + + opensbi_entry = (void (*)(ulong, ulong, ulong))spl_image->entry_point; + invalidate_icache_all(); + +#ifdef CONFIG_SPL_SMP + /* + * Start OpenSBI on all secondary harts and wait for acknowledgment. + * + * OpenSBI first relocates itself to its link address. This is done by + * the main hart. To make sure no hart is still running U-Boot SPL + * during relocation, we wait for all secondary harts to acknowledge + * the call-function request before entering OpenSBI on the main hart. + * Otherwise, code corruption can occur if the link address ranges of + * U-Boot SPL and OpenSBI overlap. + */ + ret = smp_call_function((ulong)spl_image->entry_point, + (ulong)spl_image->fdt_addr, + (ulong)&opensbi_info, 1); + if (ret) + hang(); +#endif + opensbi_entry(gd->arch.boot_hart, (ulong)spl_image->fdt_addr, + (ulong)&opensbi_info); +} diff --git a/roms/u-boot/common/spl/spl_optee.S b/roms/u-boot/common/spl/spl_optee.S new file mode 100644 index 000000000..8bd1949dd --- /dev/null +++ b/roms/u-boot/common/spl/spl_optee.S @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017 Rockchip Electronic Co.,Ltd + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(spl_optee_entry) + ldr lr, =CONFIG_SYS_TEXT_BASE + mov pc, r3 +ENDPROC(spl_optee_entry) diff --git a/roms/u-boot/common/spl/spl_ram.c b/roms/u-boot/common/spl/spl_ram.c new file mode 100644 index 000000000..df1d5b43d --- /dev/null +++ b/roms/u-boot/common/spl/spl_ram.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * Xilinx, Inc. + * + * (C) Copyright 2016 + * Toradex AG + * + * Michal Simek <michal.simek@xilinx.com> + * Stefan Agner <stefan.agner@toradex.com> + */ +#include <common.h> +#include <binman_sym.h> +#include <image.h> +#include <log.h> +#include <mapmem.h> +#include <spl.h> +#include <linux/libfdt.h> + +#ifndef CONFIG_SPL_LOAD_FIT_ADDRESS +# define CONFIG_SPL_LOAD_FIT_ADDRESS 0 +#endif + +static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + debug("%s: sector %lx, count %lx, buf %lx\n", + __func__, sector, count, (ulong)buf); + memcpy(buf, (void *)(CONFIG_SPL_LOAD_FIT_ADDRESS + sector), count); + return count; +} + +static int spl_ram_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct image_header *header; + + header = (struct image_header *)CONFIG_SPL_LOAD_FIT_ADDRESS; + +#if CONFIG_IS_ENABLED(DFU) + if (bootdev->boot_device == BOOT_DEVICE_DFU) + spl_dfu_cmd(0, "dfu_alt_info_ram", "ram", "0"); +#endif + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.bl_len = 1; + load.read = spl_ram_load_read; + spl_load_simple_fit(spl_image, &load, 0, header); + } else { + ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos); + + debug("Legacy image\n"); + /* + * Get the header. It will point to an address defined by + * handoff which will tell where the image located inside + * the flash. + */ + debug("u_boot_pos = %lx\n", u_boot_pos); + if (u_boot_pos == BINMAN_SYM_MISSING) { + /* + * No binman support or no information. For now, fix it + * to the address pointed to by U-Boot. + */ + u_boot_pos = (ulong)spl_get_load_buffer(-sizeof(*header), + sizeof(*header)); + } + header = (struct image_header *)map_sysmem(u_boot_pos, 0); + + spl_parse_image_header(spl_image, header); + } + + return 0; +} +#if CONFIG_IS_ENABLED(RAM_DEVICE) +SPL_LOAD_IMAGE_METHOD("RAM", 0, BOOT_DEVICE_RAM, spl_ram_load_image); +#endif +#if CONFIG_IS_ENABLED(DFU) +SPL_LOAD_IMAGE_METHOD("DFU", 0, BOOT_DEVICE_DFU, spl_ram_load_image); +#endif + + diff --git a/roms/u-boot/common/spl/spl_sata.c b/roms/u-boot/common/spl/spl_sata.c new file mode 100644 index 000000000..e108af057 --- /dev/null +++ b/roms/u-boot/common/spl/spl_sata.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2013 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * Derived work from spl_usb.c + */ + +#include <common.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <sata.h> +#include <scsi.h> +#include <errno.h> +#include <fat.h> +#include <image.h> + +#ifndef CONFIG_SYS_SATA_FAT_BOOT_PARTITION +#define CONFIG_SYS_SATA_FAT_BOOT_PARTITION 1 +#endif + +#ifndef CONFIG_SPL_FS_LOAD_PAYLOAD_NAME +#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.img" +#endif + +#ifndef CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR +/* Dummy value to make the compiler happy */ +#define CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR 0x100 +#endif + +static int spl_sata_load_image_raw(struct spl_image_info *spl_image, + struct blk_desc *stor_dev, unsigned long sector) +{ + struct image_header *header; + unsigned long count; + u32 image_size_sectors; + int ret; + + header = spl_get_load_buffer(-sizeof(*header), stor_dev->blksz); + count = blk_dread(stor_dev, sector, 1, header); + if (count == 0) + return -EIO; + + ret = spl_parse_image_header(spl_image, header); + if (ret) + return ret; + + image_size_sectors = DIV_ROUND_UP(spl_image->size, stor_dev->blksz); + count = blk_dread(stor_dev, sector, image_size_sectors, + (void *)spl_image->load_addr); + if (count != image_size_sectors) + return -EIO; + + return 0; +} + +static int spl_sata_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int err = 0; + struct blk_desc *stor_dev; + +#if !defined(CONFIG_DM_SCSI) && !defined(CONFIG_AHCI) + err = init_sata(CONFIG_SPL_SATA_BOOT_DEVICE); +#endif + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("spl: sata init failed: err - %d\n", err); +#endif + return err; + } else { + /* try to recognize storage devices immediately */ + scsi_scan(false); + stor_dev = blk_get_devnum_by_type(IF_TYPE_SCSI, 0); + if (!stor_dev) + return -ENODEV; + } + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || + spl_load_image_fat_os(spl_image, stor_dev, + CONFIG_SYS_SATA_FAT_BOOT_PARTITION)) +#endif + { + err = -ENOSYS; + + if (IS_ENABLED(CONFIG_SPL_FS_FAT)) { + err = spl_load_image_fat(spl_image, stor_dev, + CONFIG_SYS_SATA_FAT_BOOT_PARTITION, + CONFIG_SPL_FS_LOAD_PAYLOAD_NAME); + } else if (IS_ENABLED(CONFIG_SPL_SATA_RAW_U_BOOT_USE_SECTOR)) { + err = spl_sata_load_image_raw(spl_image, stor_dev, + CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR); + } + } + if (err) { + puts("Error loading sata device\n"); + return err; + } + + return 0; +} +SPL_LOAD_IMAGE_METHOD("SATA", 0, BOOT_DEVICE_SATA, spl_sata_load_image); diff --git a/roms/u-boot/common/spl/spl_sdp.c b/roms/u-boot/common/spl/spl_sdp.c new file mode 100644 index 000000000..ae9c09883 --- /dev/null +++ b/roms/u-boot/common/spl/spl_sdp.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Toradex + * Author: Stefan Agner <stefan.agner@toradex.com> + */ + +#include <common.h> +#include <log.h> +#include <spl.h> +#include <usb.h> +#include <g_dnl.h> +#include <sdp.h> + +static int spl_sdp_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int ret; + const int controller_index = CONFIG_SPL_SDP_USB_DEV; + + usb_gadget_initialize(controller_index); + + board_usb_init(0, USB_INIT_DEVICE); + + g_dnl_clear_detach(); + ret = g_dnl_register("usb_dnl_sdp"); + if (ret) { + pr_err("SDP dnl register failed: %d\n", ret); + return ret; + } + + ret = sdp_init(controller_index); + if (ret) { + pr_err("SDP init failed: %d\n", ret); + return -ENODEV; + } + + /* + * This command either loads a legacy image, jumps and never returns, + * or it loads a FIT image and returns it to be handled by the SPL + * code. + */ + ret = spl_sdp_handle(controller_index, spl_image); + debug("SDP ended\n"); + + usb_gadget_release(controller_index); + return ret; +} +SPL_LOAD_IMAGE_METHOD("USB SDP", 0, BOOT_DEVICE_BOARD, spl_sdp_load_image); diff --git a/roms/u-boot/common/spl/spl_spi.c b/roms/u-boot/common/spl/spl_spi.c new file mode 100644 index 000000000..6a4e03328 --- /dev/null +++ b/roms/u-boot/common/spl/spl_spi.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 OMICRON electronics GmbH + * + * based on drivers/mtd/nand/raw/nand_spl_load.c + * + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + */ + +#include <common.h> +#include <image.h> +#include <log.h> +#include <spi.h> +#include <spi_flash.h> +#include <errno.h> +#include <spl.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SPL_OS_BOOT +/* + * Load the kernel, check for a valid header we can parse, and if found load + * the kernel and then device tree. + */ +static int spi_load_image_os(struct spl_image_info *spl_image, + struct spi_flash *flash, + struct image_header *header) +{ + int err; + + /* Read for a header, parse or error out. */ + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, sizeof(*header), + (void *)header); + + if (image_get_magic(header) != IH_MAGIC) + return -1; + + err = spl_parse_image_header(spl_image, header); + if (err) + return err; + + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, + spl_image->size, (void *)spl_image->load_addr); + + /* Read device tree. */ + spi_flash_read(flash, CONFIG_SYS_SPI_ARGS_OFFS, + CONFIG_SYS_SPI_ARGS_SIZE, + (void *)CONFIG_SYS_SPL_ARGS_ADDR); + + return 0; +} +#endif + +static ulong spl_spi_fit_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + struct spi_flash *flash = load->dev; + ulong ret; + + ret = spi_flash_read(flash, sector, count, buf); + if (!ret) + return count; + else + return 0; +} + +unsigned int __weak spl_spi_get_uboot_offs(struct spi_flash *flash) +{ + return CONFIG_SYS_SPI_U_BOOT_OFFS; +} + +/* + * The main entry for SPI booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from SPI into SDRAM and starts it from there. + */ +static int spl_spi_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int err = 0; + unsigned int payload_offs; + struct spi_flash *flash; + struct image_header *header; + + /* + * Load U-Boot image from SPI flash into RAM + * In DM mode: defaults speed and mode will be + * taken from DT when available + */ + + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!flash) { + puts("SPI probe failed.\n"); + return -ENODEV; + } + + payload_offs = spl_spi_get_uboot_offs(flash); + + header = spl_get_load_buffer(-sizeof(*header), sizeof(*header)); + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + payload_offs = fdtdec_get_config_int(gd->fdt_blob, + "u-boot,spl-payload-offset", + payload_offs); +#endif + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spi_load_image_os(spl_image, flash, header)) +#endif + { + /* Load u-boot, mkimage header is 64 bytes. */ + err = spi_flash_read(flash, payload_offs, sizeof(*header), + (void *)header); + if (err) { + debug("%s: Failed to read from SPI flash (err=%d)\n", + __func__, err); + return err; + } + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) && + image_get_magic(header) == FDT_MAGIC) { + err = spi_flash_read(flash, payload_offs, + roundup(fdt_totalsize(header), 4), + (void *)CONFIG_SYS_LOAD_ADDR); + if (err) + return err; + err = spl_parse_image_header(spl_image, + (struct image_header *)CONFIG_SYS_LOAD_ADDR); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT\n"); + load.dev = flash; + load.priv = NULL; + load.filename = NULL; + load.bl_len = 1; + load.read = spl_spi_fit_read; + err = spl_load_simple_fit(spl_image, &load, + payload_offs, + header); + } else if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + load.dev = flash; + load.priv = NULL; + load.filename = NULL; + load.bl_len = 1; + load.read = spl_spi_fit_read; + + err = spl_load_imx_container(spl_image, &load, + payload_offs); + } else { + err = spl_parse_image_header(spl_image, header); + if (err) + return err; + err = spi_flash_read(flash, payload_offs, + spl_image->size, + (void *)spl_image->load_addr); + } + } + + return err; +} +/* Use priorty 1 so that boards can override this */ +SPL_LOAD_IMAGE_METHOD("SPI", 1, BOOT_DEVICE_SPI, spl_spi_load_image); diff --git a/roms/u-boot/common/spl/spl_ubi.c b/roms/u-boot/common/spl/spl_ubi.c new file mode 100644 index 000000000..de6a63bd2 --- /dev/null +++ b/roms/u-boot/common/spl/spl_ubi.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2016 + * Ladislav Michl <ladis@linux-mips.org> + */ + +#include <common.h> +#include <config.h> +#include <image.h> +#include <nand.h> +#include <onenand_uboot.h> +#include <ubispl.h> +#include <spl.h> + +int spl_ubi_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct image_header *header; + struct ubispl_info info; + struct ubispl_load volumes[2]; + int ret = 1; + + switch (bootdev->boot_device) { +#ifdef CONFIG_SPL_NAND_SUPPORT + case BOOT_DEVICE_NAND: + nand_init(); + info.read = nand_spl_read_block; + info.peb_size = CONFIG_SYS_NAND_BLOCK_SIZE; + break; +#endif +#ifdef CONFIG_SPL_ONENAND_SUPPORT + case BOOT_DEVICE_ONENAND: + info.read = onenand_spl_read_block; + info.peb_size = CONFIG_SYS_ONENAND_BLOCK_SIZE; + break; +#endif + default: + goto out; + } + info.ubi = (struct ubi_scan_info *)CONFIG_SPL_UBI_INFO_ADDR; + info.fastmap = IS_ENABLED(CONFIG_MTD_UBI_FASTMAP); + + info.peb_offset = CONFIG_SPL_UBI_PEB_OFFSET; + info.vid_offset = CONFIG_SPL_UBI_VID_OFFSET; + info.leb_start = CONFIG_SPL_UBI_LEB_START; + info.peb_count = CONFIG_SPL_UBI_MAX_PEBS - info.peb_offset; + +#ifdef CONFIG_SPL_OS_BOOT + if (!spl_start_uboot()) { + volumes[0].vol_id = CONFIG_SPL_UBI_LOAD_KERNEL_ID; + volumes[0].load_addr = (void *)CONFIG_SYS_LOAD_ADDR; + volumes[1].vol_id = CONFIG_SPL_UBI_LOAD_ARGS_ID; + volumes[1].load_addr = (void *)CONFIG_SYS_SPL_ARGS_ADDR; + + ret = ubispl_load_volumes(&info, volumes, 2); + if (!ret) { + header = (struct image_header *)volumes[0].load_addr; + spl_parse_image_header(spl_image, header); + puts("Linux loaded.\n"); + goto out; + } + puts("Loading Linux failed, falling back to U-Boot.\n"); + } +#endif + header = spl_get_load_buffer(-sizeof(*header), sizeof(header)); +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + volumes[0].vol_id = -1; + strncpy(volumes[0].name, + CONFIG_SPL_UBI_LOAD_MONITOR_VOLNAME, + UBI_VOL_NAME_MAX + 1); +#else + volumes[0].vol_id = CONFIG_SPL_UBI_LOAD_MONITOR_ID; +#endif + volumes[0].load_addr = (void *)header; + + ret = ubispl_load_volumes(&info, volumes, 1); + if (!ret) + spl_parse_image_header(spl_image, header); +out: +#ifdef CONFIG_SPL_NAND_SUPPORT + if (bootdev->boot_device == BOOT_DEVICE_NAND) + nand_deselect(); +#endif + return ret; +} +/* Use priorty 0 so that Ubi will override NAND and ONENAND methods */ +SPL_LOAD_IMAGE_METHOD("NAND", 0, BOOT_DEVICE_NAND, spl_ubi_load_image); +SPL_LOAD_IMAGE_METHOD("OneNAND", 0, BOOT_DEVICE_ONENAND, spl_ubi_load_image); diff --git a/roms/u-boot/common/spl/spl_usb.c b/roms/u-boot/common/spl/spl_usb.c new file mode 100644 index 000000000..3648de349 --- /dev/null +++ b/roms/u-boot/common/spl/spl_usb.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 + * Texas Instruments, <www.ti.com> + * + * Dan Murphy <dmurphy@ti.com> + * + * Derived work from spl_mmc.c + */ + +#include <common.h> +#include <log.h> +#include <spl.h> +#include <asm/u-boot.h> +#include <errno.h> +#include <usb.h> +#include <fat.h> + +static int usb_stor_curr_dev = -1; /* current device */ + +int spl_usb_load(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev, int partition, + const char *filename) +{ + int err = 0; + struct blk_desc *stor_dev; + static bool usb_init_pending = true; + + if (usb_init_pending) { + usb_stop(); + err = usb_init(); + usb_init_pending = false; + } + + if (err) { +#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT + printf("%s: usb init failed: err - %d\n", __func__, err); +#endif + return err; + } + + /* try to recognize storage devices immediately */ + usb_stor_curr_dev = usb_stor_scan(1); + stor_dev = blk_get_devnum_by_type(IF_TYPE_USB, usb_stor_curr_dev); + if (!stor_dev) + return -ENODEV; + + debug("boot mode - FAT\n"); + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || + spl_load_image_fat_os(spl_image, stor_dev, partition)) +#endif + { + err = spl_load_image_fat(spl_image, stor_dev, partition, filename); + } + + if (err) { + puts("Error loading from USB device\n"); + return err; + } + + return 0; +} + +static int spl_usb_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + return spl_usb_load(spl_image, bootdev, + CONFIG_SYS_USB_FAT_BOOT_PARTITION, + CONFIG_SPL_FS_LOAD_PAYLOAD_NAME); +} +SPL_LOAD_IMAGE_METHOD("USB", 0, BOOT_DEVICE_USB, spl_usb_load_image); diff --git a/roms/u-boot/common/spl/spl_xip.c b/roms/u-boot/common/spl/spl_xip.c new file mode 100644 index 000000000..8ce0a09ef --- /dev/null +++ b/roms/u-boot/common/spl/spl_xip.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. + */ + +#include <common.h> +#include <image.h> +#include <log.h> +#include <spl.h> + +static int spl_xip(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ +#ifdef CONFIG_SPL_OS_BOOT + if (!spl_start_uboot()) { + spl_image->arg = (void *)CONFIG_SYS_FDT_BASE; + spl_image->name = "Linux"; + spl_image->os = IH_OS_LINUX; + spl_image->load_addr = CONFIG_SYS_LOAD_ADDR; + spl_image->entry_point = CONFIG_SYS_LOAD_ADDR; + debug("spl: payload xipImage, load addr: 0x%lx\n", + spl_image->load_addr); + return 0; + } +#endif + return(spl_parse_image_header(spl_image, (const struct image_header *) + CONFIG_SYS_UBOOT_BASE)); +} +SPL_LOAD_IMAGE_METHOD("XIP", 0, BOOT_DEVICE_XIP, spl_xip); diff --git a/roms/u-boot/common/spl/spl_ymodem.c b/roms/u-boot/common/spl/spl_ymodem.c new file mode 100644 index 000000000..e979f780a --- /dev/null +++ b/roms/u-boot/common/spl/spl_ymodem.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2011 + * Texas Instruments, <www.ti.com> + * + * Matt Porter <mporter@ti.com> + */ +#include <common.h> +#include <gzip.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <xyzModem.h> +#include <asm/u-boot.h> +#include <linux/libfdt.h> + +#define BUF_SIZE 1024 + +/* + * Information required to load image using ymodem. + * + * @image_read: Now of bytes read from the image. + * @buf: pointer to the previous read block. + */ +struct ymodem_fit_info { + int image_read; + char *buf; +}; + +static int getcymodem(void) { + if (tstc()) + return (getchar()); + return -1; +} + +static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset, + ulong size, void *addr) +{ + int res, err, buf_offset; + struct ymodem_fit_info *info = load->priv; + char *buf = info->buf; + + while (info->image_read < offset) { + res = xyzModem_stream_read(buf, BUF_SIZE, &err); + if (res <= 0) + break; + + info->image_read += res; + } + + if (info->image_read > offset) { + res = info->image_read - offset; + if (info->image_read % BUF_SIZE) + buf_offset = (info->image_read % BUF_SIZE); + else + buf_offset = BUF_SIZE; + memcpy(addr, &buf[buf_offset - res], res); + addr = addr + res; + } + + while (info->image_read < offset + size) { + res = xyzModem_stream_read(buf, BUF_SIZE, &err); + if (res <= 0) + break; + + memcpy(addr, buf, res); + info->image_read += res; + addr += res; + } + + return size; +} + +int spl_ymodem_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + ulong size = 0; + int err; + int res; + int ret; + connection_info_t info; + char buf[BUF_SIZE]; + struct image_header *ih = NULL; + ulong addr = 0; + + info.mode = xyzModem_ymodem; + ret = xyzModem_stream_open(&info, &err); + if (ret) { + printf("spl: ymodem err - %s\n", xyzModem_error(err)); + return ret; + } + + res = xyzModem_stream_read(buf, BUF_SIZE, &err); + if (res <= 0) + goto end_stream; + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL) && + image_get_magic((struct image_header *)buf) == FDT_MAGIC) { + addr = CONFIG_SYS_LOAD_ADDR; + ih = (struct image_header *)addr; + + memcpy((void *)addr, buf, res); + size += res; + addr += res; + + while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { + memcpy((void *)addr, buf, res); + size += res; + addr += res; + } + + ret = spl_parse_image_header(spl_image, ih); + if (ret) + return ret; + } else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic((struct image_header *)buf) == FDT_MAGIC) { + struct spl_load_info load; + struct ymodem_fit_info info; + + debug("Found FIT\n"); + load.dev = NULL; + load.priv = (void *)&info; + load.filename = NULL; + load.bl_len = 1; + info.buf = buf; + info.image_read = BUF_SIZE; + load.read = ymodem_read_fit; + ret = spl_load_simple_fit(spl_image, &load, 0, (void *)buf); + size = info.image_read; + + while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) + size += res; + } else { + ih = (struct image_header *)buf; + ret = spl_parse_image_header(spl_image, ih); + if (ret) + goto end_stream; +#ifdef CONFIG_SPL_GZIP + if (ih->ih_comp == IH_COMP_GZIP) + addr = CONFIG_SYS_LOAD_ADDR; + else +#endif + addr = spl_image->load_addr; + memcpy((void *)addr, buf, res); + ih = (struct image_header *)addr; + size += res; + addr += res; + + while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { + memcpy((void *)addr, buf, res); + size += res; + addr += res; + } + } + +end_stream: + xyzModem_stream_close(&err); + xyzModem_stream_terminate(false, &getcymodem); + + printf("Loaded %lu bytes\n", size); + +#ifdef CONFIG_SPL_GZIP + if (!(IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic((struct image_header *)buf) == FDT_MAGIC) && + (ih->ih_comp == IH_COMP_GZIP)) { + if (gunzip((void *)(spl_image->load_addr + sizeof(*ih)), + CONFIG_SYS_BOOTM_LEN, + (void *)(CONFIG_SYS_LOAD_ADDR + sizeof(*ih)), + &size)) { + puts("Uncompressing error\n"); + return -EIO; + } + } +#endif + + return ret; +} +SPL_LOAD_IMAGE_METHOD("UART", 0, BOOT_DEVICE_UART, spl_ymodem_load_image); diff --git a/roms/u-boot/common/splash.c b/roms/u-boot/common/splash.c new file mode 100644 index 000000000..2b9313e03 --- /dev/null +++ b/roms/u-boot/common/splash.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2013, Boundary Devices <info@boundarydevices.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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, Inc., http://www.fsf.org/about/contact/ + * + */ + +#include <common.h> +#include <env.h> +#include <splash.h> +#include <lcd.h> + +static struct splash_location default_splash_locations[] = { + { + .name = "sf", + .storage = SPLASH_STORAGE_SF, + .flags = SPLASH_STORAGE_RAW, + .offset = 0x0, + }, + { + .name = "mmc_fs", + .storage = SPLASH_STORAGE_MMC, + .flags = SPLASH_STORAGE_FS, + .devpart = "0:1", + }, + { + .name = "usb_fs", + .storage = SPLASH_STORAGE_USB, + .flags = SPLASH_STORAGE_FS, + .devpart = "0:1", + }, + { + .name = "sata_fs", + .storage = SPLASH_STORAGE_SATA, + .flags = SPLASH_STORAGE_FS, + .devpart = "0:1", + }, +}; + +#if defined(CONFIG_DM_VIDEO) && defined(CONFIG_VIDEO_LOGO) + +#include <bmp_logo_data.h> + +static int splash_video_logo_load(void) +{ + char *splashimage; + ulong bmp_load_addr; + + splashimage = env_get("splashimage"); + if (!splashimage) + return -ENOENT; + + bmp_load_addr = simple_strtoul(splashimage, 0, 16); + if (!bmp_load_addr) { + printf("Error: bad 'splashimage' address\n"); + return -EFAULT; + } + + memcpy((void *)bmp_load_addr, bmp_logo_bitmap, + ARRAY_SIZE(bmp_logo_bitmap)); + + return 0; +} +#else +static inline int splash_video_logo_load(void) { return -ENOSYS; } +#endif + +__weak int splash_screen_prepare(void) +{ + if (CONFIG_IS_ENABLED(SPLASH_SOURCE)) + return splash_source_load(default_splash_locations, + ARRAY_SIZE(default_splash_locations)); + + return splash_video_logo_load(); +} + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN +void splash_get_pos(int *x, int *y) +{ + char *s = env_get("splashpos"); + + if (!s) + return; + + if (s[0] == 'm') + *x = BMP_ALIGN_CENTER; + else + *x = simple_strtol(s, NULL, 0); + + s = strchr(s + 1, ','); + if (s != NULL) { + if (s[1] == 'm') + *y = BMP_ALIGN_CENTER; + else + *y = simple_strtol(s + 1, NULL, 0); + } +} +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + +#if defined(CONFIG_DM_VIDEO) && !defined(CONFIG_HIDE_LOGO_VERSION) + +#ifdef CONFIG_VIDEO_LOGO +#include <bmp_logo.h> +#endif +#include <dm.h> +#include <video_console.h> +#include <video_font.h> + +void splash_display_banner(void) +{ + struct udevice *dev; + char buf[DISPLAY_OPTIONS_BANNER_LENGTH]; + int col, row, ret; + + ret = uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &dev); + if (ret) + return; + +#ifdef CONFIG_VIDEO_LOGO + col = BMP_LOGO_WIDTH / VIDEO_FONT_WIDTH + 1; + row = BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT + 1; +#else + col = 0; + row = 0; +#endif + + display_options_get_banner(false, buf, sizeof(buf)); + vidconsole_position_cursor(dev, col, 1); + vidconsole_put_string(dev, buf); + vidconsole_position_cursor(dev, 0, row); +} +#endif /* CONFIG_DM_VIDEO && !CONFIG_HIDE_LOGO_VERSION */ + +/* + * Common function to show a splash image if env("splashimage") is set. + * Is used for both dm_video and lcd video stacks. For additional + * details please refer to doc/README.splashprepare. + */ +#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) +int splash_display(void) +{ + ulong addr; + char *s; + int x = 0, y = 0, ret; + + s = env_get("splashimage"); + if (!s) + return -EINVAL; + + addr = simple_strtoul(s, NULL, 16); + ret = splash_screen_prepare(); + if (ret) + return ret; + + splash_get_pos(&x, &y); + + ret = bmp_display(addr, x, y); + + /* Skip banner output on video console if the logo is not at 0,0 */ + if (x || y) + goto end; + +#if defined(CONFIG_DM_VIDEO) && !defined(CONFIG_HIDE_LOGO_VERSION) + splash_display_banner(); +#endif +end: + return ret; +} +#endif diff --git a/roms/u-boot/common/splash_source.c b/roms/u-boot/common/splash_source.c new file mode 100644 index 000000000..3cf926d91 --- /dev/null +++ b/roms/u-boot/common/splash_source.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Igor Grinberg <grinberg@compulab.co.il> + */ + +#include <common.h> +#include <bmp_layout.h> +#include <command.h> +#include <env.h> +#include <errno.h> +#include <fs.h> +#include <fdt_support.h> +#include <image.h> +#include <log.h> +#include <nand.h> +#include <sata.h> +#include <spi.h> +#include <spi_flash.h> +#include <splash.h> +#include <usb.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SPI_FLASH +static struct spi_flash *sf; +static int splash_sf_read_raw(u32 bmp_load_addr, int offset, size_t read_size) +{ + if (!sf) { + sf = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!sf) + return -ENODEV; + } + + return spi_flash_read(sf, offset, read_size, (void *)(uintptr_t)bmp_load_addr); +} +#else +static int splash_sf_read_raw(u32 bmp_load_addr, int offset, size_t read_size) +{ + debug("%s: sf support not available\n", __func__); + return -ENOSYS; +} +#endif + +#ifdef CONFIG_CMD_NAND +static int splash_nand_read_raw(u32 bmp_load_addr, int offset, size_t read_size) +{ + struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); + return nand_read_skip_bad(mtd, offset, + &read_size, NULL, + mtd->size, + (u_char *)bmp_load_addr); +} +#else +static int splash_nand_read_raw(u32 bmp_load_addr, int offset, size_t read_size) +{ + debug("%s: nand support not available\n", __func__); + return -ENOSYS; +} +#endif + +static int splash_storage_read_raw(struct splash_location *location, + u32 bmp_load_addr, size_t read_size) +{ + u32 offset; + + if (!location) + return -EINVAL; + + offset = location->offset; + switch (location->storage) { + case SPLASH_STORAGE_NAND: + return splash_nand_read_raw(bmp_load_addr, offset, read_size); + case SPLASH_STORAGE_SF: + return splash_sf_read_raw(bmp_load_addr, offset, read_size); + default: + printf("Unknown splash location\n"); + } + + return -EINVAL; +} + +static int splash_load_raw(struct splash_location *location, u32 bmp_load_addr) +{ + struct bmp_header *bmp_hdr; + int res; + size_t bmp_size, bmp_header_size = sizeof(struct bmp_header); + + if (bmp_load_addr + bmp_header_size >= gd->start_addr_sp) + goto splash_address_too_high; + + res = splash_storage_read_raw(location, bmp_load_addr, bmp_header_size); + if (res < 0) + return res; + + bmp_hdr = (struct bmp_header *)(uintptr_t)bmp_load_addr; + bmp_size = le32_to_cpu(bmp_hdr->file_size); + + if (bmp_load_addr + bmp_size >= gd->start_addr_sp) + goto splash_address_too_high; + + return splash_storage_read_raw(location, bmp_load_addr, bmp_size); + +splash_address_too_high: + printf("Error: splashimage address too high. Data overwrites U-Boot and/or placed beyond DRAM boundaries.\n"); + + return -EFAULT; +} + +static int splash_select_fs_dev(struct splash_location *location) +{ + int res; + + switch (location->storage) { + case SPLASH_STORAGE_MMC: + res = fs_set_blk_dev("mmc", location->devpart, FS_TYPE_ANY); + break; + case SPLASH_STORAGE_USB: + res = fs_set_blk_dev("usb", location->devpart, FS_TYPE_ANY); + break; + case SPLASH_STORAGE_SATA: + res = fs_set_blk_dev("sata", location->devpart, FS_TYPE_ANY); + break; + case SPLASH_STORAGE_NAND: + if (location->ubivol != NULL) + res = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS); + else + res = -ENODEV; + break; + default: + printf("Error: unsupported location storage.\n"); + return -ENODEV; + } + + if (res) + printf("Error: could not access storage.\n"); + + return res; +} + +#ifdef CONFIG_USB_STORAGE +static int splash_init_usb(void) +{ + int err; + + err = usb_init(); + if (err) + return err; + +#ifndef CONFIG_DM_USB + err = usb_stor_scan(1) < 0 ? -ENODEV : 0; +#endif + + return err; +} +#else +static inline int splash_init_usb(void) +{ + printf("Cannot load splash image: no USB support\n"); + return -ENOSYS; +} +#endif + +#ifdef CONFIG_SATA +static int splash_init_sata(void) +{ + return sata_probe(0); +} +#else +static inline int splash_init_sata(void) +{ + printf("Cannot load splash image: no SATA support\n"); + return -ENOSYS; +} +#endif + +#ifdef CONFIG_CMD_UBIFS +static int splash_mount_ubifs(struct splash_location *location) +{ + int res; + char cmd[32]; + + sprintf(cmd, "ubi part %s", location->mtdpart); + res = run_command(cmd, 0); + if (res) + return res; + + sprintf(cmd, "ubifsmount %s", location->ubivol); + res = run_command(cmd, 0); + + return res; +} + +static inline int splash_umount_ubifs(void) +{ + return run_command("ubifsumount", 0); +} +#else +static inline int splash_mount_ubifs(struct splash_location *location) +{ + printf("Cannot load splash image: no UBIFS support\n"); + return -ENOSYS; +} + +static inline int splash_umount_ubifs(void) +{ + printf("Cannot unmount UBIFS: no UBIFS support\n"); + return -ENOSYS; +} +#endif + +#define SPLASH_SOURCE_DEFAULT_FILE_NAME "splash.bmp" + +static int splash_load_fs(struct splash_location *location, u32 bmp_load_addr) +{ + int res = 0; + loff_t bmp_size; + loff_t actread; + char *splash_file; + + splash_file = env_get("splashfile"); + if (!splash_file) + splash_file = SPLASH_SOURCE_DEFAULT_FILE_NAME; + + if (location->storage == SPLASH_STORAGE_USB) + res = splash_init_usb(); + + if (location->storage == SPLASH_STORAGE_SATA) + res = splash_init_sata(); + + if (location->ubivol != NULL) + res = splash_mount_ubifs(location); + + if (res) + return res; + + res = splash_select_fs_dev(location); + if (res) + goto out; + + res = fs_size(splash_file, &bmp_size); + if (res) { + printf("Error (%d): cannot determine file size\n", res); + goto out; + } + + if (bmp_load_addr + bmp_size >= gd->start_addr_sp) { + printf("Error: splashimage address too high. Data overwrites U-Boot and/or placed beyond DRAM boundaries.\n"); + res = -EFAULT; + goto out; + } + + splash_select_fs_dev(location); + res = fs_read(splash_file, bmp_load_addr, 0, 0, &actread); + +out: + if (location->ubivol != NULL) + splash_umount_ubifs(); + + return res; +} + +/** + * select_splash_location - return the splash location based on board support + * and env variable "splashsource". + * + * @locations: An array of supported splash locations. + * @size: Size of splash_locations array. + * + * @return: If a null set of splash locations is given, or + * splashsource env variable is set to unsupported value + * return NULL. + * If splashsource env variable is not defined + * return the first entry in splash_locations as default. + * If splashsource env variable contains a supported value + * return the location selected by splashsource. + */ +static struct splash_location *select_splash_location( + struct splash_location *locations, uint size) +{ + int i; + char *env_splashsource; + + if (!locations || size == 0) + return NULL; + + env_splashsource = env_get("splashsource"); + if (env_splashsource == NULL) + return &locations[0]; + + for (i = 0; i < size; i++) { + if (!strcmp(locations[i].name, env_splashsource)) + return &locations[i]; + } + + printf("splashsource env variable set to unsupported value\n"); + return NULL; +} + +#ifdef CONFIG_FIT +static int splash_load_fit(struct splash_location *location, u32 bmp_load_addr) +{ + int res; + int node_offset; + const char *splash_file; + const void *internal_splash_data; + size_t internal_splash_size; + int external_splash_addr; + int external_splash_size; + bool is_splash_external = false; + struct image_header *img_header; + const u32 *fit_header; + u32 fit_size; + const size_t header_size = sizeof(struct image_header); + + /* Read in image header */ + res = splash_storage_read_raw(location, bmp_load_addr, header_size); + if (res < 0) + return res; + + img_header = (struct image_header *)bmp_load_addr; + if (image_get_magic(img_header) != FDT_MAGIC) { + printf("Could not find FDT magic\n"); + return -EINVAL; + } + + fit_size = fdt_totalsize(img_header); + + /* Read in entire FIT */ + fit_header = (const u32 *)(bmp_load_addr + header_size); + res = splash_storage_read_raw(location, (u32)fit_header, fit_size); + if (res < 0) + return res; + + res = fit_check_format(fit_header, IMAGE_SIZE_INVAL); + if (res) { + debug("Could not find valid FIT image\n"); + return res; + } + + /* Get the splash image node */ + splash_file = env_get("splashfile"); + if (!splash_file) + splash_file = SPLASH_SOURCE_DEFAULT_FILE_NAME; + + node_offset = fit_image_get_node(fit_header, splash_file); + if (node_offset < 0) { + debug("Could not find splash image '%s' in FIT\n", + splash_file); + return -ENOENT; + } + + /* Extract the splash data from FIT */ + /* 1. Test if splash is in FIT internal data. */ + if (!fit_image_get_data(fit_header, node_offset, &internal_splash_data, &internal_splash_size)) + memmove((void *)bmp_load_addr, internal_splash_data, internal_splash_size); + /* 2. Test if splash is in FIT external data with fixed position. */ + else if (!fit_image_get_data_position(fit_header, node_offset, &external_splash_addr)) + is_splash_external = true; + /* 3. Test if splash is in FIT external data with offset. */ + else if (!fit_image_get_data_offset(fit_header, node_offset, &external_splash_addr)) { + /* Align data offset to 4-byte boundary */ + fit_size = ALIGN(fdt_totalsize(fit_header), 4); + /* External splash offset means the offset by end of FIT header */ + external_splash_addr += location->offset + fit_size; + is_splash_external = true; + } else { + printf("Failed to get splash image from FIT\n"); + return -ENODATA; + } + + if (is_splash_external) { + res = fit_image_get_data_size(fit_header, node_offset, &external_splash_size); + if (res < 0) { + printf("Failed to get size of splash image (err=%d)\n", res); + return res; + } + + /* Read in the splash data */ + location->offset = external_splash_addr; + res = splash_storage_read_raw(location, bmp_load_addr, external_splash_size); + if (res < 0) + return res; + } + + return 0; +} +#endif /* CONFIG_FIT */ + +/** + * splash_source_load - load splash image from a supported location. + * + * Select a splash image location based on the value of splashsource environment + * variable and the board supported splash source locations, and load a + * splashimage to the address pointed to by splashimage environment variable. + * + * @locations: An array of supported splash locations. + * @size: Size of splash_locations array. + * + * @return: 0 on success, negative value on failure. + */ +int splash_source_load(struct splash_location *locations, uint size) +{ + struct splash_location *splash_location; + char *env_splashimage_value; + u32 bmp_load_addr; + + env_splashimage_value = env_get("splashimage"); + if (env_splashimage_value == NULL) + return -ENOENT; + + bmp_load_addr = simple_strtoul(env_splashimage_value, 0, 16); + if (bmp_load_addr == 0) { + printf("Error: bad splashimage address specified\n"); + return -EFAULT; + } + + splash_location = select_splash_location(locations, size); + if (!splash_location) + return -EINVAL; + + if (splash_location->flags == SPLASH_STORAGE_RAW) + return splash_load_raw(splash_location, bmp_load_addr); + else if (splash_location->flags == SPLASH_STORAGE_FS) + return splash_load_fs(splash_location, bmp_load_addr); +#ifdef CONFIG_FIT + else if (splash_location->flags == SPLASH_STORAGE_FIT) + return splash_load_fit(splash_location, bmp_load_addr); +#endif + return -EINVAL; +} diff --git a/roms/u-boot/common/stackprot.c b/roms/u-boot/common/stackprot.c new file mode 100644 index 000000000..d5b706166 --- /dev/null +++ b/roms/u-boot/common/stackprot.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Broadcom + */ + +#include <common.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +unsigned long __stack_chk_guard = (unsigned long)(0xfeedf00ddeadbeef & ~0UL); + +void __stack_chk_fail(void) +{ + void *ra; + + ra = __builtin_extract_return_addr(__builtin_return_address(0)); + panic("Stack smashing detected in function:\n%p relocated from %p", + ra, ra - gd->reloc_off); +} diff --git a/roms/u-boot/common/stdio.c b/roms/u-boot/common/stdio.c new file mode 100644 index 000000000..d4acc5256 --- /dev/null +++ b/roms/u-boot/common/stdio.c @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2009 Sergey Kubushyn <ksi@koi8.net> + * + * Changes for multibus/multiadapter I2C support. + * + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + */ + +#include <config.h> +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <stdarg.h> +#include <malloc.h> +#include <stdio_dev.h> +#include <serial.h> +#include <splash.h> +#include <i2c.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct stdio_dev devs; +struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL }; +char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" }; + +int stdio_file_to_flags(const int file) +{ + switch (file) { + case stdin: + return DEV_FLAGS_INPUT; + case stdout: + case stderr: + return DEV_FLAGS_OUTPUT; + default: + return -EINVAL; + } +} + +#if CONFIG_IS_ENABLED(SYS_DEVICE_NULLDEV) +static void nulldev_putc(struct stdio_dev *dev, const char c) +{ + /* nulldev is empty! */ +} + +static void nulldev_puts(struct stdio_dev *dev, const char *s) +{ + /* nulldev is empty! */ +} + +static int nulldev_input(struct stdio_dev *dev) +{ + /* nulldev is empty! */ + return 0; +} + +static void nulldev_register(void) +{ + struct stdio_dev dev; + + memset(&dev, '\0', sizeof(dev)); + + strcpy(dev.name, "nulldev"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; + dev.putc = nulldev_putc; + dev.puts = nulldev_puts; + dev.getc = nulldev_input; + dev.tstc = nulldev_input; + + stdio_register(&dev); +} +#else +static inline void nulldev_register(void) {} +#endif /* SYS_DEVICE_NULLDEV */ + +static void stdio_serial_putc(struct stdio_dev *dev, const char c) +{ + serial_putc(c); +} + +static void stdio_serial_puts(struct stdio_dev *dev, const char *s) +{ + serial_puts(s); +} + +static int stdio_serial_getc(struct stdio_dev *dev) +{ + return serial_getc(); +} + +static int stdio_serial_tstc(struct stdio_dev *dev) +{ + return serial_tstc(); +} + +/************************************************************************** + * SYSTEM DRIVERS + ************************************************************************** + */ + +static void drv_system_init (void) +{ + struct stdio_dev dev; + + memset (&dev, 0, sizeof (dev)); + + strcpy (dev.name, "serial"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; + dev.putc = stdio_serial_putc; + dev.puts = stdio_serial_puts; + dev.getc = stdio_serial_getc; + dev.tstc = stdio_serial_tstc; + stdio_register (&dev); + + nulldev_register(); +} + +/************************************************************************** + * DEVICES + ************************************************************************** + */ +struct list_head* stdio_get_list(void) +{ + return &devs.list; +} + +/** + * stdio_probe_device() - Find a device which provides the given stdio device + * + * This looks for a device of the given uclass which provides a particular + * stdio device. It is currently really only useful for UCLASS_VIDEO. + * + * Ultimately we want to be able to probe a device by its stdio name. At + * present devices register in their probe function (for video devices this + * is done in vidconsole_post_probe()) and we don't know what name they will + * use until they do so. + * TODO(sjg@chromium.org): We should be able to determine the name before + * probing, and probe the required device. + * + * @name: stdio device name (e.g. "vidconsole") + * id: Uclass ID of device to look for (e.g. UCLASS_VIDEO) + * @sdevp: Returns stdout device, if found, else NULL + * @return 0 if found, -ENOENT if no device found with that name, other -ve + * on other error + */ +static int stdio_probe_device(const char *name, enum uclass_id id, + struct stdio_dev **sdevp) +{ + struct stdio_dev *sdev; + struct udevice *dev; + int seq, ret; + + *sdevp = NULL; + seq = trailing_strtoln(name, NULL); + if (seq == -1) + seq = 0; + ret = uclass_get_device_by_seq(id, seq, &dev); + if (ret == -ENODEV) + ret = uclass_first_device_err(id, &dev); + if (ret) { + debug("No %s device for seq %d (%s)\n", uclass_get_name(id), + seq, name); + return ret; + } + /* The device should be be the last one registered */ + sdev = list_empty(&devs.list) ? NULL : + list_last_entry(&devs.list, struct stdio_dev, list); + if (!sdev || strcmp(sdev->name, name)) { + debug("Device '%s' did not register with stdio as '%s'\n", + dev->name, name); + return -ENOENT; + } + *sdevp = sdev; + + return 0; +} + +struct stdio_dev *stdio_get_by_name(const char *name) +{ + struct list_head *pos; + struct stdio_dev *sdev; + + if (!name) + return NULL; + + list_for_each(pos, &devs.list) { + sdev = list_entry(pos, struct stdio_dev, list); + if (strcmp(sdev->name, name) == 0) + return sdev; + } + if (IS_ENABLED(CONFIG_DM_VIDEO)) { + /* + * We did not find a suitable stdio device. If there is a video + * driver with a name starting with 'vidconsole', we can try + * probing that in the hope that it will produce the required + * stdio device. + * + * This function is sometimes called with the entire value of + * 'stdout', which may include a list of devices separate by + * commas. Obviously this is not going to work, so we ignore + * that case. The call path in that case is + * console_init_r() -> console_search_dev() -> stdio_get_by_name() + */ + if (!strncmp(name, "vidconsole", 10) && !strchr(name, ',') && + !stdio_probe_device(name, UCLASS_VIDEO, &sdev)) + return sdev; + } + + return NULL; +} + +struct stdio_dev *stdio_clone(struct stdio_dev *dev) +{ + struct stdio_dev *_dev; + + if (!dev) + return NULL; + + _dev = calloc(1, sizeof(struct stdio_dev)); + if (!_dev) + return NULL; + + memcpy(_dev, dev, sizeof(struct stdio_dev)); + + return _dev; +} + +int stdio_register_dev(struct stdio_dev *dev, struct stdio_dev **devp) +{ + struct stdio_dev *_dev; + + _dev = stdio_clone(dev); + if (!_dev) + return -ENODEV; + list_add_tail(&_dev->list, &devs.list); + if (devp) + *devp = _dev; + + return 0; +} + +int stdio_register(struct stdio_dev *dev) +{ + return stdio_register_dev(dev, NULL); +} + +int stdio_deregister_dev(struct stdio_dev *dev, int force) +{ + struct list_head *pos; + char temp_names[3][16]; + int i; + + /* get stdio devices (ListRemoveItem changes the dev list) */ + for (i = 0 ; i < MAX_FILES; i++) { + if (stdio_devices[i] == dev) { + if (force) { + strcpy(temp_names[i], "nulldev"); + continue; + } + /* Device is assigned -> report error */ + return -EBUSY; + } + memcpy(&temp_names[i][0], stdio_devices[i]->name, + sizeof(temp_names[i])); + } + + list_del(&dev->list); + free(dev); + + /* reassign device list */ + list_for_each(pos, &devs.list) { + dev = list_entry(pos, struct stdio_dev, list); + for (i = 0 ; i < MAX_FILES; i++) { + if (strcmp(dev->name, temp_names[i]) == 0) + stdio_devices[i] = dev; + } + } + + return 0; +} + +int stdio_init_tables(void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + /* already relocated for current ARM implementation */ + ulong relocation_offset = gd->reloc_off; + int i; + + /* relocate device name pointers */ + for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { + stdio_names[i] = (char *) (((ulong) stdio_names[i]) + + relocation_offset); + } +#endif /* CONFIG_NEEDS_MANUAL_RELOC */ + + /* Initialize the list */ + INIT_LIST_HEAD(&devs.list); + + return 0; +} + +int stdio_add_devices(void) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + if (IS_ENABLED(CONFIG_DM_KEYBOARD)) { + /* + * For now we probe all the devices here. At some point this + * should be done only when the devices are required - e.g. we + * have a list of input devices to start up in the stdin + * environment variable. That work probably makes more sense + * when stdio itself is converted to driver model. + * + * TODO(sjg@chromium.org): Convert changing + * uclass_first_device() etc. to return the device even on + * error. Then we could use that here. + */ + ret = uclass_get(UCLASS_KEYBOARD, &uc); + if (ret) + return ret; + + /* + * Don't report errors to the caller - assume that they are + * non-fatal + */ + uclass_foreach_dev(dev, uc) { + ret = device_probe(dev); + if (ret) + printf("Failed to probe keyboard '%s'\n", + dev->name); + } + } +#ifdef CONFIG_SYS_I2C + i2c_init_all(); +#endif + if (IS_ENABLED(CONFIG_DM_VIDEO)) { + /* + * If the console setting is not in environment variables then + * console_init_r() will not be calling iomux_doenv() (which + * calls console_search_dev()). So we will not dynamically add + * devices by calling stdio_probe_device(). + * + * So just probe all video devices now so that whichever one is + * required will be available. + */ + struct udevice *vdev; + int ret; + + if (!IS_ENABLED(CONFIG_SYS_CONSOLE_IS_IN_ENV)) { + for (ret = uclass_first_device(UCLASS_VIDEO, &vdev); + vdev; + ret = uclass_next_device(&vdev)) + ; + if (ret) + printf("%s: Video device failed (ret=%d)\n", + __func__, ret); + } + if (IS_ENABLED(CONFIG_SPLASH_SCREEN) && + IS_ENABLED(CONFIG_CMD_BMP)) + splash_display(); + } else { + if (IS_ENABLED(CONFIG_LCD)) + drv_lcd_init(); + if (IS_ENABLED(CONFIG_VIDEO) || + IS_ENABLED(CONFIG_CFB_CONSOLE) || + IS_ENABLED(CONFIG_VIDEO_VCXK)) + drv_video_init(); + } + +#if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD) + drv_keyboard_init(); +#endif + drv_system_init(); + serial_stdio_init(); +#ifdef CONFIG_USB_TTY + drv_usbtty_init(); +#endif + if (IS_ENABLED(CONFIG_NETCONSOLE)) + drv_nc_init(); +#ifdef CONFIG_JTAG_CONSOLE + drv_jtag_console_init(); +#endif + if (IS_ENABLED(CONFIG_CBMEM_CONSOLE)) + cbmemc_init(); + + return 0; +} + +int stdio_init(void) +{ + stdio_init_tables(); + stdio_add_devices(); + + return 0; +} diff --git a/roms/u-boot/common/system_map.c b/roms/u-boot/common/system_map.c new file mode 100644 index 000000000..8307293bf --- /dev/null +++ b/roms/u-boot/common/system_map.c @@ -0,0 +1,8 @@ +/* + * The builtin symbol table for use with kallsyms + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +const char const system_map[] = SYSTEM_MAP; diff --git a/roms/u-boot/common/update.c b/roms/u-boot/common/update.c new file mode 100644 index 000000000..f0848954e --- /dev/null +++ b/roms/u-boot/common/update.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 Semihalf + * + * Written by: Rafal Czubak <rcz@semihalf.com> + * Bartlomiej Sieka <tur@semihalf.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <image.h> + +#if !(defined(CONFIG_FIT) && defined(CONFIG_OF_LIBFDT)) +#error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature" +#endif + +#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH) +#error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour" +#endif + +#include <command.h> +#include <env.h> +#include <flash.h> +#include <net.h> +#include <net/tftp.h> +#include <malloc.h> +#include <mapmem.h> +#include <dfu.h> +#include <errno.h> +#include <mtd/cfi_flash.h> + +#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP) +/* env variable holding the location of the update file */ +#define UPDATE_FILE_ENV "updatefile" + +/* set configuration defaults if needed */ +#ifndef CONFIG_UPDATE_LOAD_ADDR +#define CONFIG_UPDATE_LOAD_ADDR 0x100000 +#endif + +#ifndef CONFIG_UPDATE_TFTP_MSEC_MAX +#define CONFIG_UPDATE_TFTP_MSEC_MAX 100 +#endif + +#ifndef CONFIG_UPDATE_TFTP_CNT_MAX +#define CONFIG_UPDATE_TFTP_CNT_MAX 0 +#endif + +extern ulong tftp_timeout_ms; +extern int tftp_timeout_count_max; +#ifdef CONFIG_MTD_NOR_FLASH +extern flash_info_t flash_info[]; +static uchar *saved_prot_info; +#endif +static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr) +{ + int size, rv; + ulong saved_timeout_msecs; + int saved_timeout_count; + char *saved_netretry, *saved_bootfile; + + rv = 0; + /* save used globals and env variable */ + saved_timeout_msecs = tftp_timeout_ms; + saved_timeout_count = tftp_timeout_count_max; + saved_netretry = strdup(env_get("netretry")); + saved_bootfile = strdup(net_boot_file_name); + + /* set timeouts for auto-update */ + tftp_timeout_ms = msec_max; + tftp_timeout_count_max = cnt_max; + + /* we don't want to retry the connection if errors occur */ + env_set("netretry", "no"); + + /* download the update file */ + image_load_addr = addr; + copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name)); + size = net_loop(TFTPGET); + + if (size < 0) + rv = 1; + else if (size > 0) + flush_cache(addr, size); + + /* restore changed globals and env variable */ + tftp_timeout_ms = saved_timeout_msecs; + tftp_timeout_count_max = saved_timeout_count; + + env_set("netretry", saved_netretry); + if (saved_netretry != NULL) + free(saved_netretry); + + if (saved_bootfile != NULL) { + copy_filename(net_boot_file_name, saved_bootfile, + sizeof(net_boot_file_name)); + free(saved_bootfile); + } + + return rv; +} + +#ifdef CONFIG_MTD_NOR_FLASH +static int update_flash_protect(int prot, ulong addr_first, ulong addr_last) +{ + uchar *sp_info_ptr; + ulong s; + int i, bank, cnt; + flash_info_t *info; + + sp_info_ptr = NULL; + + if (prot == 0) { + saved_prot_info = + calloc(CONFIG_SYS_MAX_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1); + if (!saved_prot_info) + return 1; + } + + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + cnt = 0; + info = &flash_info[bank]; + + /* Nothing to do if the bank doesn't exist */ + if (info->sector_count == 0) + return 0; + + /* Point to current bank protection information */ + sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT); + + /* + * Adjust addr_first or addr_last if we are on bank boundary. + * Address space between banks must be continuous for other + * flash functions (like flash_sect_erase or flash_write) to + * succeed. Banks must also be numbered in correct order, + * according to increasing addresses. + */ + if (addr_last > info->start[0] + info->size - 1) + addr_last = info->start[0] + info->size - 1; + if (addr_first < info->start[0]) + addr_first = info->start[0]; + + for (i = 0; i < info->sector_count; i++) { + /* Save current information about protected sectors */ + if (prot == 0) { + s = info->start[i]; + if ((s >= addr_first) && (s <= addr_last)) + sp_info_ptr[i] = info->protect[i]; + + } + + /* Protect/unprotect sectors */ + if (sp_info_ptr[i] == 1) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, prot)) + return 1; +#else + info->protect[i] = prot; +#endif + cnt++; + } + } + + if (cnt) { + printf("%sProtected %d sectors\n", + prot ? "": "Un-", cnt); + } + } + + if((prot == 1) && saved_prot_info) + free(saved_prot_info); + + return 0; +} +#endif + +static int update_flash(ulong addr_source, ulong addr_first, ulong size) +{ +#ifdef CONFIG_MTD_NOR_FLASH + ulong addr_last = addr_first + size - 1; + + /* round last address to the sector boundary */ + if (flash_sect_roundb(&addr_last) > 0) + return 1; + + if (addr_first >= addr_last) { + printf("Error: end address exceeds addressing space\n"); + return 1; + } + + /* remove protection on processed sectors */ + if (update_flash_protect(0, addr_first, addr_last) > 0) { + printf("Error: could not unprotect flash sectors\n"); + return 1; + } + + printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last); + if (flash_sect_erase(addr_first, addr_last) > 0) { + printf("Error: could not erase flash\n"); + return 1; + } + + printf("Copying to flash..."); + if (flash_write((char *)addr_source, addr_first, size) > 0) { + printf("Error: could not copy to flash\n"); + return 1; + } + printf("done\n"); + + /* enable protection on processed sectors */ + if (update_flash_protect(1, addr_first, addr_last) > 0) { + printf("Error: could not protect flash sectors\n"); + return 1; + } +#endif + return 0; +} +#endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */ + +static int update_fit_getparams(const void *fit, int noffset, ulong *addr, + ulong *fladdr, ulong *size) +{ + const void *data; + + if (fit_image_get_data(fit, noffset, &data, (size_t *)size)) + return 1; + + if (fit_image_get_load(fit, noffset, (ulong *)fladdr)) + return 1; + + *addr = (ulong)data; + + return 0; +} + +#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP) +int update_tftp(ulong addr, char *interface, char *devstring) +{ + char *filename, *env_addr, *fit_image_name; + ulong update_addr, update_fladdr, update_size; + int images_noffset, ndepth, noffset; + bool update_tftp_dfu; + int ret = 0; + void *fit; + + if (interface == NULL && devstring == NULL) { + update_tftp_dfu = false; + } else if (interface && devstring) { + update_tftp_dfu = true; + } else { + pr_err("Interface: %s and devstring: %s not supported!\n", + interface, devstring); + return -EINVAL; + } + + /* use already present image */ + if (addr) + goto got_update_file; + + printf("Auto-update from TFTP: "); + + /* get the file name of the update file */ + filename = env_get(UPDATE_FILE_ENV); + if (filename == NULL) { + printf("failed, env. variable '%s' not found\n", + UPDATE_FILE_ENV); + return 1; + } + + printf("trying update file '%s'\n", filename); + + /* get load address of downloaded update file */ + env_addr = env_get("loadaddr"); + if (env_addr) + addr = simple_strtoul(env_addr, NULL, 16); + else + addr = CONFIG_UPDATE_LOAD_ADDR; + + + if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX, + CONFIG_UPDATE_TFTP_CNT_MAX, addr)) { + printf("Can't load update file, aborting auto-update\n"); + return 1; + } + +got_update_file: + fit = map_sysmem(addr, 0); + + if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) { + printf("Bad FIT format of the update file, aborting " + "auto-update\n"); + return 1; + } + + /* process updates */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + + ndepth = 0; + noffset = fdt_next_node(fit, images_noffset, &ndepth); + while (noffset >= 0 && ndepth > 0) { + if (ndepth != 1) + goto next_node; + + fit_image_name = (char *)fit_get_name(fit, noffset, NULL); + printf("Processing update '%s' :", fit_image_name); + + if (!fit_image_verify(fit, noffset)) { + printf("Error: invalid update hash, aborting\n"); + ret = 1; + goto next_node; + } + + printf("\n"); + if (update_fit_getparams(fit, noffset, &update_addr, + &update_fladdr, &update_size)) { + printf("Error: can't get update parameters, aborting\n"); + ret = 1; + goto next_node; + } + + if (!update_tftp_dfu) { + if (update_flash(update_addr, update_fladdr, + update_size)) { + printf("Error: can't flash update, aborting\n"); + ret = 1; + goto next_node; + } + } else if (fit_image_check_type(fit, noffset, + IH_TYPE_FIRMWARE)) { + ret = dfu_write_by_name(fit_image_name, + (void *)update_addr, + update_size, interface, + devstring); + if (ret) + return ret; + } +next_node: + noffset = fdt_next_node(fit, noffset, &ndepth); + } + + return ret; +} +#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */ + +#ifdef CONFIG_UPDATE_FIT +/** + * fit_update - update storage with FIT image + * @fit: Pointer to FIT image + * + * Update firmware on storage using FIT image as input. + * The storage area to be update will be identified by the name + * in FIT and matching it to "dfu_alt_info" variable. + * + * Return: 0 - on success, non-zero - otherwise + */ +int fit_update(const void *fit) +{ + char *fit_image_name; + ulong update_addr, update_fladdr, update_size; + int images_noffset, ndepth, noffset; + int ret = 0; + + if (!fit) + return -EINVAL; + + if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) { + printf("Bad FIT format of the update file, aborting auto-update\n"); + return -EINVAL; + } + + /* process updates */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + + ndepth = 0; + noffset = fdt_next_node(fit, images_noffset, &ndepth); + while (noffset >= 0 && ndepth > 0) { + if (ndepth != 1) + goto next_node; + + fit_image_name = (char *)fit_get_name(fit, noffset, NULL); + printf("Processing update '%s' :", fit_image_name); + + if (!fit_image_verify(fit, noffset)) { + printf("Error: invalid update hash, aborting\n"); + ret = 1; + goto next_node; + } + + printf("\n"); + if (update_fit_getparams(fit, noffset, &update_addr, + &update_fladdr, &update_size)) { + printf("Error: can't get update parameters, aborting\n"); + ret = 1; + goto next_node; + } + + if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) { + ret = dfu_write_by_name(fit_image_name, + (void *)update_addr, + update_size, NULL, NULL); + if (ret) + return ret; + } +next_node: + noffset = fdt_next_node(fit, noffset, &ndepth); + } + + return ret; +} +#endif /* CONFIG_UPDATE_FIT */ diff --git a/roms/u-boot/common/usb.c b/roms/u-boot/common/usb.c new file mode 100644 index 000000000..aad13fd9c --- /dev/null +++ b/roms/u-boot/common/usb.c @@ -0,0 +1,1293 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Most of this source has been derived from the Linux USB + * project: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + */ + +/* + * How it works: + * + * Since this is a bootloader, the devices will not be automatic + * (re)configured on hotplug, but after a restart of the USB the + * device should work. + * + * For each transfer (except "Interrupt") we wait for completion. + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <asm/processor.h> +#include <linux/compiler.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <errno.h> +#include <usb.h> +#include <linux/delay.h> + +#define USB_BUFSIZ 512 + +static int asynch_allowed; +char usb_started; /* flag for the started/stopped USB status */ + +#if !CONFIG_IS_ENABLED(DM_USB) +static struct usb_device usb_dev[USB_MAX_DEVICE]; +static int dev_index; + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +/*************************************************************************** + * Init USB Device + */ +int usb_init(void) +{ + void *ctrl; + struct usb_device *dev; + int i, start_index = 0; + int controllers_initialized = 0; + int ret; + + dev_index = 0; + asynch_allowed = 1; + usb_hub_reset(); + + /* first make all devices unknown */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + memset(&usb_dev[i], 0, sizeof(struct usb_device)); + usb_dev[i].devnum = -1; + } + + /* init low_level USB */ + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + /* init low_level USB */ + printf("USB%d: ", i); + ret = usb_lowlevel_init(i, USB_INIT_HOST, &ctrl); + if (ret == -ENODEV) { /* No such device. */ + puts("Port not available.\n"); + controllers_initialized++; + continue; + } + + if (ret) { /* Other error. */ + puts("lowlevel init failed\n"); + continue; + } + /* + * lowlevel init is OK, now scan the bus for devices + * i.e. search HUBs and configure them + */ + controllers_initialized++; + start_index = dev_index; + printf("scanning bus %d for devices... ", i); + ret = usb_alloc_new_device(ctrl, &dev); + if (ret) + break; + + /* + * device 0 is always present + * (root hub, so let it analyze) + */ + ret = usb_new_device(dev); + if (ret) + usb_free_device(dev->controller); + + if (start_index == dev_index) { + puts("No USB Device found\n"); + continue; + } else { + printf("%d USB Device(s) found\n", + dev_index - start_index); + } + + usb_started = 1; + } + + debug("scan end\n"); + /* if we were not able to find at least one working bus, bail out */ + if (controllers_initialized == 0) + puts("USB error: all controllers failed lowlevel init\n"); + + return usb_started ? 0 : -ENODEV; +} + +/****************************************************************************** + * Stop USB this stops the LowLevel Part and deregisters USB devices. + */ +int usb_stop(void) +{ + int i; + + if (usb_started) { + asynch_allowed = 1; + usb_started = 0; + usb_hub_reset(); + + for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + if (usb_lowlevel_stop(i)) + printf("failed to stop USB controller %d\n", i); + } + } + + return 0; +} + +/****************************************************************************** + * Detect if a USB device has been plugged or unplugged. + */ +int usb_detect_change(void) +{ + int i, j; + int change = 0; + + for (j = 0; j < USB_MAX_DEVICE; j++) { + for (i = 0; i < usb_dev[j].maxchild; i++) { + struct usb_port_status status; + + if (usb_get_port_status(&usb_dev[j], i + 1, + &status) < 0) + /* USB request failed */ + continue; + + if (le16_to_cpu(status.wPortChange) & + USB_PORT_STAT_C_CONNECTION) + change++; + } + } + + return change; +} + +/* Lock or unlock async schedule on the controller */ +__weak int usb_lock_async(struct usb_device *dev, int lock) +{ + return 0; +} + +/* + * disables the asynch behaviour of the control message. This is used for data + * transfers that uses the exclusiv access to the control and bulk messages. + * Returns the old value so it can be restored later. + */ +int usb_disable_asynch(int disable) +{ + int old_value = asynch_allowed; + + asynch_allowed = !disable; + return old_value; +} +#endif /* !CONFIG_IS_ENABLED(DM_USB) */ + + +/*------------------------------------------------------------------- + * Message wrappers. + * + */ + +/* + * submits an Interrupt Message. Some drivers may implement non-blocking + * polling: when non-block is true and the device is not responding return + * -EAGAIN instead of waiting for device to respond. + */ +int usb_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, int interval, bool nonblock) +{ + return submit_int_msg(dev, pipe, buffer, transfer_len, interval, + nonblock); +} + +/* + * submits a control message and waits for comletion (at least timeout * 1ms) + * If timeout is 0, we don't wait for completion (used as example to set and + * clear keyboards LEDs). For data transfers, (storage transfers) we don't + * allow control messages with 0 timeout, by previousely resetting the flag + * asynch_allowed (usb_disable_asynch(1)). + * returns the transferred length if OK or -1 if error. The transferred length + * and the current status are stored in the dev->act_len and dev->status. + */ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, + unsigned char request, unsigned char requesttype, + unsigned short value, unsigned short index, + void *data, unsigned short size, int timeout) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct devrequest, setup_packet, 1); + int err; + + if ((timeout == 0) && (!asynch_allowed)) { + /* request for a asynch control pipe is not allowed */ + return -EINVAL; + } + + /* set setup command */ + setup_packet->requesttype = requesttype; + setup_packet->request = request; + setup_packet->value = cpu_to_le16(value); + setup_packet->index = cpu_to_le16(index); + setup_packet->length = cpu_to_le16(size); + debug("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ + "value 0x%X index 0x%X length 0x%X\n", + request, requesttype, value, index, size); + dev->status = USB_ST_NOT_PROC; /*not yet processed */ + + err = submit_control_msg(dev, pipe, data, size, setup_packet); + if (err < 0) + return err; + if (timeout == 0) + return (int)size; + + /* + * Wait for status to update until timeout expires, USB driver + * interrupt handler may set the status when the USB operation has + * been completed. + */ + while (timeout--) { + if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + mdelay(1); + } + if (dev->status) + return -1; + + return dev->act_len; + +} + +/*------------------------------------------------------------------- + * submits bulk message, and waits for completion. returns 0 if Ok or + * negative if Error. + * synchronous behavior + */ +int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + if (len < 0) + return -EINVAL; + dev->status = USB_ST_NOT_PROC; /*not yet processed */ + if (submit_bulk_msg(dev, pipe, data, len) < 0) + return -EIO; + while (timeout--) { + if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + mdelay(1); + } + *actual_length = dev->act_len; + if (dev->status == 0) + return 0; + else + return -EIO; +} + + +/*------------------------------------------------------------------- + * Max Packet stuff + */ + +/* + * returns the max packet size, depending on the pipe direction and + * the configurations values + */ +int usb_maxpacket(struct usb_device *dev, unsigned long pipe) +{ + /* direction is out -> use emaxpacket out */ + if ((pipe & USB_DIR_IN) == 0) + return dev->epmaxpacketout[((pipe>>15) & 0xf)]; + else + return dev->epmaxpacketin[((pipe>>15) & 0xf)]; +} + +/* + * The routine usb_set_maxpacket_ep() is extracted from the loop of routine + * usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine + * when it is inlined in 1 single routine. What happens is that the register r3 + * is used as loop-count 'i', but gets overwritten later on. + * This is clearly a compiler bug, but it is easier to workaround it here than + * to update the compiler (Occurs with at least several GCC 4.{1,2},x + * CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM) + * + * NOTE: Similar behaviour was observed with GCC4.6 on ARMv5. + */ +static void noinline +usb_set_maxpacket_ep(struct usb_device *dev, int if_idx, int ep_idx) +{ + int b; + struct usb_endpoint_descriptor *ep; + u16 ep_wMaxPacketSize; + + ep = &dev->config.if_desc[if_idx].ep_desc[ep_idx]; + + b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ep_wMaxPacketSize = get_unaligned(&ep->wMaxPacketSize); + + if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL) { + /* Control => bidirectional */ + dev->epmaxpacketout[b] = ep_wMaxPacketSize; + dev->epmaxpacketin[b] = ep_wMaxPacketSize; + debug("##Control EP epmaxpacketout/in[%d] = %d\n", + b, dev->epmaxpacketin[b]); + } else { + if ((ep->bEndpointAddress & 0x80) == 0) { + /* OUT Endpoint */ + if (ep_wMaxPacketSize > dev->epmaxpacketout[b]) { + dev->epmaxpacketout[b] = ep_wMaxPacketSize; + debug("##EP epmaxpacketout[%d] = %d\n", + b, dev->epmaxpacketout[b]); + } + } else { + /* IN Endpoint */ + if (ep_wMaxPacketSize > dev->epmaxpacketin[b]) { + dev->epmaxpacketin[b] = ep_wMaxPacketSize; + debug("##EP epmaxpacketin[%d] = %d\n", + b, dev->epmaxpacketin[b]); + } + } /* if out */ + } /* if control */ +} + +/* + * set the max packed value of all endpoints in the given configuration + */ +static int usb_set_maxpacket(struct usb_device *dev) +{ + int i, ii; + + for (i = 0; i < dev->config.desc.bNumInterfaces; i++) + for (ii = 0; ii < dev->config.if_desc[i].desc.bNumEndpoints; ii++) + usb_set_maxpacket_ep(dev, i, ii); + + return 0; +} + +/******************************************************************************* + * Parse the config, located in buffer, and fills the dev->config structure. + * Note that all little/big endian swapping are done automatically. + * (wTotalLength has already been swapped and sanitized when it was read.) + */ +static int usb_parse_config(struct usb_device *dev, + unsigned char *buffer, int cfgno) +{ + struct usb_descriptor_header *head; + int index, ifno, epno, curr_if_num; + u16 ep_wMaxPacketSize; + struct usb_interface *if_desc = NULL; + + ifno = -1; + epno = -1; + curr_if_num = -1; + + dev->configno = cfgno; + head = (struct usb_descriptor_header *) &buffer[0]; + if (head->bDescriptorType != USB_DT_CONFIG) { + printf(" ERROR: NOT USB_CONFIG_DESC %x\n", + head->bDescriptorType); + return -EINVAL; + } + if (head->bLength != USB_DT_CONFIG_SIZE) { + printf("ERROR: Invalid USB CFG length (%d)\n", head->bLength); + return -EINVAL; + } + memcpy(&dev->config, head, USB_DT_CONFIG_SIZE); + dev->config.no_of_if = 0; + + index = dev->config.desc.bLength; + /* Ok the first entry must be a configuration entry, + * now process the others */ + head = (struct usb_descriptor_header *) &buffer[index]; + while (index + 1 < dev->config.desc.wTotalLength && head->bLength) { + switch (head->bDescriptorType) { + case USB_DT_INTERFACE: + if (head->bLength != USB_DT_INTERFACE_SIZE) { + printf("ERROR: Invalid USB IF length (%d)\n", + head->bLength); + break; + } + if (index + USB_DT_INTERFACE_SIZE > + dev->config.desc.wTotalLength) { + puts("USB IF descriptor overflowed buffer!\n"); + break; + } + if (((struct usb_interface_descriptor *) \ + head)->bInterfaceNumber != curr_if_num) { + /* this is a new interface, copy new desc */ + ifno = dev->config.no_of_if; + if (ifno >= USB_MAXINTERFACES) { + puts("Too many USB interfaces!\n"); + /* try to go on with what we have */ + return -EINVAL; + } + if_desc = &dev->config.if_desc[ifno]; + dev->config.no_of_if++; + memcpy(if_desc, head, + USB_DT_INTERFACE_SIZE); + if_desc->no_of_ep = 0; + if_desc->num_altsetting = 1; + curr_if_num = + if_desc->desc.bInterfaceNumber; + } else { + /* found alternate setting for the interface */ + if (ifno >= 0) { + if_desc = &dev->config.if_desc[ifno]; + if_desc->num_altsetting++; + } + } + break; + case USB_DT_ENDPOINT: + if (head->bLength != USB_DT_ENDPOINT_SIZE && + head->bLength != USB_DT_ENDPOINT_AUDIO_SIZE) { + printf("ERROR: Invalid USB EP length (%d)\n", + head->bLength); + break; + } + if (index + head->bLength > + dev->config.desc.wTotalLength) { + puts("USB EP descriptor overflowed buffer!\n"); + break; + } + if (ifno < 0) { + puts("Endpoint descriptor out of order!\n"); + break; + } + epno = dev->config.if_desc[ifno].no_of_ep; + if_desc = &dev->config.if_desc[ifno]; + if (epno >= USB_MAXENDPOINTS) { + printf("Interface %d has too many endpoints!\n", + if_desc->desc.bInterfaceNumber); + return -EINVAL; + } + /* found an endpoint */ + if_desc->no_of_ep++; + memcpy(&if_desc->ep_desc[epno], head, + USB_DT_ENDPOINT_SIZE); + ep_wMaxPacketSize = get_unaligned(&dev->config.\ + if_desc[ifno].\ + ep_desc[epno].\ + wMaxPacketSize); + put_unaligned(le16_to_cpu(ep_wMaxPacketSize), + &dev->config.\ + if_desc[ifno].\ + ep_desc[epno].\ + wMaxPacketSize); + debug("if %d, ep %d\n", ifno, epno); + break; + case USB_DT_SS_ENDPOINT_COMP: + if (head->bLength != USB_DT_SS_EP_COMP_SIZE) { + printf("ERROR: Invalid USB EPC length (%d)\n", + head->bLength); + break; + } + if (index + USB_DT_SS_EP_COMP_SIZE > + dev->config.desc.wTotalLength) { + puts("USB EPC descriptor overflowed buffer!\n"); + break; + } + if (ifno < 0 || epno < 0) { + puts("EPC descriptor out of order!\n"); + break; + } + if_desc = &dev->config.if_desc[ifno]; + memcpy(&if_desc->ss_ep_comp_desc[epno], head, + USB_DT_SS_EP_COMP_SIZE); + break; + default: + if (head->bLength == 0) + return -EINVAL; + + debug("unknown Description Type : %x\n", + head->bDescriptorType); + +#ifdef DEBUG + { + unsigned char *ch = (unsigned char *)head; + int i; + + for (i = 0; i < head->bLength; i++) + debug("%02X ", *ch++); + debug("\n\n\n"); + } +#endif + break; + } + index += head->bLength; + head = (struct usb_descriptor_header *)&buffer[index]; + } + return 0; +} + +/*********************************************************************** + * Clears an endpoint + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ +int usb_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); + + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, + endp, NULL, 0, USB_CNTL_TIMEOUT * 3); + + /* don't clear if failed */ + if (result < 0) + return result; + + /* + * NOTE: we do not get status and verify reset was successful + * as some devices are reported to lock up upon this check.. + */ + + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + /* toggle is reset on clear */ + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + return 0; +} + + +/********************************************************************** + * get_descriptor type + */ +static int usb_get_descriptor(struct usb_device *dev, unsigned char type, + unsigned char index, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, buf, size, + USB_CNTL_TIMEOUT); +} + +/********************************************************************** + * gets len of configuration cfgno + */ +int usb_get_configuration_len(struct usb_device *dev, int cfgno) +{ + int result; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, 9); + struct usb_config_descriptor *config; + + config = (struct usb_config_descriptor *)&buffer[0]; + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 9); + if (result < 9) { + if (result < 0) + printf("unable to get descriptor, error %lX\n", + dev->status); + else + printf("config descriptor too short " \ + "(expected %i, got %i)\n", 9, result); + return -EIO; + } + return le16_to_cpu(config->wTotalLength); +} + +/********************************************************************** + * gets configuration cfgno and store it in the buffer + */ +int usb_get_configuration_no(struct usb_device *dev, int cfgno, + unsigned char *buffer, int length) +{ + int result; + struct usb_config_descriptor *config; + + config = (struct usb_config_descriptor *)&buffer[0]; + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, length); + debug("get_conf_no %d Result %d, wLength %d\n", cfgno, result, + le16_to_cpu(config->wTotalLength)); + config->wTotalLength = result; /* validated, with CPU byte order */ + + return result; +} + +/******************************************************************** + * set address of a device to the value in dev->devnum. + * This can only be done by addressing the device via the default address (0) + */ +static int usb_set_address(struct usb_device *dev) +{ + debug("set address %d\n", dev->devnum); + + return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, + 0, (dev->devnum), 0, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * set interface number to interface + */ +int usb_set_interface(struct usb_device *dev, int interface, int alternate) +{ + struct usb_interface *if_face = NULL; + int ret, i; + + for (i = 0; i < dev->config.desc.bNumInterfaces; i++) { + if (dev->config.if_desc[i].desc.bInterfaceNumber == interface) { + if_face = &dev->config.if_desc[i]; + break; + } + } + if (!if_face) { + printf("selecting invalid interface %d", interface); + return -EINVAL; + } + /* + * We should return now for devices with only one alternate setting. + * According to 9.4.10 of the Universal Serial Bus Specification + * Revision 2.0 such devices can return with a STALL. This results in + * some USB sticks timeouting during initialization and then being + * unusable in U-Boot. + */ + if (if_face->num_altsetting == 1) + return 0; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, + alternate, interface, NULL, 0, + USB_CNTL_TIMEOUT * 5); + if (ret < 0) + return ret; + + return 0; +} + +/******************************************************************** + * set configuration number to configuration + */ +static int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int res; + debug("set configuration %d\n", configuration); + /* set setup command */ + res = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, + configuration, 0, + NULL, 0, USB_CNTL_TIMEOUT); + if (res == 0) { + dev->toggle[0] = 0; + dev->toggle[1] = 0; + return 0; + } else + return -EIO; +} + +/******************************************************************** + * set protocol to protocol + */ +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + protocol, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * set idle + */ +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (duration << 8) | report_id, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get report + */ +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get class descriptor + */ +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get string index in buffer + */ +static int usb_get_string(struct usb_device *dev, unsigned short langid, + unsigned char index, void *buf, int size) +{ + int i; + int result; + + for (i = 0; i < 3; ++i) { + /* some devices are flaky */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, + USB_CNTL_TIMEOUT); + + if (result > 0) + break; + } + + return result; +} + + +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + + +static int usb_string_sub(struct usb_device *dev, unsigned int langid, + unsigned int index, unsigned char *buf) +{ + int rc; + + /* Try to read the string descriptor by asking for the maximum + * possible number of bytes */ + rc = usb_get_string(dev, langid, index, buf, 255); + + /* If that failed try to read the descriptor length, then + * ask for just that many bytes */ + if (rc < 2) { + rc = usb_get_string(dev, langid, index, buf, 2); + if (rc == 2) + rc = usb_get_string(dev, langid, index, buf, buf[0]); + } + + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + + /* There might be extra junk at the end of the descriptor */ + if (buf[0] < rc) + rc = buf[0]; + + rc = rc - (rc & 1); /* force a multiple of two */ + } + + if (rc < 2) + rc = -EINVAL; + + return rc; +} + + +/******************************************************************** + * usb_string: + * Get string index and translate it to ascii. + * returns string length (> 0) or error (< 0) + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, mybuf, USB_BUFSIZ); + unsigned char *tbuf; + int err; + unsigned int u, idx; + + if (size <= 0 || !buf || !index) + return -EINVAL; + buf[0] = 0; + tbuf = &mybuf[0]; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_string_sub(dev, 0, 0, tbuf); + if (err < 0) { + debug("error getting string descriptor 0 " \ + "(error=%lx)\n", dev->status); + return -EIO; + } else if (tbuf[0] < 4) { + debug("string descriptor 0 too short\n"); + return -EIO; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + /* always use the first langid listed */ + debug("USB device number %d default " \ + "language ID 0x%x\n", + dev->devnum, dev->string_langid); + } + } + + err = usb_string_sub(dev, dev->string_langid, index, tbuf); + if (err < 0) + return err; + + size--; /* leave room for trailing NULL char in output buffer */ + for (idx = 0, u = 2; u < err; u += 2) { + if (idx >= size) + break; + if (tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non-ASCII character */ + else + buf[idx++] = tbuf[u]; + } + buf[idx] = 0; + err = idx; + return err; +} + + +/******************************************************************** + * USB device handling: + * the USB device are static allocated [USB_MAX_DEVICE]. + */ + +#if !CONFIG_IS_ENABLED(DM_USB) + +/* returns a pointer to the device with the index [index]. + * if the device is not assigned (dev->devnum==-1) returns NULL + */ +struct usb_device *usb_get_dev_index(int index) +{ + if (usb_dev[index].devnum == -1) + return NULL; + else + return &usb_dev[index]; +} + +int usb_alloc_new_device(struct udevice *controller, struct usb_device **devp) +{ + int i; + debug("New Device %d\n", dev_index); + if (dev_index == USB_MAX_DEVICE) { + printf("ERROR, too many USB Devices, max=%d\n", USB_MAX_DEVICE); + return -ENOSPC; + } + /* default Address is 0, real addresses start with 1 */ + usb_dev[dev_index].devnum = dev_index + 1; + usb_dev[dev_index].maxchild = 0; + for (i = 0; i < USB_MAXCHILDREN; i++) + usb_dev[dev_index].children[i] = NULL; + usb_dev[dev_index].parent = NULL; + usb_dev[dev_index].controller = controller; + dev_index++; + *devp = &usb_dev[dev_index - 1]; + + return 0; +} + +/* + * Free the newly created device node. + * Called in error cases where configuring a newly attached + * device fails for some reason. + */ +void usb_free_device(struct udevice *controller) +{ + dev_index--; + debug("Freeing device node: %d\n", dev_index); + memset(&usb_dev[dev_index], 0, sizeof(struct usb_device)); + usb_dev[dev_index].devnum = -1; +} + +/* + * XHCI issues Enable Slot command and thereafter + * allocates device contexts. Provide a weak alias + * function for the purpose, so that XHCI overrides it + * and EHCI/OHCI just work out of the box. + */ +__weak int usb_alloc_device(struct usb_device *udev) +{ + return 0; +} +#endif /* !CONFIG_IS_ENABLED(DM_USB) */ + +static int usb_hub_port_reset(struct usb_device *dev, struct usb_device *hub) +{ + if (!hub) + usb_reset_root_port(dev); + + return 0; +} + +static int get_descriptor_len(struct usb_device *dev, int len, int expect_len) +{ + __maybe_unused struct usb_device_descriptor *desc; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + int err; + + desc = (struct usb_device_descriptor *)tmpbuf; + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, len); + if (err < expect_len) { + if (err < 0) { + printf("unable to get device descriptor (error=%d)\n", + err); + return err; + } else { + printf("USB device descriptor short read (expected %i, got %i)\n", + expect_len, err); + return -EIO; + } + } + memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor)); + + return 0; +} + +static int usb_setup_descriptor(struct usb_device *dev, bool do_read) +{ + /* + * This is a Windows scheme of initialization sequence, with double + * reset of the device (Linux uses the same sequence) + * Some equipment is said to work only with such init sequence; this + * patch is based on the work by Alan Stern: + * http://sourceforge.net/mailarchive/forum.php? + * thread_id=5729457&forum_id=5398 + */ + + /* + * send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is + * only 18 bytes long, this will terminate with a short packet. But if + * the maxpacket size is 8 or 16 the device may be waiting to transmit + * some more, or keeps on retransmitting the 8 byte header. + */ + + if (dev->speed == USB_SPEED_LOW) { + dev->descriptor.bMaxPacketSize0 = 8; + dev->maxpacketsize = PACKET_SIZE_8; + } else { + dev->descriptor.bMaxPacketSize0 = 64; + dev->maxpacketsize = PACKET_SIZE_64; + } + dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + + if (do_read && dev->speed == USB_SPEED_FULL) { + int err; + + /* + * Validate we've received only at least 8 bytes, not that + * we've received the entire descriptor. The reasoning is: + * - The code only uses fields in the first 8 bytes, so + * that's all we need to have fetched at this stage. + * - The smallest maxpacket size is 8 bytes. Before we know + * the actual maxpacket the device uses, the USB controller + * may only accept a single packet. Consequently we are only + * guaranteed to receive 1 packet (at least 8 bytes) even in + * a non-error case. + * + * At least the DWC2 controller needs to be programmed with + * the number of packets in addition to the number of bytes. + * A request for 64 bytes of data with the maxpacket guessed + * as 64 (above) yields a request for 1 packet. + */ + err = get_descriptor_len(dev, 64, 8); + if (err) + return err; + } + + dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + switch (dev->descriptor.bMaxPacketSize0) { + case 8: + dev->maxpacketsize = PACKET_SIZE_8; + break; + case 16: + dev->maxpacketsize = PACKET_SIZE_16; + break; + case 32: + dev->maxpacketsize = PACKET_SIZE_32; + break; + case 64: + dev->maxpacketsize = PACKET_SIZE_64; + break; + default: + printf("%s: invalid max packet size\n", __func__); + return -EIO; + } + + return 0; +} + +static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, + struct usb_device *parent) +{ + int err; + + /* + * Allocate usb 3.0 device context. + * USB 3.0 (xHCI) protocol tries to allocate device slot + * and related data structures first. This call does that. + * Refer to sec 4.3.2 in xHCI spec rev1.0 + */ + err = usb_alloc_device(dev); + if (err) { + printf("Cannot allocate device context to get SLOT_ID\n"); + return err; + } + err = usb_setup_descriptor(dev, do_read); + if (err) + return err; + err = usb_hub_port_reset(dev, parent); + if (err) + return err; + + dev->devnum = addr; + + err = usb_set_address(dev); /* set address */ + + if (err < 0) { + printf("\n USB device not accepting new address " \ + "(error=%lX)\n", dev->status); + return err; + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + /* + * If we haven't read device descriptor before, read it here + * after device is assigned an address. This is only applicable + * to xHCI so far. + */ + if (!do_read) { + err = usb_setup_descriptor(dev, true); + if (err) + return err; + } + + return 0; +} + +int usb_select_config(struct usb_device *dev) +{ + unsigned char *tmpbuf = NULL; + int err; + + err = get_descriptor_len(dev, USB_DT_DEVICE_SIZE, USB_DT_DEVICE_SIZE); + if (err) + return err; + + /* correct le values */ + le16_to_cpus(&dev->descriptor.bcdUSB); + le16_to_cpus(&dev->descriptor.idVendor); + le16_to_cpus(&dev->descriptor.idProduct); + le16_to_cpus(&dev->descriptor.bcdDevice); + + /* + * Kingston DT Ultimate 32GB USB 3.0 seems to be extremely sensitive + * about this first Get Descriptor request. If there are any other + * requests in the first microframe, the stick crashes. Wait about + * one microframe duration here (1mS for USB 1.x , 125uS for USB 2.0). + */ + mdelay(1); + + /* only support for one config for now */ + err = usb_get_configuration_len(dev, 0); + if (err >= 0) { + tmpbuf = (unsigned char *)malloc_cache_aligned(err); + if (!tmpbuf) + err = -ENOMEM; + else + err = usb_get_configuration_no(dev, 0, tmpbuf, err); + } + if (err < 0) { + printf("usb_new_device: Cannot read configuration, " \ + "skipping device %04x:%04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + free(tmpbuf); + return err; + } + usb_parse_config(dev, tmpbuf, 0); + free(tmpbuf); + usb_set_maxpacket(dev); + /* + * we set the default configuration here + * This seems premature. If the driver wants a different configuration + * it will need to select itself. + */ + err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue); + if (err < 0) { + printf("failed to set default configuration " \ + "len %d, status %lX\n", dev->act_len, dev->status); + return err; + } + + /* + * Wait until the Set Configuration request gets processed by the + * device. This is required by at least SanDisk Cruzer Pop USB 2.0 + * and Kingston DT Ultimate 32GB USB 3.0 on DWC2 OTG controller. + */ + mdelay(10); + + debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", + dev->descriptor.iManufacturer, dev->descriptor.iProduct, + dev->descriptor.iSerialNumber); + memset(dev->mf, 0, sizeof(dev->mf)); + memset(dev->prod, 0, sizeof(dev->prod)); + memset(dev->serial, 0, sizeof(dev->serial)); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, + dev->mf, sizeof(dev->mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, + dev->prod, sizeof(dev->prod)); + if (dev->descriptor.iSerialNumber) + usb_string(dev, dev->descriptor.iSerialNumber, + dev->serial, sizeof(dev->serial)); + debug("Manufacturer %s\n", dev->mf); + debug("Product %s\n", dev->prod); + debug("SerialNumber %s\n", dev->serial); + + return 0; +} + +int usb_setup_device(struct usb_device *dev, bool do_read, + struct usb_device *parent) +{ + int addr; + int ret; + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + + ret = usb_prepare_device(dev, addr, do_read, parent); + if (ret) + return ret; + ret = usb_select_config(dev); + + return ret; +} + +#if !CONFIG_IS_ENABLED(DM_USB) +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + bool do_read = true; + int err; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ +#ifdef CONFIG_USB_XHCI_HCD + do_read = false; +#endif + err = usb_setup_device(dev, do_read, dev->parent); + if (err) + return err; + + /* Now probe if the device is a hub */ + err = usb_hub_probe(dev, 0); + if (err < 0) + return err; + + return 0; +} +#endif + +__weak +int board_usb_init(int index, enum usb_init_type init) +{ + return 0; +} + +__weak +int board_usb_cleanup(int index, enum usb_init_type init) +{ + return 0; +} + +bool usb_device_has_child_on_port(struct usb_device *parent, int port) +{ +#if CONFIG_IS_ENABLED(DM_USB) + return false; +#else + return parent->children[port] != NULL; +#endif +} + +#if CONFIG_IS_ENABLED(DM_USB) +void usb_find_usb2_hub_address_port(struct usb_device *udev, + uint8_t *hub_address, uint8_t *hub_port) +{ + struct udevice *parent; + struct usb_device *uparent, *ttdev; + + /* + * When called from usb-uclass.c: usb_scan_device() udev->dev points + * to the parent udevice, not the actual udevice belonging to the + * udev as the device is not instantiated yet. So when searching + * for the first usb-2 parent start with udev->dev not + * udev->dev->parent . + */ + ttdev = udev; + parent = udev->dev; + uparent = dev_get_parent_priv(parent); + + while (uparent->speed != USB_SPEED_HIGH) { + struct udevice *dev = parent; + + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) { + printf("Error: Cannot find high speed parent of usb-1 device\n"); + *hub_address = 0; + *hub_port = 0; + return; + } + + ttdev = dev_get_parent_priv(dev); + parent = dev->parent; + uparent = dev_get_parent_priv(parent); + } + *hub_address = uparent->devnum; + *hub_port = ttdev->portnr; +} +#else +void usb_find_usb2_hub_address_port(struct usb_device *udev, + uint8_t *hub_address, uint8_t *hub_port) +{ + /* Find out the nearest parent which is high speed */ + while (udev->parent->parent != NULL) + if (udev->parent->speed != USB_SPEED_HIGH) { + udev = udev->parent; + } else { + *hub_address = udev->parent->devnum; + *hub_port = udev->portnr; + return; + } + + printf("Error: Cannot find high speed parent of usb-1 device\n"); + *hub_address = 0; + *hub_port = 0; +} +#endif + + +/* EOF */ diff --git a/roms/u-boot/common/usb_hub.c b/roms/u-boot/common/usb_hub.c new file mode 100644 index 000000000..ba11a188c --- /dev/null +++ b/roms/u-boot/common/usb_hub.c @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Most of this source has been derived from the Linux USB + * project: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + */ + +/**************************************************************************** + * HUB "Driver" + * Probes device for being a hub and configurate it + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <env.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <asm/processor.h> +#include <asm/unaligned.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <asm/byteorder.h> +#ifdef CONFIG_SANDBOX +#include <asm/state.h> +#endif +#include <asm/unaligned.h> + +#include <usb.h> + +#define USB_BUFSIZ 512 + +#define HUB_SHORT_RESET_TIME 20 +#define HUB_LONG_RESET_TIME 200 + +#define PORT_OVERCURRENT_MAX_SCAN_COUNT 3 + +struct usb_device_scan { + struct usb_device *dev; /* USB hub device to scan */ + struct usb_hub_device *hub; /* USB hub struct */ + int port; /* USB port to scan */ + struct list_head list; +}; + +static LIST_HEAD(usb_scan_list); + +__weak void usb_hub_reset_devices(struct usb_hub_device *hub, int port) +{ + return; +} + +static inline bool usb_hub_is_superspeed(struct usb_device *hdev) +{ + return hdev->descriptor.bDeviceProtocol == 3; +} + +#if CONFIG_IS_ENABLED(DM_USB) +bool usb_hub_is_root_hub(struct udevice *hub) +{ + if (device_get_uclass_id(hub->parent) != UCLASS_USB_HUB) + return true; + + return false; +} + +static int usb_set_hub_depth(struct usb_device *dev, int depth) +{ + if (depth < 0 || depth > 4) + return -EINVAL; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_HUB_DEPTH, USB_DIR_OUT | USB_RT_HUB, + depth, 0, NULL, 0, USB_CNTL_TIMEOUT); +} +#endif + +static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + unsigned short dtype = USB_DT_HUB; + + if (usb_hub_is_superspeed(dev)) + dtype = USB_DT_SS_HUB; + + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + dtype << 8, 0, data, size, USB_CNTL_TIMEOUT); +} + +static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_port_status), USB_CNTL_TIMEOUT); + +#if CONFIG_IS_ENABLED(DM_USB) + if (ret < 0) + return ret; + + /* + * Translate the USB 3.0 hub port status field into the old version + * that U-Boot understands. Do this only when the hub is not root hub. + * For root hub, the port status field has already been translated + * in the host controller driver (see xhci_submit_root() in xhci.c). + * + * Note: this only supports driver model. + */ + + if (!usb_hub_is_root_hub(dev->dev) && usb_hub_is_superspeed(dev)) { + struct usb_port_status *status = (struct usb_port_status *)data; + u16 tmp = le16_to_cpu(status->wPortStatus) & + USB_SS_PORT_STAT_MASK; + + if (status->wPortStatus & USB_SS_PORT_STAT_POWER) + tmp |= USB_PORT_STAT_POWER; + if ((status->wPortStatus & USB_SS_PORT_STAT_SPEED) == + USB_SS_PORT_STAT_SPEED_5GBPS) + tmp |= USB_PORT_STAT_SUPER_SPEED; + + status->wPortStatus = cpu_to_le16(tmp); + } +#endif + + return ret; +} + + +static void usb_hub_power_on(struct usb_hub_device *hub) +{ + int i; + struct usb_device *dev; + unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2; + const char *env; + + dev = hub->pusb_dev; + + debug("enabling power on all ports\n"); + for (i = 0; i < dev->maxchild; i++) { + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + debug("port %d returns %lX\n", i + 1, dev->status); + } + +#ifdef CONFIG_SANDBOX + /* + * Don't set timeout / delay values here. This results + * in these values still being reset to 0. + */ + if (state_get_skip_delays()) + return; +#endif + + /* + * Wait for power to become stable, + * plus spec-defined max time for device to connect + * but allow this time to be increased via env variable as some + * devices break the spec and require longer warm-up times + */ + env = env_get("usb_pgood_delay"); + if (env) + pgood_delay = max(pgood_delay, + (unsigned)simple_strtol(env, NULL, 0)); + debug("pgood_delay=%dms\n", pgood_delay); + + /* + * Do a minimum delay of the larger value of 100ms or pgood_delay + * so that the power can stablize before the devices are queried + */ + hub->query_delay = get_timer(0) + max(100, (int)pgood_delay); + + /* + * Record the power-on timeout here. The max. delay (timeout) + * will be done based on this value in the USB port loop in + * usb_hub_configure() later. + */ + hub->connect_timeout = hub->query_delay + 1000; + debug("devnum=%d poweron: query_delay=%d connect_timeout=%d\n", + dev->devnum, max(100, (int)pgood_delay), + max(100, (int)pgood_delay) + 1000); +} + +#if !CONFIG_IS_ENABLED(DM_USB) +static struct usb_hub_device hub_dev[USB_MAX_HUB]; +static int usb_hub_index; + +void usb_hub_reset(void) +{ + usb_hub_index = 0; + + /* Zero out global hub_dev in case its re-used again */ + memset(hub_dev, 0, sizeof(hub_dev)); +} + +static struct usb_hub_device *usb_hub_allocate(void) +{ + if (usb_hub_index < USB_MAX_HUB) + return &hub_dev[usb_hub_index++]; + + printf("ERROR: USB_MAX_HUB (%d) reached\n", USB_MAX_HUB); + return NULL; +} +#endif + +#define MAX_TRIES 5 + +static inline const char *portspeed(int portstatus) +{ + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + return "5 Gb/s"; + case USB_PORT_STAT_HIGH_SPEED: + return "480 Mb/s"; + case USB_PORT_STAT_LOW_SPEED: + return "1.5 Mb/s"; + default: + return "12 Mb/s"; + } +} + +/** + * usb_hub_port_reset() - reset a port given its usb_device pointer + * + * Reset a hub port and see if a device is present on that port, providing + * sufficient time for it to show itself. The port status is returned. + * + * @dev: USB device to reset + * @port: Port number to reset (note ports are numbered from 0 here) + * @portstat: Returns port status + */ +static int usb_hub_port_reset(struct usb_device *dev, int port, + unsigned short *portstat) +{ + int err, tries; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus, portchange; + int delay = HUB_SHORT_RESET_TIME; /* start with short reset delay */ + +#if CONFIG_IS_ENABLED(DM_USB) + debug("%s: resetting '%s' port %d...\n", __func__, dev->dev->name, + port + 1); +#else + debug("%s: resetting port %d...\n", __func__, port + 1); +#endif + for (tries = 0; tries < MAX_TRIES; tries++) { + err = usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); + if (err < 0) + return err; + + mdelay(delay); + + if (usb_get_port_status(dev, port + 1, portsts) < 0) { + debug("get_port_status failed status %lX\n", + dev->status); + return -1; + } + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + + debug("portstatus %x, change %x, %s\n", portstatus, portchange, + portspeed(portstatus)); + + debug("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ + " USB_PORT_STAT_ENABLE %d\n", + (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + + /* + * Perhaps we should check for the following here: + * - C_CONNECTION hasn't been set. + * - CONNECTION is still set. + * + * Doing so would ensure that the device is still connected + * to the bus, and hasn't been unplugged or replaced while the + * USB bus reset was going on. + * + * However, if we do that, then (at least) a San Disk Ultra + * USB 3.0 16GB device fails to reset on (at least) an NVIDIA + * Tegra Jetson TK1 board. For some reason, the device appears + * to briefly drop off the bus when this second bus reset is + * executed, yet if we retry this loop, it'll eventually come + * back after another reset or two. + */ + + if (portstatus & USB_PORT_STAT_ENABLE) + break; + + /* Switch to long reset delay for the next round */ + delay = HUB_LONG_RESET_TIME; + } + + if (tries == MAX_TRIES) { + debug("Cannot enable port %i after %i retries, " \ + "disabling port.\n", port + 1, MAX_TRIES); + debug("Maybe the USB cable is bad?\n"); + return -1; + } + + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); + *portstat = portstatus; + return 0; +} + +int usb_hub_port_connect_change(struct usb_device *dev, int port) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus; + int ret, speed; + + /* Check status */ + ret = usb_get_port_status(dev, port + 1, portsts); + if (ret < 0) { + debug("get_port_status failed\n"); + return ret; + } + + portstatus = le16_to_cpu(portsts->wPortStatus); + debug("portstatus %x, change %x, %s\n", + portstatus, + le16_to_cpu(portsts->wPortChange), + portspeed(portstatus)); + + /* Clear the connection change status */ + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && + (!(portstatus & USB_PORT_STAT_ENABLE))) || + usb_device_has_child_on_port(dev, port)) { + debug("usb_disconnect(&hub->children[port]);\n"); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + } + + /* Reset the port */ + ret = usb_hub_port_reset(dev, port, &portstatus); + if (ret < 0) { + if (ret != -ENXIO) + printf("cannot reset port %i!?\n", port + 1); + return ret; + } + + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { + case USB_PORT_STAT_SUPER_SPEED: + speed = USB_SPEED_SUPER; + break; + case USB_PORT_STAT_HIGH_SPEED: + speed = USB_SPEED_HIGH; + break; + case USB_PORT_STAT_LOW_SPEED: + speed = USB_SPEED_LOW; + break; + default: + speed = USB_SPEED_FULL; + break; + } + +#if CONFIG_IS_ENABLED(DM_USB) + struct udevice *child; + + ret = usb_scan_device(dev->dev, port + 1, speed, &child); +#else + struct usb_device *usb; + + ret = usb_alloc_new_device(dev->controller, &usb); + if (ret) { + printf("cannot create new device: ret=%d", ret); + return ret; + } + + dev->children[port] = usb; + usb->speed = speed; + usb->parent = dev; + usb->portnr = port + 1; + /* Run it through the hoops (find a driver, etc) */ + ret = usb_new_device(usb); + if (ret < 0) { + /* Woops, disable the port */ + usb_free_device(dev->controller); + dev->children[port] = NULL; + } +#endif + if (ret < 0) { + debug("hub: disabling port %d\n", port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); + } + + return ret; +} + +static int usb_scan_port(struct usb_device_scan *usb_scan) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus; + unsigned short portchange; + struct usb_device *dev; + struct usb_hub_device *hub; + int ret = 0; + int i; + + dev = usb_scan->dev; + hub = usb_scan->hub; + i = usb_scan->port; + + /* + * Don't talk to the device before the query delay is expired. + * This is needed for voltages to stabalize. + */ + if (get_timer(0) < hub->query_delay) + return 0; + + ret = usb_get_port_status(dev, i + 1, portsts); + if (ret < 0) { + debug("get_port_status failed\n"); + if (get_timer(0) >= hub->connect_timeout) { + debug("devnum=%d port=%d: timeout\n", + dev->devnum, i + 1); + /* Remove this device from scanning list */ + list_del(&usb_scan->list); + free(usb_scan); + return 0; + } + return 0; + } + + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + debug("Port %d Status %X Change %X\n", i + 1, portstatus, portchange); + + /* + * No connection change happened, wait a bit more. + * + * For some situation, the hub reports no connection change but a + * device is connected to the port (eg: CCS bit is set but CSC is not + * in the PORTSC register of a root hub), ignore such case. + */ + if (!(portchange & USB_PORT_STAT_C_CONNECTION) && + !(portstatus & USB_PORT_STAT_CONNECTION)) { + if (get_timer(0) >= hub->connect_timeout) { + debug("devnum=%d port=%d: timeout\n", + dev->devnum, i + 1); + /* Remove this device from scanning list */ + list_del(&usb_scan->list); + free(usb_scan); + return 0; + } + return 0; + } + + if (portchange & USB_PORT_STAT_C_RESET) { + debug("port %d reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + + if ((portchange & USB_SS_PORT_STAT_C_BH_RESET) && + usb_hub_is_superspeed(dev)) { + debug("port %d BH reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_SS_PORT_FEAT_C_BH_RESET); + } + + /* A new USB device is ready at this point */ + debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1); + + usb_hub_port_connect_change(dev, i); + + if (portchange & USB_PORT_STAT_C_ENABLE) { + debug("port %d enable change, status %x\n", i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); + /* + * The following hack causes a ghost device problem + * to Faraday EHCI + */ +#ifndef CONFIG_USB_EHCI_FARADAY + /* + * EM interference sometimes causes bad shielded USB + * devices to be shutdown by the hub, this hack enables + * them again. Works at least with mouse driver + */ + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && + usb_device_has_child_on_port(dev, i)) { + debug("already running port %i disabled by hub (EMI?), re-enabling...\n", + i + 1); + usb_hub_port_connect_change(dev, i); + } +#endif + } + + if (portstatus & USB_PORT_STAT_SUSPEND) { + debug("port %d suspend change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + debug("port %d over-current change\n", i + 1); + usb_clear_port_feature(dev, i + 1, + USB_PORT_FEAT_C_OVER_CURRENT); + /* Only power-on this one port */ + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + hub->overcurrent_count[i]++; + + /* + * If the max-scan-count is not reached, return without removing + * the device from scan-list. This will re-issue a new scan. + */ + if (hub->overcurrent_count[i] <= + PORT_OVERCURRENT_MAX_SCAN_COUNT) + return 0; + + /* Otherwise the device will get removed */ + printf("Port %d over-current occurred %d times\n", i + 1, + hub->overcurrent_count[i]); + } + + /* + * We're done with this device, so let's remove this device from + * scanning list + */ + list_del(&usb_scan->list); + free(usb_scan); + + return 0; +} + +static int usb_device_list_scan(void) +{ + struct usb_device_scan *usb_scan; + struct usb_device_scan *tmp; + static int running; + int ret = 0; + + /* Only run this loop once for each controller */ + if (running) + return 0; + + running = 1; + + while (1) { + /* We're done, once the list is empty again */ + if (list_empty(&usb_scan_list)) + goto out; + + list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) { + int ret; + + /* Scan this port */ + ret = usb_scan_port(usb_scan); + if (ret) + goto out; + } + } + +out: + /* + * This USB controller has finished scanning all its connected + * USB devices. Set "running" back to 0, so that other USB controllers + * will scan their devices too. + */ + running = 0; + + return ret; +} + +static struct usb_hub_device *usb_get_hub_device(struct usb_device *dev) +{ + struct usb_hub_device *hub; + +#if !CONFIG_IS_ENABLED(DM_USB) + /* "allocate" Hub device */ + hub = usb_hub_allocate(); +#else + hub = dev_get_uclass_priv(dev->dev); +#endif + + return hub; +} + +static int usb_hub_configure(struct usb_device *dev) +{ + int i, length; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ); + unsigned char *bitmap; + short hubCharacteristics; + struct usb_hub_descriptor *descriptor; + struct usb_hub_device *hub; + struct usb_hub_status *hubsts; + int ret; + + hub = usb_get_hub_device(dev); + if (hub == NULL) + return -ENOMEM; + hub->pusb_dev = dev; + + /* Get the the hub descriptor */ + ret = usb_get_hub_descriptor(dev, buffer, 4); + if (ret < 0) { + debug("usb_hub_configure: failed to get hub " \ + "descriptor, giving up %lX\n", dev->status); + return ret; + } + descriptor = (struct usb_hub_descriptor *)buffer; + + length = min_t(int, descriptor->bLength, + sizeof(struct usb_hub_descriptor)); + + ret = usb_get_hub_descriptor(dev, buffer, length); + if (ret < 0) { + debug("usb_hub_configure: failed to get hub " \ + "descriptor 2nd giving up %lX\n", dev->status); + return ret; + } + memcpy((unsigned char *)&hub->desc, buffer, length); + /* adjust 16bit values */ + put_unaligned(le16_to_cpu(get_unaligned( + &descriptor->wHubCharacteristics)), + &hub->desc.wHubCharacteristics); + /* set the bitmap */ + bitmap = (unsigned char *)&hub->desc.u.hs.DeviceRemovable[0]; + /* devices not removable by default */ + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); + bitmap = (unsigned char *)&hub->desc.u.hs.PortPowerCtrlMask[0]; + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.u.hs.DeviceRemovable[i] = + descriptor->u.hs.DeviceRemovable[i]; + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.u.hs.PortPowerCtrlMask[i] = + descriptor->u.hs.PortPowerCtrlMask[i]; + + dev->maxchild = descriptor->bNbrPorts; + debug("%d ports detected\n", dev->maxchild); + + hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics); + switch (hubCharacteristics & HUB_CHAR_LPSM) { + case 0x00: + debug("ganged power switching\n"); + break; + case 0x01: + debug("individual port power switching\n"); + break; + case 0x02: + case 0x03: + debug("unknown reserved power switching mode\n"); + break; + } + + if (hubCharacteristics & HUB_CHAR_COMPOUND) + debug("part of a compound device\n"); + else + debug("standalone hub\n"); + + switch (hubCharacteristics & HUB_CHAR_OCPM) { + case 0x00: + debug("global over-current protection\n"); + break; + case 0x08: + debug("individual port over-current protection\n"); + break; + case 0x10: + case 0x18: + debug("no over-current protection\n"); + break; + } + + switch (dev->descriptor.bDeviceProtocol) { + case USB_HUB_PR_FS: + break; + case USB_HUB_PR_HS_SINGLE_TT: + debug("Single TT\n"); + break; + case USB_HUB_PR_HS_MULTI_TT: + ret = usb_set_interface(dev, 0, 1); + if (ret == 0) { + debug("TT per port\n"); + hub->tt.multi = true; + } else { + debug("Using single TT (err %d)\n", ret); + } + break; + case USB_HUB_PR_SS: + /* USB 3.0 hubs don't have a TT */ + break; + default: + debug("Unrecognized hub protocol %d\n", + dev->descriptor.bDeviceProtocol); + break; + } + + /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */ + switch (hubCharacteristics & HUB_CHAR_TTTT) { + case HUB_TTTT_8_BITS: + if (dev->descriptor.bDeviceProtocol != 0) { + hub->tt.think_time = 666; + debug("TT requires at most %d FS bit times (%d ns)\n", + 8, hub->tt.think_time); + } + break; + case HUB_TTTT_16_BITS: + hub->tt.think_time = 666 * 2; + debug("TT requires at most %d FS bit times (%d ns)\n", + 16, hub->tt.think_time); + break; + case HUB_TTTT_24_BITS: + hub->tt.think_time = 666 * 3; + debug("TT requires at most %d FS bit times (%d ns)\n", + 24, hub->tt.think_time); + break; + case HUB_TTTT_32_BITS: + hub->tt.think_time = 666 * 4; + debug("TT requires at most %d FS bit times (%d ns)\n", + 32, hub->tt.think_time); + break; + } + + debug("power on to power good time: %dms\n", + descriptor->bPwrOn2PwrGood * 2); + debug("hub controller current requirement: %dmA\n", + descriptor->bHubContrCurrent); + + for (i = 0; i < dev->maxchild; i++) + debug("port %d is%s removable\n", i + 1, + hub->desc.u.hs.DeviceRemovable[(i + 1) / 8] & \ + (1 << ((i + 1) % 8)) ? " not" : ""); + + if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { + debug("usb_hub_configure: failed to get Status - " \ + "too long: %d\n", descriptor->bLength); + return -EFBIG; + } + + ret = usb_get_hub_status(dev, buffer); + if (ret < 0) { + debug("usb_hub_configure: failed to get Status %lX\n", + dev->status); + return ret; + } + + hubsts = (struct usb_hub_status *)buffer; + + debug("get_hub_status returned status %X, change %X\n", + le16_to_cpu(hubsts->wHubStatus), + le16_to_cpu(hubsts->wHubChange)); + debug("local power source is %s\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ + "lost (inactive)" : "good"); + debug("%sover-current condition exists\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ + "" : "no "); + +#if CONFIG_IS_ENABLED(DM_USB) + /* + * Update USB host controller's internal representation of this hub + * after the hub descriptor is fetched. + */ + ret = usb_update_hub_device(dev); + if (ret < 0 && ret != -ENOSYS) { + debug("%s: failed to update hub device for HCD (%x)\n", + __func__, ret); + return ret; + } + + /* + * A maximum of seven tiers are allowed in a USB topology, and the + * root hub occupies the first tier. The last tier ends with a normal + * USB device. USB 3.0 hubs use a 20-bit field called 'route string' + * to route packets to the designated downstream port. The hub uses a + * hub depth value multiplied by four as an offset into the 'route + * string' to locate the bits it uses to determine the downstream + * port number. + */ + if (usb_hub_is_root_hub(dev->dev)) { + hub->hub_depth = -1; + } else { + struct udevice *hdev; + int depth = 0; + + hdev = dev->dev->parent; + while (!usb_hub_is_root_hub(hdev)) { + depth++; + hdev = hdev->parent; + } + + hub->hub_depth = depth; + + if (usb_hub_is_superspeed(dev)) { + debug("set hub (%p) depth to %d\n", dev, depth); + /* + * This request sets the value that the hub uses to + * determine the index into the 'route string index' + * for this hub. + */ + ret = usb_set_hub_depth(dev, depth); + if (ret < 0) { + debug("%s: failed to set hub depth (%lX)\n", + __func__, dev->status); + return ret; + } + } + } +#endif + + usb_hub_power_on(hub); + + /* + * Reset any devices that may be in a bad state when applying + * the power. This is a __weak function. Resetting of the devices + * should occur in the board file of the device. + */ + for (i = 0; i < dev->maxchild; i++) + usb_hub_reset_devices(hub, i + 1); + + /* + * Only add the connected USB devices, including potential hubs, + * to a scanning list. This list will get scanned and devices that + * are detected (either via port connected or via port timeout) + * will get removed from this list. Scanning of the devices on this + * list will continue until all devices are removed. + */ + for (i = 0; i < dev->maxchild; i++) { + struct usb_device_scan *usb_scan; + + usb_scan = calloc(1, sizeof(*usb_scan)); + if (!usb_scan) { + printf("Can't allocate memory for USB device!\n"); + return -ENOMEM; + } + usb_scan->dev = dev; + usb_scan->hub = hub; + usb_scan->port = i; + list_add_tail(&usb_scan->list, &usb_scan_list); + } + + /* + * And now call the scanning code which loops over the generated list + */ + ret = usb_device_list_scan(); + + return ret; +} + +static int usb_hub_check(struct usb_device *dev, int ifnum) +{ + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep = NULL; + + iface = &dev->config.if_desc[ifnum]; + /* Is it a hub? */ + if (iface->desc.bInterfaceClass != USB_CLASS_HUB) + goto err; + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ + if ((iface->desc.bInterfaceSubClass != 0) && + (iface->desc.bInterfaceSubClass != 1)) + goto err; + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if (iface->desc.bNumEndpoints != 1) + goto err; + ep = &iface->ep_desc[0]; + /* Output endpoint? Curiousier and curiousier.. */ + if (!(ep->bEndpointAddress & USB_DIR_IN)) + goto err; + /* If it's not an interrupt endpoint, we'd better punt! */ + if ((ep->bmAttributes & 3) != 3) + goto err; + /* We found a hub */ + debug("USB hub found\n"); + return 0; + +err: + debug("USB hub not found: bInterfaceClass=%d, bInterfaceSubClass=%d, bNumEndpoints=%d\n", + iface->desc.bInterfaceClass, iface->desc.bInterfaceSubClass, + iface->desc.bNumEndpoints); + if (ep) { + debug(" bEndpointAddress=%#x, bmAttributes=%d", + ep->bEndpointAddress, ep->bmAttributes); + } + + return -ENOENT; +} + +int usb_hub_probe(struct usb_device *dev, int ifnum) +{ + int ret; + + ret = usb_hub_check(dev, ifnum); + if (ret) + return 0; + ret = usb_hub_configure(dev); + return ret; +} + +#if CONFIG_IS_ENABLED(DM_USB) +int usb_hub_scan(struct udevice *hub) +{ + struct usb_device *udev = dev_get_parent_priv(hub); + + return usb_hub_configure(udev); +} + +static int usb_hub_post_probe(struct udevice *dev) +{ + debug("%s\n", __func__); + return usb_hub_scan(dev); +} + +static const struct udevice_id usb_hub_ids[] = { + { .compatible = "usb-hub" }, + { } +}; + +U_BOOT_DRIVER(usb_generic_hub) = { + .name = "usb_hub", + .id = UCLASS_USB_HUB, + .of_match = usb_hub_ids, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; + +UCLASS_DRIVER(usb_hub) = { + .id = UCLASS_USB_HUB, + .name = "usb_hub", + .post_bind = dm_scan_fdt_dev, + .post_probe = usb_hub_post_probe, + .child_pre_probe = usb_child_pre_probe, + .per_child_auto = sizeof(struct usb_device), + .per_child_plat_auto = sizeof(struct usb_dev_plat), + .per_device_auto = sizeof(struct usb_hub_device), +}; + +static const struct usb_device_id hub_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, + .bDeviceClass = USB_CLASS_HUB + }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(usb_generic_hub, hub_id_table); + +#endif diff --git a/roms/u-boot/common/usb_kbd.c b/roms/u-boot/common/usb_kbd.c new file mode 100644 index 000000000..afad260d3 --- /dev/null +++ b/roms/u-boot/common/usb_kbd.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Part of this source has been derived from the Linux USB + * project. + */ +#include <common.h> +#include <console.h> +#include <dm.h> +#include <env.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <stdio_dev.h> +#include <watchdog.h> +#include <asm/byteorder.h> + +#include <usb.h> + +/* + * If overwrite_console returns 1, the stdin, stderr and stdout + * are switched to the serial port, else the settings in the + * environment are used + */ +#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console(void); +#else +int overwrite_console(void) +{ + return 0; +} +#endif + +/* Keyboard sampling rate */ +#define REPEAT_RATE 40 /* 40msec -> 25cps */ +#define REPEAT_DELAY 10 /* 10 x REPEAT_RATE = 400msec */ + +#define NUM_LOCK 0x53 +#define CAPS_LOCK 0x39 +#define SCROLL_LOCK 0x47 + +/* Modifier bits */ +#define LEFT_CNTR (1 << 0) +#define LEFT_SHIFT (1 << 1) +#define LEFT_ALT (1 << 2) +#define LEFT_GUI (1 << 3) +#define RIGHT_CNTR (1 << 4) +#define RIGHT_SHIFT (1 << 5) +#define RIGHT_ALT (1 << 6) +#define RIGHT_GUI (1 << 7) + +/* Size of the keyboard buffer */ +#define USB_KBD_BUFFER_LEN 0x20 + +/* Device name */ +#define DEVNAME "usbkbd" + +/* Keyboard maps */ +static const unsigned char usb_kbd_numkey[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '\r', 0x1b, '\b', '\t', ' ', '-', '=', '[', ']', + '\\', '#', ';', '\'', '`', ',', '.', '/' +}; +static const unsigned char usb_kbd_numkey_shifted[] = { + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '\r', 0x1b, '\b', '\t', ' ', '_', '+', '{', '}', + '|', '~', ':', '"', '~', '<', '>', '?' +}; + +static const unsigned char usb_kbd_num_keypad[] = { + '/', '*', '-', '+', '\r', + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '.', 0, 0, 0, '=' +}; + +static const u8 usb_special_keys[] = { +#ifdef CONFIG_USB_KEYBOARD_FN_KEYS + '2', 'H', '5', '3', 'F', '6', 'C', 'D', 'B', 'A' +#else + 'C', 'D', 'B', 'A' +#endif +}; + +/* + * NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this + * order. See usb_kbd_setled() function! + */ +#define USB_KBD_NUMLOCK (1 << 0) +#define USB_KBD_CAPSLOCK (1 << 1) +#define USB_KBD_SCROLLLOCK (1 << 2) +#define USB_KBD_CTRL (1 << 3) + +#define USB_KBD_LEDMASK \ + (USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK) + +struct usb_kbd_pdata { + unsigned long intpipe; + int intpktsize; + int intinterval; + unsigned long last_report; + struct int_queue *intq; + + uint32_t repeat_delay; + + uint32_t usb_in_pointer; + uint32_t usb_out_pointer; + uint8_t usb_kbd_buffer[USB_KBD_BUFFER_LEN]; + + uint8_t *new; + uint8_t old[USB_KBD_BOOT_REPORT_SIZE]; + + uint8_t flags; +}; + +extern int __maybe_unused net_busy_flag; + +/* The period of time between two calls of usb_kbd_testc(). */ +static unsigned long __maybe_unused kbd_testc_tms; + +/* Puts character in the queue and sets up the in and out pointer. */ +static void usb_kbd_put_queue(struct usb_kbd_pdata *data, u8 c) +{ + if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) { + /* Check for buffer full. */ + if (data->usb_out_pointer == 0) + return; + + data->usb_in_pointer = 0; + } else { + /* Check for buffer full. */ + if (data->usb_in_pointer == data->usb_out_pointer - 1) + return; + + data->usb_in_pointer++; + } + + data->usb_kbd_buffer[data->usb_in_pointer] = c; +} + +/* + * Set the LEDs. Since this is used in the irq routine, the control job is + * issued with a timeout of 0. This means, that the job is queued without + * waiting for job completion. + */ +static void usb_kbd_setled(struct usb_device *dev) +{ + struct usb_interface *iface = &dev->config.if_desc[0]; + struct usb_kbd_pdata *data = dev->privptr; + ALLOC_ALIGN_BUFFER(uint32_t, leds, 1, USB_DMA_MINALIGN); + + *leds = data->flags & USB_KBD_LEDMASK; + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x200, iface->desc.bInterfaceNumber, leds, 1, 0); +} + +#define CAPITAL_MASK 0x20 +/* Translate the scancode in ASCII */ +static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode, + unsigned char modifier, int pressed) +{ + uint8_t keycode = 0; + + /* Key released */ + if (pressed == 0) { + data->repeat_delay = 0; + return 0; + } + + if (pressed == 2) { + data->repeat_delay++; + if (data->repeat_delay < REPEAT_DELAY) + return 0; + + data->repeat_delay = REPEAT_DELAY; + } + + /* Alphanumeric values */ + if ((scancode > 3) && (scancode <= 0x1d)) { + keycode = scancode - 4 + 'a'; + + if (data->flags & USB_KBD_CAPSLOCK) + keycode &= ~CAPITAL_MASK; + + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) { + /* Handle CAPSLock + Shift pressed simultaneously */ + if (keycode & CAPITAL_MASK) + keycode &= ~CAPITAL_MASK; + else + keycode |= CAPITAL_MASK; + } + } + + if ((scancode > 0x1d) && (scancode < 0x39)) { + /* Shift pressed */ + if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) + keycode = usb_kbd_numkey_shifted[scancode - 0x1e]; + else + keycode = usb_kbd_numkey[scancode - 0x1e]; + } + + /* Numeric keypad */ + if ((scancode >= 0x54) && (scancode <= 0x67)) + keycode = usb_kbd_num_keypad[scancode - 0x54]; + + if (data->flags & USB_KBD_CTRL) + keycode = scancode - 0x3; + + if (pressed == 1) { + if (scancode == NUM_LOCK) { + data->flags ^= USB_KBD_NUMLOCK; + return 1; + } + + if (scancode == CAPS_LOCK) { + data->flags ^= USB_KBD_CAPSLOCK; + return 1; + } + if (scancode == SCROLL_LOCK) { + data->flags ^= USB_KBD_SCROLLLOCK; + return 1; + } + } + + /* Report keycode if any */ + if (keycode) { + debug("%c", keycode); + usb_kbd_put_queue(data, keycode); + return 0; + } + +#ifdef CONFIG_USB_KEYBOARD_FN_KEYS + if (scancode < 0x3a || scancode > 0x52 || + scancode == 0x46 || scancode == 0x47) + return 1; + + usb_kbd_put_queue(data, 0x1b); + if (scancode < 0x3e) { + /* F1 - F4 */ + usb_kbd_put_queue(data, 0x4f); + usb_kbd_put_queue(data, scancode - 0x3a + 'P'); + return 0; + } + usb_kbd_put_queue(data, '['); + if (scancode < 0x42) { + /* F5 - F8 */ + usb_kbd_put_queue(data, '1'); + if (scancode == 0x3e) + --scancode; + keycode = scancode - 0x3f + '7'; + } else if (scancode < 0x49) { + /* F9 - F12 */ + usb_kbd_put_queue(data, '2'); + if (scancode > 0x43) + ++scancode; + keycode = scancode - 0x42 + '0'; + } else { + /* + * INSERT, HOME, PAGE UP, DELETE, END, PAGE DOWN, + * RIGHT, LEFT, DOWN, UP + */ + keycode = usb_special_keys[scancode - 0x49]; + } + usb_kbd_put_queue(data, keycode); + if (scancode < 0x4f && scancode != 0x4a && scancode != 0x4d) + usb_kbd_put_queue(data, '~'); + return 0; +#else + /* Left, Right, Up, Down */ + if (scancode > 0x4e && scancode < 0x53) { + usb_kbd_put_queue(data, 0x1b); + usb_kbd_put_queue(data, '['); + usb_kbd_put_queue(data, usb_special_keys[scancode - 0x4f]); + return 0; + } + return 1; +#endif /* CONFIG_USB_KEYBOARD_FN_KEYS */ +} + +static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up) +{ + uint32_t res = 0; + struct usb_kbd_pdata *data = dev->privptr; + uint8_t *new; + uint8_t *old; + + if (up) { + new = data->old; + old = data->new; + } else { + new = data->new; + old = data->old; + } + + if ((old[i] > 3) && + (memscan(new + 2, old[i], USB_KBD_BOOT_REPORT_SIZE - 2) == + new + USB_KBD_BOOT_REPORT_SIZE)) { + res |= usb_kbd_translate(data, old[i], data->new[0], up); + } + + return res; +} + +/* Interrupt service routine */ +static int usb_kbd_irq_worker(struct usb_device *dev) +{ + struct usb_kbd_pdata *data = dev->privptr; + int i, res = 0; + + /* No combo key pressed */ + if (data->new[0] == 0x00) + data->flags &= ~USB_KBD_CTRL; + /* Left or Right Ctrl pressed */ + else if ((data->new[0] == LEFT_CNTR) || (data->new[0] == RIGHT_CNTR)) + data->flags |= USB_KBD_CTRL; + + for (i = 2; i < USB_KBD_BOOT_REPORT_SIZE; i++) { + res |= usb_kbd_service_key(dev, i, 0); + res |= usb_kbd_service_key(dev, i, 1); + } + + /* Key is still pressed */ + if ((data->new[2] > 3) && (data->old[2] == data->new[2])) + res |= usb_kbd_translate(data, data->new[2], data->new[0], 2); + + if (res == 1) + usb_kbd_setled(dev); + + memcpy(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE); + + return 1; +} + +/* Keyboard interrupt handler */ +static int usb_kbd_irq(struct usb_device *dev) +{ + if ((dev->irq_status != 0) || + (dev->irq_act_len != USB_KBD_BOOT_REPORT_SIZE)) { + debug("USB KBD: Error %lX, len %d\n", + dev->irq_status, dev->irq_act_len); + return 1; + } + + return usb_kbd_irq_worker(dev); +} + +/* Interrupt polling */ +static inline void usb_kbd_poll_for_event(struct usb_device *dev) +{ +#if defined(CONFIG_SYS_USB_EVENT_POLL) + struct usb_kbd_pdata *data = dev->privptr; + + /* Submit an interrupt transfer request */ + if (usb_int_msg(dev, data->intpipe, &data->new[0], + data->intpktsize, data->intinterval, true) >= 0) + usb_kbd_irq_worker(dev); +#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) || \ + defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE) +#if defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) + struct usb_interface *iface; + struct usb_kbd_pdata *data = dev->privptr; + iface = &dev->config.if_desc[0]; + usb_get_report(dev, iface->desc.bInterfaceNumber, + 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE); + if (memcmp(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE)) { + usb_kbd_irq_worker(dev); +#else + struct usb_kbd_pdata *data = dev->privptr; + if (poll_int_queue(dev, data->intq)) { + usb_kbd_irq_worker(dev); + /* We've consumed all queued int packets, create new */ + destroy_int_queue(dev, data->intq); + data->intq = create_int_queue(dev, data->intpipe, 1, + USB_KBD_BOOT_REPORT_SIZE, data->new, + data->intinterval); +#endif + data->last_report = get_timer(0); + /* Repeat last usb hid report every REPEAT_RATE ms for keyrepeat */ + } else if (data->last_report != -1 && + get_timer(data->last_report) > REPEAT_RATE) { + usb_kbd_irq_worker(dev); + data->last_report = get_timer(0); + } +#endif +} + +/* test if a character is in the queue */ +static int usb_kbd_testc(struct stdio_dev *sdev) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + +#ifdef CONFIG_CMD_NET + /* + * If net_busy_flag is 1, NET transfer is running, + * then we check key-pressed every second (first check may be + * less than 1 second) to improve TFTP booting performance. + */ + if (net_busy_flag && (get_timer(kbd_testc_tms) < CONFIG_SYS_HZ)) + return 0; + kbd_testc_tms = get_timer(0); +#endif + dev = stdio_get_by_name(sdev->name); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + usb_kbd_poll_for_event(usb_kbd_dev); + + return !(data->usb_in_pointer == data->usb_out_pointer); +} + +/* gets the character from the queue */ +static int usb_kbd_getc(struct stdio_dev *sdev) +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + + dev = stdio_get_by_name(sdev->name); + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; + + while (data->usb_in_pointer == data->usb_out_pointer) { + WATCHDOG_RESET(); + usb_kbd_poll_for_event(usb_kbd_dev); + } + + if (data->usb_out_pointer == USB_KBD_BUFFER_LEN - 1) + data->usb_out_pointer = 0; + else + data->usb_out_pointer++; + + return data->usb_kbd_buffer[data->usb_out_pointer]; +} + +/* probes the USB device dev for keyboard type. */ +static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + struct usb_kbd_pdata *data; + int epNum; + + if (dev->descriptor.bNumConfigurations != 1) + return 0; + + iface = &dev->config.if_desc[ifnum]; + + if (iface->desc.bInterfaceClass != USB_CLASS_HID) + return 0; + + if (iface->desc.bInterfaceSubClass != USB_SUB_HID_BOOT) + return 0; + + if (iface->desc.bInterfaceProtocol != USB_PROT_HID_KEYBOARD) + return 0; + + for (epNum = 0; epNum < iface->desc.bNumEndpoints; epNum++) { + ep = &iface->ep_desc[epNum]; + + /* Check if endpoint is interrupt IN endpoint */ + if ((ep->bmAttributes & 3) != 3) + continue; + + if (ep->bEndpointAddress & 0x80) + break; + } + + if (epNum == iface->desc.bNumEndpoints) + return 0; + + debug("USB KBD: found interrupt EP: 0x%x\n", ep->bEndpointAddress); + + data = malloc(sizeof(struct usb_kbd_pdata)); + if (!data) { + printf("USB KBD: Error allocating private data\n"); + return 0; + } + + /* Clear private data */ + memset(data, 0, sizeof(struct usb_kbd_pdata)); + + /* allocate input buffer aligned and sized to USB DMA alignment */ + data->new = memalign(USB_DMA_MINALIGN, + roundup(USB_KBD_BOOT_REPORT_SIZE, USB_DMA_MINALIGN)); + + /* Insert private data into USB device structure */ + dev->privptr = data; + + /* Set IRQ handler */ + dev->irq_handle = usb_kbd_irq; + + data->intpipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + data->intpktsize = min(usb_maxpacket(dev, data->intpipe), + USB_KBD_BOOT_REPORT_SIZE); + data->intinterval = ep->bInterval; + data->last_report = -1; + + /* We found a USB Keyboard, install it. */ + debug("USB KBD: set boot protocol\n"); + usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); + +#if !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) && \ + !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE) + debug("USB KBD: set idle interval...\n"); + usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE / 4, 0); +#else + debug("USB KBD: set idle interval=0...\n"); + usb_set_idle(dev, iface->desc.bInterfaceNumber, 0, 0); +#endif + + debug("USB KBD: enable interrupt pipe...\n"); +#ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE + data->intq = create_int_queue(dev, data->intpipe, 1, + USB_KBD_BOOT_REPORT_SIZE, data->new, + data->intinterval); + if (!data->intq) { +#elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) + if (usb_get_report(dev, iface->desc.bInterfaceNumber, + 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE) < 0) { +#else + if (usb_int_msg(dev, data->intpipe, data->new, data->intpktsize, + data->intinterval, false) < 0) { +#endif + printf("Failed to get keyboard state from device %04x:%04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + /* Abort, we don't want to use that non-functional keyboard. */ + return 0; + } + + /* Success. */ + return 1; +} + +static int probe_usb_keyboard(struct usb_device *dev) +{ + char *stdinname; + struct stdio_dev usb_kbd_dev; + int error; + + /* Try probing the keyboard */ + if (usb_kbd_probe_dev(dev, 0) != 1) + return -ENOENT; + + /* Register the keyboard */ + debug("USB KBD: register.\n"); + memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); + strcpy(usb_kbd_dev.name, DEVNAME); + usb_kbd_dev.flags = DEV_FLAGS_INPUT; + usb_kbd_dev.getc = usb_kbd_getc; + usb_kbd_dev.tstc = usb_kbd_testc; + usb_kbd_dev.priv = (void *)dev; + error = stdio_register(&usb_kbd_dev); + if (error) + return error; + + stdinname = env_get("stdin"); +#if CONFIG_IS_ENABLED(CONSOLE_MUX) + error = iomux_doenv(stdin, stdinname); + if (error) + return error; +#else + /* Check if this is the standard input device. */ + if (strcmp(stdinname, DEVNAME)) + return 1; + + /* Reassign the console */ + if (overwrite_console()) + return 1; + + error = console_assign(stdin, DEVNAME); + if (error) + return error; +#endif + + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_USB) +/* Search for keyboard and register it if found. */ +int drv_usb_kbd_init(void) +{ + int error, i; + + debug("%s: Probing for keyboard\n", __func__); + /* Scan all USB Devices */ + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + /* Get USB device. */ + dev = usb_get_dev_index(i); + if (!dev) + break; + + if (dev->devnum == -1) + continue; + + error = probe_usb_keyboard(dev); + if (!error) + return 1; + if (error && error != -ENOENT) + return error; + } + + /* No USB Keyboard found */ + return -1; +} + +/* Deregister the keyboard. */ +int usb_kbd_deregister(int force) +{ +#if CONFIG_IS_ENABLED(SYS_STDIO_DEREGISTER) + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_kbd_pdata *data; + + dev = stdio_get_by_name(DEVNAME); + if (dev) { + usb_kbd_dev = (struct usb_device *)dev->priv; + data = usb_kbd_dev->privptr; +#if CONFIG_IS_ENABLED(CONSOLE_MUX) + if (iomux_replace_device(stdin, DEVNAME, force ? "nulldev" : "")) + return 1; +#endif + if (stdio_deregister_dev(dev, force) != 0) + return 1; +#ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE + destroy_int_queue(usb_kbd_dev, data->intq); +#endif + free(data->new); + free(data); + } + + return 0; +#else + return 1; +#endif +} + +#endif + +#if CONFIG_IS_ENABLED(DM_USB) + +static int usb_kbd_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + + return probe_usb_keyboard(udev); +} + +static int usb_kbd_remove(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_kbd_pdata *data; + struct stdio_dev *sdev; + int ret; + + sdev = stdio_get_by_name(DEVNAME); + if (!sdev) { + ret = -ENXIO; + goto err; + } + data = udev->privptr; +#if CONFIG_IS_ENABLED(CONSOLE_MUX) + if (iomux_replace_device(stdin, DEVNAME, "nulldev")) { + ret = -ENOLINK; + goto err; + } +#endif + if (stdio_deregister_dev(sdev, true)) { + ret = -EPERM; + goto err; + } +#ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE + destroy_int_queue(udev, data->intq); +#endif + free(data->new); + free(data); + + return 0; +err: + printf("%s: warning, ret=%d", __func__, ret); + return ret; +} + +static const struct udevice_id usb_kbd_ids[] = { + { .compatible = "usb-keyboard" }, + { } +}; + +U_BOOT_DRIVER(usb_kbd) = { + .name = "usb_kbd", + .id = UCLASS_KEYBOARD, + .of_match = usb_kbd_ids, + .probe = usb_kbd_probe, + .remove = usb_kbd_remove, +}; + +static const struct usb_device_id kbd_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_SUB_HID_BOOT, + .bInterfaceProtocol = USB_PROT_HID_KEYBOARD, + }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(usb_kbd, kbd_id_table); + +#endif diff --git a/roms/u-boot/common/usb_storage.c b/roms/u-boot/common/usb_storage.c new file mode 100644 index 000000000..946c6b2b3 --- /dev/null +++ b/roms/u-boot/common/usb_storage.c @@ -0,0 +1,1570 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Most of this source has been derived from the Linux USB + * project: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * (c) 2000 Yggdrasil Computing, Inc. + * + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * Driver model conversion: + * (C) Copyright 2015 Google, Inc + * + * For BBB support (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * BBB support based on /sys/dev/usb/umass.c from + * FreeBSD. + */ + +/* Note: + * Currently only the CBI transport protocoll has been implemented, and it + * is only tested with a TEAC USB Floppy. Other Massstorages with CBI or CB + * transport protocoll may work as well. + */ +/* + * New Note: + * Support for USB Mass Storage Devices (BBB) has been added. It has + * only been tested with USB memory sticks. + */ + + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <mapmem.h> +#include <memalign.h> +#include <asm/byteorder.h> +#include <asm/cache.h> +#include <asm/processor.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <linux/delay.h> + +#include <part.h> +#include <usb.h> + +#undef BBB_COMDAT_TRACE +#undef BBB_XPORT_TRACE + +#include <scsi.h> +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +static const unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +static struct scsi_cmd usb_ccb __aligned(ARCH_DMA_MINALIGN); +static __u32 CBWTag; + +static int usb_max_devs; /* number of highest available usb device */ + +#if !CONFIG_IS_ENABLED(BLK) +static struct blk_desc usb_dev_desc[USB_MAX_STOR_DEV]; +#endif + +struct us_data; +typedef int (*trans_cmnd)(struct scsi_cmd *cb, struct us_data *data); +typedef int (*trans_reset)(struct us_data *data); + +struct us_data { + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ +# define USB_READY (1 << 0) + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ + unsigned char attention_done; /* force attn on first cmd */ + unsigned short ip_data; /* interrupt data */ + int action; /* what to do */ + int ip_wanted; /* needed */ + int *irq_handle; /* for USB int requests */ + unsigned int irqpipe; /* pipe for release_irq */ + unsigned char irqmaxp; /* max packed for irq Pipe */ + unsigned char irqinterval; /* Intervall for IRQ Pipe */ + struct scsi_cmd *srb; /* current srb */ + trans_reset transport_reset; /* reset routine */ + trans_cmnd transport; /* transport routine */ + unsigned short max_xfer_blk; /* maximum transfer blocks */ +}; + +#if !CONFIG_IS_ENABLED(BLK) +static struct us_data usb_stor[USB_MAX_STOR_DEV]; +#endif + +#define USB_STOR_TRANSPORT_GOOD 0 +#define USB_STOR_TRANSPORT_FAILED -1 +#define USB_STOR_TRANSPORT_ERROR -2 + +int usb_stor_get_info(struct usb_device *dev, struct us_data *us, + struct blk_desc *dev_desc); +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss); +#if CONFIG_IS_ENABLED(BLK) +static unsigned long usb_stor_read(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer); +static unsigned long usb_stor_write(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer); +#else +static unsigned long usb_stor_read(struct blk_desc *block_dev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer); +static unsigned long usb_stor_write(struct blk_desc *block_dev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer); +#endif +void uhci_show_temp_int_td(void); + +static void usb_show_progress(void) +{ + debug("."); +} + +/******************************************************************************* + * show info on storage devices; 'usb start/init' must be invoked earlier + * as we only retrieve structures populated during devices initialization + */ +int usb_stor_info(void) +{ + int count = 0; +#if CONFIG_IS_ENABLED(BLK) + struct udevice *dev; + + for (blk_first_device(IF_TYPE_USB, &dev); + dev; + blk_next_device(&dev)) { + struct blk_desc *desc = dev_get_uclass_plat(dev); + + printf(" Device %d: ", desc->devnum); + dev_print(desc); + count++; + } +#else + int i; + + if (usb_max_devs > 0) { + for (i = 0; i < usb_max_devs; i++) { + printf(" Device %d: ", i); + dev_print(&usb_dev_desc[i]); + } + return 0; + } +#endif + if (!count) { + printf("No storage devices, perhaps not 'usb start'ed..?\n"); + return 1; + } + + return 0; +} + +static unsigned int usb_get_max_lun(struct us_data *us) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, result, 1); + len = usb_control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev, 0), + US_BBB_GET_MAX_LUN, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + 0, us->ifnum, + result, sizeof(char), + USB_CNTL_TIMEOUT * 5); + debug("Get Max LUN -> len = %i, result = %i\n", len, (int) *result); + return (len > 0) ? *result : 0; +} + +static int usb_stor_probe_device(struct usb_device *udev) +{ + int lun, max_lun; + +#if CONFIG_IS_ENABLED(BLK) + struct us_data *data; + int ret; +#else + int start; + + if (udev == NULL) + return -ENOENT; /* no more devices available */ +#endif + + debug("\n\nProbing for storage\n"); +#if CONFIG_IS_ENABLED(BLK) + /* + * We store the us_data in the mass storage device's plat. It + * is shared by all LUNs (block devices) attached to this mass storage + * device. + */ + data = dev_get_plat(udev->dev); + if (!usb_storage_probe(udev, 0, data)) + return 0; + max_lun = usb_get_max_lun(data); + for (lun = 0; lun <= max_lun; lun++) { + struct blk_desc *blkdev; + struct udevice *dev; + char str[10]; + + snprintf(str, sizeof(str), "lun%d", lun); + ret = blk_create_devicef(udev->dev, "usb_storage_blk", str, + IF_TYPE_USB, usb_max_devs, 512, 0, + &dev); + if (ret) { + debug("Cannot bind driver\n"); + return ret; + } + + blkdev = dev_get_uclass_plat(dev); + blkdev->target = 0xff; + blkdev->lun = lun; + + ret = usb_stor_get_info(udev, data, blkdev); + if (ret == 1) { + usb_max_devs++; + debug("%s: Found device %p\n", __func__, udev); + } else { + debug("usb_stor_get_info: Invalid device\n"); + ret = device_unbind(dev); + if (ret) + return ret; + } + } +#else + /* We don't have space to even probe if we hit the maximum */ + if (usb_max_devs == USB_MAX_STOR_DEV) { + printf("max USB Storage Device reached: %d stopping\n", + usb_max_devs); + return -ENOSPC; + } + + if (!usb_storage_probe(udev, 0, &usb_stor[usb_max_devs])) + return 0; + + /* + * OK, it's a storage device. Iterate over its LUNs and populate + * usb_dev_desc' + */ + start = usb_max_devs; + + max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); + for (lun = 0; lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; + lun++) { + struct blk_desc *blkdev; + + blkdev = &usb_dev_desc[usb_max_devs]; + memset(blkdev, '\0', sizeof(struct blk_desc)); + blkdev->if_type = IF_TYPE_USB; + blkdev->devnum = usb_max_devs; + blkdev->part_type = PART_TYPE_UNKNOWN; + blkdev->target = 0xff; + blkdev->type = DEV_TYPE_UNKNOWN; + blkdev->block_read = usb_stor_read; + blkdev->block_write = usb_stor_write; + blkdev->lun = lun; + blkdev->priv = udev; + + if (usb_stor_get_info(udev, &usb_stor[start], + &usb_dev_desc[usb_max_devs]) == 1) { + debug("partype: %d\n", blkdev->part_type); + part_init(blkdev); + debug("partype: %d\n", blkdev->part_type); + usb_max_devs++; + debug("%s: Found device %p\n", __func__, udev); + } + } +#endif + + return 0; +} + +void usb_stor_reset(void) +{ + usb_max_devs = 0; +} + +/******************************************************************************* + * scan the usb and reports device info + * to the user if mode = 1 + * returns current device or -1 if no + */ +int usb_stor_scan(int mode) +{ + if (mode == 1) + printf(" scanning usb for storage devices... "); + +#if !CONFIG_IS_ENABLED(DM_USB) + unsigned char i; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + + usb_stor_reset(); + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + dev = usb_get_dev_index(i); /* get device */ + debug("i=%d\n", i); + if (usb_stor_probe_device(dev)) + break; + } /* for */ + + usb_disable_asynch(0); /* asynch transfer allowed */ +#endif + printf("%d Storage Device(s) found\n", usb_max_devs); + if (usb_max_devs > 0) + return 0; + return -1; +} + +static int usb_stor_irq(struct usb_device *dev) +{ + struct us_data *us; + us = (struct us_data *)dev->privptr; + + if (us->ip_wanted) + us->ip_wanted = 0; + return 0; +} + + +#ifdef DEBUG + +static void usb_show_srb(struct scsi_cmd *pccb) +{ + int i; + printf("SRB: len %d datalen 0x%lX\n ", pccb->cmdlen, pccb->datalen); + for (i = 0; i < 12; i++) + printf("%02X ", pccb->cmd[i]); + printf("\n"); +} + +static void display_int_status(unsigned long tmp) +{ + printf("Status: %s %s %s %s %s %s %s\n", + (tmp & USB_ST_ACTIVE) ? "Active" : "", + (tmp & USB_ST_STALLED) ? "Stalled" : "", + (tmp & USB_ST_BUF_ERR) ? "Buffer Error" : "", + (tmp & USB_ST_BABBLE_DET) ? "Babble Det" : "", + (tmp & USB_ST_NAK_REC) ? "NAKed" : "", + (tmp & USB_ST_CRC_ERR) ? "CRC Error" : "", + (tmp & USB_ST_BIT_ERR) ? "Bitstuff Error" : ""); +} +#endif +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +{ + int max_size; + int this_xfer; + int result; + int partial; + int maxtry; + int stat; + + /* determine the maximum packet size for these transfers */ + max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + + /* while we have data left to transfer */ + while (length) { + + /* calculate how long this will be -- maximum or a remainder */ + this_xfer = length > max_size ? max_size : length; + length -= this_xfer; + + /* setup the retry counter */ + maxtry = 10; + + /* set up the transfer loop */ + do { + /* transfer the data */ + debug("Bulk xfer 0x%lx(%d) try #%d\n", + (ulong)map_to_sysmem(buf), this_xfer, + 11 - maxtry); + result = usb_bulk_msg(us->pusb_dev, pipe, buf, + this_xfer, &partial, + USB_CNTL_TIMEOUT * 5); + debug("bulk_msg returned %d xferred %d/%d\n", + result, partial, this_xfer); + if (us->pusb_dev->status != 0) { + /* if we stall, we need to clear it before + * we go on + */ +#ifdef DEBUG + display_int_status(us->pusb_dev->status); +#endif + if (us->pusb_dev->status & USB_ST_STALLED) { + debug("stalled ->clearing endpoint" \ + "halt for pipe 0x%x\n", pipe); + stat = us->pusb_dev->status; + usb_clear_halt(us->pusb_dev, pipe); + us->pusb_dev->status = stat; + if (this_xfer == partial) { + debug("bulk transferred" \ + "with error %lX," \ + " but data ok\n", + us->pusb_dev->status); + return 0; + } + else + return result; + } + if (us->pusb_dev->status & USB_ST_NAK_REC) { + debug("Device NAKed bulk_msg\n"); + return result; + } + debug("bulk transferred with error"); + if (this_xfer == partial) { + debug(" %ld, but data ok\n", + us->pusb_dev->status); + return 0; + } + /* if our try counter reaches 0, bail out */ + debug(" %ld, data %d\n", + us->pusb_dev->status, partial); + if (!maxtry--) + return result; + } + /* update to show what data was transferred */ + this_xfer -= partial; + buf += partial; + /* continue until this transfer is done */ + } while (this_xfer); + } + + /* if we get here, we're done and successful */ + return 0; +} + +static int usb_stor_BBB_reset(struct us_data *us) +{ + int result; + unsigned int pipe; + + /* + * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class) + * + * For Reset Recovery the host shall issue in the following order: + * a) a Bulk-Only Mass Storage Reset + * b) a Clear Feature HALT to the Bulk-In endpoint + * c) a Clear Feature HALT to the Bulk-Out endpoint + * + * This is done in 3 steps. + * + * If the reset doesn't succeed, the device should be port reset. + * + * This comment stolen from FreeBSD's /sys/dev/usb/umass.c. + */ + debug("BBB_reset\n"); + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_BBB_RESET, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, NULL, 0, USB_CNTL_TIMEOUT * 5); + + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { + debug("RESET:stall\n"); + return -1; + } + + /* long wait for reset */ + mdelay(150); + debug("BBB_reset result %d: status %lX reset\n", + result, us->pusb_dev->status); + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + result = usb_clear_halt(us->pusb_dev, pipe); + /* long wait for reset */ + mdelay(150); + debug("BBB_reset result %d: status %lX clearing IN endpoint\n", + result, us->pusb_dev->status); + /* long wait for reset */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + result = usb_clear_halt(us->pusb_dev, pipe); + mdelay(150); + debug("BBB_reset result %d: status %lX clearing OUT endpoint\n", + result, us->pusb_dev->status); + debug("BBB_reset done\n"); + return 0; +} + +/* FIXME: this reset function doesn't really reset the port, and it + * should. Actually it should probably do what it's doing here, and + * reset the port physically + */ +static int usb_stor_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + + debug("CB_reset\n"); + memset(cmd, 0xff, sizeof(cmd)); + cmd[0] = SCSI_SEND_DIAG; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), + USB_CNTL_TIMEOUT * 5); + + /* long wait for reset */ + mdelay(1500); + debug("CB_reset result %d: status %lX clearing endpoint halt\n", + result, us->pusb_dev->status); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + + debug("CB_reset done\n"); + return 0; +} + +/* + * Set up the command for a BBB device. Note that the actual SCSI + * command is copied into cbw.CBWCDB. + */ +static int usb_stor_BBB_comdat(struct scsi_cmd *srb, struct us_data *us) +{ + int result; + int actlen; + int dir_in; + unsigned int pipe; + ALLOC_CACHE_ALIGN_BUFFER(struct umass_bbb_cbw, cbw, 1); + + dir_in = US_DIRECTION(srb->cmd[0]); + +#ifdef BBB_COMDAT_TRACE + printf("dir %d lun %d cmdlen %d cmd %p datalen %lu pdata %p\n", + dir_in, srb->lun, srb->cmdlen, srb->cmd, srb->datalen, + srb->pdata); + if (srb->cmdlen) { + for (result = 0; result < srb->cmdlen; result++) + printf("cmd[%d] %#x ", result, srb->cmd[result]); + printf("\n"); + } +#endif + /* sanity checks */ + if (!(srb->cmdlen <= CBWCDBLENGTH)) { + debug("usb_stor_BBB_comdat:cmdlen too large\n"); + return -1; + } + + /* always OUT to the ep */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + cbw->dCBWSignature = cpu_to_le32(CBWSIGNATURE); + cbw->dCBWTag = cpu_to_le32(CBWTag++); + cbw->dCBWDataTransferLength = cpu_to_le32(srb->datalen); + cbw->bCBWFlags = (dir_in ? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw->bCBWLUN = srb->lun; + cbw->bCDBLength = srb->cmdlen; + /* copy the command data into the CBW command data buffer */ + /* DST SRC LEN!!! */ + + memcpy(cbw->CBWCDB, srb->cmd, srb->cmdlen); + result = usb_bulk_msg(us->pusb_dev, pipe, cbw, UMASS_BBB_CBW_SIZE, + &actlen, USB_CNTL_TIMEOUT * 5); + if (result < 0) + debug("usb_stor_BBB_comdat:usb_bulk_msg error\n"); + return result; +} + +/* FIXME: we also need a CBI_command which sets up the completion + * interrupt, and waits for it + */ +static int usb_stor_CB_comdat(struct scsi_cmd *srb, struct us_data *us) +{ + int result = 0; + int dir_in, retry; + unsigned int pipe; + unsigned long status; + + retry = 5; + dir_in = US_DIRECTION(srb->cmd[0]); + + if (dir_in) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + while (retry--) { + debug("CBI gets a command: Try %d\n", 5 - retry); +#ifdef DEBUG + usb_show_srb(srb); +#endif + /* let's send the command via the control pipe */ + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev , 0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, + srb->cmd, srb->cmdlen, + USB_CNTL_TIMEOUT * 5); + debug("CB_transport: control msg returned %d, status %lX\n", + result, us->pusb_dev->status); + /* check the return code for the command */ + if (result < 0) { + if (us->pusb_dev->status & USB_ST_STALLED) { + status = us->pusb_dev->status; + debug(" stall during command found," \ + " clear pipe\n"); + usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, 0)); + us->pusb_dev->status = status; + } + debug(" error during command %02X" \ + " Stat = %lX\n", srb->cmd[0], + us->pusb_dev->status); + return result; + } + /* transfer the data payload for this command, if one exists*/ + + debug("CB_transport: control msg returned %d," \ + " direction is %s to go 0x%lx\n", result, + dir_in ? "IN" : "OUT", srb->datalen); + if (srb->datalen) { + result = us_one_transfer(us, pipe, (char *)srb->pdata, + srb->datalen); + debug("CBI attempted to transfer data," \ + " result is %d status %lX, len %d\n", + result, us->pusb_dev->status, + us->pusb_dev->act_len); + if (!(us->pusb_dev->status & USB_ST_NAK_REC)) + break; + } /* if (srb->datalen) */ + else + break; + } + /* return result */ + + return result; +} + + +static int usb_stor_CBI_get_status(struct scsi_cmd *srb, struct us_data *us) +{ + int timeout; + + us->ip_wanted = 1; + usb_int_msg(us->pusb_dev, us->irqpipe, + (void *)&us->ip_data, us->irqmaxp, us->irqinterval, false); + timeout = 1000; + while (timeout--) { + if (us->ip_wanted == 0) + break; + mdelay(10); + } + if (us->ip_wanted) { + printf(" Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + debug("Got interrupt data 0x%x, transferred %d status 0x%lX\n", + us->ip_data, us->pusb_dev->irq_act_len, + us->pusb_dev->irq_status); + /* UFI gives us ASC and ASCQ, like a request sense */ + if (us->subclass == US_SC_UFI) { + if (srb->cmd[0] == SCSI_REQ_SENSE || + srb->cmd[0] == SCSI_INQUIRY) + return USB_STOR_TRANSPORT_GOOD; /* Good */ + else if (us->ip_data) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + /* otherwise, we interpret the data normally */ + switch (us->ip_data) { + case 0x0001: + return USB_STOR_TRANSPORT_GOOD; + case 0x0002: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } /* switch */ + return USB_STOR_TRANSPORT_ERROR; +} + +#define USB_TRANSPORT_UNKNOWN_RETRY 5 +#define USB_TRANSPORT_NOT_READY_RETRY 10 + +/* clear a stall on an endpoint - special for BBB devices */ +static int usb_stor_BBB_clear_endpt_stall(struct us_data *us, __u8 endpt) +{ + /* ENDPOINT_HALT = 0, so set value to 0 */ + return usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, + endpt, NULL, 0, USB_CNTL_TIMEOUT * 5); +} + +static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us) +{ + int result, retry; + int dir_in; + int actlen, data_actlen; + unsigned int pipe, pipein, pipeout; + ALLOC_CACHE_ALIGN_BUFFER(struct umass_bbb_csw, csw, 1); +#ifdef BBB_XPORT_TRACE + unsigned char *ptr; + int index; +#endif + + dir_in = US_DIRECTION(srb->cmd[0]); + + /* COMMAND phase */ + debug("COMMAND phase\n"); + result = usb_stor_BBB_comdat(srb, us); + if (result < 0) { + debug("failed to send CBW status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } + if (!(us->flags & USB_READY)) + mdelay(5); + pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + /* DATA phase + error handling */ + data_actlen = 0; + /* no data, go immediately to the STATUS phase */ + if (srb->datalen == 0) + goto st; + debug("DATA phase\n"); + if (dir_in) + pipe = pipein; + else + pipe = pipeout; + + result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, + &data_actlen, USB_CNTL_TIMEOUT * 5); + /* special handling of STALL in DATA phase */ + if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { + debug("DATA:stall\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, + dir_in ? us->ep_in : us->ep_out); + if (result >= 0) + /* continue on to STATUS phase */ + goto st; + } + if (result < 0) { + debug("usb_bulk_msg error status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + for (index = 0; index < data_actlen; index++) + printf("pdata[%d] %#x ", index, srb->pdata[index]); + printf("\n"); +#endif + /* STATUS phase + error handling */ +st: + retry = 0; +again: + debug("STATUS phase\n"); + result = usb_bulk_msg(us->pusb_dev, pipein, csw, UMASS_BBB_CSW_SIZE, + &actlen, USB_CNTL_TIMEOUT*5); + + /* special handling of STALL in STATUS phase */ + if ((result < 0) && (retry < 1) && + (us->pusb_dev->status & USB_ST_STALLED)) { + debug("STATUS:stall\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, us->ep_in); + if (result >= 0 && (retry++ < 1)) + /* do a retry */ + goto again; + } + if (result < 0) { + debug("usb_bulk_msg error status %ld\n", + us->pusb_dev->status); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + ptr = (unsigned char *)csw; + for (index = 0; index < UMASS_BBB_CSW_SIZE; index++) + printf("ptr[%d] %#x ", index, ptr[index]); + printf("\n"); +#endif + /* misuse pipe to get the residue */ + pipe = le32_to_cpu(csw->dCSWDataResidue); + if (pipe == 0 && srb->datalen != 0 && srb->datalen - data_actlen != 0) + pipe = srb->datalen - data_actlen; + if (CSWSIGNATURE != le32_to_cpu(csw->dCSWSignature)) { + debug("!CSWSIGNATURE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if ((CBWTag - 1) != le32_to_cpu(csw->dCSWTag)) { + debug("!Tag\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus > CSWSTATUS_PHASE) { + debug(">PHASE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus == CSWSTATUS_PHASE) { + debug("=PHASE\n"); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } else if (data_actlen > srb->datalen) { + debug("transferred %dB instead of %ldB\n", + data_actlen, srb->datalen); + return USB_STOR_TRANSPORT_FAILED; + } else if (csw->bCSWStatus == CSWSTATUS_FAILED) { + debug("FAILED\n"); + return USB_STOR_TRANSPORT_FAILED; + } + + return result; +} + +static int usb_stor_CB_transport(struct scsi_cmd *srb, struct us_data *us) +{ + int result, status; + struct scsi_cmd *psrb; + struct scsi_cmd reqsrb; + int retry, notready; + + psrb = &reqsrb; + status = USB_STOR_TRANSPORT_GOOD; + retry = 0; + notready = 0; + /* issue the command */ +do_retry: + result = usb_stor_CB_comdat(srb, us); + debug("command / Data returned %d, status %lX\n", + result, us->pusb_dev->status); + /* if this is an CBI Protocol, get IRQ */ + if (us->protocol == US_PR_CBI) { + status = usb_stor_CBI_get_status(srb, us); + /* if the status is error, report it */ + if (status == USB_STOR_TRANSPORT_ERROR) { + debug(" USB CBI Command Error\n"); + return status; + } + srb->sense_buf[12] = (unsigned char)(us->ip_data >> 8); + srb->sense_buf[13] = (unsigned char)(us->ip_data & 0xff); + if (!us->ip_data) { + /* if the status is good, report it */ + if (status == USB_STOR_TRANSPORT_GOOD) { + debug(" USB CBI Command Good\n"); + return status; + } + } + } + /* do we have to issue an auto request? */ + /* HERE we have to check the result */ + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + debug("ERROR %lX\n", us->pusb_dev->status); + us->transport_reset(us); + return USB_STOR_TRANSPORT_ERROR; + } + if ((us->protocol == US_PR_CBI) && + ((srb->cmd[0] == SCSI_REQ_SENSE) || + (srb->cmd[0] == SCSI_INQUIRY))) { + /* do not issue an autorequest after request sense */ + debug("No auto request and good\n"); + return USB_STOR_TRANSPORT_GOOD; + } + /* issue an request_sense */ + memset(&psrb->cmd[0], 0, 12); + psrb->cmd[0] = SCSI_REQ_SENSE; + psrb->cmd[1] = srb->lun << 5; + psrb->cmd[4] = 18; + psrb->datalen = 18; + psrb->pdata = &srb->sense_buf[0]; + psrb->cmdlen = 12; + /* issue the command */ + result = usb_stor_CB_comdat(psrb, us); + debug("auto request returned %d\n", result); + /* if this is an CBI Protocol, get IRQ */ + if (us->protocol == US_PR_CBI) + status = usb_stor_CBI_get_status(psrb, us); + + if ((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + debug(" AUTO REQUEST ERROR %ld\n", + us->pusb_dev->status); + return USB_STOR_TRANSPORT_ERROR; + } + debug("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n", + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); + /* Check the auto request result */ + if ((srb->sense_buf[2] == 0) && + (srb->sense_buf[12] == 0) && + (srb->sense_buf[13] == 0)) { + /* ok, no sense */ + return USB_STOR_TRANSPORT_GOOD; + } + + /* Check the auto request result */ + switch (srb->sense_buf[2]) { + case 0x01: + /* Recovered Error */ + return USB_STOR_TRANSPORT_GOOD; + break; + case 0x02: + /* Not Ready */ + if (notready++ > USB_TRANSPORT_NOT_READY_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X (NOT READY)\n", srb->cmd[0], + srb->sense_buf[0], srb->sense_buf[2], + srb->sense_buf[12], srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } else { + mdelay(100); + goto do_retry; + } + break; + default: + if (retry++ > USB_TRANSPORT_UNKNOWN_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X" + " 0x%02X\n", srb->cmd[0], srb->sense_buf[0], + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } else + goto do_retry; + break; + } + return USB_STOR_TRANSPORT_FAILED; +} + +static void usb_stor_set_max_xfer_blk(struct usb_device *udev, + struct us_data *us) +{ + /* + * Limit the total size of a transfer to 120 KB. + * + * Some devices are known to choke with anything larger. It seems like + * the problem stems from the fact that original IDE controllers had + * only an 8-bit register to hold the number of sectors in one transfer + * and even those couldn't handle a full 256 sectors. + * + * Because we want to make sure we interoperate with as many devices as + * possible, we will maintain a 240 sector transfer size limit for USB + * Mass Storage devices. + * + * Tests show that other operating have similar limits with Microsoft + * Windows 7 limiting transfers to 128 sectors for both USB2 and USB3 + * and Apple Mac OS X 10.11 limiting transfers to 256 sectors for USB2 + * and 2048 for USB3 devices. + */ + unsigned short blk = 240; + +#if CONFIG_IS_ENABLED(DM_USB) + size_t size; + int ret; + + ret = usb_get_max_xfer_size(udev, (size_t *)&size); + if ((ret >= 0) && (size < blk * 512)) + blk = size / 512; +#endif + + us->max_xfer_blk = blk; +} + +static int usb_inquiry(struct scsi_cmd *srb, struct us_data *ss) +{ + int retry, i; + retry = 5; + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_INQUIRY; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 36; + srb->datalen = 36; + srb->cmdlen = 12; + i = ss->transport(srb, ss); + debug("inquiry returns %d\n", i); + if (i == 0) + break; + } while (--retry); + + if (!retry) { + printf("error in inquiry\n"); + return -1; + } + return 0; +} + +static int usb_request_sense(struct scsi_cmd *srb, struct us_data *ss) +{ + char *ptr; + + ptr = (char *)srb->pdata; + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_REQ_SENSE; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 18; + srb->datalen = 18; + srb->pdata = &srb->sense_buf[0]; + srb->cmdlen = 12; + ss->transport(srb, ss); + debug("Request Sense returned %02X %02X %02X\n", + srb->sense_buf[2], srb->sense_buf[12], + srb->sense_buf[13]); + srb->pdata = (uchar *)ptr; + return 0; +} + +static int usb_test_unit_ready(struct scsi_cmd *srb, struct us_data *ss) +{ + int retries = 10; + + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_TST_U_RDY; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 0; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) { + ss->flags |= USB_READY; + return 0; + } + usb_request_sense(srb, ss); + /* + * Check the Key Code Qualifier, if it matches + * "Not Ready - medium not present" + * (the sense Key equals 0x2 and the ASC is 0x3a) + * return immediately as the medium being absent won't change + * unless there is a user action. + */ + if ((srb->sense_buf[2] == 0x02) && + (srb->sense_buf[12] == 0x3a)) + return -1; + mdelay(100); + } while (retries--); + + return -1; +} + +static int usb_read_capacity(struct scsi_cmd *srb, struct us_data *ss) +{ + int retry; + /* XXX retries */ + retry = 3; + do { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_RD_CAPAC; + srb->cmd[1] = srb->lun << 5; + srb->datalen = 8; + srb->cmdlen = 12; + if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) + return 0; + } while (retry--); + + return -1; +} + +static int usb_read_10(struct scsi_cmd *srb, struct us_data *ss, + unsigned long start, unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_READ10; + srb->cmd[1] = srb->lun << 5; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + debug("read10: start %lx blocks %x\n", start, blocks); + return ss->transport(srb, ss); +} + +static int usb_write_10(struct scsi_cmd *srb, struct us_data *ss, + unsigned long start, unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_WRITE10; + srb->cmd[1] = srb->lun << 5; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + debug("write10: start %lx blocks %x\n", start, blocks); + return ss->transport(srb, ss); +} + + +#ifdef CONFIG_USB_BIN_FIXUP +/* + * Some USB storage devices queried for SCSI identification data respond with + * binary strings, which if output to the console freeze the terminal. The + * workaround is to modify the vendor and product strings read from such + * device with proper values (as reported by 'usb info'). + * + * Vendor and product length limits are taken from the definition of + * struct blk_desc in include/part.h. + */ +static void usb_bin_fixup(struct usb_device_descriptor descriptor, + unsigned char vendor[], + unsigned char product[]) { + const unsigned char max_vendor_len = 40; + const unsigned char max_product_len = 20; + if (descriptor.idVendor == 0x0424 && descriptor.idProduct == 0x223a) { + strncpy((char *)vendor, "SMSC", max_vendor_len); + strncpy((char *)product, "Flash Media Cntrller", + max_product_len); + } +} +#endif /* CONFIG_USB_BIN_FIXUP */ + +#if CONFIG_IS_ENABLED(BLK) +static unsigned long usb_stor_read(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer) +#else +static unsigned long usb_stor_read(struct blk_desc *block_dev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer) +#endif +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + struct usb_device *udev; + struct us_data *ss; + int retry; + struct scsi_cmd *srb = &usb_ccb; +#if CONFIG_IS_ENABLED(BLK) + struct blk_desc *block_dev; +#endif + + if (blkcnt == 0) + return 0; + /* Setup device */ +#if CONFIG_IS_ENABLED(BLK) + block_dev = dev_get_uclass_plat(dev); + udev = dev_get_parent_priv(dev_get_parent(dev)); + debug("\nusb_read: udev %d\n", block_dev->devnum); +#else + debug("\nusb_read: udev %d\n", block_dev->devnum); + udev = usb_dev_desc[block_dev->devnum].priv; + if (!udev) { + debug("%s: No device\n", __func__); + return 0; + } +#endif + ss = (struct us_data *)udev->privptr; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + usb_lock_async(udev, 1); + srb->lun = block_dev->lun; + buf_addr = (uintptr_t)buffer; + start = blknr; + blks = blkcnt; + + debug("\nusb_read: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", + block_dev->devnum, start, blks, buf_addr); + + do { + /* XXX need some comment here */ + retry = 2; + srb->pdata = (unsigned char *)buf_addr; + if (blks > ss->max_xfer_blk) + smallblks = ss->max_xfer_blk; + else + smallblks = (unsigned short) blks; +retry_it: + if (smallblks == ss->max_xfer_blk) + usb_show_progress(); + srb->datalen = block_dev->blksz * smallblks; + srb->pdata = (unsigned char *)buf_addr; + if (usb_read_10(srb, ss, start, smallblks)) { + debug("Read ERROR\n"); + ss->flags &= ~USB_READY; + usb_request_sense(srb, ss); + if (retry--) + goto retry_it; + blkcnt -= blks; + break; + } + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } while (blks != 0); + + debug("usb_read: end startblk " LBAF ", blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + + usb_lock_async(udev, 0); + usb_disable_asynch(0); /* asynch transfer allowed */ + if (blkcnt >= ss->max_xfer_blk) + debug("\n"); + return blkcnt; +} + +#if CONFIG_IS_ENABLED(BLK) +static unsigned long usb_stor_write(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +#else +static unsigned long usb_stor_write(struct blk_desc *block_dev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +#endif +{ + lbaint_t start, blks; + uintptr_t buf_addr; + unsigned short smallblks; + struct usb_device *udev; + struct us_data *ss; + int retry; + struct scsi_cmd *srb = &usb_ccb; +#if CONFIG_IS_ENABLED(BLK) + struct blk_desc *block_dev; +#endif + + if (blkcnt == 0) + return 0; + + /* Setup device */ +#if CONFIG_IS_ENABLED(BLK) + block_dev = dev_get_uclass_plat(dev); + udev = dev_get_parent_priv(dev_get_parent(dev)); + debug("\nusb_read: udev %d\n", block_dev->devnum); +#else + debug("\nusb_read: udev %d\n", block_dev->devnum); + udev = usb_dev_desc[block_dev->devnum].priv; + if (!udev) { + debug("%s: No device\n", __func__); + return 0; + } +#endif + ss = (struct us_data *)udev->privptr; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + usb_lock_async(udev, 1); + + srb->lun = block_dev->lun; + buf_addr = (uintptr_t)buffer; + start = blknr; + blks = blkcnt; + + debug("\nusb_write: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", + block_dev->devnum, start, blks, buf_addr); + + do { + /* If write fails retry for max retry count else + * return with number of blocks written successfully. + */ + retry = 2; + srb->pdata = (unsigned char *)buf_addr; + if (blks > ss->max_xfer_blk) + smallblks = ss->max_xfer_blk; + else + smallblks = (unsigned short) blks; +retry_it: + if (smallblks == ss->max_xfer_blk) + usb_show_progress(); + srb->datalen = block_dev->blksz * smallblks; + srb->pdata = (unsigned char *)buf_addr; + if (usb_write_10(srb, ss, start, smallblks)) { + debug("Write ERROR\n"); + ss->flags &= ~USB_READY; + usb_request_sense(srb, ss); + if (retry--) + goto retry_it; + blkcnt -= blks; + break; + } + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } while (blks != 0); + + debug("usb_write: end startblk " LBAF ", blccnt %x buffer %lx\n", + start, smallblks, buf_addr); + + usb_lock_async(udev, 0); + usb_disable_asynch(0); /* asynch transfer allowed */ + if (blkcnt >= ss->max_xfer_blk) + debug("\n"); + return blkcnt; + +} + +/* Probe to see if a new device is actually a Storage device */ +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, + struct us_data *ss) +{ + struct usb_interface *iface; + int i; + struct usb_endpoint_descriptor *ep_desc; + unsigned int flags = 0; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + + if (dev->descriptor.bDeviceClass != 0 || + iface->desc.bInterfaceClass != USB_CLASS_MASS_STORAGE || + iface->desc.bInterfaceSubClass < US_SC_MIN || + iface->desc.bInterfaceSubClass > US_SC_MAX) { + debug("Not mass storage\n"); + /* if it's not a mass storage, we go no further */ + return 0; + } + + memset(ss, 0, sizeof(struct us_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Mass Storage device detected\n"); + + /* Initialize the us_data structure with some useful info */ + ss->flags = flags; + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->attention_done = 0; + ss->subclass = iface->desc.bInterfaceSubClass; + ss->protocol = iface->desc.bInterfaceProtocol; + + /* set the handler pointers based on the protocol */ + debug("Transport: "); + switch (ss->protocol) { + case US_PR_CB: + debug("Control/Bulk\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + + case US_PR_CBI: + debug("Control/Bulk/Interrupt\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + case US_PR_BULK: + debug("Bulk/Bulk/Bulk\n"); + ss->transport = usb_stor_BBB_transport; + ss->transport_reset = usb_stor_BBB_reset; + break; + default: + printf("USB Storage Transport unknown / not yet implemented\n"); + return 0; + break; + } + + /* + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_desc = &iface->ep_desc[i]; + /* is it an BULK endpoint? */ + if ((ep_desc->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_desc->bEndpointAddress & USB_DIR_IN) + ss->ep_in = ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else + ss->ep_out = + ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + + /* is it an interrupt endpoint? */ + if ((ep_desc->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = ep_desc->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = ep_desc->bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface->desc.bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || + (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { + debug("Problems with device\n"); + return 0; + } + /* set class specific stuff */ + /* We only handle certain protocols. Currently, these are + * the only ones. + * The SFF8070 accepts the requests used in u-boot + */ + if (ss->subclass != US_SC_UFI && ss->subclass != US_SC_SCSI && + ss->subclass != US_SC_8070) { + printf("Sorry, protocol %d not yet supported.\n", ss->subclass); + return 0; + } + if (ss->ep_int) { + /* we had found an interrupt endpoint, prepare irq pipe + * set up the IRQ pipe and handler + */ + ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255; + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe); + dev->irq_handle = usb_stor_irq; + } + + /* Set the maximum transfer size per host controller setting */ + usb_stor_set_max_xfer_blk(dev, ss); + + dev->privptr = (void *)ss; + return 1; +} + +int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, + struct blk_desc *dev_desc) +{ + unsigned char perq, modi; + ALLOC_CACHE_ALIGN_BUFFER(u32, cap, 2); + ALLOC_CACHE_ALIGN_BUFFER(u8, usb_stor_buf, 36); + u32 capacity, blksz; + struct scsi_cmd *pccb = &usb_ccb; + + pccb->pdata = usb_stor_buf; + + dev_desc->target = dev->devnum; + pccb->lun = dev_desc->lun; + debug(" address %d\n", dev_desc->target); + + if (usb_inquiry(pccb, ss)) { + debug("%s: usb_inquiry() failed\n", __func__); + return -1; + } + + perq = usb_stor_buf[0]; + modi = usb_stor_buf[1]; + + /* + * Skip unknown devices (0x1f) and enclosure service devices (0x0d), + * they would not respond to test_unit_ready . + */ + if (((perq & 0x1f) == 0x1f) || ((perq & 0x1f) == 0x0d)) { + debug("%s: unknown/unsupported device\n", __func__); + return 0; + } + if ((modi&0x80) == 0x80) { + /* drive is removable */ + dev_desc->removable = 1; + } + memcpy(dev_desc->vendor, (const void *)&usb_stor_buf[8], 8); + memcpy(dev_desc->product, (const void *)&usb_stor_buf[16], 16); + memcpy(dev_desc->revision, (const void *)&usb_stor_buf[32], 4); + dev_desc->vendor[8] = 0; + dev_desc->product[16] = 0; + dev_desc->revision[4] = 0; +#ifdef CONFIG_USB_BIN_FIXUP + usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor, + (uchar *)dev_desc->product); +#endif /* CONFIG_USB_BIN_FIXUP */ + debug("ISO Vers %X, Response Data %X\n", usb_stor_buf[2], + usb_stor_buf[3]); + if (usb_test_unit_ready(pccb, ss)) { + printf("Device NOT ready\n" + " Request Sense returned %02X %02X %02X\n", + pccb->sense_buf[2], pccb->sense_buf[12], + pccb->sense_buf[13]); + if (dev_desc->removable == 1) + dev_desc->type = perq; + return 0; + } + pccb->pdata = (unsigned char *)cap; + memset(pccb->pdata, 0, 8); + if (usb_read_capacity(pccb, ss) != 0) { + printf("READ_CAP ERROR\n"); + ss->flags &= ~USB_READY; + cap[0] = 2880; + cap[1] = 0x200; + } + debug("Read Capacity returns: 0x%08x, 0x%08x\n", cap[0], cap[1]); +#if 0 + if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ + cap[0] >>= 16; + + cap[0] = cpu_to_be32(cap[0]); + cap[1] = cpu_to_be32(cap[1]); +#endif + + capacity = be32_to_cpu(cap[0]) + 1; + blksz = be32_to_cpu(cap[1]); + + debug("Capacity = 0x%08x, blocksz = 0x%08x\n", capacity, blksz); + dev_desc->lba = capacity; + dev_desc->blksz = blksz; + dev_desc->log2blksz = LOG2(dev_desc->blksz); + dev_desc->type = perq; + debug(" address %d\n", dev_desc->target); + + return 1; +} + +#if CONFIG_IS_ENABLED(DM_USB) + +static int usb_mass_storage_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + int ret; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + ret = usb_stor_probe_device(udev); + usb_disable_asynch(0); /* asynch transfer allowed */ + + return ret; +} + +static const struct udevice_id usb_mass_storage_ids[] = { + { .compatible = "usb-mass-storage" }, + { } +}; + +U_BOOT_DRIVER(usb_mass_storage) = { + .name = "usb_mass_storage", + .id = UCLASS_MASS_STORAGE, + .of_match = usb_mass_storage_ids, + .probe = usb_mass_storage_probe, +#if CONFIG_IS_ENABLED(BLK) + .plat_auto = sizeof(struct us_data), +#endif +}; + +UCLASS_DRIVER(usb_mass_storage) = { + .id = UCLASS_MASS_STORAGE, + .name = "usb_mass_storage", +}; + +static const struct usb_device_id mass_storage_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_MASS_STORAGE + }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(usb_mass_storage, mass_storage_id_table); +#endif + +#if CONFIG_IS_ENABLED(BLK) +static const struct blk_ops usb_storage_ops = { + .read = usb_stor_read, + .write = usb_stor_write, +}; + +U_BOOT_DRIVER(usb_storage_blk) = { + .name = "usb_storage_blk", + .id = UCLASS_BLK, + .ops = &usb_storage_ops, +}; +#else +U_BOOT_LEGACY_BLK(usb) = { + .if_typename = "usb", + .if_type = IF_TYPE_USB, + .max_devs = USB_MAX_STOR_DEV, + .desc = usb_dev_desc, +}; +#endif diff --git a/roms/u-boot/common/xyzModem.c b/roms/u-boot/common/xyzModem.c new file mode 100644 index 000000000..fc3459ebb --- /dev/null +++ b/roms/u-boot/common/xyzModem.c @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: eCos-2.0 +/* + *========================================================================== + * + * xyzModem.c + * + * RedBoot stream handler for xyzModem protocol + * + *========================================================================== + *#####DESCRIPTIONBEGIN#### + * + * Author(s): gthomas + * Contributors: gthomas, tsmith, Yoshinori Sato + * Date: 2000-07-14 + * Purpose: + * Description: + * + * This code is part of RedBoot (tm). + * + *####DESCRIPTIONEND#### + * + *========================================================================== + */ +#include <common.h> +#include <xyzModem.h> +#include <stdarg.h> +#include <u-boot/crc.h> +#include <watchdog.h> + +/* Assumption - run xyzModem protocol over the console port */ + +/* Values magic to the protocol */ +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define BSP 0x08 +#define NAK 0x15 +#define CAN 0x18 +#define EOF 0x1A /* ^Z for DOS officionados */ + +/* Data & state local to the protocol */ +static struct +{ + int *__chan; + unsigned char pkt[1024], *bufp; + unsigned char blk, cblk, crc1, crc2; + unsigned char next_blk; /* Expected block */ + int len, mode, total_retries; + int total_SOH, total_STX, total_CAN; + bool crc_mode, at_eof, tx_ack; + unsigned long file_length, read_length; +} xyz; + +#define xyzModem_CHAR_TIMEOUT 2000 /* 2 seconds */ +#define xyzModem_MAX_RETRIES 20 +#define xyzModem_MAX_RETRIES_WITH_CRC 10 +#define xyzModem_CAN_COUNT 3 /* Wait for 3 CAN before quitting */ + + +typedef int cyg_int32; +static int +CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c) +{ + + ulong now = get_timer(0); + WATCHDOG_RESET(); + while (!tstc ()) + { + if (get_timer(now) > xyzModem_CHAR_TIMEOUT) + break; + } + if (tstc ()) + { + *c = getchar(); + return 1; + } + return 0; +} + +static void +CYGACC_COMM_IF_PUTC (char x, char y) +{ + putc (y); +} + +/* Validate a hex character */ +__inline__ static bool +_is_hex (char c) +{ + return (((c >= '0') && (c <= '9')) || + ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))); +} + +/* Convert a single hex nibble */ +__inline__ static int +_from_hex (char c) +{ + int ret = 0; + + if ((c >= '0') && (c <= '9')) + { + ret = (c - '0'); + } + else if ((c >= 'a') && (c <= 'f')) + { + ret = (c - 'a' + 0x0a); + } + else if ((c >= 'A') && (c <= 'F')) + { + ret = (c - 'A' + 0x0A); + } + return ret; +} + +/* Convert a character to lower case */ +__inline__ static char +_tolower (char c) +{ + if ((c >= 'A') && (c <= 'Z')) + { + c = (c - 'A') + 'a'; + } + return c; +} + +/* Parse (scan) a number */ +static bool +parse_num (char *s, unsigned long *val, char **es, char *delim) +{ + bool first = true; + int radix = 10; + char c; + unsigned long result = 0; + int digit; + + while (*s == ' ') + s++; + while (*s) + { + if (first && (s[0] == '0') && (_tolower (s[1]) == 'x')) + { + radix = 16; + s += 2; + } + first = false; + c = *s++; + if (_is_hex (c) && ((digit = _from_hex (c)) < radix)) + { + /* Valid digit */ + result = (result * radix) + digit; + } + else + { + if (delim != (char *) 0) + { + /* See if this character is one of the delimiters */ + char *dp = delim; + while (*dp && (c != *dp)) + dp++; + if (*dp) + break; /* Found a good delimiter */ + } + return false; /* Malformatted number */ + } + } + *val = result; + if (es != (char **) 0) + { + *es = s; + } + return true; +} + + +#if defined(DEBUG) && !CONFIG_IS_ENABLED(USE_TINY_PRINTF) +/* + * Note: this debug setup works by storing the strings in a fixed buffer + */ +static char zm_debug_buf[8192]; +static char *zm_out = zm_debug_buf; +static char *zm_out_start = zm_debug_buf; + +static int +zm_dprintf(char *fmt, ...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = diag_vsprintf(zm_out, fmt, args); + va_end(args); + zm_out += len; + return len; +} + +static void +zm_flush (void) +{ + zm_out = zm_out_start; +} + +static void +zm_dump_buf (void *buf, int len) +{ + +} + +static unsigned char zm_buf[2048]; +static unsigned char *zm_bp; + +static void +zm_new (void) +{ + zm_bp = zm_buf; +} + +static void +zm_save (unsigned char c) +{ + *zm_bp++ = c; +} + +static void +zm_dump (int line) +{ + zm_dprintf ("Packet at line: %d\n", line); + zm_dump_buf (zm_buf, zm_bp - zm_buf); +} + +#define ZM_DEBUG(x) x +#else +#define ZM_DEBUG(x) +#endif + +/* Wait for the line to go idle */ +static void +xyzModem_flush (void) +{ + int res; + char c; + while (true) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + if (!res) + return; + } +} + +static int +xyzModem_get_hdr (void) +{ + char c; + int res; + bool hdr_found = false; + int i, can_total, hdr_chars; + unsigned short cksum; + + ZM_DEBUG (zm_new ()); + /* Find the start of a header */ + can_total = 0; + hdr_chars = 0; + + if (xyz.tx_ack) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + xyz.tx_ack = false; + } + while (!hdr_found) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + ZM_DEBUG (zm_save (c)); + if (res) + { + hdr_chars++; + switch (c) + { + case SOH: + xyz.total_SOH++; + case STX: + if (c == STX) + xyz.total_STX++; + hdr_found = true; + break; + case CAN: + xyz.total_CAN++; + ZM_DEBUG (zm_dump (__LINE__)); + if (++can_total == xyzModem_CAN_COUNT) + { + return xyzModem_cancel; + } + else + { + /* Wait for multiple CAN to avoid early quits */ + break; + } + case EOT: + /* EOT only supported if no noise */ + if (hdr_chars == 1) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__)); + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_eof; + } + default: + /* Ignore, waiting for start of header */ + ; + } + } + else + { + /* Data stream timed out */ + xyzModem_flush (); /* Toss any current input */ + ZM_DEBUG (zm_dump (__LINE__)); + CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000); + return xyzModem_timeout; + } + } + + /* Header found, now read the data */ + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk); + ZM_DEBUG (zm_save (xyz.blk)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk); + ZM_DEBUG (zm_save (xyz.cblk)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + xyz.len = (c == SOH) ? 128 : 1024; + xyz.bufp = xyz.pkt; + for (i = 0; i < xyz.len; i++) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c); + ZM_DEBUG (zm_save (c)); + if (res) + { + xyz.pkt[i] = c; + } + else + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + } + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1); + ZM_DEBUG (zm_save (xyz.crc1)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + if (xyz.crc_mode) + { + res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2); + ZM_DEBUG (zm_save (xyz.crc2)); + if (!res) + { + ZM_DEBUG (zm_dump (__LINE__)); + return xyzModem_timeout; + } + } + ZM_DEBUG (zm_dump (__LINE__)); + /* Validate the message */ + if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF) + { + ZM_DEBUG (zm_dprintf + ("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, + (xyz.blk ^ xyz.cblk))); + ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len)); + xyzModem_flush (); + return xyzModem_frame; + } + /* Verify checksum/CRC */ + if (xyz.crc_mode) + { + cksum = crc16_ccitt(0, xyz.pkt, xyz.len); + if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) + { + ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n", + xyz.crc1, xyz.crc2, cksum & 0xFFFF)); + return xyzModem_cksum; + } + } + else + { + cksum = 0; + for (i = 0; i < xyz.len; i++) + { + cksum += xyz.pkt[i]; + } + if (xyz.crc1 != (cksum & 0xFF)) + { + ZM_DEBUG (zm_dprintf + ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, + cksum & 0xFF)); + return xyzModem_cksum; + } + } + /* If we get here, the message passes [structural] muster */ + return 0; +} + +int +xyzModem_stream_open (connection_info_t * info, int *err) +{ + int stat = 0; + int retries = xyzModem_MAX_RETRIES; + int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC; + +/* ZM_DEBUG(zm_out = zm_out_start); */ +#ifdef xyzModem_zmodem + if (info->mode == xyzModem_zmodem) + { + *err = xyzModem_noZmodem; + return -1; + } +#endif + +/* TODO: CHECK ! */ + int dummy = 0; + xyz.__chan = &dummy; + xyz.len = 0; + xyz.crc_mode = true; + xyz.at_eof = false; + xyz.tx_ack = false; + xyz.mode = info->mode; + xyz.total_retries = 0; + xyz.total_SOH = 0; + xyz.total_STX = 0; + xyz.total_CAN = 0; + xyz.read_length = 0; + xyz.file_length = 0; + + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + + if (xyz.mode == xyzModem_xmodem) + { + /* X-modem doesn't have an information header - exit here */ + xyz.next_blk = 1; + return 0; + } + + while (retries-- > 0) + { + stat = xyzModem_get_hdr (); + if (stat == 0) + { + /* Y-modem file information header */ + if (xyz.blk == 0) + { + /* skip filename */ + while (*xyz.bufp++); + /* get the length */ + parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " "); + /* The rest of the file name data block quietly discarded */ + xyz.tx_ack = true; + } + xyz.next_blk = 1; + xyz.len = 0; + return 0; + } + else if (stat == xyzModem_timeout) + { + if (--crc_retries <= 0) + xyz.crc_mode = false; + CYGACC_CALL_IF_DELAY_US (5 * 100000); /* Extra delay for startup */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__)); + } + if (stat == xyzModem_cancel) + { + break; + } + } + *err = stat; + ZM_DEBUG (zm_flush ()); + return -1; +} + +int +xyzModem_stream_read (char *buf, int size, int *err) +{ + int stat, total, len; + int retries; + + total = 0; + stat = xyzModem_cancel; + /* Try and get 'size' bytes into the buffer */ + while (!xyz.at_eof && (size > 0)) + { + if (xyz.len == 0) + { + retries = xyzModem_MAX_RETRIES; + while (retries-- > 0) + { + stat = xyzModem_get_hdr (); + if (stat == 0) + { + if (xyz.blk == xyz.next_blk) + { + xyz.tx_ack = true; + ZM_DEBUG (zm_dprintf + ("ACK block %d (%d)\n", xyz.blk, __LINE__)); + xyz.next_blk = (xyz.next_blk + 1) & 0xFF; + + if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0) + { + /* Data blocks can be padded with ^Z (EOF) characters */ + /* This code tries to detect and remove them */ + if ((xyz.bufp[xyz.len - 1] == EOF) && + (xyz.bufp[xyz.len - 2] == EOF) && + (xyz.bufp[xyz.len - 3] == EOF)) + { + while (xyz.len + && (xyz.bufp[xyz.len - 1] == EOF)) + { + xyz.len--; + } + } + } + + /* + * See if accumulated length exceeds that of the file. + * If so, reduce size (i.e., cut out pad bytes) + * Only do this for Y-modem (and Z-modem should it ever + * be supported since it can fall back to Y-modem mode). + */ + if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) + { + xyz.read_length += xyz.len; + if (xyz.read_length > xyz.file_length) + { + xyz.len -= (xyz.read_length - xyz.file_length); + } + } + break; + } + else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) + { + /* Just re-ACK this so sender will get on with it */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + continue; /* Need new header */ + } + else + { + stat = xyzModem_sequence; + } + } + if (stat == xyzModem_cancel) + { + break; + } + if (stat == xyzModem_eof) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__)); + if (xyz.mode == xyzModem_ymodem) + { + CYGACC_COMM_IF_PUTC (*xyz.__chan, + (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("Reading Final Header\n")); + stat = xyzModem_get_hdr (); + CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK); + ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__)); + } + xyz.at_eof = true; + break; + } + CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK)); + xyz.total_retries++; + ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__)); + } + if (stat < 0) + { + *err = stat; + xyz.len = -1; + return total; + } + } + /* Don't "read" data from the EOF protocol package */ + if (!xyz.at_eof) + { + len = xyz.len; + if (size < len) + len = size; + memcpy (buf, xyz.bufp, len); + size -= len; + buf += len; + total += len; + xyz.len -= len; + xyz.bufp += len; + } + } + return total; +} + +void +xyzModem_stream_close (int *err) +{ + diag_printf + ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n", + xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX, + xyz.total_CAN, xyz.total_retries); + ZM_DEBUG (zm_flush ()); +} + +/* Need to be able to clean out the input buffer, so have to take the */ +/* getc */ +void +xyzModem_stream_terminate (bool abort, int (*getc) (void)) +{ + int c; + + if (abort) + { + ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n")); + switch (xyz.mode) + { + case xyzModem_xmodem: + case xyzModem_ymodem: + /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */ + /* number of Backspaces is a friendly way to get the other end to abort. */ + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP); + /* Now consume the rest of what's waiting on the line. */ + ZM_DEBUG (zm_dprintf ("Flushing serial line.\n")); + xyzModem_flush (); + xyz.at_eof = true; + break; +#ifdef xyzModem_zmodem + case xyzModem_zmodem: + /* Might support it some day I suppose. */ +#endif + break; + } + } + else + { + ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n")); + /* + * Consume any trailing crap left in the inbuffer from + * previous received blocks. Since very few files are an exact multiple + * of the transfer block size, there will almost always be some gunk here. + * If we don't eat it now, RedBoot will think the user typed it. + */ + ZM_DEBUG (zm_dprintf ("Trailing gunk:\n")); + while ((c = (*getc) ()) > -1) + ; + ZM_DEBUG (zm_dprintf ("\n")); + /* + * Make a small delay to give terminal programs like minicom + * time to get control again after their file transfer program + * exits. + */ + CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000); + } +} + +char * +xyzModem_error (int err) +{ + switch (err) + { + case xyzModem_access: + return "Can't access file"; + break; + case xyzModem_noZmodem: + return "Sorry, zModem not available yet"; + break; + case xyzModem_timeout: + return "Timed out"; + break; + case xyzModem_eof: + return "End of file"; + break; + case xyzModem_cancel: + return "Cancelled"; + break; + case xyzModem_frame: + return "Invalid framing"; + break; + case xyzModem_cksum: + return "CRC/checksum error"; + break; + case xyzModem_sequence: + return "Block sequence error"; + break; + default: + return "Unknown error"; + break; + } +} + +/* + * RedBoot interface + */ |