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/cmd | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/cmd')
209 files changed, 57768 insertions, 0 deletions
diff --git a/roms/u-boot/cmd/.gitignore b/roms/u-boot/cmd/.gitignore new file mode 100644 index 000000000..bab889fd3 --- /dev/null +++ b/roms/u-boot/cmd/.gitignore @@ -0,0 +1,6 @@ +config_data.gz +config_data_gz.h +config_data_size.h +license_data.gz +license_data_gz.h +license_data_size.h diff --git a/roms/u-boot/cmd/Kconfig b/roms/u-boot/cmd/Kconfig new file mode 100644 index 000000000..a9fb4eead --- /dev/null +++ b/roms/u-boot/cmd/Kconfig @@ -0,0 +1,2383 @@ +menu "Command line interface" + +config CMDLINE + bool "Support U-Boot commands" + default y + help + Enable U-Boot's command-line functions. This provides a means + to enter commands into U-Boot for a wide variety of purposes. It + also allows scripts (containing commands) to be executed. + Various commands and command categorys can be indivdually enabled. + Depending on the number of commands enabled, this can add + substantially to the size of U-Boot. + +config HUSH_PARSER + bool "Use hush shell" + depends on CMDLINE + help + This option enables the "hush" shell (from Busybox) as command line + interpreter, thus enabling powerful command line syntax like + if...then...else...fi conditionals or `&&' and '||' + constructs ("shell scripts"). + + If disabled, you get the old, much simpler behaviour with a somewhat + smaller memory footprint. + +config CMDLINE_EDITING + bool "Enable command line editing" + depends on CMDLINE + default y + help + Enable editing and History functions for interactive command line + input operations + +config AUTO_COMPLETE + bool "Enable auto complete using TAB" + depends on CMDLINE + default y + help + Enable auto completion of commands using TAB. + +config SYS_LONGHELP + bool "Enable long help messages" + depends on CMDLINE + default y if CMDLINE + help + Defined when you want long help messages included + Do not set this option when short of memory. + +config SYS_PROMPT + string "Shell prompt" + default "Zynq> " if ARCH_ZYNQ + default "ZynqMP> " if ARCH_ZYNQMP + default "=> " + help + This string is displayed in the command line to the left of the + cursor. + +config SYS_PROMPT_HUSH_PS2 + string "Hush shell secondary prompt" + depends on HUSH_PARSER + default "> " + help + This defines the secondary prompt string, which is + printed when the command interpreter needs more input + to complete a command. Usually "> ". + +config SYS_XTRACE + bool "Command execution tracer" + depends on CMDLINE + default y if CMDLINE + help + This option enables the possiblity to print all commands before + executing them and after all variables are evaluated (similar + to Bash's xtrace/'set -x' feature). + To enable the tracer a variable "xtrace" needs to be defined in + the environment. + +config BUILD_BIN2C + bool + +comment "Commands" + +menu "Info commands" + +config CMD_ACPI + bool "acpi" + depends on ACPIGEN + default y + help + List and dump ACPI tables. ACPI (Advanced Configuration and Power + Interface) is used mostly on x86 for providing information to the + Operating System about devices in the system. The tables are set up + by the firmware, typically U-Boot but possibly an earlier firmware + module, if U-Boot is chain-loaded from something else. ACPI tables + can also include code, to perform hardware-specific tasks required + by the Operating Systems. This allows some amount of separation + between the firmware and OS, and is particularly useful when you + want to make hardware changes without the OS needing to be adjusted. + +config CMD_ADDRMAP + bool "addrmap" + depends on ADDR_MAP + default y + help + List non-identity virtual-physical memory mappings for 32-bit CPUs. + +config CMD_BDI + bool "bdinfo" + default y + help + Print board info + +config CMD_CONFIG + bool "config" + default SANDBOX + select BUILD_BIN2C + help + Print ".config" contents. + + If this option is enabled, the ".config" file contents are embedded + in the U-Boot image and can be printed on the console by the "config" + command. This provides information of which options are enabled on + the running U-Boot. + +config CMD_CONSOLE + bool "coninfo" + default y + help + Print console devices and information. + +config CMD_CPU + bool "cpu" + depends on CPU + help + Print information about available CPUs. This normally shows the + number of CPUs, type (e.g. manufacturer, architecture, product or + internal name) and clock frequency. Other information may be + available depending on the CPU driver. + +config CMD_LICENSE + bool "license" + select BUILD_BIN2C + help + Print GPL license text + +config CMD_PMC + bool "pmc" + help + Provides access to the Intel Power-Management Controller (PMC) so + that its state can be examined. This does not currently support + changing the state but it is still useful for debugging and seeing + what is going on. + +config CMD_REGINFO + bool "reginfo" + depends on PPC + help + Register dump + +config CMD_TLV_EEPROM + bool "tlv_eeprom" + depends on I2C_EEPROM + help + Display and program the system EEPROM data block in ONIE Tlvinfo + format. TLV stands for Type-Length-Value. + +config SPL_CMD_TLV_EEPROM + bool "tlv_eeprom for SPL" + depends on SPL_I2C_EEPROM + select SPL_DRIVERS_MISC_SUPPORT + help + Read system EEPROM data block in ONIE Tlvinfo format from SPL. + +config CMD_SBI + bool "sbi" + depends on RISCV_SMODE && SBI_V02 + help + Display information about the SBI implementation. + +endmenu + +menu "Boot commands" + +config CMD_BOOTD + bool "bootd" + default y + help + Run the command stored in the environment "bootcmd", i.e. + "bootd" does the same thing as "run bootcmd". + +config CMD_BOOTM + bool "bootm" + default y + help + Boot an application image from the memory. + +config BOOTM_EFI + bool "Support booting UEFI FIT images" + depends on CMD_BOOTEFI && CMD_BOOTM && FIT + default y + help + Support booting UEFI FIT images via the bootm command. + +config CMD_BOOTZ + bool "bootz" + help + Boot the Linux zImage + +config CMD_BOOTI + bool "booti" + depends on ARM64 || RISCV + default y + help + Boot an AArch64 Linux Kernel image from memory. + +config BOOTM_LINUX + bool "Support booting Linux OS images" + depends on CMD_BOOTM || CMD_BOOTZ || CMD_BOOTI + default y + help + Support booting the Linux kernel directly via a command such as bootm + or booti or bootz. + +config BOOTM_NETBSD + bool "Support booting NetBSD (non-EFI) loader images" + depends on CMD_BOOTM + default y + help + Support booting NetBSD via the bootm command. + +config BOOTM_OPENRTOS + bool "Support booting OPENRTOS / FreeRTOS images" + depends on CMD_BOOTM + help + Support booting OPENRTOS / FreeRTOS via the bootm command. + +config BOOTM_OSE + bool "Support booting Enea OSE images" + depends on (ARM && (ARM64 || CPU_V7A || CPU_V7R) || SANDBOX || PPC || X86) + depends on CMD_BOOTM + help + Support booting Enea OSE images via the bootm command. + +config BOOTM_PLAN9 + bool "Support booting Plan9 OS images" + depends on CMD_BOOTM + default y + help + Support booting Plan9 images via the bootm command. + +config BOOTM_RTEMS + bool "Support booting RTEMS OS images" + depends on CMD_BOOTM + default y + help + Support booting RTEMS images via the bootm command. + +config BOOTM_VXWORKS + bool "Support booting VxWorks OS images" + depends on CMD_BOOTM + default y + help + Support booting VxWorks images via the bootm command. + +config CMD_BOOTEFI + bool "bootefi" + depends on EFI_LOADER + default y + help + Boot an EFI image from memory. + +config CMD_BOOTEFI_HELLO_COMPILE + bool "Compile a standard EFI hello world binary for testing" + depends on CMD_BOOTEFI && !CPU_V7M + default y + help + This compiles a standard EFI hello world application with U-Boot so + that it can be used with the test/py testing framework. This is useful + for testing that EFI is working at a basic level, and for bringing + up EFI support on a new architecture. + + No additional space will be required in the resulting U-Boot binary + when this option is enabled. + +config CMD_BOOTEFI_HELLO + bool "Allow booting a standard EFI hello world for testing" + depends on CMD_BOOTEFI_HELLO_COMPILE + default y if CMD_BOOTEFI_SELFTEST + help + This adds a standard EFI hello world application to U-Boot so that + it can be used with the 'bootefi hello' command. This is useful + for testing that EFI is working at a basic level, and for bringing + up EFI support on a new architecture. + +source lib/efi_selftest/Kconfig + +config CMD_BOOTMENU + bool "bootmenu" + select MENU + help + Add an ANSI terminal boot menu command. + +config CMD_ADTIMG + bool "adtimg" + help + Android DTB/DTBO image manipulation commands. Read dtb/dtbo files from + image into RAM, dump image structure information, etc. Those dtb/dtbo + files should be merged in one dtb further, which needs to be passed to + the kernel, as part of a boot process. + +config CMD_ABOOTIMG + bool "abootimg" + depends on ANDROID_BOOT_IMAGE + help + Android Boot Image manipulation commands. Allows one to extract + images contained in boot.img, like kernel, ramdisk, dtb, etc, and + obtain corresponding meta-information from boot.img. + + See doc/android/boot-image.rst for details. + +config CMD_ELF + bool "bootelf, bootvx" + default y + select LIB_ELF + help + Boot an ELF/vxWorks image from the memory. + +config CMD_FDT + bool "Flattened Device Tree utility commands" + default y + depends on OF_LIBFDT + help + Do FDT related setup before booting into the Operating System. + +config SUPPORT_EXTENSION_SCAN + bool + +config CMD_EXTENSION + bool "Extension board management command" + select CMD_FDT + depends on SUPPORT_EXTENSION_SCAN + help + Enables the "extension" command, which allows to detect + extension boards connected to the system, and apply + corresponding Device Tree overlays. + +config CMD_GO + bool "go" + default y + help + Start an application at a given address. + +config CMD_RUN + bool "run" + default y + help + Run the command in the given environment variable. + +config CMD_IMI + bool "iminfo" + default y + help + Print header information for application image. + +config CMD_IMLS + bool "imls" + help + List all images found in flash + +config CMD_XIMG + bool "imxtract" + default y + help + Extract a part of a multi-image. + +config CMD_SPL + bool "spl export - Export boot information for Falcon boot" + depends on SPL + help + Falcon mode allows booting directly from SPL into an Operating + System such as Linux, thus skipping U-Boot proper. See + doc/README.falcon for full information about how to use this + command. + +config CMD_SPL_NAND_OFS + hex "Offset of OS args or dtb for Falcon-mode NAND boot" + depends on CMD_SPL && (TPL_NAND_SUPPORT || SPL_NAND_SUPPORT) + default 0 + help + This provides the offset of the command line arguments for Linux + when booting from NAND in Falcon mode. See doc/README.falcon + for full information about how to use this option (and also see + board/gateworks/gw_ventana/README for an example). + +config CMD_SPL_NOR_OFS + hex "Offset of OS args or dtb for Falcon-mode NOR boot" + depends on CMD_SPL && SPL_NOR_SUPPORT + default 0 + help + This provides the offset of the command line arguments or dtb for + Linux when booting from NOR in Falcon mode. + +config CMD_SPL_WRITE_SIZE + hex "Size of argument area" + depends on CMD_SPL + default 0x2000 + help + This provides the size of the command-line argument area in NAND + flash used by Falcon-mode boot. See the documentation until CMD_SPL + for detail. + +config CMD_THOR_DOWNLOAD + bool "thor - TIZEN 'thor' download" + select DFU + help + Implements the 'thor' download protocol. This is a way of + downloading a software update over USB from an attached host. + There is no documentation about this within the U-Boot source code + but you should be able to find something on the interwebs. + +config CMD_ZBOOT + bool "zboot - x86 boot command" + help + With x86 machines it is common to boot a bzImage file which + contains both a kernel and a setup.bin file. The latter includes + configuration information from the dark ages which x86 boards still + need to pick things out of. + + Consider using FIT in preference to this since it supports directly + booting both 32- and 64-bit kernels, as well as secure boot. + Documentation is available in doc/uImage.FIT/x86-fit-boot.txt + +endmenu + +menu "Environment commands" + +config CMD_ASKENV + bool "ask for env variable" + help + Ask for environment variable + +config CMD_EXPORTENV + bool "env export" + default y + help + Export environments. + +config CMD_IMPORTENV + bool "env import" + default y + help + Import environments. + +config CMD_EDITENV + bool "editenv" + default y + help + Edit environment variable. + +config CMD_GREPENV + bool "search env" + help + Allow for searching environment variables + +config CMD_SAVEENV + bool "saveenv" + default y + help + Save all environment variables into the compiled-in persistent + storage. + +config CMD_ERASEENV + bool "eraseenv" + default n + depends on CMD_SAVEENV + help + Erase environment variables from the compiled-in persistent + storage. + +config CMD_ENV_EXISTS + bool "env exists" + default y + help + Check if a variable is defined in the environment for use in + shell scripting. + +config CMD_ENV_CALLBACK + bool "env callbacks - print callbacks and their associated variables" + help + Some environment variable have callbacks defined by + U_BOOT_ENV_CALLBACK. These are called when the variable changes. + For example changing "baudrate" adjust the serial baud rate. This + command lists the currently defined callbacks. + +config CMD_ENV_FLAGS + bool "env flags -print variables that have non-default flags" + help + Some environment variables have special flags that control their + behaviour. For example, serial# can only be written once and cannot + be deleted. This command shows the variables that have special + flags. + +config CMD_NVEDIT_EFI + bool "env [set|print] -e - set/print UEFI variables" + depends on EFI_LOADER + imply HEXDUMP + help + UEFI variables are encoded as some form of U-Boot variables. + If enabled, we are allowed to set/print UEFI variables using + "env" command with "-e" option without knowing details. + +config CMD_NVEDIT_INFO + bool "env info - print or evaluate environment information" + help + Print environment information: + - env_valid : is environment valid + - env_ready : is environment imported into hash table + - env_use_default : is default environment used + + This command can be optionally used for evaluation in scripts: + [-d] : evaluate whether default environment is used + [-p] : evaluate whether environment can be persisted + [-q] : quiet output + The result of multiple evaluations will be combined with AND. + +config CMD_NVEDIT_LOAD + bool "env load" + help + Load all environment variables from the compiled-in persistent + storage. + +config CMD_NVEDIT_SELECT + bool "env select" + help + Select the compiled-in persistent storage of environment variables. + +endmenu + +menu "Memory commands" + +config CMD_BINOP + bool "binop" + help + Compute binary operations (xor, or, and) of byte arrays of arbitrary + size from memory and store the result in memory or the environment. + +config CMD_BLOBLIST + bool "bloblist" + default y if BLOBLIST + help + Show information about the bloblist, a collection of binary blobs + held in memory that persist between SPL and U-Boot. In the case of + x86 devices the bloblist can be used to hold ACPI tables so that they + remain available in memory. + +config CMD_CRC32 + bool "crc32" + default y + select HASH + help + Compute CRC32. + +config CRC32_VERIFY + bool "crc32 -v" + depends on CMD_CRC32 + help + Add -v option to verify data against a crc32 checksum. + +config CMD_EEPROM + bool "eeprom - EEPROM subsystem" + help + (deprecated, needs conversion to driver model) + Provides commands to read and write EEPROM (Electrically Erasable + Programmable Read Only Memory) chips that are connected over an + I2C bus. + +config CMD_EEPROM_LAYOUT + bool "Enable layout-aware eeprom commands" + depends on CMD_EEPROM + help + (deprecated, needs conversion to driver model) + When enabled, additional eeprom sub-commands become available. + + eeprom print - prints the contents of the eeprom in a human-readable + way (eeprom layout fields, and data formatted to be fit for human + consumption). + + eeprom update - allows user to update eeprom fields by specifying + the field name, and providing the new data in a human readable format + (same format as displayed by the eeprom print command). + + Both commands can either auto detect the layout, or be told which + layout to use. + + Feature API: + __weak int parse_layout_version(char *str) + - override to provide your own layout name parsing + __weak void __eeprom_layout_assign(struct eeprom_layout *layout, + int layout_version); + - override to setup the layout metadata based on the version + __weak int eeprom_layout_detect(unsigned char *data) + - override to provide your own algorithm for detecting layout + version + eeprom_field.c + - contains various printing and updating functions for common + types of eeprom fields. Can be used for defining + custom layouts. + +config EEPROM_LAYOUT_HELP_STRING + string "Tells user what layout names are supported" + depends on CMD_EEPROM_LAYOUT + default "<not defined>" + help + Help printed with the LAYOUT VERSIONS part of the 'eeprom' + command's help. + +config LOOPW + bool "loopw" + help + Infinite write loop on address range + +config CMD_MD5SUM + bool "md5sum" + default n + select MD5 + help + Compute MD5 checksum. + +config MD5SUM_VERIFY + bool "md5sum -v" + default n + depends on CMD_MD5SUM + help + Add -v option to verify data against an MD5 checksum. + +config CMD_MEMINFO + bool "meminfo" + help + Display memory information. + +config CMD_MEMORY + bool "md, mm, nm, mw, cp, cmp, base, loop" + default y + help + Memory commands. + md - memory display + mm - memory modify (auto-incrementing address) + nm - memory modify (constant address) + mw - memory write (fill) + cp - memory copy + cmp - memory compare + base - print or set address offset + loop - initialize loop on address range + +config CMD_MEM_SEARCH + bool "ms - Memory search" + help + Memory-search command + + This allows searching through a region of memory looking for hex + data (byte, 16-bit word, 32-bit long, also 64-bit on machines that + support it). It is also possible to search for a string. The + command accepts a memory range and a list of values to search for. + The values need to appear in memory in the same order they are given + in the command. At most 10 matches can be returned at a time, but + pressing return will show the next 10 matches. Environment variables + are set for use with scripting (memmatches, memaddr, mempos). + +config CMD_MX_CYCLIC + bool "Enable cyclic md/mw commands" + depends on CMD_MEMORY + help + Add the "mdc" and "mwc" memory commands. These are cyclic + "md/mw" commands. + Examples: + + => mdc.b 10 4 500 + This command will print 4 bytes (10,11,12,13) each 500 ms. + + => mwc.l 100 12345678 10 + This command will write 12345678 to address 100 all 10 ms. + +config CMD_RANDOM + bool "random" + default y + depends on CMD_MEMORY && (LIB_RAND || LIB_HW_RAND) + help + random - fill memory with random data + +config CMD_MEMTEST + bool "memtest" + help + Simple RAM read/write test. + +if CMD_MEMTEST + +config SYS_ALT_MEMTEST + bool "Alternative test" + help + Use a more complete alternative memory test. + +if SYS_ALT_MEMTEST + +config SYS_ALT_MEMTEST_BITFLIP + bool "Bitflip test" + default y + help + The alternative memory test includes bitflip test since 2020.07. + The bitflip test significantly increases the overall test time. + Bitflip test can optionally be disabled here. + +endif + +config SYS_MEMTEST_START + hex "default start address for mtest" + default 0 + help + This is the default start address for mtest for simple read/write + test. If no arguments are given to mtest, default address is used + as start address. + +config SYS_MEMTEST_END + hex "default end address for mtest" + default 0x1000 + help + This is the default end address for mtest for simple read/write + test. If no arguments are given to mtest, default address is used + as end address. + +endif + +config CMD_SHA1SUM + bool "sha1sum" + select SHA1 + help + Compute SHA1 checksum. + +config SHA1SUM_VERIFY + bool "sha1sum -v" + depends on CMD_SHA1SUM + help + Add -v option to verify data against a SHA1 checksum. + +config CMD_STRINGS + bool "strings - display strings in memory" + help + This works similarly to the Unix 'strings' command except that it + works with a memory range. String of printable characters found + within the range are displayed. The minimum number of characters + for a sequence to be considered a string can be provided. + +endmenu + +menu "Compression commands" + +config CMD_LZMADEC + bool "lzmadec" + default y if CMD_BOOTI + select LZMA + help + Support decompressing an LZMA (Lempel-Ziv-Markov chain algorithm) + image from memory. + +config CMD_UNLZ4 + bool "unlz4" + default y if CMD_BOOTI + select LZ4 + help + Support decompressing an LZ4 image from memory region. + +config CMD_UNZIP + bool "unzip" + default y if CMD_BOOTI + select GZIP + help + Uncompress a zip-compressed memory region. + +config CMD_ZIP + bool "zip" + select GZIP_COMPRESSED + help + Compress a memory region with zlib deflate method. + +endmenu + +menu "Device access commands" + +config CMD_ARMFLASH + #depends on FLASH_CFI_DRIVER + bool "armflash" + help + ARM Ltd reference designs flash partition access + +config CMD_ADC + bool "adc - Access Analog to Digital Converters info and data" + select ADC + depends on DM_REGULATOR + help + Shows ADC device info and permit printing one-shot analog converted + data from a named Analog to Digital Converter. + +config CMD_BCB + bool "bcb" + depends on MMC + depends on PARTITIONS + help + Read/modify/write the fields of Bootloader Control Block, usually + stored on the flash "misc" partition with its structure defined in: + https://android.googlesource.com/platform/bootable/recovery/+/master/ + bootloader_message/include/bootloader_message/bootloader_message.h + + Some real-life use-cases include (but are not limited to): + - Determine the "boot reason" (and act accordingly): + https://source.android.com/devices/bootloader/boot-reason + - Get/pass a list of commands from/to recovery: + https://android.googlesource.com/platform/bootable/recovery + - Inspect/dump the contents of the BCB fields + +config CMD_BIND + bool "bind/unbind - Bind or unbind a device to/from a driver" + depends on DM + help + Bind or unbind a device to/from a driver from the command line. + This is useful in situations where a device may be handled by several + drivers. For example, this can be used to bind a UDC to the usb ether + gadget driver from the command line. + +config CMD_CLK + bool "clk - Show clock frequencies" + help + (deprecated) + Shows clock frequences by calling a sock_clk_dump() hook function. + This is depreated in favour of using the CLK uclass and accessing + clock values from associated drivers. However currently no command + exists for this. + +config CMD_DEMO + bool "demo - Demonstration commands for driver model" + depends on DM + help + Provides a 'demo' command which can be used to play around with + driver model. To use this properly you will need to enable one or + both of the demo devices (DM_DEMO_SHAPE and DM_DEMO_SIMPLE). + Otherwise you will always get an empty list of devices. The demo + devices are defined in the sandbox device tree, so the easiest + option is to use sandbox and pass the -d point to sandbox's + u-boot.dtb file. + +config CMD_DFU + bool "dfu" + select DFU + help + Enables the command "dfu" which is used to have U-Boot create a DFU + class device via USB. This command requires that the "dfu_alt_info" + environment variable be set and define the alt settings to expose to + the host. + +config CMD_DM + bool "dm - Access to driver model information" + depends on DM + help + Provides access to driver model data structures and information, + such as a list of devices, list of uclasses and the state of each + device (e.g. activated). This is not required for operation, but + can be useful to see the state of driver model for debugging or + interest. + +config CMD_FASTBOOT + bool "fastboot - Android fastboot support" + depends on FASTBOOT + help + This enables the command "fastboot" which enables the Android + fastboot mode for the platform. Fastboot is a protocol for + downloading images, flashing and device control used on + Android devices. Fastboot requires either the network stack + enabled or support for acting as a USB device. + + See doc/android/fastboot.rst for more information. + +config CMD_FLASH + bool "flinfo, erase, protect" + default y + depends on MTD || FLASH_CFI_DRIVER || MTD_NOR_FLASH + help + NOR flash support. + flinfo - print FLASH memory information + erase - FLASH memory + protect - enable or disable FLASH write protection + +config CMD_FPGA + bool "fpga" + depends on FPGA + default y + help + FPGA support. + +config CMD_FPGA_LOADBP + bool "fpga loadbp - load partial bitstream (Xilinx only)" + depends on CMD_FPGA + help + Supports loading an FPGA device from a bitstream buffer containing + a partial bitstream. + +config CMD_FPGA_LOADFS + bool "fpga loadfs - load bitstream from FAT filesystem (Xilinx only)" + depends on CMD_FPGA + help + Supports loading an FPGA device from a FAT filesystem. + +config CMD_FPGA_LOADMK + bool "fpga loadmk - load bitstream from image" + depends on CMD_FPGA + help + Supports loading an FPGA device from a image generated by mkimage. + +config CMD_FPGA_LOADP + bool "fpga loadp - load partial bitstream" + depends on CMD_FPGA + help + Supports loading an FPGA device from a bitstream buffer containing + a partial bitstream. + +config CMD_FPGA_LOAD_SECURE + bool "fpga loads - loads secure bitstreams (Xilinx only)" + depends on CMD_FPGA + help + Enables the fpga loads command which is used to load secure + (authenticated or encrypted or both) bitstreams on to FPGA. + +config CMD_FPGAD + bool "fpgad - dump FPGA registers" + help + (legacy, needs conversion to driver model) + Provides a way to dump FPGA registers by calling the board-specific + fpga_get_reg() function. This functions similarly to the 'md' + command. + +config CMD_FUSE + bool "fuse - support for the fuse subssystem" + help + (deprecated - needs conversion to driver model) + This allows reading, sensing, programming or overriding fuses + which control the behaviour of the device. The command uses the + fuse_...() API. + +config CMD_GPIO + bool "gpio" + help + GPIO support. + +config CMD_PWM + bool "pwm" + depends on DM_PWM + help + Control PWM channels, this allows invert/config/enable/disable PWM channels. + +config CMD_GPT + bool "GPT (GUID Partition Table) command" + select EFI_PARTITION + select HAVE_BLOCK_DEVICE + select PARTITION_UUIDS + imply RANDOM_UUID + help + Enable the 'gpt' command to ready and write GPT style partition + tables. + +config RANDOM_UUID + bool "GPT Random UUID generation" + select LIB_UUID + help + Enable the generation of partitions with random UUIDs if none + are provided. + +config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + +config CMD_IDE + bool "ide - Support for IDE drivers" + select IDE + help + Provides an 'ide' command which allows accessing the IDE drive, + reseting the IDE interface, printing the partition table and + geting device info. It also enables the 'diskboot' command which + permits booting from an IDE drive. + +config CMD_IO + bool "io - Support for performing I/O accesses" + help + Provides an 'iod' command to display I/O space and an 'iow' command + to write values to the I/O space. This can be useful for manually + checking the state of devices during boot when debugging device + drivers, etc. + +config CMD_IOTRACE + bool "iotrace - Support for tracing I/O activity" + help + Provides an 'iotrace' command which supports recording I/O reads and + writes in a trace buffer in memory . It also maintains a checksum + of the trace records (even if space is exhausted) so that the + sequence of I/O accesses can be verified. + + When debugging drivers it is useful to see what I/O accesses were + done and in what order. + + Even if the individual accesses are of little interest it can be + useful to verify that the access pattern is consistent each time + an operation is performed. In this case a checksum can be used to + characterise the operation of a driver. The checksum can be compared + across different runs of the operation to verify that the driver is + working properly. + + In particular, when performing major refactoring of the driver, where + the access pattern should not change, the checksum provides assurance + that the refactoring work has not broken the driver. + + This works by sneaking into the io.h heder for an architecture and + redirecting I/O accesses through iotrace's tracing mechanism. + + For now no commands are provided to examine the trace buffer. The + format is fairly simple, so 'md' is a reasonable substitute. + + Note: The checksum feature is only useful for I/O regions where the + contents do not change outside of software control. Where this is not + suitable you can fall back to manually comparing the addresses. It + might be useful to enhance tracing to only checksum the accesses and + not the data read/written. + +config CMD_I2C + bool "i2c" + help + I2C support. + +config CMD_W1 + depends on W1 + default y if W1 + bool "w1 - Support for Dallas 1-Wire protocol" + help + Dallas 1-wire protocol support + +config CMD_LOADB + bool "loadb" + default y + help + Load a binary file over serial line. + +config CMD_LOADS + bool "loads" + default y + help + Load an S-Record file over serial line + +config CMD_LSBLK + depends on BLK + bool "lsblk - list block drivers and devices" + help + Print list of available block device drivers, and for each, the list + of known block devices. + +config CMD_MBR + bool "MBR (Master Boot Record) command" + select DOS_PARTITION + select HAVE_BLOCK_DEVICE + help + Enable the 'mbr' command to ready and write MBR (Master Boot Record) + style partition tables. + +config CMD_MISC + bool "misc" + depends on MISC + help + Enable the command "misc" for accessing miscellaneous devices with + a MISC uclass driver. The command provides listing all MISC devices + as well as read and write functionalities via their drivers. + +config CMD_MMC + bool "mmc" + depends on MMC + help + MMC memory mapped support. + +if CMD_MMC + +config CMD_BKOPS_ENABLE + bool "mmc bkops enable" + depends on CMD_MMC + default n + help + Enable command for setting manual background operations handshake + on a eMMC device. The feature is optionally available on eMMC devices + conforming to standard >= 4.41. + +config CMD_MMC_RPMB + bool "Enable support for RPMB in the mmc command" + depends on SUPPORT_EMMC_RPMB + help + Enable the commands for reading, writing and programming the + key for the Replay Protection Memory Block partition in eMMC. + +config CMD_MMC_SWRITE + bool "mmc swrite" + depends on MMC_WRITE + select IMAGE_SPARSE + help + Enable support for the "mmc swrite" command to write Android sparse + images to eMMC. + +endif + +config CMD_CLONE + bool "clone" + depends on BLK + help + Enable storage cloning over block devices, useful for + initial flashing by external block device without network + or usb support. + +config CMD_OPTEE_RPMB + bool "Enable read/write support on RPMB via OPTEE" + depends on SUPPORT_EMMC_RPMB && OPTEE + help + Enable the commands for reading, writing persistent named values + in the Replay Protection Memory Block partition in eMMC by + using Persistent Objects in OPTEE + +config CMD_MTD + bool "mtd" + depends on MTD + select MTD_PARTITIONS + help + MTD commands support. + +config CMD_MUX + bool "mux" + depends on MULTIPLEXER + help + List, select, and deselect mux controllers on the fly. + +config CMD_NAND + bool "nand" + default y if NAND_SUNXI + depends on MTD_RAW_NAND + help + NAND support. + +if CMD_NAND +config CMD_NAND_TRIMFFS + bool "nand write.trimffs" + default y if ARCH_SUNXI + help + Allows one to skip empty pages when flashing something on a NAND. + +config CMD_NAND_LOCK_UNLOCK + bool "nand lock/unlock" + help + NAND locking support. + +config CMD_NAND_TORTURE + bool "nand torture" + help + NAND torture support. + +endif # CMD_NAND + +config CMD_NVME + bool "nvme" + depends on NVME + default y if NVME + help + NVM Express device support + +config CMD_ONENAND + bool "onenand - access to onenand device" + depends on MTD + help + OneNAND is a brand of NAND ('Not AND' gate) flash which provides + various useful features. This command allows reading, writing, + and erasing blocks. It allso provides a way to show and change + bad blocks, and test the device. + +config CMD_OSD + bool "osd" + help + Enable the 'osd' command which allows to query information from and + write text data to a on-screen display (OSD) device; a virtual device + associated with a display capable of displaying a text overlay on the + display it's associated with.. + +config CMD_PART + bool "part" + select HAVE_BLOCK_DEVICE + select PARTITION_UUIDS + help + Read and display information about the partition table on + various media. + +config CMD_PCI + bool "pci - Access PCI devices" + help + Provide access to PCI (Peripheral Interconnect Bus), a type of bus + used on some devices to allow the CPU to communicate with its + peripherals. Sub-commands allow bus enumeration, displaying and + changing configuration space and a few other features. + +config CMD_PINMUX + bool "pinmux - show pins muxing" + depends on PINCTRL + default y if PINCTRL + help + Parse all available pin-controllers and show pins muxing. This + is useful for debug purpoer to check the pin muxing and to know if + a pin is configured as a GPIO or as an alternate function. + +config CMD_POWEROFF + bool "poweroff" + help + Poweroff/Shutdown the system + +config CMD_READ + bool "read - Read binary data from a partition" + help + Provides low-level access to the data in a partition. + +config CMD_REMOTEPROC + bool "remoteproc" + depends on REMOTEPROC + help + Support for Remote Processor control + +config CMD_SATA + bool "sata - Access SATA subsystem" + select SATA + help + SATA (Serial Advanced Technology Attachment) is a serial bus + standard for connecting to hard drives and other storage devices. + This command provides information about attached devices and allows + reading, writing and other operations. + + SATA replaces PATA (originally just ATA), which stands for Parallel AT + Attachment, where AT refers to an IBM AT (Advanced Technology) + computer released in 1984. + +config CMD_SAVES + bool "saves - Save a file over serial in S-Record format" + help + Provides a way to save a binary file using the Motorola S-Record + format over the serial line. + +config CMD_SCSI + bool "scsi - Access to SCSI devices" + default y if SCSI + help + This provides a 'scsi' command which provides access to SCSI (Small + Computer System Interface) devices. The command provides a way to + scan the bus, reset the bus, read and write data and get information + about devices. + +config CMD_SDRAM + bool "sdram - Print SDRAM configuration information" + help + Provides information about attached SDRAM. This assumed that the + SDRAM has an EEPROM with information that can be read using the + I2C bus. This is only available on some boards. + +config CMD_SF + bool "sf" + depends on DM_SPI_FLASH || SPI_FLASH + default y if DM_SPI_FLASH + help + SPI Flash support + +config CMD_SF_TEST + bool "sf test - Allow testing of SPI flash" + depends on CMD_SF + help + Provides a way to test that SPI flash is working correctly. The + test is destructive, in that an area of SPI flash must be provided + for the test to use. Performance information is also provided, + measuring the performance of reading, writing and erasing in + Mbps (Million Bits Per Second). This value should approximately + equal the SPI bus speed for a single-bit-wide SPI bus, assuming + everything is working properly. + +config CMD_SPI + bool "sspi - Command to access spi device" + depends on SPI + help + SPI utility command. + +config DEFAULT_SPI_BUS + int "default spi bus used by sspi command" + depends on CMD_SPI + default 0 + +config DEFAULT_SPI_MODE + hex "default spi mode used by sspi command (see include/spi.h)" + depends on CMD_SPI + default 0 + +config CMD_TSI148 + bool "tsi148 - Command to access tsi148 device" + help + This provides various sub-commands to initialise and configure the + Turndra tsi148 device. See the command help for full details. + +config CMD_UFS + bool "Enable UFS - Universal Flash Subsystem commands" + depends on UFS + help + "This provides commands to initialise and configure universal flash + subsystem devices" + +config CMD_UNIVERSE + bool "universe - Command to set up the Turndra Universe controller" + help + This allows setting up the VMEbus provided by this controller. + See the command help for full details. + +config CMD_USB + bool "usb" + depends on USB + select HAVE_BLOCK_DEVICE + help + USB support. + +config CMD_USB_SDP + bool "sdp" + select USB_FUNCTION_SDP + help + Enables the command "sdp" which is used to have U-Boot emulating the + Serial Download Protocol (SDP) via USB. + +config CMD_ROCKUSB + bool "rockusb" + depends on USB_FUNCTION_ROCKUSB + help + Rockusb protocol is widely used by Rockchip SoC based devices. It can + read/write info, image to/from devices. This enable rockusb command + support to communication with rockusb device. for more detail about + this command, please read doc/README.rockusb. + +config CMD_USB_MASS_STORAGE + bool "UMS usb mass storage" + select USB_FUNCTION_MASS_STORAGE + depends on BLK && USB_GADGET + help + Enables the command "ums" and the USB mass storage support to the + export a block device: U-Boot, the USB device, acts as a simple + external hard drive plugged on the host USB port. + +config CMD_PVBLOCK + bool "Xen para-virtualized block device" + depends on XEN + select PVBLOCK + help + Xen para-virtualized block device support + +config CMD_VIRTIO + bool "virtio" + depends on VIRTIO + depends on HAVE_BLOCK_DEVICE + default y if VIRTIO + help + VirtIO block device support + +config CMD_WDT + bool "wdt" + depends on WDT + help + This provides commands to control the watchdog timer devices. + +config CMD_AXI + bool "axi" + depends on AXI + help + Enable the command "axi" for accessing AXI (Advanced eXtensible + Interface) busses, a on-chip interconnect specification for managing + functional blocks in SoC designs, which is also often used in designs + involving FPGAs (e.g. communication with IP cores in Xilinx FPGAs). +endmenu + + +menu "Shell scripting commands" + +config CMD_ECHO + bool "echo" + default y + help + Echo args to console + +config CMD_ITEST + bool "itest" + default y + help + Return true/false on integer compare. + +config CMD_SOURCE + bool "source" + default y + help + Run script from memory + +config CMD_SETEXPR + bool "setexpr" + default y + help + Evaluate boolean and math expressions and store the result in an env + variable. + Also supports loading the value at a memory location into a variable. + If CONFIG_REGEX is enabled, setexpr also supports a gsub function. + +endmenu + +menu "Android support commands" + +config CMD_AB_SELECT + bool "ab_select" + default n + depends on ANDROID_AB + help + On Android devices with more than one boot slot (multiple copies of + the kernel and system images) this provides a command to select which + slot should be used to boot from and register the boot attempt. This + is used by the new A/B update model where one slot is updated in the + background while running from the other slot. + +endmenu + +if NET + +menuconfig CMD_NET + bool "Network commands" + default y + imply NETDEVICES + +if CMD_NET + +config CMD_BOOTP + bool "bootp" + default y + help + bootp - boot image via network using BOOTP/TFTP protocol + +config CMD_DHCP + bool "dhcp" + depends on CMD_BOOTP + help + Boot image via network using DHCP/TFTP protocol + +config BOOTP_BOOTPATH + bool "Request & store 'rootpath' from BOOTP/DHCP server" + default y + depends on CMD_BOOTP + help + Even though the config is called BOOTP_BOOTPATH, it stores the + path in the variable 'rootpath'. + +config BOOTP_DNS + bool "Request & store 'dnsip' from BOOTP/DHCP server" + default y + depends on CMD_BOOTP + help + The primary DNS server is stored as 'dnsip'. If two servers are + returned, you must set BOOTP_DNS2 to store that second server IP + also. + +config BOOTP_DNS2 + bool "Store 'dnsip2' from BOOTP/DHCP server" + depends on BOOTP_DNS + help + If a DHCP client requests the DNS server IP from a DHCP server, + it is possible that more than one DNS serverip is offered to the + client. If CONFIG_BOOTP_DNS2 is enabled, the secondary DNS + server IP will be stored in the additional environment + variable "dnsip2". The first DNS serverip is always + stored in the variable "dnsip", when BOOTP_DNS is defined. + +config BOOTP_GATEWAY + bool "Request & store 'gatewayip' from BOOTP/DHCP server" + default y + depends on CMD_BOOTP + +config BOOTP_HOSTNAME + bool "Request & store 'hostname' from BOOTP/DHCP server" + default y + depends on CMD_BOOTP + help + The name may or may not be qualified with the local domain name. + +config BOOTP_PREFER_SERVERIP + bool "serverip variable takes precedent over DHCP server IP." + depends on CMD_BOOTP + help + By default a BOOTP/DHCP reply will overwrite the 'serverip' variable. + + With this option enabled, the 'serverip' variable in the environment + takes precedence over DHCP server IP and will only be set by the DHCP + server if not already set in the environment. + +config BOOTP_SUBNETMASK + bool "Request & store 'netmask' from BOOTP/DHCP server" + default y + depends on CMD_BOOTP + +config BOOTP_NTPSERVER + bool "Request & store 'ntpserverip' from BOOTP/DHCP server" + depends on CMD_BOOTP + +config CMD_PCAP + bool "pcap capture" + help + Selecting this will allow capturing all Ethernet packets and store + them in physical memory in a PCAP formated file, + later to be analyzed by PCAP reader application (IE. WireShark). + +config BOOTP_PXE + bool "Send PXE client arch to BOOTP/DHCP server" + default y + depends on CMD_BOOTP && CMD_PXE + help + Supported for ARM, ARM64, and x86 for now. + +config BOOTP_PXE_CLIENTARCH + hex + depends on BOOTP_PXE + default 0x16 if ARM64 + default 0x15 if ARM + default 0 if X86 + +config BOOTP_VCI_STRING + string + depends on CMD_BOOTP + default "U-Boot.armv7" if CPU_V7A || CPU_V7M || CPU_V7R + default "U-Boot.armv8" if ARM64 + default "U-Boot.arm" if ARM + default "U-Boot" + +config CMD_TFTPBOOT + bool "tftpboot" + default y + help + tftpboot - boot image via network using TFTP protocol + +config CMD_TFTPPUT + bool "tftp put" + depends on CMD_TFTPBOOT + help + TFTP put command, for uploading files to a server + +config CMD_TFTPSRV + bool "tftpsrv" + depends on CMD_TFTPBOOT + help + Act as a TFTP server and boot the first received file + +config NET_TFTP_VARS + bool "Control TFTP timeout and count through environment" + depends on CMD_TFTPBOOT + default y + help + If set, allows controlling the TFTP timeout through the + environment variable tftptimeout, and the TFTP maximum + timeout count through the variable tftptimeoutcountmax. + If unset, timeout and maximum are hard-defined as 1 second + and 10 timouts per TFTP transfer. + +config CMD_RARP + bool "rarpboot" + help + Boot image via network using RARP/TFTP protocol + +config CMD_NFS + bool "nfs" + default y + help + Boot image via network using NFS protocol. + +config CMD_MII + bool "mii" + imply CMD_MDIO + help + If set, allows 802.3(clause 22) MII Management functions interface access + The management interface specified in Clause 22 provides + a simple, two signal, serial interface to connect a + Station Management entity and a managed PHY for providing access + to management parameters and services. + The interface is referred to as the MII management interface. + +config CMD_MDIO + bool "mdio" + depends on PHYLIB + help + If set, allows Enable 802.3(clause 45) MDIO interface registers access + The MDIO interface is orthogonal to the MII interface and extends + it by adding access to more registers through indirect addressing. + +config CMD_PING + bool "ping" + help + Send ICMP ECHO_REQUEST to network host + +config CMD_CDP + bool "cdp" + help + Perform CDP network configuration + +config CMD_SNTP + bool "sntp" + select PROT_UDP + help + Synchronize RTC via network + +config CMD_DNS + bool "dns" + help + Lookup the IP of a hostname + +config CMD_LINK_LOCAL + bool "linklocal" + select LIB_RAND + help + Acquire a network IP address using the link-local protocol + +endif + +config CMD_ETHSW + bool "ethsw" + help + Allow control of L2 Ethernet switch commands. These are supported + by the vsc9953 Ethernet driver at present. Sub-commands allow + operations such as enabling / disabling a port and + viewing/maintaining the filtering database (FDB) + +config CMD_PXE + bool "pxe" + select MENU + help + Boot image via network using PXE protocol + +config CMD_WOL + bool "wol" + help + Wait for wake-on-lan Magic Packet + +endif + +menu "Misc commands" + +config CMD_BMP + bool "Enable 'bmp' command" + depends on LCD || DM_VIDEO || VIDEO + help + This provides a way to obtain information about a BMP-format image + and to display it. BMP (which presumably stands for BitMaP) is a + file format defined by Microsoft which supports images of various + depths, formats and compression methods. Headers on the file + determine the formats used. This command can be used by first loading + the image into RAM, then using this command to look at it or display + it. + +config CMD_BOOTCOUNT + bool "bootcount" + depends on BOOTCOUNT_LIMIT + help + Enable the bootcount command, which allows interrogation and + reset of the bootcounter. + +config CMD_BSP + bool "Enable board-specific commands" + help + (deprecated: instead, please define a Kconfig option for each command) + + Some boards have board-specific commands which are only enabled + during developemnt and need to be turned off for production. This + option provides a way to control this. The commands that are enabled + vary depending on the board. + +config CMD_BLOCK_CACHE + bool "blkcache - control and stats for block cache" + depends on BLOCK_CACHE + default y if BLOCK_CACHE + help + Enable the blkcache command, which can be used to control the + operation of the cache functions. + This is most useful when fine-tuning the operation of the cache + during development, but also allows the cache to be disabled when + it might hurt performance (e.g. when using the ums command). + +config CMD_BUTTON + bool "button" + depends on BUTTON + default y if BUTTON + help + Enable the 'button' command which allows to get the status of + buttons supported by the board. The buttonss can be listed with + 'button list' and state can be known with 'button <label>'. + Any button drivers can be controlled with this command, e.g. + button_gpio. + +config CMD_CACHE + bool "icache or dcache" + help + Enable the "icache" and "dcache" commands + +config CMD_CONITRACE + bool "conitrace - trace console input codes" + help + Enable the 'conitrace' command which displays the codes received + from the console input as hexadecimal numbers. + +config CMD_CLS + bool "Enable clear screen command 'cls'" + depends on CFB_CONSOLE || DM_VIDEO || LCD || VIDEO + default y if LCD + help + Enable the 'cls' command which clears the screen contents + on video frame buffer. + +config CMD_EFIDEBUG + bool "efidebug - display/configure UEFI environment" + depends on EFI_LOADER + select EFI_DEVICE_PATH_TO_TEXT + default n + help + Enable the 'efidebug' command which provides a subset of UEFI + shell utility with simplified functionality. It will be useful + particularly for managing boot parameters as well as examining + various EFI status for debugging. + +config CMD_EXCEPTION + bool "exception - raise exception" + depends on ARM || RISCV || SANDBOX || X86 + help + Enable the 'exception' command which allows to raise an exception. + +config CMD_LED + bool "led" + depends on LED + default y if LED + help + Enable the 'led' command which allows for control of LEDs supported + by the board. The LEDs can be listed with 'led list' and controlled + with led on/off/togle/blink. Any LED drivers can be controlled with + this command, e.g. led_gpio. + +config CMD_DATE + bool "date" + default y if DM_RTC + select LIB_DATE + help + Enable the 'date' command for getting/setting the time/date in RTC + devices. + +config CMD_RTC + bool "rtc" + depends on DM_RTC + help + Enable the 'rtc' command for low-level access to RTC devices. + +config CMD_TIME + bool "time" + help + Run commands and summarize execution time. + +config CMD_GETTIME + bool "gettime - read elapsed time" + help + Enable the 'gettime' command which reads the elapsed time since + U-Boot started running. This shows the time in seconds and + milliseconds. See also the 'bootstage' command which provides more + flexibility for boot timing. + +config CMD_RNG + bool "rng command" + depends on DM_RNG + select HEXDUMP + help + Print bytes from the hardware random number generator. + +config CMD_SLEEP + bool "sleep" + default y + help + Delay execution for some time + +config MP + bool "support for multiprocessor" + help + This provides an option to brinup + different processors in multiprocessor + cases. + +config CMD_TIMER + bool "timer" + help + Access the system timer. + +config CMD_SOUND + bool "sound" + depends on SOUND + help + This provides basic access to the U-Boot's sound support. The main + feature is to play a beep. + + sound init - set up sound system + sound play - play a sound + +config CMD_SYSBOOT + bool "sysboot" + select MENU + help + Boot image via local extlinux.conf file + +config CMD_QFW + bool "qfw" + select QFW + help + This provides access to the QEMU firmware interface. The main + feature is to allow easy loading of files passed to qemu-system + via -kernel / -initrd + +config CMD_PSTORE + bool "pstore" + help + This provides access to Linux PStore with Rammoops backend. The main + feature is to allow to display or save PStore records. + + See doc/pstore.rst for more information. + +if CMD_PSTORE + +config CMD_PSTORE_MEM_ADDR + hex "Memory Address" + depends on CMD_PSTORE + help + Base addr used for PStore ramoops memory, should be identical to + ramoops.mem_address parameter used by kernel + +config CMD_PSTORE_MEM_SIZE + hex "Memory size" + depends on CMD_PSTORE + default "0x10000" + help + Size of PStore ramoops memory, should be identical to ramoops.mem_size + parameter used by kernel, a power of 2 and larger than the sum of the + record sizes + +config CMD_PSTORE_RECORD_SIZE + hex "Dump record size" + depends on CMD_PSTORE + default "0x1000" + help + Size of each dump done on oops/panic, should be identical to + ramoops.record_size parameter used by kernel and a power of 2 + Must be non-zero + +config CMD_PSTORE_CONSOLE_SIZE + hex "Kernel console log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of kernel console log, should be identical to + ramoops.console_size parameter used by kernel and a power of 2 + Must be non-zero + +config CMD_PSTORE_FTRACE_SIZE + hex "FTrace log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of ftrace log, should be identical to ramoops.ftrace_size + parameter used by kernel and a power of 2 + +config CMD_PSTORE_PMSG_SIZE + hex "User space message log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of user space message log, should be identical to + ramoops.pmsg_size parameter used by kernel and a power of 2 + +config CMD_PSTORE_ECC_SIZE + int "ECC size" + depends on CMD_PSTORE + default "0" + help + if non-zero, the option enables ECC support and specifies ECC buffer + size in bytes (1 is a special value, means 16 bytes ECC), should be + identical to ramoops.ramoops_ecc parameter used by kernel + +endif + +source "cmd/mvebu/Kconfig" + +config CMD_TERMINAL + bool "terminal - provides a way to attach a serial terminal" + help + Provides a 'cu'-like serial terminal command. This can be used to + access other serial ports from the system console. The terminal + is very simple with no special processing of characters. As with + cu, you can press ~. (tilde followed by period) to exit. + +config CMD_UUID + bool "uuid, guid - generation of unique IDs" + select LIB_UUID + help + This enables two commands: + + uuid - generate random Universally Unique Identifier + guid - generate Globally Unique Identifier based on random UUID + + The two commands are very similar except for the endianness of the + output. + +endmenu + +source "cmd/ti/Kconfig" + +config CMD_BOOTSTAGE + bool "Enable the 'bootstage' command" + depends on BOOTSTAGE + help + Add a 'bootstage' command which supports printing a report + and un/stashing of bootstage data. + +menu "Power commands" +config CMD_PMIC + bool "Enable Driver Model PMIC command" + depends on DM_PMIC + help + This is the pmic command, based on a driver model pmic's API. + Command features are unchanged: + - list - list pmic devices + - pmic dev <id> - show or [set] operating pmic device (NEW) + - pmic dump - dump registers + - pmic read address - read byte of register at address + - pmic write address - write byte to register at address + The only one change for this command is 'dev' subcommand. + +config CMD_REGULATOR + bool "Enable Driver Model REGULATOR command" + depends on DM_REGULATOR + help + This command is based on driver model regulator's API. + User interface features: + - list - list regulator devices + - regulator dev <id> - show or [set] operating regulator device + - regulator info - print constraints info + - regulator status - print operating status + - regulator value <val] <-f> - print/[set] voltage value [uV] + - regulator current <val> - print/[set] current value [uA] + - regulator mode <id> - print/[set] operating mode id + - regulator enable - enable the regulator output + - regulator disable - disable the regulator output + + The '-f' (force) option can be used for set the value which exceeds + the limits, which are found in device-tree and are kept in regulator's + uclass plat structure. + +endmenu + +menu "Security commands" +config CMD_AES + bool "Enable the 'aes' command" + select AES + help + This provides a means to encrypt and decrypt data using the AES + (Advanced Encryption Standard). This algorithm uses a symetric key + and is widely used as a streaming cipher. Different key lengths are + supported by the algorithm but this command only supports 128 bits + at present. + +config CMD_BLOB + bool "Enable the 'blob' command" + depends on !MX6ULL && !MX6SLL && !MX6SL + select IMX_HAB if ARCH_MX6 || ARCH_MX7 || ARCH_MX7ULP || ARCH_IMX8M + help + This is used with the Freescale secure boot mechanism. + + Freescale's SEC block has built-in Blob Protocol which provides + a method for protecting user-defined data across system power + cycles. SEC block protects data in a data structure called a Blob, + which provides both confidentiality and integrity protection. + + Encapsulating data as a blob + Each time that the Blob Protocol is used to protect data, a + different randomly generated key is used to encrypt the data. + This random key is itself encrypted using a key which is derived + from SoC's non-volatile secret key and a 16 bit Key identifier. + The resulting encrypted key along with encrypted data is called a + blob. The non-volatile secure key is available for use only during + secure boot. + + During decapsulation, the reverse process is performed to get back + the original data. + + Sub-commands: + blob enc - encapsulating data as a cryptgraphic blob + blob dec - decapsulating cryptgraphic blob to get the data + + Syntax: + + blob enc src dst len km + + Encapsulate and create blob of data $len bytes long + at address $src and store the result at address $dst. + $km is the 16 byte key modifier is also required for + generation/use as key for cryptographic operation. Key + modifier should be 16 byte long. + + blob dec src dst len km + + Decapsulate the blob of data at address $src and + store result of $len byte at addr $dst. + $km is the 16 byte key modifier is also required for + generation/use as key for cryptographic operation. Key + modifier should be 16 byte long. + +config CMD_HASH + bool "Support 'hash' command" + select HASH + help + This provides a way to hash data in memory using various supported + algorithms (such as SHA1, MD5, CRC32). The computed digest can be + saved to memory or to an environment variable. It is also possible + to verify a hash against data in memory. + +config CMD_HVC + bool "Support the 'hvc' command" + depends on ARM_SMCCC + help + Allows issuing Hypervisor Calls (HVCs). Mostly useful for + development and testing. + +config CMD_SMC + bool "Support the 'smc' command" + depends on ARM_SMCCC + help + Allows issuing Secure Monitor Calls (SMCs). Mostly useful for + development and testing. + +config HASH_VERIFY + bool "hash -v" + depends on CMD_HASH + help + Add -v option to verify data against a hash. + +config CMD_SCP03 + bool "scp03 - SCP03 enable and rotate/provision operations" + depends on SCP03 + help + This command provides access to a Trusted Application + running in a TEE to request Secure Channel Protocol 03 + (SCP03) enablement and/or rotation of its SCP03 keys. + +config CMD_TPM_V1 + bool + +config CMD_TPM_V2 + bool + +config CMD_TPM + bool "Enable the 'tpm' command" + depends on TPM_V1 || TPM_V2 + select CMD_TPM_V1 if TPM_V1 + select CMD_TPM_V2 if TPM_V2 + help + This provides a means to talk to a TPM from the command line. A wide + range of commands if provided - see 'tpm help' for details. The + command requires a suitable TPM on your board and the correct driver + must be enabled. + +if CMD_TPM + +config CMD_TPM_TEST + bool "Enable the 'tpm test' command" + depends on TPM_V1 + help + This provides a a series of tests to confirm that the TPMv1.x is + working correctly. The tests cover initialisation, non-volatile RAM, + extend, global lock and checking that timing is within expectations. + The tests pass correctly on Infineon TPMs but may need to be adjusted + for other devices. + +endif + +endmenu + +menu "Firmware commands" +config CMD_CROS_EC + bool "Enable crosec command" + depends on CROS_EC + default y + help + Enable command-line access to the Chrome OS EC (Embedded + Controller). This provides the 'crosec' command which has + a number of sub-commands for performing EC tasks such as + updating its flash, accessing a small saved context area + and talking to the I2C bus behind the EC (if there is one). +endmenu + +menu "Filesystem commands" +config CMD_BTRFS + bool "Enable the 'btrsubvol' command" + select FS_BTRFS + help + This enables the 'btrsubvol' command to list subvolumes + of a BTRFS filesystem. There are no special commands for + listing BTRFS directories or loading BTRFS files - this + can be done by the generic 'fs' commands (see CMD_FS_GENERIC) + when BTRFS is enabled (see FS_BTRFS). + +config CMD_CBFS + bool "Enable the 'cbfs' command" + depends on FS_CBFS + help + Define this to enable support for reading from a Coreboot + filesystem. This is a ROM-based filesystem used for accessing files + on systems that use coreboot as the first boot-loader and then load + U-Boot to actually boot the Operating System. Available commands are + cbfsinit, cbfsinfo, cbfsls and cbfsload. + +config CMD_CRAMFS + bool "Enable the 'cramfs' command" + depends on FS_CRAMFS + help + This provides commands for dealing with CRAMFS (Compressed ROM + filesystem). CRAMFS is useful when space is tight since files are + compressed. Two commands are provided: + + cramfsls - lists files in a cramfs image + cramfsload - loads a file from a cramfs image + +config CMD_EXT2 + bool "ext2 command support" + select FS_EXT4 + help + Enables EXT2 FS command + +config CMD_EXT4 + bool "ext4 command support" + select FS_EXT4 + help + Enables EXT4 FS command + +config CMD_EXT4_WRITE + depends on CMD_EXT4 + bool "ext4 write command support" + select EXT4_WRITE + help + Enables EXT4 FS write command + +config CMD_FAT + bool "FAT command support" + select FS_FAT + help + Support for the FAT fs + +config CMD_SQUASHFS + bool "SquashFS command support" + select FS_SQUASHFS + help + Enables SquashFS filesystem commands (e.g. load, ls). + +config CMD_FS_GENERIC + bool "filesystem commands" + help + Enables filesystem commands (e.g. load, ls) that work for multiple + fs types. + +config CMD_FS_UUID + bool "fsuuid command" + help + Enables fsuuid command for filesystem UUID. + +config CMD_JFFS2 + bool "jffs2 command" + select FS_JFFS2 + help + Enables commands to support the JFFS2 (Journalling Flash File System + version 2) filesystem. This enables fsload, ls and fsinfo which + provide the ability to load files, list directories and obtain + filesystem information. + +config CMD_MTDPARTS + bool "MTD partition support" + depends on MTD + help + MTD partitioning tool support. + It is strongly encouraged to avoid using this command + anymore along with 'sf', 'nand', 'onenand'. One can still + declare the partitions in the mtdparts environment variable + but better use the MTD stack and the 'mtd' command instead. + +config CMD_MTDPARTS_SPREAD + bool "Padd partition size to take account of bad blocks" + depends on CMD_MTDPARTS + help + This enables the 'spread' sub-command of the mtdparts command. + This command will modify the existing mtdparts variable by increasing + the size of the partitions such that 1) each partition's net size is + at least as large as the size specified in the mtdparts variable and + 2) each partition starts on a good block. + +config CMD_MTDPARTS_SHOW_NET_SIZES + bool "Show net size (w/o bad blocks) of partitions" + depends on CMD_MTDPARTS + help + Adds two columns to the printed partition table showing the + effective usable size of a partition, if bad blocks are taken + into account. + +config MTDIDS_DEFAULT + string "Default MTD IDs" + depends on MTD || SPI_FLASH + help + Defines a default MTD IDs list for use with MTD partitions in the + Linux MTD command line partitions format. + +config MTDPARTS_DEFAULT + string "Default MTD partition scheme" + depends on MTD || SPI_FLASH + help + Defines a default MTD partitioning scheme in the Linux MTD command + line partitions format + +config CMD_REISER + bool "reiser - Access to reiserfs filesystems" + help + This provides two commands which operate on a resierfs filesystem, + commonly used some years ago: + + reiserls - list files + reiserload - load a file + +config CMD_YAFFS2 + bool "yaffs2 - Access of YAFFS2 filesystem" + depends on YAFFS2 + default y + help + This provides commands for accessing a YAFFS2 filesystem. Yet + Another Flash Filesystem 2 is a filesystem designed specifically + for NAND flash. It incorporates bad-block management and ensures + that device writes are sequential regardless of filesystem + activity. + +config CMD_ZFS + bool "zfs - Access of ZFS filesystem" + help + This provides commands to accessing a ZFS filesystem, commonly used + on Solaris systems. Two sub-commands are provided: + + zfsls - list files in a directory + zfsload - load a file + + See doc/README.zfs for more details. + +endmenu + +menu "Debug commands" + +config CMD_BEDBUG + bool "bedbug" + help + The bedbug (emBEDded deBUGger) command provides debugging features + for some PowerPC processors. For details please see the + documentation in doc/README.bedbug. + +config CMD_CBSYSINFO + bool "cbsysinfo" + depends on X86 + default y if SYS_COREBOOT + help + This provides information about the coreboot sysinfo table stored in + memory by coreboot before jumping to U-Boot. It can be useful for + debugging the beaaviour of coreboot or U-Boot. + +config CMD_DIAG + bool "diag - Board diagnostics" + help + This command provides access to board diagnostic tests. These are + called Power-on Self Tests (POST). The command allows listing of + available tests and running either all the tests, or specific tests + identified by name. + +config CMD_IRQ + bool "irq - Show information about interrupts" + depends on !ARM && !MIPS && !RISCV && !SH + help + This enables two commands: + + interrupts - enable or disable interrupts + irqinfo - print device-specific interrupt information + +config CMD_KGDB + bool "kgdb - Allow debugging of U-Boot with gdb" + depends on PPC + help + This enables a 'kgdb' command which allows gdb to connect to U-Boot + over a serial link for debugging purposes. This allows + single-stepping, inspecting variables, etc. This is supported only + on PowerPC at present. + +config CMD_LOG + bool "log - Generation, control and access to logging" + select LOG + select GETOPT + help + This provides access to logging features. It allows the output of + log data to be controlled to a limited extent (setting up the default + maximum log level for emitting of records). It also provides access + to a command used for testing the log system. + +config CMD_TRACE + bool "trace - Support tracing of function calls and timing" + help + Enables a command to control using of function tracing within + U-Boot. This allows recording of call traces including timing + information. The command can write data to memory for exporting + for analysis (e.g. using bootchart). See doc/README.trace for full + details. + +config CMD_AVB + bool "avb - Android Verified Boot 2.0 operations" + depends on AVB_VERIFY + default n + help + Enables a "avb" command to perform verification of partitions using + Android Verified Boot 2.0 functionality. It includes such subcommands: + avb init - initialize avb2 subsystem + avb read_rb - read rollback index + avb write_rb - write rollback index + avb is_unlocked - check device lock state + avb get_uuid - read and print uuid of a partition + avb read_part - read data from partition + avb read_part_hex - read data from partition and output to stdout + avb write_part - write data to partition + avb verify - run full verification chain + +config CMD_STACKPROTECTOR_TEST + bool "Test command for stack protector" + depends on STACKPROTECTOR + help + Enable stackprot_test command + The stackprot_test command will force a stack overrun to test + the stack smashing detection mechanisms. + +endmenu + +config CMD_UBI + tristate "Enable UBI - Unsorted block images commands" + select MTD_UBI + help + UBI is a software layer above MTD layer which admits use of LVM-like + logical volumes on top of MTD devices, hides some complexities of + flash chips like wear and bad blocks and provides some other useful + capabilities. Please, consult the MTD web site for more details + (www.linux-mtd.infradead.org). Activate this option if you want + to use U-Boot UBI commands. + It is also strongly encouraged to also enable CONFIG_MTD to get full + partition support. + +config CMD_UBI_RENAME + bool "Enable rename" + depends on CMD_UBI + default n + help + Enable a "ubi" command to rename ubi volume: + ubi rename <oldname> <newname> + +config CMD_UBIFS + tristate "Enable UBIFS - Unsorted block images filesystem commands" + depends on CMD_UBI + default y if CMD_UBI + select LZO + help + UBIFS is a file system for flash devices which works on top of UBI. + +endmenu diff --git a/roms/u-boot/cmd/Makefile b/roms/u-boot/cmd/Makefile new file mode 100644 index 000000000..9d10e07f0 --- /dev/null +++ b/roms/u-boot/cmd/Makefile @@ -0,0 +1,255 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2004-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +ifndef CONFIG_SPL_BUILD +# core command +obj-y += boot.o +obj-$(CONFIG_CMD_BOOTM) += bootm.o +obj-y += help.o +obj-y += panic.o +obj-y += version.o + +# command +obj-$(CONFIG_CMD_ACPI) += acpi.o +obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o +obj-$(CONFIG_CMD_AES) += aes.o +obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o +obj-$(CONFIG_CMD_ADC) += adc.o +obj-$(CONFIG_CMD_ARMFLASH) += armflash.o +obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_common.o +obj-$(CONFIG_CMD_SOURCE) += source.o +obj-$(CONFIG_CMD_BCB) += bcb.o +obj-$(CONFIG_CMD_BDI) += bdinfo.o +obj-$(CONFIG_CMD_BEDBUG) += bedbug.o +obj-$(CONFIG_CMD_BIND) += bind.o +obj-$(CONFIG_CMD_BINOP) += binop.o +obj-$(CONFIG_CMD_BLOBLIST) += bloblist.o +obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o +obj-$(CONFIG_CMD_BMP) += bmp.o +obj-$(CONFIG_CMD_BOOTCOUNT) += bootcount.o +obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o +obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o +obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o +obj-$(CONFIG_CMD_BOOTZ) += bootz.o +obj-$(CONFIG_CMD_BOOTI) += booti.o +obj-$(CONFIG_CMD_BTRFS) += btrfs.o +obj-$(CONFIG_CMD_BUTTON) += button.o +obj-$(CONFIG_CMD_CACHE) += cache.o +obj-$(CONFIG_CMD_CBFS) += cbfs.o +obj-$(CONFIG_CMD_CLK) += clk.o +obj-$(CONFIG_CMD_CLS) += cls.o +obj-$(CONFIG_CMD_CONFIG) += config.o +obj-$(CONFIG_CMD_CONITRACE) += conitrace.o +obj-$(CONFIG_CMD_CONSOLE) += console.o +obj-$(CONFIG_CMD_CPU) += cpu.o +obj-$(CONFIG_DATAFLASH_MMC_SELECT) += dataflash_mmc_mux.o +obj-$(CONFIG_CMD_DATE) += date.o +obj-$(CONFIG_CMD_DEMO) += demo.o +obj-$(CONFIG_CMD_DM) += dm.o +obj-$(CONFIG_CMD_SOUND) += sound.o +ifdef CONFIG_POST +obj-$(CONFIG_CMD_DIAG) += diag.o +endif +obj-$(CONFIG_CMD_ADTIMG) += adtimg.o +obj-$(CONFIG_CMD_ABOOTIMG) += abootimg.o +obj-$(CONFIG_CMD_EXTENSION) += extension_board.o +obj-$(CONFIG_CMD_ECHO) += echo.o +obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o +obj-$(CONFIG_CMD_EEPROM) += eeprom.o +obj-$(CONFIG_EFI_STUB) += efi.o +obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o +obj-$(CONFIG_CMD_ELF) += elf.o +obj-$(CONFIG_HUSH_PARSER) += exit.o +obj-$(CONFIG_CMD_EXT4) += ext4.o +obj-$(CONFIG_CMD_EXT2) += ext2.o +obj-$(CONFIG_CMD_FAT) += fat.o +obj-$(CONFIG_CMD_FDT) += fdt.o +obj-$(CONFIG_CMD_SQUASHFS) += sqfs.o +obj-$(CONFIG_CMD_FLASH) += flash.o +obj-$(CONFIG_CMD_FPGA) += fpga.o +obj-$(CONFIG_CMD_FPGAD) += fpgad.o +obj-$(CONFIG_CMD_FS_GENERIC) += fs.o +obj-$(CONFIG_CMD_FUSE) += fuse.o +obj-$(CONFIG_CMD_GETTIME) += gettime.o +obj-$(CONFIG_CMD_GPIO) += gpio.o +obj-$(CONFIG_CMD_HVC) += smccc.o +obj-$(CONFIG_CMD_I2C) += i2c.o +obj-$(CONFIG_CMD_IOTRACE) += iotrace.o +obj-$(CONFIG_CMD_HASH) += hash.o +obj-$(CONFIG_CMD_IDE) += ide.o disk.o +obj-$(CONFIG_CMD_INI) += ini.o +obj-$(CONFIG_CMD_IRQ) += irq.o +obj-$(CONFIG_CMD_ITEST) += itest.o +obj-$(CONFIG_CMD_JFFS2) += jffs2.o +obj-$(CONFIG_CMD_CRAMFS) += cramfs.o +obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o +obj-$(CONFIG_CMD_LED) += led.o +obj-$(CONFIG_CMD_LICENSE) += license.o +obj-y += load.o +obj-$(CONFIG_CMD_LOG) += log.o +obj-$(CONFIG_CMD_LSBLK) += lsblk.o +obj-$(CONFIG_ID_EEPROM) += mac.o +obj-$(CONFIG_CMD_MD5SUM) += md5sum.o +obj-$(CONFIG_CMD_MEMORY) += mem.o +obj-$(CONFIG_CMD_IO) += io.o +obj-$(CONFIG_CMD_MFSL) += mfsl.o +obj-$(CONFIG_CMD_MII) += mii.o +obj-$(CONFIG_CMD_MISC) += misc.o +obj-$(CONFIG_CMD_MDIO) += mdio.o +obj-$(CONFIG_CMD_SLEEP) += sleep.o +obj-$(CONFIG_CMD_MMC) += mmc.o +obj-$(CONFIG_CMD_OPTEE_RPMB) += optee_rpmb.o +obj-$(CONFIG_MP) += mp.o +obj-$(CONFIG_CMD_MTD) += mtd.o +obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o +obj-$(CONFIG_CMD_CLONE) += clone.o +ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),) +obj-y += legacy-mtd-utils.o +endif +obj-$(CONFIG_CMD_MUX) += mux.o +obj-$(CONFIG_CMD_NAND) += nand.o +obj-$(CONFIG_CMD_NET) += net.o +obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o +obj-$(CONFIG_CMD_ONENAND) += onenand.o +obj-$(CONFIG_CMD_OSD) += osd.o +obj-$(CONFIG_CMD_PART) += part.o +obj-$(CONFIG_CMD_PCAP) += pcap.o +ifdef CONFIG_PCI +obj-$(CONFIG_CMD_PCI) += pci.o +endif +obj-$(CONFIG_CMD_PINMUX) += pinmux.o +obj-$(CONFIG_CMD_PMC) += pmc.o +obj-$(CONFIG_CMD_PSTORE) += pstore.o +obj-$(CONFIG_CMD_PWM) += pwm.o +obj-$(CONFIG_CMD_PXE) += pxe.o pxe_utils.o +obj-$(CONFIG_CMD_WOL) += wol.o +obj-$(CONFIG_CMD_QFW) += qfw.o +obj-$(CONFIG_CMD_READ) += read.o +obj-$(CONFIG_CMD_REGINFO) += reginfo.o +obj-$(CONFIG_CMD_REISER) += reiser.o +obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o +obj-$(CONFIG_CMD_RNG) += rng.o +obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o +obj-$(CONFIG_CMD_RTC) += rtc.o +obj-$(CONFIG_SANDBOX) += host.o +obj-$(CONFIG_CMD_SATA) += sata.o +obj-$(CONFIG_CMD_NVME) += nvme.o +obj-$(CONFIG_SANDBOX) += sb.o +obj-$(CONFIG_CMD_SF) += sf.o +obj-$(CONFIG_CMD_SCSI) += scsi.o disk.o +obj-$(CONFIG_CMD_SHA1SUM) += sha1sum.o +obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_CMD_SPI) += spi.o +obj-$(CONFIG_CMD_STRINGS) += strings.o +obj-$(CONFIG_CMD_SMC) += smccc.o +obj-$(CONFIG_CMD_SYSBOOT) += sysboot.o pxe_utils.o +obj-$(CONFIG_CMD_STACKPROTECTOR_TEST) += stackprot_test.o +obj-$(CONFIG_CMD_TERMINAL) += terminal.o +obj-$(CONFIG_CMD_TIME) += time.o +obj-$(CONFIG_CMD_TIMER) += timer.o +obj-$(CONFIG_CMD_TRACE) += trace.o +obj-$(CONFIG_HUSH_PARSER) += test.o +obj-$(CONFIG_CMD_TPM) += tpm-common.o +obj-$(CONFIG_CMD_TPM_V1) += tpm-v1.o +obj-$(CONFIG_CMD_TPM_TEST) += tpm_test.o +obj-$(CONFIG_CMD_TPM_V2) += tpm-v2.o +obj-$(CONFIG_CMD_CROS_EC) += cros_ec.o +obj-$(CONFIG_CMD_TSI148) += tsi148.o +obj-$(CONFIG_CMD_UBI) += ubi.o +obj-$(CONFIG_CMD_UBIFS) += ubifs.o +obj-$(CONFIG_CMD_UNIVERSE) += universe.o +obj-$(CONFIG_CMD_UNLZ4) += unlz4.o +obj-$(CONFIG_CMD_UNZIP) += unzip.o +obj-$(CONFIG_CMD_VIRTIO) += virtio.o +obj-$(CONFIG_CMD_WDT) += wdt.o +obj-$(CONFIG_CMD_LZMADEC) += lzmadec.o +obj-$(CONFIG_CMD_UFS) += ufs.o +obj-$(CONFIG_CMD_USB) += usb.o disk.o +obj-$(CONFIG_CMD_FASTBOOT) += fastboot.o +obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o + +obj-$(CONFIG_CMD_USB_MASS_STORAGE) += usb_mass_storage.o +obj-$(CONFIG_CMD_USB_SDP) += usb_gadget_sdp.o +obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o +obj-$(CONFIG_CMD_XIMG) += ximg.o +obj-$(CONFIG_CMD_YAFFS2) += yaffs2.o +obj-$(CONFIG_CMD_SPL) += spl.o +obj-$(CONFIG_CMD_W1) += w1.o +obj-$(CONFIG_CMD_ZIP) += zip.o +obj-$(CONFIG_CMD_ZFS) += zfs.o + +obj-$(CONFIG_CMD_DFU) += dfu.o +obj-$(CONFIG_CMD_GPT) += gpt.o +obj-$(CONFIG_CMD_MBR) += mbr.o +obj-$(CONFIG_CMD_ETHSW) += ethsw.o +obj-$(CONFIG_CMD_AXI) += axi.o +obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o + +# Power +obj-$(CONFIG_CMD_PMIC) += pmic.o +obj-$(CONFIG_CMD_REGULATOR) += regulator.o + +obj-$(CONFIG_CMD_BLOB) += blob.o + +# Android Verified Boot 2.0 +obj-$(CONFIG_CMD_AVB) += avb.o + +# Foundries.IO SCP03 +obj-$(CONFIG_CMD_SCP03) += scp03.o + +obj-$(CONFIG_ARM) += arm/ +obj-$(CONFIG_RISCV) += riscv/ +obj-$(CONFIG_SANDBOX) += sandbox/ +obj-$(CONFIG_X86) += x86/ + +obj-$(CONFIG_ARCH_MVEBU) += mvebu/ +endif # !CONFIG_SPL_BUILD + +obj-$(CONFIG_$(SPL_)CMD_TLV_EEPROM) += tlv_eeprom.o + +# core command +obj-y += nvedit.o + +obj-$(CONFIG_CMD_BCM_EXT_UTILS) += broadcom/ + +obj-$(CONFIG_TI_COMMON_CMD_OPTIONS) += ti/ + +filechk_data_gz = (echo "static const char data_gz[] ="; cat $< | scripts/bin2c; echo ";") + +filechk_data_size = \ + (echo "static const size_t data_size = "; \ + cat $< | wc -c; echo ";") + +# "config" command +$(obj)/config.o: $(obj)/config_data_gz.h $(obj)/config_data_size.h + +targets += config_data.gz +$(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE + $(call if_changed,gzip) + +targets += config_data_gz.h +$(obj)/config_data_gz.h: $(obj)/config_data.gz FORCE + $(call filechk,data_gz) + +targets += config_data_size.h +$(obj)/config_data_size.h: $(KCONFIG_CONFIG) FORCE + $(call filechk,data_size) + +# "license" command +$(obj)/license.o: $(obj)/license_data_gz.h $(obj)/license_data_size.h + +targets += license_data.gz +$(obj)/license_data.gz: $(srctree)/Licenses/gpl-2.0.txt FORCE + $(call if_changed,gzip) + +targets += license_data_gz.h +$(obj)/license_data_gz.h: $(obj)/license_data.gz FORCE + $(call filechk,data_gz) + +targets += license_data_size.h +$(obj)/license_data_size.h: $(srctree)/Licenses/gpl-2.0.txt FORCE + $(call filechk,data_size) + +CFLAGS_ethsw.o := -Wno-enum-conversion diff --git a/roms/u-boot/cmd/ab_select.c b/roms/u-boot/cmd/ab_select.c new file mode 100644 index 000000000..3e46663d6 --- /dev/null +++ b/roms/u-boot/cmd/ab_select.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2017 The Android Open Source Project + */ + +#include <common.h> +#include <android_ab.h> +#include <command.h> +#include <env.h> +#include <part.h> + +static int do_ab_select(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + struct blk_desc *dev_desc; + struct disk_partition part_info; + char slot[2]; + + if (argc != 4) + return CMD_RET_USAGE; + + /* Lookup the "misc" partition from argv[2] and argv[3] */ + if (part_get_info_by_dev_and_name_or_num(argv[2], argv[3], + &dev_desc, &part_info, + false) < 0) { + return CMD_RET_FAILURE; + } + + ret = ab_select_slot(dev_desc, &part_info); + if (ret < 0) { + printf("Android boot failed, error %d.\n", ret); + return CMD_RET_FAILURE; + } + + /* Android standard slot names are 'a', 'b', ... */ + slot[0] = BOOT_SLOT_NAME(ret); + slot[1] = '\0'; + env_set(argv[1], slot); + printf("ANDROID: Booting slot: %s\n", slot); + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(ab_select, 4, 0, do_ab_select, + "Select the slot used to boot from and register the boot attempt.", + "<slot_var_name> <interface> <dev[:part|#part_name]>\n" + " - Load the slot metadata from the partition 'part' on\n" + " device type 'interface' instance 'dev' and store the active\n" + " slot in the 'slot_var_name' variable. This also updates the\n" + " Android slot metadata with a boot attempt, which can cause\n" + " successive calls to this function to return a different result\n" + " if the returned slot runs out of boot attempts.\n" + " - If 'part_name' is passed, preceded with a # instead of :, the\n" + " partition name whose label is 'part_name' will be looked up in\n" + " the partition table. This is commonly the \"misc\" partition.\n" +); diff --git a/roms/u-boot/cmd/abootimg.c b/roms/u-boot/cmd/abootimg.c new file mode 100644 index 000000000..40e8978f1 --- /dev/null +++ b/roms/u-boot/cmd/abootimg.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Sam Protsenko <joe.skb7@gmail.com> + */ + +#include <android_image.h> +#include <common.h> +#include <command.h> +#include <image.h> +#include <mapmem.h> + +#define abootimg_addr() \ + (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr) + +/* Please use abootimg_addr() macro to obtain the boot image address */ +static ulong _abootimg_addr = -1; + +static int abootimg_get_ver(int argc, char *const argv[]) +{ + const struct andr_img_hdr *hdr; + int res = CMD_RET_SUCCESS; + + if (argc > 1) + return CMD_RET_USAGE; + + hdr = map_sysmem(abootimg_addr(), sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + res = CMD_RET_FAILURE; + goto exit; + } + + if (argc == 0) + printf("%u\n", hdr->header_version); + else + env_set_ulong(argv[0], hdr->header_version); + +exit: + unmap_sysmem(hdr); + return res; +} + +static int abootimg_get_recovery_dtbo(int argc, char *const argv[]) +{ + ulong addr; + u32 size; + + if (argc > 2) + return CMD_RET_USAGE; + + if (!android_image_get_dtbo(abootimg_addr(), &addr, &size)) + return CMD_RET_FAILURE; + + if (argc == 0) { + printf("%lx\n", addr); + } else { + env_set_hex(argv[0], addr); + if (argc == 2) + env_set_hex(argv[1], size); + } + + return CMD_RET_SUCCESS; +} + +static int abootimg_get_dtb_load_addr(int argc, char *const argv[]) +{ + const struct andr_img_hdr *hdr; + int res = CMD_RET_SUCCESS; + + if (argc > 1) + return CMD_RET_USAGE; + + hdr = map_sysmem(abootimg_addr(), sizeof(*hdr)); + if (android_image_check_header(hdr)) { + printf("Error: Boot Image header is incorrect\n"); + res = CMD_RET_FAILURE; + goto exit; + } + + if (hdr->header_version < 2) { + printf("Error: header_version must be >= 2 for this\n"); + res = CMD_RET_FAILURE; + goto exit; + } + + if (argc == 0) + printf("%lx\n", (ulong)hdr->dtb_addr); + else + env_set_hex(argv[0], (ulong)hdr->dtb_addr); + +exit: + unmap_sysmem(hdr); + return res; +} + +static int abootimg_get_dtb_by_index(int argc, char *const argv[]) +{ + const char *index_str; + u32 num; + char *endp; + ulong addr; + u32 size; + + if (argc < 1 || argc > 3) + return CMD_RET_USAGE; + + index_str = argv[0] + strlen("--index="); + if (index_str[0] == '\0') { + printf("Error: Wrong index num\n"); + return CMD_RET_FAILURE; + } + + num = simple_strtoul(index_str, &endp, 0); + if (*endp != '\0') { + printf("Error: Wrong index num\n"); + return CMD_RET_FAILURE; + } + + if (!android_image_get_dtb_by_index(abootimg_addr(), num, + &addr, &size)) { + return CMD_RET_FAILURE; + } + + if (argc == 1) { + printf("%lx\n", addr); + } else { + if (env_set_hex(argv[1], addr)) { + printf("Error: Can't set [addr_var]\n"); + return CMD_RET_FAILURE; + } + + if (argc == 3) { + if (env_set_hex(argv[2], size)) { + printf("Error: Can't set [size_var]\n"); + return CMD_RET_FAILURE; + } + } + } + + return CMD_RET_SUCCESS; +} + +static int abootimg_get_dtb(int argc, char *const argv[]) +{ + if (argc < 1) + return CMD_RET_USAGE; + + if (strstr(argv[0], "--index=")) + return abootimg_get_dtb_by_index(argc, argv); + + return CMD_RET_USAGE; +} + +static int do_abootimg_addr(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *endp; + ulong img_addr; + + if (argc != 2) + return CMD_RET_USAGE; + + img_addr = simple_strtoul(argv[1], &endp, 16); + if (*endp != '\0') { + printf("Error: Wrong image address\n"); + return CMD_RET_FAILURE; + } + + _abootimg_addr = img_addr; + return CMD_RET_SUCCESS; +} + +static int do_abootimg_get(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *param; + + if (argc < 2) + return CMD_RET_USAGE; + + param = argv[1]; + argc -= 2; + argv += 2; + if (!strcmp(param, "ver")) + return abootimg_get_ver(argc, argv); + else if (!strcmp(param, "recovery_dtbo")) + return abootimg_get_recovery_dtbo(argc, argv); + else if (!strcmp(param, "dtb_load_addr")) + return abootimg_get_dtb_load_addr(argc, argv); + else if (!strcmp(param, "dtb")) + return abootimg_get_dtb(argc, argv); + + return CMD_RET_USAGE; +} + +static int do_abootimg_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "dtb")) { + if (android_image_print_dtb_contents(abootimg_addr())) + return CMD_RET_FAILURE; + } else { + return CMD_RET_USAGE; + } + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_abootimg_sub[] = { + U_BOOT_CMD_MKENT(addr, 2, 1, do_abootimg_addr, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 1, do_abootimg_dump, "", ""), + U_BOOT_CMD_MKENT(get, 5, 1, do_abootimg_get, "", ""), +}; + +static int do_abootimg(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + cp = find_cmd_tbl(argv[1], cmd_abootimg_sub, + ARRAY_SIZE(cmd_abootimg_sub)); + + /* Strip off leading 'abootimg' command argument */ + argc--; + argv++; + + if (!cp || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) + return CMD_RET_SUCCESS; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + abootimg, CONFIG_SYS_MAXARGS, 0, do_abootimg, + "manipulate Android Boot Image", + "addr <addr>\n" + " - set the address in RAM where boot image is located\n" + " ($loadaddr is used by default)\n" + "abootimg dump dtb\n" + " - print info for all DT blobs in DTB area\n" + "abootimg get ver [varname]\n" + " - get header version\n" + "abootimg get recovery_dtbo [addr_var [size_var]]\n" + " - get address and size (hex) of recovery DTBO area in the image\n" + " [addr_var]: variable name to contain DTBO area address\n" + " [size_var]: variable name to contain DTBO area size\n" + "abootimg get dtb_load_addr [varname]\n" + " - get load address (hex) of DTB, from image header\n" + "abootimg get dtb --index=<num> [addr_var [size_var]]\n" + " - get address and size (hex) of DT blob in the image by index\n" + " <num>: index number of desired DT blob in DTB area\n" + " [addr_var]: variable name to contain DT blob address\n" + " [size_var]: variable name to contain DT blob size" +); diff --git a/roms/u-boot/cmd/acpi.c b/roms/u-boot/cmd/acpi.c new file mode 100644 index 000000000..e5b9a1752 --- /dev/null +++ b/roms/u-boot/cmd/acpi.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ +#include <common.h> +#include <command.h> +#include <mapmem.h> +#include <acpi/acpi_table.h> +#include <asm/acpi_table.h> +#include <asm/global_data.h> +#include <dm/acpi.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * dump_hdr() - Dump an ACPI header + * + * If the header is for FACS then it shows the revision information as well + * + * @hdr: ACPI header to dump + */ +static void dump_hdr(struct acpi_table_header *hdr) +{ + bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN); + + printf("%.*s %08lx %06x", ACPI_NAME_LEN, hdr->signature, + (ulong)map_to_sysmem(hdr), hdr->length); + if (has_hdr) { + printf(" (v%02d %.6s %.8s %x %.4s %x)\n", hdr->revision, + hdr->oem_id, hdr->oem_table_id, hdr->oem_revision, + hdr->aslc_id, hdr->aslc_revision); + } else { + printf("\n"); + } +} + +/** + * find_table() - Look up an ACPI table + * + * @sig: Signature of table (4 characters, upper case) + * @return pointer to table header, or NULL if not found + */ +struct acpi_table_header *find_table(const char *sig) +{ + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; + int len, i, count; + + rsdp = map_sysmem(gd->arch.acpi_start, 0); + if (!rsdp) + return NULL; + rsdt = map_sysmem(rsdp->rsdt_address, 0); + len = rsdt->header.length - sizeof(rsdt->header); + count = len / sizeof(u32); + for (i = 0; i < count; i++) { + struct acpi_table_header *hdr; + + hdr = map_sysmem(rsdt->entry[i], 0); + if (!memcmp(hdr->signature, sig, ACPI_NAME_LEN)) + return hdr; + if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) { + struct acpi_fadt *fadt = (struct acpi_fadt *)hdr; + + if (!memcmp(sig, "DSDT", ACPI_NAME_LEN) && fadt->dsdt) + return map_sysmem(fadt->dsdt, 0); + if (!memcmp(sig, "FACS", ACPI_NAME_LEN) && + fadt->firmware_ctrl) + return map_sysmem(fadt->firmware_ctrl, 0); + } + } + + return NULL; +} + +static int dump_table_name(const char *sig) +{ + struct acpi_table_header *hdr; + + hdr = find_table(sig); + if (!hdr) + return -ENOENT; + printf("%.*s @ %08lx\n", ACPI_NAME_LEN, hdr->signature, + (ulong)map_to_sysmem(hdr)); + print_buffer(0, hdr, 1, hdr->length, 0); + + return 0; +} + +static void list_fadt(struct acpi_fadt *fadt) +{ + if (fadt->dsdt) + dump_hdr(map_sysmem(fadt->dsdt, 0)); + if (fadt->firmware_ctrl) + dump_hdr(map_sysmem(fadt->firmware_ctrl, 0)); +} + +static int list_rsdt(struct acpi_rsdt *rsdt, struct acpi_xsdt *xsdt) +{ + int len, i, count; + + dump_hdr(&rsdt->header); + if (xsdt) + dump_hdr(&xsdt->header); + len = rsdt->header.length - sizeof(rsdt->header); + count = len / sizeof(u32); + for (i = 0; i < count; i++) { + struct acpi_table_header *hdr; + + if (!rsdt->entry[i]) + break; + hdr = map_sysmem(rsdt->entry[i], 0); + dump_hdr(hdr); + if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) + list_fadt((struct acpi_fadt *)hdr); + if (xsdt) { + if (xsdt->entry[i] != rsdt->entry[i]) { + printf(" (xsdt mismatch %llx)\n", + xsdt->entry[i]); + } + } + } + + return 0; +} + +static int list_rsdp(struct acpi_rsdp *rsdp) +{ + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; + + printf("RSDP %08lx %06x (v%02d %.6s)\n", (ulong)map_to_sysmem(rsdp), + rsdp->length, rsdp->revision, rsdp->oem_id); + rsdt = map_sysmem(rsdp->rsdt_address, 0); + xsdt = map_sysmem(rsdp->xsdt_address, 0); + list_rsdt(rsdt, xsdt); + + return 0; +} + +static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct acpi_rsdp *rsdp; + + rsdp = map_sysmem(gd->arch.acpi_start, 0); + if (!rsdp) { + printf("No ACPI tables present\n"); + return 0; + } + printf("ACPI tables start at %lx\n", gd->arch.acpi_start); + list_rsdp(rsdp); + + return 0; +} + +static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool dump_contents; + + dump_contents = argc >= 2 && !strcmp("-d", argv[1]); + acpi_dump_items(dump_contents ? ACPI_DUMP_CONTENTS : ACPI_DUMP_LIST); + + return 0; +} + +static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *name; + char sig[ACPI_NAME_LEN]; + int ret; + + name = argv[1]; + if (strlen(name) != ACPI_NAME_LEN) { + printf("Table name '%s' must be four characters\n", name); + return CMD_RET_FAILURE; + } + str_to_upper(name, sig, -1); + ret = dump_table_name(sig); + if (ret) { + printf("Table '%.*s' not found\n", ACPI_NAME_LEN, sig); + return CMD_RET_FAILURE; + } + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char acpi_help_text[] = + "list - list ACPI tables\n" + "acpi items [-d] - List/dump each piece of ACPI data from devices\n" + "acpi dump <name> - Dump ACPI table"; +#endif + +U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list), + U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items), + U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump)); diff --git a/roms/u-boot/cmd/adc.c b/roms/u-boot/cmd/adc.c new file mode 100644 index 000000000..16f914a03 --- /dev/null +++ b/roms/u-boot/cmd/adc.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <adc.h> + +static int do_adc_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_ADC, &dev); + if (ret) { + printf("No available ADC device\n"); + return CMD_RET_FAILURE; + } + + do { + printf("- %s\n", dev->name); + + ret = uclass_next_device(&dev); + if (ret) + return CMD_RET_FAILURE; + } while (dev); + + return CMD_RET_SUCCESS; +} + +static int do_adc_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + unsigned int data_mask, ch_mask; + int ret, vss, vdd; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev); + if (ret) { + printf("Unknown ADC device %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + printf("ADC Device '%s' :\n", argv[1]); + + ret = adc_channel_mask(dev, &ch_mask); + if (!ret) + printf("channel mask: %x\n", ch_mask); + + ret = adc_data_mask(dev, &data_mask); + if (!ret) + printf("data mask: %x\n", data_mask); + + ret = adc_vdd_value(dev, &vdd); + if (!ret) + printf("vdd: %duV\n", vdd); + + ret = adc_vss_value(dev, &vss); + if (!ret) + printf("vss: %duV\n", vss); + + return CMD_RET_SUCCESS; +} + +static int do_adc_single(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + unsigned int data; + int ret, uV; + + if (argc < 3) + return CMD_RET_USAGE; + + ret = adc_channel_single_shot(argv[1], simple_strtol(argv[2], NULL, 0), + &data); + if (ret) { + printf("Error getting single shot for device %s channel %s\n", + argv[1], argv[2]); + return CMD_RET_FAILURE; + } + + ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev); + if (!ret && !adc_raw_to_uV(dev, data, &uV)) + printf("%u, %d uV\n", data, uV); + else + printf("%u\n", data); + + return CMD_RET_SUCCESS; +} + +static int do_adc_scan(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct adc_channel ch[ADC_MAX_CHANNEL]; + struct udevice *dev; + unsigned int ch_mask; + int i, chan, ret, uV; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = uclass_get_device_by_name(UCLASS_ADC, argv[1], &dev); + if (ret) { + pr_err("Can't get the ADC %s: %d\n", argv[1], ret); + return CMD_RET_FAILURE; + } + + switch (argc) { + case 3: + ch_mask = simple_strtoul(argv[2], NULL, 0); + if (ch_mask) + break; + case 2: + ret = adc_channel_mask(dev, &ch_mask); + if (ret) { + pr_err("Can't get mask for %s: %d\n", dev->name, ret); + return CMD_RET_FAILURE; + } + break; + } + + ret = adc_channels_single_shot(dev->name, ch_mask, ch); + if (ret) { + pr_err("Can't get single shot for %s (chans mask: 0x%x): %d\n", + dev->name, ch_mask, ret); + return CMD_RET_FAILURE; + } + + for (chan = 0, i = 0; chan < ADC_MAX_CHANNEL; chan++) { + if (!(ch_mask & ADC_CHANNEL(chan))) + continue; + if (!adc_raw_to_uV(dev, ch[i].data, &uV)) + printf("[%02d]: %u, %d uV\n", ch[i].id, ch[i].data, uV); + else + printf("[%02d]: %u\n", ch[i].id, ch[i].data); + i++; + } + + return CMD_RET_SUCCESS; +} + +static char adc_help_text[] = + "list - list ADC devices\n" + "adc info <name> - Get ADC device info\n" + "adc single <name> <channel> - Get Single data of ADC device channel\n" + "adc scan <name> [channel mask] - Scan all [or masked] ADC channels"; + +U_BOOT_CMD_WITH_SUBCMDS(adc, "ADC sub-system", adc_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_adc_list), + U_BOOT_SUBCMD_MKENT(info, 2, 1, do_adc_info), + U_BOOT_SUBCMD_MKENT(single, 3, 1, do_adc_single), + U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_adc_scan)); diff --git a/roms/u-boot/cmd/addrmap.c b/roms/u-boot/cmd/addrmap.c new file mode 100644 index 000000000..bd23549f3 --- /dev/null +++ b/roms/u-boot/cmd/addrmap.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <addr_map.h> + +static int do_addrmap(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + + printf(" vaddr paddr size\n"); + printf("================ ================ ================\n"); + + for (i = 0; i < CONFIG_SYS_NUM_ADDR_MAP; i++) { + if (address_map[i].size == 0) + continue; + + printf("%16.8lx %16.8llx %16.8llx\n", + address_map[i].vaddr, + (unsigned long long)address_map[i].paddr, + (unsigned long long)address_map[i].size); + } + + return 0; +} + +U_BOOT_CMD( + addrmap, 1, 1, do_addrmap, + "List non-identity virtual-physical memory mappings for 32-bit CPUs", + "" +); diff --git a/roms/u-boot/cmd/adtimg.c b/roms/u-boot/cmd/adtimg.c new file mode 100644 index 000000000..aa943007f --- /dev/null +++ b/roms/u-boot/cmd/adtimg.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Linaro Ltd. + * Sam Protsenko <semen.protsenko@linaro.org> + * Eugeniu Rosca <rosca.eugeniu@gmail.com> + */ + +#include <command.h> +#include <env.h> +#include <image-android-dt.h> +#include <common.h> + +#define OPT_INDEX "--index" + +/* + * Current/working DTB/DTBO Android image address. + * Similar to 'working_fdt' variable in 'fdt' command. + */ +static ulong working_img; + +static int do_adtimg_addr(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *endp; + ulong hdr_addr; + + if (argc != 2) + return CMD_RET_USAGE; + + hdr_addr = simple_strtoul(argv[1], &endp, 16); + if (*endp != '\0') { + printf("Error: Wrong image address '%s'\n", argv[1]); + return CMD_RET_FAILURE; + } + + /* + * Allow users to set an address prior to copying the DTB/DTBO + * image to that same address, i.e. skip header verification. + */ + + working_img = hdr_addr; + return CMD_RET_SUCCESS; +} + +static int adtimg_check_working_img(void) +{ + if (!working_img) { + printf("Error: Please, call 'adtimg addr <addr>'. Aborting!\n"); + return CMD_RET_FAILURE; + } + + if (!android_dt_check_header(working_img)) { + printf("Error: Invalid image header at 0x%lx\n", working_img); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_adtimg_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 1) + return CMD_RET_USAGE; + + if (adtimg_check_working_img() != CMD_RET_SUCCESS) + return CMD_RET_FAILURE; + + android_dt_print_contents(working_img); + + return CMD_RET_SUCCESS; +} + +static int adtimg_getopt_u32(char * const opt, char * const name, u32 *optval) +{ + char *endp, *str; + u32 val; + + if (!opt || !name || !optval) + return CMD_RET_FAILURE; + + str = strchr(opt, '='); + if (!str) { + printf("Error: Option '%s' not followed by '='\n", name); + return CMD_RET_FAILURE; + } + + if (*++str == '\0') { + printf("Error: Option '%s=' not followed by value\n", name); + return CMD_RET_FAILURE; + } + + val = simple_strtoul(str, &endp, 0); + if (*endp != '\0') { + printf("Error: Wrong integer value '%s=%s'\n", name, str); + return CMD_RET_FAILURE; + } + + *optval = val; + return CMD_RET_SUCCESS; +} + +static int adtimg_getopt_index(int argc, char *const argv[], u32 *index, + char **avar, char **svar) +{ + int ret; + + if (!argv || !avar || !svar) + return CMD_RET_FAILURE; + + if (argc > 3) { + printf("Error: Unexpected argument '%s'\n", argv[3]); + return CMD_RET_FAILURE; + } + + ret = adtimg_getopt_u32(argv[0], OPT_INDEX, index); + if (ret != CMD_RET_SUCCESS) + return ret; + + if (argc > 1) + *avar = argv[1]; + if (argc > 2) + *svar = argv[2]; + + return CMD_RET_SUCCESS; +} + +static int adtimg_get_dt_by_index(int argc, char *const argv[]) +{ + ulong addr; + u32 index, size; + int ret; + char *avar = NULL, *svar = NULL; + + ret = adtimg_getopt_index(argc, argv, &index, &avar, &svar); + if (ret != CMD_RET_SUCCESS) + return ret; + + if (!android_dt_get_fdt_by_index(working_img, index, &addr, &size)) + return CMD_RET_FAILURE; + + if (avar && svar) { + ret = env_set_hex(avar, addr); + if (ret) { + printf("Error: Can't set '%s' to 0x%lx\n", avar, addr); + return CMD_RET_FAILURE; + } + ret = env_set_hex(svar, size); + if (ret) { + printf("Error: Can't set '%s' to 0x%x\n", svar, size); + return CMD_RET_FAILURE; + } + } else if (avar) { + ret = env_set_hex(avar, addr); + if (ret) { + printf("Error: Can't set '%s' to 0x%lx\n", avar, addr); + return CMD_RET_FAILURE; + } + printf("0x%x (%d)\n", size, size); + } else { + printf("0x%lx, 0x%x (%d)\n", addr, size, size); + } + + return CMD_RET_SUCCESS; +} + +static int adtimg_get_dt(int argc, char *const argv[]) +{ + if (argc < 2) { + printf("Error: No options passed to '%s'\n", argv[0]); + return CMD_RET_FAILURE; + } + + /* Strip off leading 'dt' command argument */ + argc--; + argv++; + + if (!strncmp(argv[0], OPT_INDEX, sizeof(OPT_INDEX) - 1)) + return adtimg_get_dt_by_index(argc, argv); + + printf("Error: Option '%s' not supported\n", argv[0]); + return CMD_RET_FAILURE; +} + +static int do_adtimg_get(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc < 2) { + printf("Error: No arguments passed to '%s'\n", argv[0]); + return CMD_RET_FAILURE; + } + + if (adtimg_check_working_img() != CMD_RET_SUCCESS) + return CMD_RET_FAILURE; + + /* Strip off leading 'get' command argument */ + argc--; + argv++; + + if (!strcmp(argv[0], "dt")) + return adtimg_get_dt(argc, argv); + + printf("Error: Wrong argument '%s'\n", argv[0]); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_adtimg_sub[] = { + U_BOOT_CMD_MKENT(addr, CONFIG_SYS_MAXARGS, 1, do_adtimg_addr, "", ""), + U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_adtimg_dump, "", ""), + U_BOOT_CMD_MKENT(get, CONFIG_SYS_MAXARGS, 1, do_adtimg_get, "", ""), +}; + +static int do_adtimg(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + cp = find_cmd_tbl(argv[1], cmd_adtimg_sub, ARRAY_SIZE(cmd_adtimg_sub)); + + /* Strip off leading 'adtimg' command argument */ + argc--; + argv++; + + if (!cp || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) + return CMD_RET_SUCCESS; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + adtimg, CONFIG_SYS_MAXARGS, 0, do_adtimg, + "manipulate dtb/dtbo Android image", + "addr <addr> - Set image location to <addr>\n" + "adtimg dump - Print out image contents\n" + "adtimg get dt --index=<index> [avar [svar]] - Get DT address/size by index\n" + "\n" + "Legend:\n" + " - <addr>: DTB/DTBO image address (hex) in RAM\n" + " - <index>: index (hex/dec) of desired DT in the image\n" + " - <avar>: variable name to contain DT address (hex)\n" + " - <svar>: variable name to contain DT size (hex)" +); diff --git a/roms/u-boot/cmd/aes.c b/roms/u-boot/cmd/aes.c new file mode 100644 index 000000000..4c0dad993 --- /dev/null +++ b/roms/u-boot/cmd/aes.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Command for en/de-crypting block of memory with AES-[128/192/256]-CBC cipher. + */ + +#include <common.h> +#include <command.h> +#include <uboot_aes.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> +#include <mapmem.h> + +u32 aes_get_key_len(char *command) +{ + u32 key_len = AES128_KEY_LENGTH; + + if (!strcmp(command, "aes.192")) + key_len = AES192_KEY_LENGTH; + else if (!strcmp(command, "aes.256")) + key_len = AES256_KEY_LENGTH; + + return key_len; +} + +/** + * do_aes() - Handle the "aes" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_aes(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + uint32_t key_addr, iv_addr, src_addr, dst_addr, len; + uint8_t *key_ptr, *iv_ptr, *src_ptr, *dst_ptr; + u8 key_exp[AES256_EXPAND_KEY_LENGTH]; + u32 aes_blocks, key_len; + int enc; + + if (argc != 7) + return CMD_RET_USAGE; + + key_len = aes_get_key_len(argv[0]); + + if (!strncmp(argv[1], "enc", 3)) + enc = 1; + else if (!strncmp(argv[1], "dec", 3)) + enc = 0; + else + return CMD_RET_USAGE; + + key_addr = simple_strtoul(argv[2], NULL, 16); + iv_addr = simple_strtoul(argv[3], NULL, 16); + src_addr = simple_strtoul(argv[4], NULL, 16); + dst_addr = simple_strtoul(argv[5], NULL, 16); + len = simple_strtoul(argv[6], NULL, 16); + + key_ptr = (uint8_t *)map_sysmem(key_addr, key_len); + iv_ptr = (uint8_t *)map_sysmem(iv_addr, 128 / 8); + src_ptr = (uint8_t *)map_sysmem(src_addr, len); + dst_ptr = (uint8_t *)map_sysmem(dst_addr, len); + + /* First we expand the key. */ + aes_expand_key(key_ptr, key_len, key_exp); + + /* Calculate the number of AES blocks to encrypt. */ + aes_blocks = DIV_ROUND_UP(len, AES_BLOCK_LENGTH); + + if (enc) + aes_cbc_encrypt_blocks(key_len, key_exp, iv_ptr, src_ptr, + dst_ptr, aes_blocks); + else + aes_cbc_decrypt_blocks(key_len, key_exp, iv_ptr, src_ptr, + dst_ptr, aes_blocks); + + unmap_sysmem(key_ptr); + unmap_sysmem(iv_ptr); + unmap_sysmem(src_ptr); + unmap_sysmem(dst_ptr); + + return 0; +} + +/***************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char aes_help_text[] = + "[.128,.192,.256] enc key iv src dst len - Encrypt block of data $len bytes long\n" + " at address $src using a key at address\n" + " $key with initialization vector at address\n" + " $iv. Store the result at address $dst.\n" + " The $len size must be multiple of 16 bytes.\n" + " The $key and $iv must be 16 bytes long.\n" + "aes [.128,.192,.256] dec key iv src dst len - Decrypt block of data $len bytes long\n" + " at address $src using a key at address\n" + " $key with initialization vector at address\n" + " $iv. Store the result at address $dst.\n" + " The $len size must be multiple of 16 bytes.\n" + " The $key and $iv must be 16 bytes long."; +#endif + +U_BOOT_CMD( + aes, 7, 1, do_aes, + "AES 128/192/256 CBC encryption", + aes_help_text +); diff --git a/roms/u-boot/cmd/arm/Makefile b/roms/u-boot/cmd/arm/Makefile new file mode 100644 index 000000000..94367dcb4 --- /dev/null +++ b/roms/u-boot/cmd/arm/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +ifdef CONFIG_ARM64 +obj-$(CONFIG_CMD_EXCEPTION) += exception64.o +else +obj-$(CONFIG_CMD_EXCEPTION) += exception.o +endif diff --git a/roms/u-boot/cmd/arm/exception.c b/roms/u-boot/cmd/arm/exception.c new file mode 100644 index 000000000..522f6dff5 --- /dev/null +++ b/roms/u-boot/cmd/arm/exception.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_unaligned(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * The LDRD instruction requires the data source to be four byte aligned + * even if strict alignment fault checking is disabled in the system + * control register. + */ + asm volatile ( + "MOV r5, sp\n" + "ADD r5, #1\n" + "LDRD r6, r7, [r5]\n"); + return CMD_RET_FAILURE; +} + +static int do_breakpoint(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile ("BKPT #123\n"); + return CMD_RET_FAILURE; +} + +static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * 0xe7f...f. is undefined in ARM mode + * 0xde.. is undefined in Thumb mode + */ + asm volatile (".word 0xe7f7defb\n"); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_sub[] = { + U_BOOT_CMD_MKENT(breakpoint, CONFIG_SYS_MAXARGS, 1, do_breakpoint, + "", ""), + U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned, + "", ""), + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " breakpoint - prefetch abort\n" + " unaligned - data abort\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/roms/u-boot/cmd/arm/exception64.c b/roms/u-boot/cmd/arm/exception64.c new file mode 100644 index 000000000..d5de50a08 --- /dev/null +++ b/roms/u-boot/cmd/arm/exception64.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * 0xe7f...f. is undefined in ARM mode + * 0xde.. is undefined in Thumb mode + */ + asm volatile (".word 0xe7f7defb\n"); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_sub[] = { + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/roms/u-boot/cmd/armflash.c b/roms/u-boot/cmd/armflash.c new file mode 100644 index 000000000..5e7315eb7 --- /dev/null +++ b/roms/u-boot/cmd/armflash.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Linus Walleij, Linaro + * + * Support for ARM Flash Partitions + */ +#include <common.h> +#include <command.h> +#include <console.h> +#include <flash.h> +#include <asm/io.h> + +#define MAX_REGIONS 4 +#define MAX_IMAGES 32 + +struct afs_region { + u32 load_address; + u32 size; + u32 offset; +}; + +struct afs_image { + flash_info_t *flinfo; + const char *name; + u32 version; + u32 entrypoint; + u32 attributes; + u32 region_count; + struct afs_region regions[MAX_REGIONS]; + ulong flash_mem_start; + ulong flash_mem_end; +}; + +static struct afs_image afs_images[MAX_IMAGES]; +static int num_afs_images; + +static u32 compute_crc(ulong start, u32 len) +{ + u32 sum = 0; + int i; + + if (len % 4 != 0) { + printf("bad checksumming\n"); + return 0; + } + + for (i = 0; i < len; i += 4) { + u32 val; + + val = readl((void *)start + i); + if (val > ~sum) + sum++; + sum += val; + } + return ~sum; +} + +static void parse_bank(ulong bank) +{ + int i; + ulong flstart, flend; + flash_info_t *info; + + info = &flash_info[bank]; + if (info->flash_id != FLASH_MAN_CFI) { + printf("Bank %lu: missing or unknown FLASH type\n", bank); + return; + } + if (!info->sector_count) { + printf("Bank %lu: no FLASH sectors\n", bank); + return; + } + + flstart = info->start[0]; + flend = flstart + info->size; + + for (i = 0; i < info->sector_count; ++i) { + ulong secend; + u32 foot1, foot2; + + if (ctrlc()) + break; + + if (i == info->sector_count-1) + secend = flend; + else + secend = info->start[i+1]; + + /* Check for v1 header */ + foot1 = readl((void *)secend - 0x0c); + if (foot1 == 0xA0FFFF9FU) { + struct afs_image *afi = &afs_images[num_afs_images]; + ulong imginfo; + + afi->flinfo = info; + afi->version = 1; + afi->flash_mem_start = readl((void *)secend - 0x10); + afi->flash_mem_end = readl((void *)secend - 0x14); + afi->attributes = readl((void *)secend - 0x08); + /* Adjust to even address */ + imginfo = afi->flash_mem_end + afi->flash_mem_end % 4; + /* Record as a single region */ + afi->region_count = 1; + afi->regions[0].offset = readl((void *)imginfo + 0x04); + afi->regions[0].load_address = + readl((void *)imginfo + 0x08); + afi->regions[0].size = readl((void *)imginfo + 0x0C); + afi->entrypoint = readl((void *)imginfo + 0x10); + afi->name = (const char *)imginfo + 0x14; + num_afs_images++; + } + + /* Check for v2 header */ + foot1 = readl((void *)secend - 0x04); + foot2 = readl((void *)secend - 0x08); + /* This makes up the string "HSLFTOOF" flash footer */ + if (foot1 == 0x464F4F54U && foot2 == 0x464C5348U) { + struct afs_image *afi = &afs_images[num_afs_images]; + ulong imginfo; + u32 block_start, block_end; + int j; + + afi->flinfo = info; + afi->version = readl((void *)secend - 0x0c); + imginfo = secend - 0x30 - readl((void *)secend - 0x10); + afi->name = (const char *)secend - 0x30; + + afi->entrypoint = readl((void *)imginfo+0x08); + afi->attributes = readl((void *)imginfo+0x0c); + afi->region_count = readl((void *)imginfo+0x10); + block_start = readl((void *)imginfo+0x54); + block_end = readl((void *)imginfo+0x58); + afi->flash_mem_start = afi->flinfo->start[block_start]; + afi->flash_mem_end = afi->flinfo->start[block_end]; + + /* + * Check footer CRC, the algorithm saves the inverse + * checksum as part of the summed words, and thus + * the result should be zero. + */ + if (compute_crc(imginfo + 8, 0x88) != 0) { + printf("BAD CRC on ARM image info\n"); + printf("(continuing anyway)\n"); + } + + /* Parse regions */ + for (j = 0; j < afi->region_count; j++) { + afi->regions[j].load_address = + readl((void *)imginfo+0x14 + j*0x10); + afi->regions[j].size = + readl((void *)imginfo+0x18 + j*0x10); + afi->regions[j].offset = + readl((void *)imginfo+0x1c + j*0x10); + /* + * At offset 0x20 + j*0x10 there is a region + * checksum which seems to be the running + * sum + 3, however since we anyway checksum + * the entire footer this is skipped over for + * checking here. + */ + } + num_afs_images++; + } + } +} + +static void parse_flash(void) +{ + ulong bank; + + /* We have already parsed the images in flash */ + if (num_afs_images > 0) + return; + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) + parse_bank(bank); +} + +static int load_image(const char * const name, const ulong address) +{ + struct afs_image *afi = NULL; + int i; + + parse_flash(); + for (i = 0; i < num_afs_images; i++) { + struct afs_image *tmp = &afs_images[i]; + + if (!strcmp(tmp->name, name)) { + afi = tmp; + break; + } + } + if (!afi) { + printf("image \"%s\" not found in flash\n", name); + return CMD_RET_FAILURE; + } + + for (i = 0; i < afi->region_count; i++) { + ulong from, to; + + from = afi->flash_mem_start + afi->regions[i].offset; + if (address) { + to = address; + } else if (afi->regions[i].load_address) { + to = afi->regions[i].load_address; + } else { + printf("no valid load address\n"); + return CMD_RET_FAILURE; + } + + memcpy((void *)to, (void *)from, afi->regions[i].size); + + printf("loaded region %d from %08lX to %08lX, %08X bytes\n", + i, + from, + to, + afi->regions[i].size); + } + return CMD_RET_SUCCESS; +} + +static void print_images(void) +{ + int i; + + parse_flash(); + for (i = 0; i < num_afs_images; i++) { + struct afs_image *afi = &afs_images[i]; + int j; + + printf("Image: \"%s\" (v%d):\n", afi->name, afi->version); + printf(" Entry point: 0x%08X\n", afi->entrypoint); + printf(" Attributes: 0x%08X: ", afi->attributes); + if (afi->attributes == 0x01) + printf("ARM executable"); + if (afi->attributes == 0x08) + printf("ARM backup"); + printf("\n"); + printf(" Flash mem start: 0x%08lX\n", + afi->flash_mem_start); + printf(" Flash mem end: 0x%08lX\n", + afi->flash_mem_end); + for (j = 0; j < afi->region_count; j++) { + printf(" region %d\n" + " load address: %08X\n" + " size: %08X\n" + " offset: %08X\n", + j, + afi->regions[j].load_address, + afi->regions[j].size, + afi->regions[j].offset); + } + } +} + +static int exists(const char * const name) +{ + int i; + + parse_flash(); + for (i = 0; i < num_afs_images; i++) { + struct afs_image *afi = &afs_images[i]; + + if (strcmp(afi->name, name) == 0) + return CMD_RET_SUCCESS; + } + return CMD_RET_FAILURE; +} + +static int do_afs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = CMD_RET_SUCCESS; + + if (argc == 1) { + print_images(); + } else if (argc == 3 && !strcmp(argv[1], "exists")) { + ret = exists(argv[2]); + } else if (argc == 3 && !strcmp(argv[1], "load")) { + ret = load_image(argv[2], 0x0); + } else if (argc == 4 && !strcmp(argv[1], "load")) { + ulong load_addr; + + load_addr = simple_strtoul(argv[3], NULL, 16); + ret = load_image(argv[2], load_addr); + } else { + return CMD_RET_USAGE; + } + + return ret; +} + +U_BOOT_CMD(afs, 4, 0, do_afs, "show AFS partitions", + "no arguments\n" + " - list images in flash\n" + "exists <image>\n" + " - returns 1 if an image exists, else 0\n" + "load <image>\n" + " - load an image to the location indicated in the header\n" + "load <image> 0x<address>\n" + " - load an image to the location specified\n"); diff --git a/roms/u-boot/cmd/avb.c b/roms/u-boot/cmd/avb.c new file mode 100644 index 000000000..88172a9ee --- /dev/null +++ b/roms/u-boot/cmd/avb.c @@ -0,0 +1,476 @@ + +/* + * (C) Copyright 2018, Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <avb_verify.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <malloc.h> +#include <mmc.h> + +#define AVB_BOOTARGS "avb_bootargs" +static struct AvbOps *avb_ops; + +int do_avb_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long mmc_dev; + + if (argc != 2) + return CMD_RET_USAGE; + + mmc_dev = simple_strtoul(argv[1], NULL, 16); + + if (avb_ops) + avb_ops_free(avb_ops); + + avb_ops = avb_ops_alloc(mmc_dev); + if (avb_ops) + return CMD_RET_SUCCESS; + + printf("Failed to initialize avb2\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_read_part(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *part; + s64 offset; + size_t bytes, bytes_read = 0; + void *buffer; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, please run 'avb init'\n"); + return CMD_RET_USAGE; + } + + if (argc != 5) + return CMD_RET_USAGE; + + part = argv[1]; + offset = simple_strtoul(argv[2], NULL, 16); + bytes = simple_strtoul(argv[3], NULL, 16); + buffer = (void *)simple_strtoul(argv[4], NULL, 16); + + if (avb_ops->read_from_partition(avb_ops, part, offset, bytes, + buffer, &bytes_read) == + AVB_IO_RESULT_OK) { + printf("Read %zu bytes\n", bytes_read); + return CMD_RET_SUCCESS; + } + + printf("Failed to read from partition\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_read_part_hex(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *part; + s64 offset; + size_t bytes, bytes_read = 0; + char *buffer; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, please run 'avb init'\n"); + return CMD_RET_USAGE; + } + + if (argc != 4) + return CMD_RET_USAGE; + + part = argv[1]; + offset = simple_strtoul(argv[2], NULL, 16); + bytes = simple_strtoul(argv[3], NULL, 16); + + buffer = malloc(bytes); + if (!buffer) { + printf("Failed to tlb_allocate buffer for data\n"); + return CMD_RET_FAILURE; + } + memset(buffer, 0, bytes); + + if (avb_ops->read_from_partition(avb_ops, part, offset, bytes, buffer, + &bytes_read) == AVB_IO_RESULT_OK) { + printf("Requested %zu, read %zu bytes\n", bytes, bytes_read); + printf("Data: "); + for (int i = 0; i < bytes_read; i++) + printf("%02X", buffer[i]); + + printf("\n"); + + free(buffer); + return CMD_RET_SUCCESS; + } + + printf("Failed to read from partition\n"); + + free(buffer); + return CMD_RET_FAILURE; +} + +int do_avb_write_part(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *part; + s64 offset; + size_t bytes; + void *buffer; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 5) + return CMD_RET_USAGE; + + part = argv[1]; + offset = simple_strtoul(argv[2], NULL, 16); + bytes = simple_strtoul(argv[3], NULL, 16); + buffer = (void *)simple_strtoul(argv[4], NULL, 16); + + if (avb_ops->write_to_partition(avb_ops, part, offset, bytes, buffer) == + AVB_IO_RESULT_OK) { + printf("Wrote %zu bytes\n", bytes); + return CMD_RET_SUCCESS; + } + + printf("Failed to write in partition\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_read_rb(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t index; + u64 rb_idx; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 2) + return CMD_RET_USAGE; + + index = (size_t)simple_strtoul(argv[1], NULL, 16); + + if (avb_ops->read_rollback_index(avb_ops, index, &rb_idx) == + AVB_IO_RESULT_OK) { + printf("Rollback index: %llx\n", rb_idx); + return CMD_RET_SUCCESS; + } + + printf("Failed to read rollback index\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_write_rb(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t index; + u64 rb_idx; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 3) + return CMD_RET_USAGE; + + index = (size_t)simple_strtoul(argv[1], NULL, 16); + rb_idx = simple_strtoul(argv[2], NULL, 16); + + if (avb_ops->write_rollback_index(avb_ops, index, rb_idx) == + AVB_IO_RESULT_OK) + return CMD_RET_SUCCESS; + + printf("Failed to write rollback index\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_get_uuid(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + const char *part; + char buffer[UUID_STR_LEN + 1]; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 2) + return CMD_RET_USAGE; + + part = argv[1]; + + if (avb_ops->get_unique_guid_for_partition(avb_ops, part, buffer, + UUID_STR_LEN + 1) == + AVB_IO_RESULT_OK) { + printf("'%s' UUID: %s\n", part, buffer); + return CMD_RET_SUCCESS; + } + + printf("Failed to read UUID\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_verify_part(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + const char * const requested_partitions[] = {"boot", NULL}; + AvbSlotVerifyResult slot_result; + AvbSlotVerifyData *out_data; + char *cmdline; + char *extra_args; + char *slot_suffix = ""; + + bool unlocked = false; + int res = CMD_RET_FAILURE; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc < 1 || argc > 2) + return CMD_RET_USAGE; + + if (argc == 2) + slot_suffix = argv[1]; + + printf("## Android Verified Boot 2.0 version %s\n", + avb_version_string()); + + if (avb_ops->read_is_device_unlocked(avb_ops, &unlocked) != + AVB_IO_RESULT_OK) { + printf("Can't determine device lock state.\n"); + return CMD_RET_FAILURE; + } + + slot_result = + avb_slot_verify(avb_ops, + requested_partitions, + slot_suffix, + unlocked, + AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, + &out_data); + + switch (slot_result) { + case AVB_SLOT_VERIFY_RESULT_OK: + /* Until we don't have support of changing unlock states, we + * assume that we are by default in locked state. + * So in this case we can boot only when verification is + * successful; we also supply in cmdline GREEN boot state + */ + printf("Verification passed successfully\n"); + + /* export additional bootargs to AVB_BOOTARGS env var */ + + extra_args = avb_set_state(avb_ops, AVB_GREEN); + if (extra_args) + cmdline = append_cmd_line(out_data->cmdline, + extra_args); + else + cmdline = out_data->cmdline; + + env_set(AVB_BOOTARGS, cmdline); + + res = CMD_RET_SUCCESS; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + printf("Verification failed\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_IO: + printf("I/O error occurred during verification\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: + printf("OOM error occurred during verification\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: + printf("Corrupted dm-verity metadata detected\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + printf("Unsupported version avbtool was used\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + printf("Checking rollback index failed\n"); + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + printf("Public key was rejected\n"); + break; + default: + printf("Unknown error occurred\n"); + } + + if (out_data) + avb_slot_verify_data_free(out_data); + + return res; +} + +int do_avb_is_unlocked(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + bool unlock; + + if (!avb_ops) { + printf("AVB not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 1) { + printf("--%s(-1)\n", __func__); + return CMD_RET_USAGE; + } + + if (avb_ops->read_is_device_unlocked(avb_ops, &unlock) == + AVB_IO_RESULT_OK) { + printf("Unlocked = %d\n", unlock); + return CMD_RET_SUCCESS; + } + + printf("Can't determine device lock state.\n"); + + return CMD_RET_FAILURE; +} + +int do_avb_read_pvalue(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *name; + size_t bytes; + size_t bytes_read; + void *buffer; + char *endp; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 3) + return CMD_RET_USAGE; + + name = argv[1]; + bytes = simple_strtoul(argv[2], &endp, 10); + if (*endp && *endp != '\n') + return CMD_RET_USAGE; + + buffer = malloc(bytes); + if (!buffer) + return CMD_RET_FAILURE; + + if (avb_ops->read_persistent_value(avb_ops, name, bytes, buffer, + &bytes_read) == AVB_IO_RESULT_OK) { + printf("Read %zu bytes, value = %s\n", bytes_read, + (char *)buffer); + free(buffer); + return CMD_RET_SUCCESS; + } + + printf("Failed to read persistent value\n"); + + free(buffer); + + return CMD_RET_FAILURE; +} + +int do_avb_write_pvalue(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *name; + const char *value; + + if (!avb_ops) { + printf("AVB 2.0 is not initialized, run 'avb init' first\n"); + return CMD_RET_FAILURE; + } + + if (argc != 3) + return CMD_RET_USAGE; + + name = argv[1]; + value = argv[2]; + + if (avb_ops->write_persistent_value(avb_ops, name, strlen(value) + 1, + (const uint8_t *)value) == + AVB_IO_RESULT_OK) { + printf("Wrote %zu bytes\n", strlen(value) + 1); + return CMD_RET_SUCCESS; + } + + printf("Failed to write persistent value\n"); + + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_avb[] = { + U_BOOT_CMD_MKENT(init, 2, 0, do_avb_init, "", ""), + U_BOOT_CMD_MKENT(read_rb, 2, 0, do_avb_read_rb, "", ""), + U_BOOT_CMD_MKENT(write_rb, 3, 0, do_avb_write_rb, "", ""), + U_BOOT_CMD_MKENT(is_unlocked, 1, 0, do_avb_is_unlocked, "", ""), + U_BOOT_CMD_MKENT(get_uuid, 2, 0, do_avb_get_uuid, "", ""), + U_BOOT_CMD_MKENT(read_part, 5, 0, do_avb_read_part, "", ""), + U_BOOT_CMD_MKENT(read_part_hex, 4, 0, do_avb_read_part_hex, "", ""), + U_BOOT_CMD_MKENT(write_part, 5, 0, do_avb_write_part, "", ""), + U_BOOT_CMD_MKENT(verify, 2, 0, do_avb_verify_part, "", ""), +#ifdef CONFIG_OPTEE_TA_AVB + U_BOOT_CMD_MKENT(read_pvalue, 3, 0, do_avb_read_pvalue, "", ""), + U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_avb_write_pvalue, "", ""), +#endif +}; + +static int do_avb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + + cp = find_cmd_tbl(argv[1], cmd_avb, ARRAY_SIZE(cmd_avb)); + + argc--; + argv++; + + if (!cp || argc > cp->maxargs) + return CMD_RET_USAGE; + + if (flag == CMD_FLAG_REPEAT) + return CMD_RET_FAILURE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + avb, 29, 0, do_avb, + "Provides commands for testing Android Verified Boot 2.0 functionality", + "init <dev> - initialize avb2 for <dev>\n" + "avb read_rb <num> - read rollback index at location <num>\n" + "avb write_rb <num> <rb> - write rollback index <rb> to <num>\n" + "avb is_unlocked - returns unlock status of the device\n" + "avb get_uuid <partname> - read and print uuid of partition <part>\n" + "avb read_part <partname> <offset> <num> <addr> - read <num> bytes from\n" + " partition <partname> to buffer <addr>\n" + "avb read_part_hex <partname> <offset> <num> - read <num> bytes from\n" + " partition <partname> and print to stdout\n" + "avb write_part <partname> <offset> <num> <addr> - write <num> bytes to\n" + " <partname> by <offset> using data from <addr>\n" +#ifdef CONFIG_OPTEE_TA_AVB + "avb read_pvalue <name> <bytes> - read a persistent value <name>\n" + "avb write_pvalue <name> <value> - write a persistent value <name>\n" +#endif + "avb verify [slot_suffix] - run verification process using hash data\n" + " from vbmeta structure\n" + " [slot_suffix] - _a, _b, etc (if vbmeta partition is slotted)\n" + ); diff --git a/roms/u-boot/cmd/axi.c b/roms/u-boot/cmd/axi.c new file mode 100644 index 000000000..c72197ee8 --- /dev/null +++ b/roms/u-boot/cmd/axi.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * (C) Copyright 2017, 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <axi.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <log.h> + +/* Currently selected AXI bus device */ +static struct udevice *axi_cur_bus; +/* Transmission size from last command */ +static uint dp_last_size; +/* Address from last command */ +static uint dp_last_addr; +/* Number of bytes to display from last command; default = 64 */ +static uint dp_last_length = 0x40; + +/** + * show_bus() - Show devices on a single AXI bus + * @bus: The AXI bus device to printt information for + */ +static void show_bus(struct udevice *bus) +{ + struct udevice *dev; + + printf("Bus %d:\t%s", dev_seq(bus), bus->name); + if (device_active(bus)) + printf(" (active)"); + printf("\n"); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) + printf(" %s\n", dev->name); +} + +/** + * axi_set_cur_bus() - Set the currently active AXI bus + * @busnum: The number of the bus (i.e. its sequence number) that should be + * made active + * + * The operations supplied by this command operate only on the currently active + * bus. + * + * Return: 0 if OK, -ve on error + */ +static int axi_set_cur_bus(unsigned int busnum) +{ + struct udevice *bus; + struct udevice *dummy; + int ret; + + /* Make sure that all sequence numbers are initialized */ + for (uclass_first_device(UCLASS_AXI, &dummy); + dummy; + uclass_next_device(&dummy)) + ; + + ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus); + if (ret) { + debug("%s: No bus %d\n", __func__, busnum); + return ret; + } + axi_cur_bus = bus; + + return 0; +} + +/** + * axi_get_cur_bus() - Retrieve the currently active AXI bus device + * @busp: Pointer to a struct udevice that receives the currently active bus + * device + * + * Return: 0 if OK, -ve on error + */ +static int axi_get_cur_bus(struct udevice **busp) +{ + if (!axi_cur_bus) { + puts("No AXI bus selected\n"); + return -ENODEV; + } + *busp = axi_cur_bus; + + return 0; +} + +/* + * Command handlers + */ + +static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dummy; + + /* Make sure that all sequence numbers are initialized */ + for (uclass_first_device(UCLASS_AXI, &dummy); + dummy; + uclass_next_device(&dummy)) + ; + + if (argc == 1) { + /* show all busses */ + struct udevice *bus; + + for (uclass_first_device(UCLASS_AXI, &bus); + bus; + uclass_next_device(&bus)) + show_bus(bus); + } else { + int i; + + /* show specific bus */ + i = simple_strtoul(argv[1], NULL, 10); + + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus); + if (ret) { + printf("Invalid bus %d: err=%d\n", i, ret); + return CMD_RET_FAILURE; + } + show_bus(bus); + } + + return 0; +} + +static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0; + int bus_no; + + if (argc == 1) { + /* querying current setting */ + struct udevice *bus; + + if (!axi_get_cur_bus(&bus)) + bus_no = dev_seq(bus); + else + bus_no = -1; + + printf("Current bus is %d\n", bus_no); + } else { + bus_no = simple_strtoul(argv[1], NULL, 10); + printf("Setting bus to %d\n", bus_no); + + ret = axi_set_cur_bus(bus_no); + if (ret) + printf("Failure changing bus number (%d)\n", ret); + } + + return ret ? CMD_RET_FAILURE : 0; +} + +static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* Print that many bytes per line */ + const uint DISP_LINE_LEN = 16; + u8 linebuf[DISP_LINE_LEN]; + unsigned int k; + ulong addr, length, size; + ulong nbytes; + enum axi_size_t axisize; + int unitsize; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + size = dp_last_size; + addr = dp_last_addr; + length = dp_last_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if (!axi_cur_bus) { + puts("No AXI bus selected\n"); + return CMD_RET_FAILURE; + } + + if ((flag & CMD_FLAG_REPEAT) == 0) { + size = simple_strtoul(argv[1], NULL, 10); + + /* + * Address is specified since argc >= 3 + */ + addr = simple_strtoul(argv[2], NULL, 16); + + /* + * If there's another parameter, it is the length to display; + * length is the number of objects, not number of bytes + */ + if (argc > 3) + length = simple_strtoul(argv[3], NULL, 16); + } + + switch (size) { + case 8: + axisize = AXI_SIZE_8; + unitsize = 1; + break; + case 16: + axisize = AXI_SIZE_16; + unitsize = 2; + break; + case 32: + axisize = AXI_SIZE_32; + unitsize = 4; + break; + default: + printf("Unknown read size '%lu'\n", size); + return CMD_RET_USAGE; + }; + + nbytes = length * unitsize; + do { + ulong linebytes = (nbytes > DISP_LINE_LEN) ? + DISP_LINE_LEN : nbytes; + + for (k = 0; k < linebytes / unitsize; ++k) { + int ret = axi_read(axi_cur_bus, addr + k * unitsize, + linebuf + k * unitsize, axisize); + + if (!ret) /* Continue if axi_read was successful */ + continue; + + if (ret == -ENOSYS) + printf("axi_read failed; read size not supported?\n"); + else + printf("axi_read failed: err = %d\n", ret); + + return CMD_RET_FAILURE; + } + print_buffer(addr, (void *)linebuf, unitsize, + linebytes / unitsize, + DISP_LINE_LEN / unitsize); + + nbytes -= max(linebytes, 1UL); + addr += linebytes; + + if (ctrlc()) + break; + } while (nbytes > 0); + + dp_last_size = size; + dp_last_addr = addr; + dp_last_length = length; + + return 0; +} + +static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 writeval; + ulong addr, count, size; + enum axi_size_t axisize; + + if (argc <= 3 || argc >= 6) + return CMD_RET_USAGE; + + size = simple_strtoul(argv[1], NULL, 10); + + switch (size) { + case 8: + axisize = AXI_SIZE_8; + break; + case 16: + axisize = AXI_SIZE_16; + break; + case 32: + axisize = AXI_SIZE_32; + break; + default: + printf("Unknown write size '%lu'\n", size); + return CMD_RET_USAGE; + }; + + /* Address is specified since argc > 4 */ + addr = simple_strtoul(argv[2], NULL, 16); + + /* Get the value to write */ + writeval = simple_strtoul(argv[3], NULL, 16); + + /* Count ? */ + if (argc == 5) + count = simple_strtoul(argv[4], NULL, 16); + else + count = 1; + + while (count-- > 0) { + int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32), + &writeval, axisize); + + if (ret) { + printf("axi_write failed: err = %d\n", ret); + return CMD_RET_FAILURE; + } + } + + return 0; +} + +static struct cmd_tbl cmd_axi_sub[] = { + U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""), + U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""), + U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""), + U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""), +}; + +static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'axi' command argument */ + argc--; + argv++; + + /* Hand off rest of command line to sub-commands */ + c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +static char axi_help_text[] = + "bus - show AXI bus info\n" + "axi dev [bus] - show or set current AXI bus to bus number [bus]\n" + "axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n" + "axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"; + +U_BOOT_CMD(axi, 7, 1, do_ihs_axi, + "AXI sub-system", + axi_help_text +); diff --git a/roms/u-boot/cmd/bcb.c b/roms/u-boot/cmd/bcb.c new file mode 100644 index 000000000..6b6f1e9a2 --- /dev/null +++ b/roms/u-boot/cmd/bcb.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Eugeniu Rosca <rosca.eugeniu@gmail.com> + * + * Command to read/modify/write Android BCB fields + */ + +#include <android_bootloader_message.h> +#include <bcb.h> +#include <command.h> +#include <common.h> +#include <log.h> +#include <part.h> +#include <malloc.h> + +enum bcb_cmd { + BCB_CMD_LOAD, + BCB_CMD_FIELD_SET, + BCB_CMD_FIELD_CLEAR, + BCB_CMD_FIELD_TEST, + BCB_CMD_FIELD_DUMP, + BCB_CMD_STORE, +}; + +static int bcb_dev = -1; +static int bcb_part = -1; +static struct bootloader_message bcb = { { 0 } }; + +static int bcb_cmd_get(char *cmd) +{ + if (!strcmp(cmd, "load")) + return BCB_CMD_LOAD; + if (!strcmp(cmd, "set")) + return BCB_CMD_FIELD_SET; + if (!strcmp(cmd, "clear")) + return BCB_CMD_FIELD_CLEAR; + if (!strcmp(cmd, "test")) + return BCB_CMD_FIELD_TEST; + if (!strcmp(cmd, "store")) + return BCB_CMD_STORE; + if (!strcmp(cmd, "dump")) + return BCB_CMD_FIELD_DUMP; + else + return -1; +} + +static int bcb_is_misused(int argc, char *const argv[]) +{ + int cmd = bcb_cmd_get(argv[0]); + + switch (cmd) { + case BCB_CMD_LOAD: + case BCB_CMD_FIELD_SET: + if (argc != 3) + goto err; + break; + case BCB_CMD_FIELD_TEST: + if (argc != 4) + goto err; + break; + case BCB_CMD_FIELD_CLEAR: + if (argc != 1 && argc != 2) + goto err; + break; + case BCB_CMD_STORE: + if (argc != 1) + goto err; + break; + case BCB_CMD_FIELD_DUMP: + if (argc != 2) + goto err; + break; + default: + printf("Error: 'bcb %s' not supported\n", argv[0]); + return -1; + } + + if (cmd != BCB_CMD_LOAD && (bcb_dev < 0 || bcb_part < 0)) { + printf("Error: Please, load BCB first!\n"); + return -1; + } + + return 0; +err: + printf("Error: Bad usage of 'bcb %s'\n", argv[0]); + + return -1; +} + +static int bcb_field_get(char *name, char **fieldp, int *sizep) +{ + if (!strcmp(name, "command")) { + *fieldp = bcb.command; + *sizep = sizeof(bcb.command); + } else if (!strcmp(name, "status")) { + *fieldp = bcb.status; + *sizep = sizeof(bcb.status); + } else if (!strcmp(name, "recovery")) { + *fieldp = bcb.recovery; + *sizep = sizeof(bcb.recovery); + } else if (!strcmp(name, "stage")) { + *fieldp = bcb.stage; + *sizep = sizeof(bcb.stage); + } else if (!strcmp(name, "reserved")) { + *fieldp = bcb.reserved; + *sizep = sizeof(bcb.reserved); + } else { + printf("Error: Unknown bcb field '%s'\n", name); + return -1; + } + + return 0; +} + +static int __bcb_load(int devnum, const char *partp) +{ + struct blk_desc *desc; + struct disk_partition info; + u64 cnt; + char *endp; + int part, ret; + + desc = blk_get_devnum_by_type(IF_TYPE_MMC, devnum); + if (!desc) { + ret = -ENODEV; + goto err_read_fail; + } + + part = simple_strtoul(partp, &endp, 0); + if (*endp == '\0') { + ret = part_get_info(desc, part, &info); + if (ret) + goto err_read_fail; + } else { + part = part_get_info_by_name(desc, partp, &info); + if (part < 0) { + ret = part; + goto err_read_fail; + } + } + + cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); + if (cnt > info.size) + goto err_too_small; + + if (blk_dread(desc, info.start, cnt, &bcb) != cnt) { + ret = -EIO; + goto err_read_fail; + } + + bcb_dev = desc->devnum; + bcb_part = part; + debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part); + + return CMD_RET_SUCCESS; +err_read_fail: + printf("Error: mmc %d:%s read failed (%d)\n", devnum, partp, ret); + goto err; +err_too_small: + printf("Error: mmc %d:%s too small!", devnum, partp); + goto err; +err: + bcb_dev = -1; + bcb_part = -1; + + return CMD_RET_FAILURE; +} + +static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + char *endp; + int devnum = simple_strtoul(argv[1], &endp, 0); + + if (*endp != '\0') { + printf("Error: Device id '%s' not a number\n", argv[1]); + return CMD_RET_FAILURE; + } + + return __bcb_load(devnum, argv[2]); +} + +static int __bcb_set(char *fieldp, const char *valp) +{ + int size, len; + char *field, *str, *found, *tmp; + + if (bcb_field_get(fieldp, &field, &size)) + return CMD_RET_FAILURE; + + len = strlen(valp); + if (len >= size) { + printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n", + valp, len, size, fieldp); + return CMD_RET_FAILURE; + } + str = strdup(valp); + if (!str) { + printf("Error: Out of memory while strdup\n"); + return CMD_RET_FAILURE; + } + + tmp = str; + field[0] = '\0'; + while ((found = strsep(&tmp, ":"))) { + if (field[0] != '\0') + strcat(field, "\n"); + strcat(field, found); + } + free(str); + + return CMD_RET_SUCCESS; +} + +static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + return __bcb_set(argv[1], argv[2]); +} + +static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int size; + char *field; + + if (argc == 1) { + memset(&bcb, 0, sizeof(bcb)); + return CMD_RET_SUCCESS; + } + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + memset(field, 0, size); + + return CMD_RET_SUCCESS; +} + +static int do_bcb_test(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int size; + char *field; + char *op = argv[2]; + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + if (*op == '=' && *(op + 1) == '\0') { + if (!strncmp(argv[3], field, size)) + return CMD_RET_SUCCESS; + else + return CMD_RET_FAILURE; + } else if (*op == '~' && *(op + 1) == '\0') { + if (!strstr(field, argv[3])) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; + } else { + printf("Error: Unknown operator '%s'\n", op); + } + + return CMD_RET_FAILURE; +} + +static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int size; + char *field; + + if (bcb_field_get(argv[1], &field, &size)) + return CMD_RET_FAILURE; + + print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16); + + return CMD_RET_SUCCESS; +} + +static int __bcb_store(void) +{ + struct blk_desc *desc; + struct disk_partition info; + u64 cnt; + int ret; + + desc = blk_get_devnum_by_type(IF_TYPE_MMC, bcb_dev); + if (!desc) { + ret = -ENODEV; + goto err; + } + + ret = part_get_info(desc, bcb_part, &info); + if (ret) + goto err; + + cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); + + if (blk_dwrite(desc, info.start, cnt, &bcb) != cnt) { + ret = -EIO; + goto err; + } + + return CMD_RET_SUCCESS; +err: + printf("Error: mmc %d:%d write failed (%d)\n", bcb_dev, bcb_part, ret); + + return CMD_RET_FAILURE; +} + +static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + return __bcb_store(); +} + +int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp) +{ + int ret; + + ret = __bcb_load(devnum, partp); + if (ret != CMD_RET_SUCCESS) + return ret; + + ret = __bcb_set("command", reasonp); + if (ret != CMD_RET_SUCCESS) + return ret; + + ret = __bcb_store(); + if (ret != CMD_RET_SUCCESS) + return ret; + + return 0; +} + +static struct cmd_tbl cmd_bcb_sub[] = { + U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""), + U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""), + U_BOOT_CMD_MKENT(clear, CONFIG_SYS_MAXARGS, 1, do_bcb_clear, "", ""), + U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_bcb_test, "", ""), + U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_bcb_dump, "", ""), + U_BOOT_CMD_MKENT(store, CONFIG_SYS_MAXARGS, 1, do_bcb_store, "", ""), +}; + +static int do_bcb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_bcb_sub, ARRAY_SIZE(cmd_bcb_sub)); + if (!c) + return CMD_RET_USAGE; + + if (bcb_is_misused(argc, argv)) { + /* + * We try to improve the user experience by reporting the + * root-cause of misusage, so don't return CMD_RET_USAGE, + * since the latter prints out the full-blown help text + */ + return CMD_RET_FAILURE; + } + + return c->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + bcb, CONFIG_SYS_MAXARGS, 1, do_bcb, + "Load/set/clear/test/dump/store Android BCB fields", + "load <dev> <part> - load BCB from mmc <dev>:<part>\n" + "bcb set <field> <val> - set BCB <field> to <val>\n" + "bcb clear [<field>] - clear BCB <field> or all fields\n" + "bcb test <field> <op> <val> - test BCB <field> against <val>\n" + "bcb dump <field> - dump BCB <field>\n" + "bcb store - store BCB back to mmc\n" + "\n" + "Legend:\n" + "<dev> - MMC device index containing the BCB partition\n" + "<part> - MMC partition index or name containing the BCB\n" + "<field> - one of {command,status,recovery,stage,reserved}\n" + "<op> - the binary operator used in 'bcb test':\n" + " '=' returns true if <val> matches the string stored in <field>\n" + " '~' returns true if <val> matches a subset of <field>'s string\n" + "<val> - string/text provided as input to bcb {set,test}\n" + " NOTE: any ':' character in <val> will be replaced by line feed\n" + " during 'bcb set' and used as separator by upper layers\n" +); diff --git a/roms/u-boot/cmd/bdinfo.c b/roms/u-boot/cmd/bdinfo.c new file mode 100644 index 000000000..bf63cc6d6 --- /dev/null +++ b/roms/u-boot/cmd/bdinfo.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Implements the 'bd' command to show board information + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <env.h> +#include <lmb.h> +#include <net.h> +#include <video.h> +#include <vsprintf.h> +#include <asm/cache.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +void bdinfo_print_num_l(const char *name, ulong value) +{ + printf("%-12s= 0x%0*lx\n", name, 2 * (int)sizeof(value), value); +} + +void bdinfo_print_num_ll(const char *name, unsigned long long value) +{ + printf("%-12s= 0x%.*llx\n", name, 2 * (int)sizeof(ulong), value); +} + +static void print_eth(int idx) +{ + char name[10], *val; + if (idx) + sprintf(name, "eth%iaddr", idx); + else + strcpy(name, "ethaddr"); + val = env_get(name); + if (!val) + val = "(not set)"; + printf("%-12s= %s\n", name, val); +} + +void bdinfo_print_mhz(const char *name, unsigned long hz) +{ + char buf[32]; + + printf("%-12s= %6s MHz\n", name, strmhz(buf, hz)); +} + +static void print_bi_dram(const struct bd_info *bd) +{ + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { + if (bd->bi_dram[i].size) { + bdinfo_print_num_l("DRAM bank", i); + bdinfo_print_num_ll("-> start", bd->bi_dram[i].start); + bdinfo_print_num_ll("-> size", bd->bi_dram[i].size); + } + } +} + +__weak void arch_print_bdinfo(void) +{ +} + +static void show_video_info(void) +{ + const struct udevice *dev; + struct uclass *uc; + + uclass_id_foreach_dev(UCLASS_VIDEO, dev, uc) { + printf("%-12s= %s %sactive\n", "Video", dev->name, + device_active(dev) ? "" : "in"); + if (device_active(dev)) { + struct video_priv *upriv = dev_get_uclass_priv(dev); + + bdinfo_print_num_ll("FB base", (ulong)upriv->fb); + if (upriv->copy_fb) + bdinfo_print_num_ll("FB copy", + (ulong)upriv->copy_fb); + printf("%-12s= %dx%dx%d\n", "FB size", upriv->xsize, + upriv->ysize, 1 << upriv->bpix); + } + } +} + +int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct bd_info *bd = gd->bd; + +#ifdef DEBUG + bdinfo_print_num_l("bd address", (ulong)bd); +#endif + bdinfo_print_num_l("boot_params", (ulong)bd->bi_boot_params); + print_bi_dram(bd); + if (IS_ENABLED(CONFIG_SYS_HAS_SRAM)) { + bdinfo_print_num_l("sramstart", (ulong)bd->bi_sramstart); + bdinfo_print_num_l("sramsize", (ulong)bd->bi_sramsize); + } + bdinfo_print_num_l("flashstart", (ulong)bd->bi_flashstart); + bdinfo_print_num_l("flashsize", (ulong)bd->bi_flashsize); + bdinfo_print_num_l("flashoffset", (ulong)bd->bi_flashoffset); + printf("baudrate = %u bps\n", gd->baudrate); + bdinfo_print_num_l("relocaddr", gd->relocaddr); + bdinfo_print_num_l("reloc off", gd->reloc_off); + printf("%-12s= %u-bit\n", "Build", (uint)sizeof(void *) * 8); + if (IS_ENABLED(CONFIG_CMD_NET)) { + printf("current eth = %s\n", eth_get_name()); + print_eth(0); + printf("IP addr = %s\n", env_get("ipaddr")); + } + bdinfo_print_num_l("fdt_blob", (ulong)gd->fdt_blob); + bdinfo_print_num_l("new_fdt", (ulong)gd->new_fdt); + bdinfo_print_num_l("fdt_size", (ulong)gd->fdt_size); + if (IS_ENABLED(CONFIG_DM_VIDEO)) + show_video_info(); +#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO) + bdinfo_print_num_l("FB base ", gd->fb_base); +#endif +#if CONFIG_IS_ENABLED(MULTI_DTB_FIT) + bdinfo_print_num_l("multi_dtb_fit", (ulong)gd->multi_dtb_fit); +#endif + if (gd->fdt_blob) { + struct lmb lmb; + + lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob); + lmb_dump_all_force(&lmb); + } + + arch_print_bdinfo(); + + return 0; +} + +U_BOOT_CMD( + bdinfo, 1, 1, do_bdinfo, + "print Board Info structure", + "" +); diff --git a/roms/u-boot/cmd/bedbug.c b/roms/u-boot/cmd/bedbug.c new file mode 100644 index 000000000..bef617b6a --- /dev/null +++ b/roms/u-boot/cmd/bedbug.c @@ -0,0 +1,414 @@ +/* + * BedBug Functions + */ + +#include <common.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <asm/global_data.h> +#include <asm/ptrace.h> +#include <linux/ctype.h> +#include <net.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern void show_regs __P ((struct pt_regs *)); +extern int run_command __P ((const char *, int)); + +ulong dis_last_addr = 0; /* Last address disassembled */ +ulong dis_last_len = 20; /* Default disassembler length */ +CPU_DEBUG_CTX bug_ctx; /* Bedbug context structure */ + + +/* ====================================================================== + * U-Boot's puts function does not append a newline, so the bedbug stuff + * will use this for the output of the dis/assembler. + * ====================================================================== */ + +int bedbug_puts (const char *str) +{ + /* -------------------------------------------------- */ + + printf ("%s\r\n", str); + return 0; +} /* bedbug_puts */ + + + +/* ====================================================================== + * Initialize the bug_ctx structure used by the bedbug debugger. This is + * specific to the CPU since each has different debug registers and + * settings. + * ====================================================================== */ + +int bedbug_init(void) +{ + /* -------------------------------------------------- */ + return 0; +} /* bedbug_init */ + + + +/* ====================================================================== + * Entry point from the interpreter to the disassembler. Repeated calls + * will resume from the last disassembled address. + * ====================================================================== */ +int do_bedbug_dis(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; /* Address to start disassembly from */ + ulong len; /* # of instructions to disassemble */ + + /* -------------------------------------------------- */ + + /* Setup to go from the last address if none is given */ + addr = dis_last_addr; + len = dis_last_len; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command */ + addr = simple_strtoul (argv[1], NULL, 16); + + /* If an extra param is given then it is the length */ + if (argc > 2) + len = simple_strtoul (argv[2], NULL, 16); + } + + /* Run the disassembler */ + disppc ((unsigned char *) addr, 0, len, bedbug_puts, F_RADHEX); + + dis_last_addr = addr + (len * 4); + dis_last_len = len; + return 0; +} /* do_bedbug_dis */ + +U_BOOT_CMD (ds, 3, 1, do_bedbug_dis, + "disassemble memory", + "ds <address> [# instructions]"); + +/* ====================================================================== + * Entry point from the interpreter to the assembler. Assembles + * instructions in consecutive memory locations until a '.' (period) is + * entered on a line by itself. + * ====================================================================== */ +int do_bedbug_asm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + long mem_addr; /* Address to assemble into */ + unsigned long instr; /* Machine code for text */ + char prompt[15]; /* Prompt string for user input */ + int asm_err; /* Error code from the assembler */ + + /* -------------------------------------------------- */ + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + printf ("\nEnter '.' when done\n"); + mem_addr = simple_strtoul (argv[1], NULL, 16); + + while (1) { + putc ('\n'); + disppc ((unsigned char *) mem_addr, 0, 1, bedbug_puts, + F_RADHEX); + + sprintf (prompt, "%08lx: ", mem_addr); + cli_readline(prompt); + + if (console_buffer[0] && strcmp (console_buffer, ".")) { + if ((instr = + asmppc (mem_addr, console_buffer, + &asm_err)) != 0) { + *(unsigned long *) mem_addr = instr; + mem_addr += 4; + } else { + printf ("*** Error: %s ***\n", + asm_error_str (asm_err)); + rcode = 1; + } + } else { + break; + } + } + return rcode; +} /* do_bedbug_asm */ + +U_BOOT_CMD (as, 2, 0, do_bedbug_asm, + "assemble memory", "as <address>"); + +/* ====================================================================== + * Used to set a break point from the interpreter. Simply calls into the + * CPU-specific break point set routine. + * ====================================================================== */ + +int do_bedbug_break(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* -------------------------------------------------- */ + if (bug_ctx.do_break) + (*bug_ctx.do_break) (cmdtp, flag, argc, argv); + return 0; + +} /* do_bedbug_break */ + +U_BOOT_CMD (break, 3, 0, do_bedbug_break, + "set or clear a breakpoint", + " - Set or clear a breakpoint\n" + "break <address> - Break at an address\n" + "break off <bp#> - Disable breakpoint.\n" + "break show - List breakpoints."); + +/* ====================================================================== + * Called from the debug interrupt routine. Simply calls the CPU-specific + * breakpoint handling routine. + * ====================================================================== */ + +void do_bedbug_breakpoint (struct pt_regs *regs) +{ + /* -------------------------------------------------- */ + + if (bug_ctx.break_isr) + (*bug_ctx.break_isr) (regs); + + return; +} /* do_bedbug_breakpoint */ + + + +/* ====================================================================== + * Called from the CPU-specific breakpoint handling routine. Enter a + * mini main loop until the stopped flag is cleared from the breakpoint + * context. + * + * This handles the parts of the debugger that are common to all CPU's. + * ====================================================================== */ + +void bedbug_main_loop (unsigned long addr, struct pt_regs *regs) +{ + int len; /* Length of command line */ + int flag; /* Command flags */ + int rc = 0; /* Result from run_command */ + char prompt_str[20]; /* Prompt string */ + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0 }; /* previous command */ + /* -------------------------------------------------- */ + + if (bug_ctx.clear) + (*bug_ctx.clear) (bug_ctx.current_bp); + + printf ("Breakpoint %d: ", bug_ctx.current_bp); + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); + + bug_ctx.stopped = 1; + bug_ctx.regs = regs; + + sprintf (prompt_str, "BEDBUG.%d =>", bug_ctx.current_bp); + + /* A miniature main loop */ + while (bug_ctx.stopped) { + len = cli_readline(prompt_str); + + flag = 0; /* assume no special flags for now */ + + if (len > 0) + strcpy (lastcommand, console_buffer); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; + + if (len == -1) + printf ("<INTERRUPT>\n"); + else + rc = run_command_repeatable(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } + + bug_ctx.regs = NULL; + bug_ctx.current_bp = 0; + + return; +} /* bedbug_main_loop */ + + + +/* ====================================================================== + * Interpreter command to continue from a breakpoint. Just clears the + * stopped flag in the context so that the breakpoint routine will + * return. + * ====================================================================== */ +int do_bedbug_continue(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_continue */ + +U_BOOT_CMD (continue, 1, 0, do_bedbug_continue, + "continue from a breakpoint", + ""); + +/* ====================================================================== + * Interpreter command to continue to the next instruction, stepping into + * subroutines. Works by calling the find_next_addr() routine to compute + * the address passes control to the CPU-specific set breakpoint routine + * for the current breakpoint number. + * ====================================================================== */ +int do_bedbug_step(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long addr; /* Address to stop at */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + if (!find_next_address((unsigned char *) &addr, false, bug_ctx.regs)) + return 1; + + if (bug_ctx.set) + (*bug_ctx.set) (bug_ctx.current_bp, addr); + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_step */ + +U_BOOT_CMD (step, 1, 1, do_bedbug_step, + "single step execution.", + ""); + +/* ====================================================================== + * Interpreter command to continue to the next instruction, stepping over + * subroutines. Works by calling the find_next_addr() routine to compute + * the address passes control to the CPU-specific set breakpoint routine + * for the current breakpoint number. + * ====================================================================== */ +int do_bedbug_next(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long addr; /* Address to stop at */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + if (!find_next_address((unsigned char *) &addr, true, bug_ctx.regs)) + return 1; + + if (bug_ctx.set) + (*bug_ctx.set) (bug_ctx.current_bp, addr); + + bug_ctx.stopped = 0; + return 0; +} /* do_bedbug_next */ + +U_BOOT_CMD (next, 1, 1, do_bedbug_next, + "single step execution, stepping over subroutines.", + ""); + +/* ====================================================================== + * Interpreter command to print the current stack. This assumes an EABI + * architecture, so it starts with GPR R1 and works back up the stack. + * ====================================================================== */ +int do_bedbug_stack(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long sp; /* Stack pointer */ + unsigned long func; /* LR from stack */ + int depth; /* Stack iteration level */ + int skip = 1; /* Flag to skip the first entry */ + unsigned long top; /* Top of memory address */ + + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + top = gd->ram_start + gd->ram_size; + depth = 0; + + printf ("Depth PC\n"); + printf ("----- --------\n"); + printf ("%5d %08lx\n", depth++, bug_ctx.regs->nip); + + sp = bug_ctx.regs->gpr[1]; + func = *(unsigned long *) (sp + 4); + + while ((func < top) && (sp < top)) { + if (!skip) + printf ("%5d %08lx\n", depth++, func); + else + --skip; + + sp = *(unsigned long *) sp; + func = *(unsigned long *) (sp + 4); + } + return 0; +} /* do_bedbug_stack */ + +U_BOOT_CMD (where, 1, 1, do_bedbug_stack, + "Print the running stack.", + ""); + +/* ====================================================================== + * Interpreter command to dump the registers. Calls the CPU-specific + * show registers routine. + * ====================================================================== */ +int do_bedbug_rdump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* -------------------------------------------------- */ + + if (!bug_ctx.stopped) { + printf ("Not at a breakpoint\n"); + return 1; + } + + show_regs (bug_ctx.regs); + return 0; +} /* do_bedbug_rdump */ + +U_BOOT_CMD (rdump, 1, 1, do_bedbug_rdump, + "Show registers.", ""); +/* ====================================================================== */ + + +/* + * Copyright (c) 2001 William L. Pitts + * 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/cmd/bind.c b/roms/u-boot/cmd/bind.c new file mode 100644 index 000000000..af2f22cc4 --- /dev/null +++ b/roms/u-boot/cmd/bind.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 JJ Hiblot <jjhiblot@ti.com> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +static int bind_by_class_index(const char *uclass, int index, + const char *drv_name) +{ + static enum uclass_id uclass_id; + struct udevice *dev; + struct udevice *parent; + int ret; + struct driver *drv; + + drv = lists_driver_lookup_name(drv_name); + if (!drv) { + printf("Cannot find driver '%s'\n", drv_name); + return -ENOENT; + } + + uclass_id = uclass_get_by_name(uclass); + if (uclass_id == UCLASS_INVALID) { + printf("%s is not a valid uclass\n", uclass); + return -EINVAL; + } + + ret = uclass_find_device(uclass_id, index, &parent); + if (!parent || ret) { + printf("Cannot find device %d of class %s\n", index, uclass); + return ret; + } + + ret = device_bind_with_driver_data(parent, drv, drv->name, 0, + ofnode_null(), &dev); + if (!dev || ret) { + printf("Unable to bind. err:%d\n", ret); + return ret; + } + + return 0; +} + +static int find_dev(const char *uclass, int index, struct udevice **devp) +{ + static enum uclass_id uclass_id; + int rc; + + uclass_id = uclass_get_by_name(uclass); + if (uclass_id == UCLASS_INVALID) { + printf("%s is not a valid uclass\n", uclass); + return -EINVAL; + } + + rc = uclass_find_device(uclass_id, index, devp); + if (!*devp || rc) { + printf("Cannot find device %d of class %s\n", index, uclass); + return rc; + } + + return 0; +} + +static int unbind_by_class_index(const char *uclass, int index) +{ + int ret; + struct udevice *dev; + + ret = find_dev(uclass, index, &dev); + if (ret) + return ret; + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + printf("Unable to remove. err:%d\n", ret); + return ret; + } + + ret = device_unbind(dev); + if (ret) { + printf("Unable to unbind. err:%d\n", ret); + return ret; + } + + return 0; +} + +static int unbind_child_by_class_index(const char *uclass, int index, + const char *drv_name) +{ + struct udevice *parent; + int ret; + struct driver *drv; + + drv = lists_driver_lookup_name(drv_name); + if (!drv) { + printf("Cannot find driver '%s'\n", drv_name); + return -ENOENT; + } + + ret = find_dev(uclass, index, &parent); + if (ret) + return ret; + + ret = device_chld_remove(parent, drv, DM_REMOVE_NORMAL); + if (ret) + printf("Unable to remove all. err:%d\n", ret); + + ret = device_chld_unbind(parent, drv); + if (ret) + printf("Unable to unbind all. err:%d\n", ret); + + return ret; +} + +static int bind_by_node_path(const char *path, const char *drv_name) +{ + struct udevice *dev; + struct udevice *parent = NULL; + int ret; + ofnode ofnode; + struct driver *drv; + + drv = lists_driver_lookup_name(drv_name); + if (!drv) { + printf("%s is not a valid driver name\n", drv_name); + return -ENOENT; + } + + ofnode = ofnode_path(path); + if (!ofnode_valid(ofnode)) { + printf("%s is not a valid node path\n", path); + return -EINVAL; + } + + while (ofnode_valid(ofnode)) { + if (!device_find_global_by_ofnode(ofnode, &parent)) + break; + ofnode = ofnode_get_parent(ofnode); + } + + if (!parent) { + printf("Cannot find a parent device for node path %s\n", path); + return -ENODEV; + } + + ofnode = ofnode_path(path); + ret = lists_bind_fdt(parent, ofnode, &dev, false); + + if (!dev || ret) { + printf("Unable to bind. err:%d\n", ret); + return ret; + } + + return 0; +} + +static int unbind_by_node_path(const char *path) +{ + struct udevice *dev; + int ret; + ofnode ofnode; + + ofnode = ofnode_path(path); + if (!ofnode_valid(ofnode)) { + printf("%s is not a valid node path\n", path); + return -EINVAL; + } + + ret = device_find_global_by_ofnode(ofnode, &dev); + + if (!dev || ret) { + printf("Cannot find a device with path %s\n", path); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + printf("Unable to remove. err:%d\n", ret); + return ret; + } + + ret = device_unbind(dev); + if (ret) { + printf("Unable to unbind. err:%d\n", ret); + return ret; + } + + return 0; +} + +static int do_bind_unbind(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0; + bool bind; + bool by_node; + + if (argc < 2) + return CMD_RET_USAGE; + + bind = (argv[0][0] == 'b'); + by_node = (argv[1][0] == '/'); + + if (by_node && bind) { + if (argc != 3) + return CMD_RET_USAGE; + ret = bind_by_node_path(argv[1], argv[2]); + } else if (by_node && !bind) { + if (argc != 2) + return CMD_RET_USAGE; + ret = unbind_by_node_path(argv[1]); + } else if (!by_node && bind) { + int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0; + + if (argc != 4) + return CMD_RET_USAGE; + ret = bind_by_class_index(argv[1], index, argv[3]); + } else if (!by_node && !bind) { + int index = (argc > 2) ? simple_strtoul(argv[2], NULL, 10) : 0; + + if (argc == 3) + ret = unbind_by_class_index(argv[1], index); + else if (argc == 4) + ret = unbind_child_by_class_index(argv[1], index, + argv[3]); + else + return CMD_RET_USAGE; + } + + if (ret) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + bind, 4, 0, do_bind_unbind, + "Bind a device to a driver", + "<node path> <driver>\n" + "bind <class> <index> <driver>\n" +); + +U_BOOT_CMD( + unbind, 4, 0, do_bind_unbind, + "Unbind a device from a driver", + "<node path>\n" + "unbind <class> <index>\n" + "unbind <class> <index> <driver>\n" +); diff --git a/roms/u-boot/cmd/binop.c b/roms/u-boot/cmd/binop.c new file mode 100644 index 000000000..c85cb5156 --- /dev/null +++ b/roms/u-boot/cmd/binop.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <hexdump.h> +#include <malloc.h> +#include <mapmem.h> +#include <linux/ctype.h> + +enum { + OP_ID_XOR, + OP_ID_AND, + OP_ID_OR, +}; + +void write_to_env_var(char *varname, u8 *result, ulong len) +{ + char *str_output; + char *str_ptr; + int i; + + str_output = malloc(len * 2 + 1); + str_ptr = str_output; + + for (i = 0; i < len; i++) { + sprintf(str_ptr, "%02x", result[i]); + str_ptr += 2; + } + *str_ptr = '\0'; + env_set(varname, str_output); + + free(str_output); +} + +void read_from_env_var(char *varname, u8 *result) +{ + char *str_value; + + str_value = env_get(varname); + if (str_value) + hex2bin(result, str_value, strlen(str_value) / 2); + else + hex2bin(result, varname, strlen(varname) / 2); +} + +void read_from_mem(ulong addr, u8 *result, ulong len) +{ + u8 *src; + + src = map_sysmem(addr, len); + memcpy(result, src, len); + unmap_sysmem(src); +} + +void write_to_mem(char *varname, u8 *result, ulong len) +{ + ulong addr; + u8 *buf; + + addr = simple_strtoul(varname, NULL, 16); + buf = map_sysmem(addr, len); + memcpy(buf, result, len); + unmap_sysmem(buf); +} + +static int do_binop(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong len; + u8 *result, *src1, *src2; + char *oparg, *lenarg, *src1arg, *src2arg, *destarg; + int i, op; + + if (argc < 5) + return CMD_RET_USAGE; + + oparg = argv[1]; + lenarg = argv[2]; + src1arg = argv[3]; + src2arg = argv[4]; + + if (!strcmp(oparg, "xor")) + op = OP_ID_XOR; + else if (!strcmp(oparg, "or")) + op = OP_ID_OR; + else if (!strcmp(oparg, "and")) + op = OP_ID_AND; + else + return CMD_RET_USAGE; + + len = simple_strtoul(lenarg, NULL, 10); + + src1 = malloc(len); + src2 = malloc(len); + + if (*src1arg == '*') + read_from_mem(simple_strtoul(src1arg + 1, NULL, 16), src1, len); + else + read_from_env_var(src1arg, src1); + + if (*src2arg == '*') + read_from_mem(simple_strtoul(src2arg + 1, NULL, 16), src2, len); + else + read_from_env_var(src2arg, src2); + + result = malloc(len); + + switch (op) { + case OP_ID_XOR: + for (i = 0; i < len; i++) + result[i] = src1[i] ^ src2[i]; + break; + case OP_ID_OR: + for (i = 0; i < len; i++) + result[i] = src1[i] | src2[i]; + break; + case OP_ID_AND: + for (i = 0; i < len; i++) + result[i] = src1[i] & src2[i]; + break; + } + + if (argc == 5) { + for (i = 0; i < len; i++) { + printf("%02x ", result[i]); + if (i % 16 == 15) + puts("\n"); + } + puts("\n"); + + goto exit; + } + + destarg = argv[5]; + + if (*destarg == '*') + write_to_mem(destarg + 1, result, len); /* Skip asterisk */ + else + write_to_env_var(destarg, result, len); +exit: + free(result); + free(src2); + free(src1); + + return 0; +} + +U_BOOT_CMD( + binop, 6, 1, do_binop, + "compute binary operation", + "op count [*]src1 [*]src2 [[*]dest]\n" + " - compute binary operation of data at/in src1 and\n src2 (either *memaddr, env var name or hex string)\n and store result in/at dest, where op is one of\n xor, or, and." +); diff --git a/roms/u-boot/cmd/blk_common.c b/roms/u-boot/cmd/blk_common.c new file mode 100644 index 000000000..87f94b5ab --- /dev/null +++ b/roms/u-boot/cmd/blk_common.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Handling of common block commands + * + * Copyright (c) 2017 Google, Inc + * + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <blk.h> +#include <command.h> + +int blk_common_cmd(int argc, char *const argv[], enum if_type if_type, + int *cur_devnump) +{ + const char *if_name = blk_get_if_type_name(if_type); + + switch (argc) { + case 0: + case 1: + return CMD_RET_USAGE; + case 2: + if (strncmp(argv[1], "inf", 3) == 0) { + blk_list_devices(if_type); + return 0; + } else if (strncmp(argv[1], "dev", 3) == 0) { + if (blk_print_device_num(if_type, *cur_devnump)) { + printf("\nno %s devices available\n", if_name); + return CMD_RET_FAILURE; + } + return 0; + } else if (strncmp(argv[1], "part", 4) == 0) { + if (blk_list_part(if_type)) + printf("\nno %s partition table available\n", + if_name); + return 0; + } + return CMD_RET_USAGE; + case 3: + if (strncmp(argv[1], "dev", 3) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + + if (!blk_show_device(if_type, dev)) { + *cur_devnump = dev; + printf("... is now current device\n"); + } else { + return CMD_RET_FAILURE; + } + return 0; + } else if (strncmp(argv[1], "part", 4) == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + + if (blk_print_part_devnum(if_type, dev)) { + printf("\n%s device %d not available\n", + if_name, dev); + return CMD_RET_FAILURE; + } + return 0; + } + return CMD_RET_USAGE; + + default: /* at least 4 args */ + if (strcmp(argv[1], "read") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + + printf("\n%s read: device %d block # "LBAFU", count %lu ... ", + if_name, *cur_devnump, blk, cnt); + + n = blk_read_devnum(if_type, *cur_devnump, blk, cnt, + (ulong *)addr); + + printf("%ld blocks read: %s\n", n, + n == cnt ? "OK" : "ERROR"); + return n == cnt ? 0 : 1; + } else if (strcmp(argv[1], "write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + lbaint_t blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + + printf("\n%s write: device %d block # "LBAFU", count %lu ... ", + if_name, *cur_devnump, blk, cnt); + + n = blk_write_devnum(if_type, *cur_devnump, blk, cnt, + (ulong *)addr); + + printf("%ld blocks written: %s\n", n, + n == cnt ? "OK" : "ERROR"); + return n == cnt ? 0 : 1; + } else { + return CMD_RET_USAGE; + } + } +} diff --git a/roms/u-boot/cmd/blkcache.c b/roms/u-boot/cmd/blkcache.c new file mode 100644 index 000000000..25f252e45 --- /dev/null +++ b/roms/u-boot/cmd/blkcache.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Nelson Integration, LLC 2016 + * Author: Eric Nelson<eric@nelint.com> + * + */ +#include <command.h> +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <part.h> + +static int blkc_show(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct block_cache_stats stats; + blkcache_stats(&stats); + + printf("hits: %u\n" + "misses: %u\n" + "entries: %u\n" + "max blocks/entry: %u\n" + "max cache entries: %u\n", + stats.hits, stats.misses, stats.entries, + stats.max_blocks_per_entry, stats.max_entries); + return 0; +} + +static int blkc_configure(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + unsigned blocks_per_entry, max_entries; + if (argc != 3) + return CMD_RET_USAGE; + + blocks_per_entry = simple_strtoul(argv[1], 0, 0); + max_entries = simple_strtoul(argv[2], 0, 0); + blkcache_configure(blocks_per_entry, max_entries); + printf("changed to max of %u entries of %u blocks each\n", + max_entries, blocks_per_entry); + return 0; +} + +static struct cmd_tbl cmd_blkc_sub[] = { + U_BOOT_CMD_MKENT(show, 0, 0, blkc_show, "", ""), + U_BOOT_CMD_MKENT(configure, 3, 0, blkc_configure, "", ""), +}; + +static __maybe_unused void blkc_reloc(void) +{ + static int relocated; + + if (!relocated) { + fixup_cmdtable(cmd_blkc_sub, ARRAY_SIZE(cmd_blkc_sub)); + relocated = 1; + }; +} + +static int do_blkcache(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct cmd_tbl *c; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + blkc_reloc(); +#endif + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_blkc_sub[0], ARRAY_SIZE(cmd_blkc_sub)); + + if (!c) + return CMD_RET_USAGE; + + return c->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + blkcache, 4, 0, do_blkcache, + "block cache diagnostics and control", + "show - show and reset statistics\n" + "blkcache configure blocks entries\n" +); diff --git a/roms/u-boot/cmd/blob.c b/roms/u-boot/cmd/blob.c new file mode 100644 index 000000000..887219cc0 --- /dev/null +++ b/roms/u-boot/cmd/blob.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * + * Command for encapsulating/decapsulating blob of memory. + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> +#if defined(CONFIG_ARCH_MX6) || defined(CONFIG_ARCH_MX7) || \ + defined(CONFIG_ARCH_MX7ULP) || defined(CONFIG_ARCH_IMX8M) +#include <fsl_sec.h> +#include <asm/arch/clock.h> +#endif + +/** + * blob_decap() - Decapsulate the data as a blob + * @key_mod: - Pointer to key modifier/key + * @src: - Address of data to be decapsulated + * @dst: - Address of data to be decapsulated + * @len: - Size of data to be decapsulated + * + * Returns zero on success,and negative on error. + */ +__weak int blob_decap(u8 *key_mod, u8 *src, u8 *dst, u32 len) +{ + return 0; +} + +/** + * blob_encap() - Encapsulate the data as a blob + * @key_mod: - Pointer to key modifier/key + * @src: - Address of data to be encapsulated + * @dst: - Address of data to be encapsulated + * @len: - Size of data to be encapsulated + * + * Returns zero on success,and negative on error. + */ +__weak int blob_encap(u8 *key_mod, u8 *src, u8 *dst, u32 len) +{ + return 0; +} + +/** + * do_blob() - Handle the "blob" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_blob(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong key_addr, src_addr, dst_addr, len; + uint8_t *km_ptr, *src_ptr, *dst_ptr; + int enc, ret = 0; + + if (argc != 6) + return CMD_RET_USAGE; + + if (!strncmp(argv[1], "enc", 3)) + enc = 1; + else if (!strncmp(argv[1], "dec", 3)) + enc = 0; + else + return CMD_RET_USAGE; + + src_addr = simple_strtoul(argv[2], NULL, 16); + dst_addr = simple_strtoul(argv[3], NULL, 16); + len = simple_strtoul(argv[4], NULL, 16); + key_addr = simple_strtoul(argv[5], NULL, 16); + + km_ptr = (uint8_t *)(uintptr_t)key_addr; + src_ptr = (uint8_t *)(uintptr_t)src_addr; + dst_ptr = (uint8_t *)(uintptr_t)dst_addr; + +#if defined(CONFIG_ARCH_MX6) || defined(CONFIG_ARCH_MX7) || \ + defined(CONFIG_ARCH_MX7ULP) || defined(CONFIG_ARCH_IMX8M) + + hab_caam_clock_enable(1); + + u32 out_jr_size = sec_in32(CONFIG_SYS_FSL_JR0_ADDR + + FSL_CAAM_ORSR_JRa_OFFSET); + if (out_jr_size != FSL_CAAM_MAX_JR_SIZE) + sec_init(); +#endif + + if (enc) + ret = blob_encap(km_ptr, src_ptr, dst_ptr, len); + else + ret = blob_decap(km_ptr, src_ptr, dst_ptr, len); + + return ret; +} + +/***************************************************/ +static char blob_help_text[] = + "enc src dst len km - Encapsulate and create blob of data\n" + " $len bytes long at address $src and\n" + " store the result at address $dst.\n" + " $km is the address where the key\n" + " modifier is stored.\n" + " The modifier is required for generation\n" + " /use as key for cryptographic operation.\n" + " Key modifier should be 16 byte long.\n" + "blob dec src dst len km - Decapsulate the blob of data at address\n" + " $src and store result of $len byte at\n" + " addr $dst.\n" + " $km is the address where the key\n" + " modifier is stored.\n" + " The modifier is required for generation\n" + " /use as key for cryptographic operation.\n" + " Key modifier should be 16 byte long.\n"; + +U_BOOT_CMD( + blob, 6, 1, do_blob, + "Blob encapsulation/decryption", + blob_help_text +); diff --git a/roms/u-boot/cmd/bloblist.c b/roms/u-boot/cmd/bloblist.c new file mode 100644 index 000000000..21e7ff67a --- /dev/null +++ b/roms/u-boot/cmd/bloblist.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Command-line access to bloblist features + * + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <bloblist.h> +#include <command.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int do_bloblist_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bloblist_show_stats(); + + return 0; +} + +static int do_bloblist_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bloblist_show_list(); + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bloblist_help_text[] = + "info - show information about the bloblist\n" + "bloblist list - list blobs in the bloblist"; +#endif + +U_BOOT_CMD_WITH_SUBCMDS(bloblist, "Bloblists", bloblist_help_text, + U_BOOT_SUBCMD_MKENT(info, 1, 1, do_bloblist_info), + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_bloblist_list)); diff --git a/roms/u-boot/cmd/bmp.c b/roms/u-boot/cmd/bmp.c new file mode 100644 index 000000000..6040fa5d9 --- /dev/null +++ b/roms/u-boot/cmd/bmp.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de. + */ + +/* + * BMP handling routines + */ + +#include <common.h> +#include <bmp_layout.h> +#include <command.h> +#include <dm.h> +#include <gzip.h> +#include <image.h> +#include <lcd.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <splash.h> +#include <video.h> +#include <asm/byteorder.h> + +static int bmp_info (ulong addr); + +/* + * Allocate and decompress a BMP image using gunzip(). + * + * Returns a pointer to the decompressed image data. This pointer is + * aligned to 32-bit-aligned-address + 2. + * See doc/README.displaying-bmps for explanation. + * + * The allocation address is passed to 'alloc_addr' and must be freed + * by the caller after use. + * + * Returns NULL if decompression failed, or if the decompressed data + * didn't contain a valid BMP signature. + */ +#ifdef CONFIG_VIDEO_BMP_GZIP +struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + void *dst; + unsigned long len; + struct bmp_image *bmp; + + /* + * Decompress bmp image + */ + len = CONFIG_SYS_VIDEO_LOGO_MAX_SIZE; + /* allocate extra 3 bytes for 32-bit-aligned-address + 2 alignment */ + dst = malloc(CONFIG_SYS_VIDEO_LOGO_MAX_SIZE + 3); + if (dst == NULL) { + puts("Error: malloc in gunzip failed!\n"); + return NULL; + } + + bmp = dst; + + /* align to 32-bit-aligned-address + 2 */ + bmp = (struct bmp_image *)((((uintptr_t)dst + 1) & ~3) + 2); + + if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), + &len) != 0) { + free(dst); + return NULL; + } + if (len == CONFIG_SYS_VIDEO_LOGO_MAX_SIZE) + puts("Image could be truncated" + " (increase CONFIG_SYS_VIDEO_LOGO_MAX_SIZE)!\n"); + + /* + * Check for bmp mark 'BM' + */ + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + free(dst); + return NULL; + } + + debug("Gzipped BMP image detected!\n"); + + *alloc_addr = dst; + return bmp; +} +#else +struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr) +{ + return NULL; +} +#endif + +static int do_bmp_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; + + switch (argc) { + case 1: /* use image_load_addr as default address */ + addr = image_load_addr; + break; + case 2: /* use argument */ + addr = simple_strtoul(argv[1], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + return (bmp_info(addr)); +} + +static int do_bmp_display(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; + int x = 0, y = 0; + + splash_get_pos(&x, &y); + + switch (argc) { + case 1: /* use image_load_addr as default address */ + addr = image_load_addr; + break; + case 2: /* use argument */ + addr = simple_strtoul(argv[1], NULL, 16); + break; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + if (!strcmp(argv[2], "m")) + x = BMP_ALIGN_CENTER; + else + x = simple_strtoul(argv[2], NULL, 10); + if (!strcmp(argv[3], "m")) + y = BMP_ALIGN_CENTER; + else + y = simple_strtoul(argv[3], NULL, 10); + break; + default: + return CMD_RET_USAGE; + } + + return (bmp_display(addr, x, y)); +} + +static struct cmd_tbl cmd_bmp_sub[] = { + U_BOOT_CMD_MKENT(info, 3, 0, do_bmp_info, "", ""), + U_BOOT_CMD_MKENT(display, 5, 0, do_bmp_display, "", ""), +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void bmp_reloc(void) { + fixup_cmdtable(cmd_bmp_sub, ARRAY_SIZE(cmd_bmp_sub)); +} +#endif + +/* + * Subroutine: do_bmp + * + * Description: Handler for 'bmp' command.. + * + * Inputs: argv[1] contains the subcommand + * + * Return: None + * + */ +static int do_bmp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *c; + + /* Strip off leading 'bmp' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_bmp_sub[0], ARRAY_SIZE(cmd_bmp_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + bmp, 5, 1, do_bmp, + "manipulate BMP image data", + "info <imageAddr> - display image info\n" + "bmp display <imageAddr> [x y] - display image at x,y" +); + +/* + * Subroutine: bmp_info + * + * Description: Show information about bmp file in memory + * + * Inputs: addr address of the bmp file + * + * Return: None + * + */ +static int bmp_info(ulong addr) +{ + struct bmp_image *bmp = (struct bmp_image *)map_sysmem(addr, 0); + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0]=='B') && + (bmp->header.signature[1]=='M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (bmp == NULL) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + + printf("Image size : %d x %d\n", le32_to_cpu(bmp->header.width), + le32_to_cpu(bmp->header.height)); + printf("Bits per pixel: %d\n", le16_to_cpu(bmp->header.bit_count)); + printf("Compression : %d\n", le32_to_cpu(bmp->header.compression)); + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return(0); +} + +/* + * Subroutine: bmp_display + * + * Description: Display bmp file located in memory + * + * Inputs: addr address of the bmp file + * + * Return: None + * + */ +int bmp_display(ulong addr, int x, int y) +{ +#ifdef CONFIG_DM_VIDEO + struct udevice *dev; +#endif + int ret; + struct bmp_image *bmp = map_sysmem(addr, 0); + void *bmp_alloc_addr = NULL; + unsigned long len; + + if (!((bmp->header.signature[0]=='B') && + (bmp->header.signature[1]=='M'))) + bmp = gunzip_bmp(addr, &len, &bmp_alloc_addr); + + if (!bmp) { + printf("There is no valid bmp file at the given address\n"); + return 1; + } + addr = map_to_sysmem(bmp); + +#ifdef CONFIG_DM_VIDEO + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (!ret) { + bool align = false; + + if (CONFIG_IS_ENABLED(SPLASH_SCREEN_ALIGN) || + x == BMP_ALIGN_CENTER || + y == BMP_ALIGN_CENTER) + align = true; + + ret = video_bmp_display(dev, addr, x, y, align); + } +#elif defined(CONFIG_LCD) + ret = lcd_display_bitmap(addr, x, y); +#elif defined(CONFIG_VIDEO) + ret = video_display_bitmap(addr, x, y); +#else +# error bmp_display() requires CONFIG_LCD or CONFIG_VIDEO +#endif + + if (bmp_alloc_addr) + free(bmp_alloc_addr); + + return ret ? CMD_RET_FAILURE : 0; +} diff --git a/roms/u-boot/cmd/boot.c b/roms/u-boot/cmd/boot.c new file mode 100644 index 000000000..b84c0ed89 --- /dev/null +++ b/roms/u-boot/cmd/boot.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Misc boot support + */ +#include <common.h> +#include <command.h> +#include <net.h> + +#ifdef CONFIG_CMD_GO + +/* Allow ports to override the default behavior */ +__attribute__((weak)) +unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc, + char *const argv[]) +{ + return entry (argc, argv); +} + +static int do_go(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr, rc; + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + + printf ("## Starting application at 0x%08lX ...\n", addr); + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + rc = do_go_exec ((void *)addr, argc - 1, argv + 1); + if (rc != 0) rcode = 1; + + printf ("## Application terminated, rc = 0x%lX\n", rc); + return rcode; +} + +/* -------------------------------------------------------------------- */ + +U_BOOT_CMD( + go, CONFIG_SYS_MAXARGS, 1, do_go, + "start application at address 'addr'", + "addr [arg ...]\n - start application at address 'addr'\n" + " passing 'arg' as arguments" +); + +#endif + +U_BOOT_CMD( + reset, 2, 0, do_reset, + "Perform RESET of the CPU", + "" +); + +#ifdef CONFIG_CMD_POWEROFF +U_BOOT_CMD( + poweroff, 1, 0, do_poweroff, + "Perform POWEROFF of the device", + "" +); +#endif diff --git a/roms/u-boot/cmd/bootcount.c b/roms/u-boot/cmd/bootcount.c new file mode 100644 index 000000000..654bbb805 --- /dev/null +++ b/roms/u-boot/cmd/bootcount.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <command.h> +#include <bootcount.h> + +static int do_bootcount_print(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + printf("%lu\n", bootcount_load()); + return CMD_RET_SUCCESS; +} + +static int do_bootcount_reset(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * note that we're explicitly not resetting the environment + * variable, so you still have the old bootcounter available + */ + bootcount_store(0); + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl bootcount_sub[] = { + U_BOOT_CMD_MKENT(print, 1, 1, do_bootcount_print, "", ""), + U_BOOT_CMD_MKENT(reset, 1, 1, do_bootcount_reset, "", ""), +}; + +static int do_bootcount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + /* drop initial "bootcount" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], bootcount_sub, ARRAY_SIZE(bootcount_sub)); + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +#if CONFIG_IS_ENABLED(SYS_LONGHELP) +static char bootcount_help_text[] = + "print - print current bootcounter\n" + "reset - reset the bootcounter" + ; +#endif + +U_BOOT_CMD(bootcount, 2, 1, do_bootcount, + "bootcount", +#if CONFIG_IS_ENABLED(SYS_LONGHELP) + bootcount_help_text +#endif +); diff --git a/roms/u-boot/cmd/bootefi.c b/roms/u-boot/cmd/bootefi.c new file mode 100644 index 000000000..cba81ffe7 --- /dev/null +++ b/roms/u-boot/cmd/bootefi.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI application loader + * + * Copyright (c) 2016 Alexander Graf + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <common.h> +#include <bootm.h> +#include <charset.h> +#include <command.h> +#include <dm.h> +#include <efi_loader.h> +#include <efi_selftest.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <linux/libfdt_env.h> +#include <mapmem.h> +#include <memalign.h> +#include <asm-generic/sections.h> +#include <linux/linkage.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct efi_device_path *bootefi_image_path; +static struct efi_device_path *bootefi_device_path; +static void *image_addr; +static size_t image_size; + +/** + * efi_clear_bootdev() - clear boot device + */ +static void efi_clear_bootdev(void) +{ + efi_free_pool(bootefi_device_path); + efi_free_pool(bootefi_image_path); + bootefi_device_path = NULL; + bootefi_image_path = NULL; + image_addr = NULL; + image_size = 0; +} + +/** + * efi_set_bootdev() - set boot device + * + * This function is called when a file is loaded, e.g. via the 'load' command. + * We use the path to this file to inform the UEFI binary about the boot device. + * + * @dev: device, e.g. "MMC" + * @devnr: number of the device, e.g. "1:2" + * @path: path to file loaded + * @buffer: buffer with file loaded + * @buffer_size: size of file loaded + */ +void efi_set_bootdev(const char *dev, const char *devnr, const char *path, + void *buffer, size_t buffer_size) +{ + struct efi_device_path *device, *image; + efi_status_t ret; + + /* Forget overwritten image */ + if (buffer + buffer_size >= image_addr && + image_addr + image_size >= buffer) + efi_clear_bootdev(); + + /* Remember only PE-COFF and FIT images */ + if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) { +#ifdef CONFIG_FIT + if (fit_check_format(buffer, IMAGE_SIZE_INVAL)) + return; + /* + * FIT images of type EFI_OS are started via command bootm. + * We should not use their boot device with the bootefi command. + */ + buffer = 0; + buffer_size = 0; +#else + return; +#endif + } + + /* efi_set_bootdev() is typically called repeatedly, recover memory */ + efi_clear_bootdev(); + + image_addr = buffer; + image_size = buffer_size; + + ret = efi_dp_from_name(dev, devnr, path, &device, &image); + if (ret == EFI_SUCCESS) { + bootefi_device_path = device; + if (image) { + /* FIXME: image should not contain device */ + struct efi_device_path *image_tmp = image; + + efi_dp_split_file_path(image, &device, &image); + efi_free_pool(image_tmp); + } + bootefi_image_path = image; + } else { + efi_clear_bootdev(); + } +} + +/** + * efi_env_set_load_options() - set load options from environment variable + * + * @handle: the image handle + * @env_var: name of the environment variable + * @load_options: pointer to load options (output) + * Return: status code + */ +static efi_status_t efi_env_set_load_options(efi_handle_t handle, + const char *env_var, + u16 **load_options) +{ + const char *env = env_get(env_var); + size_t size; + u16 *pos; + efi_status_t ret; + + *load_options = NULL; + if (!env) + return EFI_SUCCESS; + size = sizeof(u16) * (utf8_utf16_strlen(env) + 1); + pos = calloc(size, 1); + if (!pos) + return EFI_OUT_OF_RESOURCES; + *load_options = pos; + utf8_utf16_strcpy(&pos, env); + ret = efi_set_load_options(handle, size, *load_options); + if (ret != EFI_SUCCESS) { + free(*load_options); + *load_options = NULL; + } + return ret; +} + +#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) + +/** + * copy_fdt() - Copy the device tree to a new location available to EFI + * + * The FDT is copied to a suitable location within the EFI memory map. + * Additional 12 KiB are added to the space in case the device tree needs to be + * expanded later with fdt_open_into(). + * + * @fdtp: On entry a pointer to the flattened device tree. + * On exit a pointer to the copy of the flattened device tree. + * FDT start + * Return: status code + */ +static efi_status_t copy_fdt(void **fdtp) +{ + unsigned long fdt_ram_start = -1L, fdt_pages; + efi_status_t ret = 0; + void *fdt, *new_fdt; + u64 new_fdt_addr; + uint fdt_size; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + u64 ram_start = gd->bd->bi_dram[i].start; + u64 ram_size = gd->bd->bi_dram[i].size; + + if (!ram_size) + continue; + + if (ram_start < fdt_ram_start) + fdt_ram_start = ram_start; + } + + /* + * Give us at least 12 KiB of breathing room in case the device tree + * needs to be expanded later. + */ + fdt = *fdtp; + fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000); + fdt_size = fdt_pages << EFI_PAGE_SHIFT; + + /* + * Safe fdt location is at 127 MiB. + * On the sandbox convert from the sandbox address space. + */ + new_fdt_addr = (uintptr_t)map_sysmem(fdt_ram_start + 0x7f00000 + + fdt_size, 0); + ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_ACPI_RECLAIM_MEMORY, fdt_pages, + &new_fdt_addr); + if (ret != EFI_SUCCESS) { + /* If we can't put it there, put it somewhere */ + new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size); + ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_ACPI_RECLAIM_MEMORY, fdt_pages, + &new_fdt_addr); + if (ret != EFI_SUCCESS) { + log_err("ERROR: Failed to reserve space for FDT\n"); + goto done; + } + } + new_fdt = (void *)(uintptr_t)new_fdt_addr; + memcpy(new_fdt, fdt, fdt_totalsize(fdt)); + fdt_set_totalsize(new_fdt, fdt_size); + + *fdtp = (void *)(uintptr_t)new_fdt_addr; +done: + return ret; +} + +/** + * get_config_table() - get configuration table + * + * @guid: GUID of the configuration table + * Return: pointer to configuration table or NULL + */ +static void *get_config_table(const efi_guid_t *guid) +{ + size_t i; + + for (i = 0; i < systab.nr_tables; i++) { + if (!guidcmp(guid, &systab.tables[i].guid)) + return systab.tables[i].table; + } + return NULL; +} + +#endif /* !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) */ + +/** + * efi_install_fdt() - install device tree + * + * If fdt is not EFI_FDT_USE_INTERNAL, the device tree located at that memory + * address will will be installed as configuration table, otherwise the device + * tree located at the address indicated by environment variable fdt_addr or as + * fallback fdtcontroladdr will be used. + * + * On architectures using ACPI tables device trees shall not be installed as + * configuration table. + * + * @fdt: address of device tree or EFI_FDT_USE_INTERNAL to use the + * the hardware device tree as indicated by environment variable + * fdt_addr or as fallback the internal device tree as indicated by + * the environment variable fdtcontroladdr + * Return: status code + */ +efi_status_t efi_install_fdt(void *fdt) +{ + /* + * The EBBR spec requires that we have either an FDT or an ACPI table + * but not both. + */ +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) + if (fdt) { + log_err("ERROR: can't have ACPI table and device tree.\n"); + return EFI_LOAD_ERROR; + } +#else + bootm_headers_t img = { 0 }; + efi_status_t ret; + + if (fdt == EFI_FDT_USE_INTERNAL) { + const char *fdt_opt; + uintptr_t fdt_addr; + + /* Look for device tree that is already installed */ + if (get_config_table(&efi_guid_fdt)) + return EFI_SUCCESS; + /* Check if there is a hardware device tree */ + fdt_opt = env_get("fdt_addr"); + /* Use our own device tree as fallback */ + if (!fdt_opt) { + fdt_opt = env_get("fdtcontroladdr"); + if (!fdt_opt) { + log_err("ERROR: need device tree\n"); + return EFI_NOT_FOUND; + } + } + fdt_addr = simple_strtoul(fdt_opt, NULL, 16); + if (!fdt_addr) { + log_err("ERROR: invalid $fdt_addr or $fdtcontroladdr\n"); + return EFI_LOAD_ERROR; + } + fdt = map_sysmem(fdt_addr, 0); + } + + /* Install device tree */ + if (fdt_check_header(fdt)) { + log_err("ERROR: invalid device tree\n"); + return EFI_LOAD_ERROR; + } + + /* Prepare device tree for payload */ + ret = copy_fdt(&fdt); + if (ret) { + log_err("ERROR: out of memory\n"); + return EFI_OUT_OF_RESOURCES; + } + + if (image_setup_libfdt(&img, fdt, 0, NULL)) { + log_err("ERROR: failed to process device tree\n"); + return EFI_LOAD_ERROR; + } + + /* Create memory reservations as indicated by the device tree */ + efi_carve_out_dt_rsv(fdt); + + /* Install device tree as UEFI table */ + ret = efi_install_configuration_table(&efi_guid_fdt, fdt); + if (ret != EFI_SUCCESS) { + log_err("ERROR: failed to install device tree\n"); + return ret; + } +#endif /* GENERATE_ACPI_TABLE */ + + return EFI_SUCCESS; +} + +/** + * do_bootefi_exec() - execute EFI binary + * + * The image indicated by @handle is started. When it returns the allocated + * memory for the @load_options is freed. + * + * @handle: handle of loaded image + * @load_options: load options + * Return: status code + * + * Load the EFI binary into a newly assigned memory unwinding the relocation + * information, install the loaded image protocol, and call the binary. + */ +static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) +{ + efi_status_t ret; + efi_uintn_t exit_data_size = 0; + u16 *exit_data = NULL; + + /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ + switch_to_non_secure_mode(); + + /* Call our payload! */ + ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data)); + if (ret != EFI_SUCCESS) { + log_err("## Application failed, r = %lu\n", + ret & ~EFI_ERROR_MASK); + if (exit_data) { + log_err("## %ls\n", exit_data); + efi_free_pool(exit_data); + } + } + + efi_restore_gd(); + + free(load_options); + + if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) + efi_initrd_deregister(); + + return ret; +} + +/** + * do_efibootmgr() - execute EFI boot manager + * + * Return: status code + */ +static int do_efibootmgr(void) +{ + efi_handle_t handle; + efi_status_t ret; + void *load_options; + + ret = efi_bootmgr_load(&handle, &load_options); + if (ret != EFI_SUCCESS) { + log_notice("EFI boot manager: Cannot load any image\n"); + return CMD_RET_FAILURE; + } + + ret = do_bootefi_exec(handle, load_options); + + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +/** + * do_bootefi_image() - execute EFI binary + * + * Set up memory image for the binary to be loaded, prepare device path, and + * then call do_bootefi_exec() to execute it. + * + * @image_opt: string of image start address + * Return: status code + */ +static int do_bootefi_image(const char *image_opt) +{ + void *image_buf; + unsigned long addr, size; + efi_status_t ret; + +#ifdef CONFIG_CMD_BOOTEFI_HELLO + if (!strcmp(image_opt, "hello")) { + image_buf = __efi_helloworld_begin; + size = __efi_helloworld_end - __efi_helloworld_begin; + efi_clear_bootdev(); + } else +#endif + { + addr = strtoul(image_opt, NULL, 16); + /* Check that a numeric value was passed */ + if (!addr) + return CMD_RET_USAGE; + + image_buf = map_sysmem(addr, 0); + + if (image_buf != image_addr) { + log_err("No UEFI binary known at %s\n", image_opt); + return CMD_RET_FAILURE; + } + size = image_size; + } + ret = efi_run_image(image_buf, size); + + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +/** + * efi_run_image() - run loaded UEFI image + * + * @source_buffer: memory address of the UEFI image + * @source_size: size of the UEFI image + * Return: status code + */ +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size) +{ + efi_handle_t mem_handle = NULL, handle; + struct efi_device_path *file_path = NULL; + struct efi_device_path *msg_path; + efi_status_t ret; + u16 *load_options; + + if (!bootefi_device_path || !bootefi_image_path) { + /* + * Special case for efi payload not loaded from disk, + * such as 'bootefi hello' or for example payload + * loaded directly into memory via JTAG, etc: + */ + file_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, + (uintptr_t)source_buffer, + source_size); + /* + * Make sure that device for device_path exist + * in load_image(). Otherwise, shell and grub will fail. + */ + ret = efi_create_handle(&mem_handle); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_add_protocol(mem_handle, &efi_guid_device_path, + file_path); + if (ret != EFI_SUCCESS) + goto out; + msg_path = file_path; + } else { + file_path = efi_dp_append(bootefi_device_path, + bootefi_image_path); + msg_path = bootefi_image_path; + } + + log_info("Booting %pD\n", msg_path); + + ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer, + source_size, &handle)); + if (ret != EFI_SUCCESS) { + log_err("Loading image failed\n"); + goto out; + } + + /* Transfer environment variable as load options */ + ret = efi_env_set_load_options(handle, "bootargs", &load_options); + if (ret != EFI_SUCCESS) + goto out; + + ret = do_bootefi_exec(handle, load_options); + +out: + efi_delete_handle(mem_handle); + efi_free_pool(file_path); + return ret; +} + +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST +static efi_status_t bootefi_run_prepare(const char *load_options_path, + struct efi_device_path *device_path, + struct efi_device_path *image_path, + struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop) +{ + efi_status_t ret; + u16 *load_options; + + ret = efi_setup_loaded_image(device_path, image_path, image_objp, + loaded_image_infop); + if (ret != EFI_SUCCESS) + return ret; + + /* Transfer environment variable as load options */ + return efi_env_set_load_options((efi_handle_t)*image_objp, + load_options_path, + &load_options); +} + +/** + * bootefi_test_prepare() - prepare to run an EFI test + * + * Prepare to run a test as if it were provided by a loaded image. + * + * @image_objp: pointer to be set to the loaded image handle + * @loaded_image_infop: pointer to be set to the loaded image protocol + * @path: dummy file path used to construct the device path + * set in the loaded image protocol + * @load_options_path: name of a U-Boot environment variable. Its value is + * set as load options in the loaded image protocol. + * Return: status code + */ +static efi_status_t bootefi_test_prepare + (struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop, const char *path, + const char *load_options_path) +{ + efi_status_t ret; + + /* Construct a dummy device path */ + bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0); + if (!bootefi_device_path) + return EFI_OUT_OF_RESOURCES; + + bootefi_image_path = efi_dp_from_file(NULL, 0, path); + if (!bootefi_image_path) { + ret = EFI_OUT_OF_RESOURCES; + goto failure; + } + + ret = bootefi_run_prepare(load_options_path, bootefi_device_path, + bootefi_image_path, image_objp, + loaded_image_infop); + if (ret == EFI_SUCCESS) + return ret; + +failure: + efi_clear_bootdev(); + return ret; +} + +/** + * bootefi_run_finish() - finish up after running an EFI test + * + * @loaded_image_info: Pointer to a struct which holds the loaded image info + * @image_obj: Pointer to a struct which holds the loaded image object + */ +static void bootefi_run_finish(struct efi_loaded_image_obj *image_obj, + struct efi_loaded_image *loaded_image_info) +{ + efi_restore_gd(); + free(loaded_image_info->load_options); + efi_delete_handle(&image_obj->header); +} + +/** + * do_efi_selftest() - execute EFI selftest + * + * Return: status code + */ +static int do_efi_selftest(void) +{ + struct efi_loaded_image_obj *image_obj; + struct efi_loaded_image *loaded_image_info; + efi_status_t ret; + + ret = bootefi_test_prepare(&image_obj, &loaded_image_info, + "\\selftest", "efi_selftest"); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + /* Execute the test */ + ret = EFI_CALL(efi_selftest(&image_obj->header, &systab)); + bootefi_run_finish(image_obj, loaded_image_info); + + return ret != EFI_SUCCESS; +} +#endif /* CONFIG_CMD_BOOTEFI_SELFTEST */ + +/** + * do_bootefi() - execute `bootefi` command + * + * @cmdtp: table entry describing command + * @flag: bitmap indicating how the command was invoked + * @argc: number of arguments + * @argv: command line arguments + * Return: status code + */ +static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + efi_status_t ret; + void *fdt; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + if (argc > 2) { + uintptr_t fdt_addr; + + fdt_addr = simple_strtoul(argv[2], NULL, 16); + fdt = map_sysmem(fdt_addr, 0); + } else { + fdt = EFI_FDT_USE_INTERNAL; + } + ret = efi_install_fdt(fdt); + if (ret == EFI_INVALID_PARAMETER) + return CMD_RET_USAGE; + else if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) { + if (!strcmp(argv[1], "bootmgr")) + return do_efibootmgr(); + } +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST + if (!strcmp(argv[1], "selftest")) + return do_efi_selftest(); +#endif + + return do_bootefi_image(argv[1]); +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootefi_help_text[] = + "<image address> [fdt address]\n" + " - boot EFI payload stored at address <image address>.\n" + " If specified, the device tree located at <fdt address> gets\n" + " exposed as EFI configuration table.\n" +#ifdef CONFIG_CMD_BOOTEFI_HELLO + "bootefi hello\n" + " - boot a sample Hello World application stored within U-Boot\n" +#endif +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST + "bootefi selftest [fdt address]\n" + " - boot an EFI selftest application stored within U-Boot\n" + " Use environment variable efi_selftest to select a single test.\n" + " Use 'setenv efi_selftest list' to enumerate all tests.\n" +#endif +#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR + "bootefi bootmgr [fdt address]\n" + " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" + "\n" + " If specified, the device tree located at <fdt address> gets\n" + " exposed as EFI configuration table.\n" +#endif + ; +#endif + +U_BOOT_CMD( + bootefi, 3, 0, do_bootefi, + "Boots an EFI payload from memory", + bootefi_help_text +); diff --git a/roms/u-boot/cmd/booti.c b/roms/u-boot/cmd/booti.c new file mode 100644 index 000000000..3df70ea9c --- /dev/null +++ b/roms/u-boot/cmd/booti.c @@ -0,0 +1,160 @@ +// 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 <command.h> +#include <image.h> +#include <irq_func.h> +#include <lmb.h> +#include <log.h> +#include <mapmem.h> +#include <asm/global_data.h> +#include <linux/kernel.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; +/* + * Image booting support + */ +static int booti_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images) +{ + int ret; + ulong ld; + ulong relocated_addr; + ulong image_size; + uint8_t *temp; + ulong dest; + ulong dest_end; + unsigned long comp_len; + unsigned long decomp_len; + int ctype; + + ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, + images, 1); + + /* Setup Linux kernel Image entry point */ + if (!argc) { + ld = image_load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + image_load_addr); + } else { + ld = simple_strtoul(argv[0], NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", ld); + } + + temp = map_sysmem(ld, 0); + ctype = image_decomp_type(temp, 2); + if (ctype > 0) { + dest = env_get_ulong("kernel_comp_addr_r", 16, 0); + comp_len = env_get_ulong("kernel_comp_size", 16, 0); + if (!dest || !comp_len) { + puts("kernel_comp_addr_r or kernel_comp_size is not provided!\n"); + return -EINVAL; + } + if (dest < gd->ram_base || dest > gd->ram_top) { + puts("kernel_comp_addr_r is outside of DRAM range!\n"); + return -EINVAL; + } + + debug("kernel image compression type %d size = 0x%08lx address = 0x%08lx\n", + ctype, comp_len, (ulong)dest); + decomp_len = comp_len * 10; + ret = image_decomp(ctype, 0, ld, IH_TYPE_KERNEL, + (void *)dest, (void *)ld, comp_len, + decomp_len, &dest_end); + if (ret) + return ret; + /* dest_end contains the uncompressed Image size */ + memmove((void *) ld, (void *)dest, dest_end); + } + unmap_sysmem((void *)ld); + + ret = booti_setup(ld, &relocated_addr, &image_size, false); + if (ret != 0) + return 1; + + /* Handle BOOTM_STATE_LOADOS */ + if (relocated_addr != ld) { + printf("Moving Image from 0x%lx to 0x%lx, end=%lx\n", ld, + relocated_addr, relocated_addr + image_size); + memmove((void *)relocated_addr, (void *)ld, image_size); + } + + images->ep = relocated_addr; + images->os.start = relocated_addr; + images->os.end = relocated_addr + image_size; + + lmb_reserve(&images->lmb, images->ep, le32_to_cpu(image_size)); + + /* + * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not + * have a header that provide this informaiton. + */ + if (bootm_find_images(flag, argc, argv, relocated_addr, image_size)) + return 1; + + return 0; +} + +int do_booti(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret; + + /* Consume 'booti' */ + argc--; argv++; + + if (booti_start(cmdtp, flag, argc, argv, &images)) + return 1; + + /* + * We are doing the BOOTM_STATE_LOADOS state ourselves, so must + * disable interrupts ourselves + */ + bootm_disable_interrupts(); + + images.os.os = IH_OS_LINUX; +#ifdef CONFIG_RISCV_SMODE + images.os.arch = IH_ARCH_RISCV; +#elif CONFIG_ARM64 + images.os.arch = IH_ARCH_ARM64; +#endif + ret = do_bootm_states(cmdtp, flag, argc, argv, +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + BOOTM_STATE_RAMDISK | +#endif + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO, + &images, 1); + + return ret; +} + +#ifdef CONFIG_SYS_LONGHELP +static char booti_help_text[] = + "[addr [initrd[:size]] [fdt]]\n" + " - boot Linux flat or compressed 'Image' stored at 'addr'\n" + "\tThe argument 'initrd' is optional and specifies the address\n" + "\tof an initrd in memory. The optional parameter ':size' allows\n" + "\tspecifying the size of a RAW initrd.\n" + "\tCurrently only booting from gz, bz2, lzma and lz4 compression\n" + "\ttypes are supported. In order to boot from any of these compressed\n" + "\timages, user have to set kernel_comp_addr_r and kernel_comp_size environment\n" + "\tvariables beforehand.\n" +#if defined(CONFIG_OF_LIBFDT) + "\tSince booting a Linux kernel requires a flat device-tree, a\n" + "\tthird argument providing the address of the device-tree blob\n" + "\tis required. To boot a kernel with a device-tree blob but\n" + "\twithout an initrd image, use a '-' for the initrd argument.\n" +#endif + ""; +#endif + +U_BOOT_CMD( + booti, CONFIG_SYS_MAXARGS, 1, do_booti, + "boot Linux kernel 'Image' format from memory", booti_help_text +); diff --git a/roms/u-boot/cmd/bootm.c b/roms/u-boot/cmd/bootm.c new file mode 100644 index 000000000..81c6b9397 --- /dev/null +++ b/roms/u-boot/cmd/bootm.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Boot support + */ +#include <common.h> +#include <bootm.h> +#include <command.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <malloc.h> +#include <nand.h> +#include <asm/byteorder.h> +#include <asm/global_data.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <u-boot/zlib.h> +#include <mapmem.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_IMI) +static int image_info(unsigned long addr); +#endif + +#if defined(CONFIG_CMD_IMLS) +#include <flash.h> +#include <mtd/cfi_flash.h> +extern flash_info_t flash_info[]; /* info for FLASH chips */ +#endif + +#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND) +static int do_imls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); +#endif + +/* we overload the cmd field with our state machine info instead of a + * function pointer */ +static struct cmd_tbl cmd_bootm_sub[] = { + U_BOOT_CMD_MKENT(start, 0, 1, (void *)BOOTM_STATE_START, "", ""), + U_BOOT_CMD_MKENT(loados, 0, 1, (void *)BOOTM_STATE_LOADOS, "", ""), +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + U_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""), +#endif +#ifdef CONFIG_OF_LIBFDT + U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)BOOTM_STATE_FDT, "", ""), +#endif + U_BOOT_CMD_MKENT(cmdline, 0, 1, (void *)BOOTM_STATE_OS_CMDLINE, "", ""), + U_BOOT_CMD_MKENT(bdt, 0, 1, (void *)BOOTM_STATE_OS_BD_T, "", ""), + U_BOOT_CMD_MKENT(prep, 0, 1, (void *)BOOTM_STATE_OS_PREP, "", ""), + U_BOOT_CMD_MKENT(fake, 0, 1, (void *)BOOTM_STATE_OS_FAKE_GO, "", ""), + U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""), +}; + +static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0; + long state; + struct cmd_tbl *c; + + c = find_cmd_tbl(argv[0], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub)); + argc--; argv++; + + if (c) { + state = (long)c->cmd; + if (state == BOOTM_STATE_START) + state |= BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER; + } else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + + if (((state & BOOTM_STATE_START) != BOOTM_STATE_START) && + images.state >= state) { + printf("Trying to execute a command out of order\n"); + return CMD_RET_USAGE; + } + + ret = do_bootm_states(cmdtp, flag, argc, argv, state, &images, 0); + + return ret; +} + +/*******************************************************************/ +/* bootm - boot application image from image in memory */ +/*******************************************************************/ + +int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static int relocated = 0; + + if (!relocated) { + int i; + + /* relocate names of sub-command table */ + for (i = 0; i < ARRAY_SIZE(cmd_bootm_sub); i++) + cmd_bootm_sub[i].name += gd->reloc_off; + + relocated = 1; + } +#endif + + /* determine if we have a sub command */ + argc--; argv++; + if (argc > 0) { + char *endp; + + simple_strtoul(argv[0], &endp, 16); + /* endp pointing to NULL means that argv[0] was just a + * valid number, pass it along to the normal bootm processing + * + * If endp is ':' or '#' assume a FIT identifier so pass + * along for normal processing. + * + * Right now we assume the first arg should never be '-' + */ + if ((*endp != 0) && (*endp != ':') && (*endp != '#')) + return do_bootm_subcommand(cmdtp, flag, argc, argv); + } + + return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | + BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_LOADOS | +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + BOOTM_STATE_RAMDISK | +#endif +#if defined(CONFIG_PPC) || defined(CONFIG_MIPS) + BOOTM_STATE_OS_CMDLINE | +#endif + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO, &images, 1); +} + +int bootm_maybe_autostart(struct cmd_tbl *cmdtp, const char *cmd) +{ + const char *ep = env_get("autostart"); + + if (ep && !strcmp(ep, "yes")) { + char *local_args[2]; + local_args[0] = (char *)cmd; + local_args[1] = NULL; + printf("Automatic boot of image at addr 0x%08lX ...\n", + image_load_addr); + return do_bootm(cmdtp, 0, 1, local_args); + } + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootm_help_text[] = + "[addr [arg ...]]\n - boot application image stored in memory\n" + "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n" + "\t'arg' can be the address of an initrd image\n" +#if defined(CONFIG_OF_LIBFDT) + "\tWhen booting a Linux kernel which requires a flat device-tree\n" + "\ta third argument is required which is the address of the\n" + "\tdevice-tree blob. To boot that kernel without an initrd image,\n" + "\tuse a '-' for the second argument. If you do not pass a third\n" + "\ta bd_info struct will be passed instead\n" +#endif +#if defined(CONFIG_FIT) + "\t\nFor the new multi component uImage format (FIT) addresses\n" + "\tmust be extended to include component or configuration unit name:\n" + "\taddr:<subimg_uname> - direct component image specification\n" + "\taddr#<conf_uname> - configuration specification\n" + "\tUse iminfo command to get the list of existing component\n" + "\timages and configurations.\n" +#endif + "\nSub-commands to do part of the bootm sequence. The sub-commands " + "must be\n" + "issued in the order below (it's ok to not issue all sub-commands):\n" + "\tstart [addr [arg ...]]\n" + "\tloados - load OS image\n" +#if defined(CONFIG_SYS_BOOT_RAMDISK_HIGH) + "\tramdisk - relocate initrd, set env initrd_start/initrd_end\n" +#endif +#if defined(CONFIG_OF_LIBFDT) + "\tfdt - relocate flat device tree\n" +#endif + "\tcmdline - OS specific command line processing/setup\n" + "\tbdt - OS specific bd_info processing\n" + "\tprep - OS specific prep before relocation or go\n" +#if defined(CONFIG_TRACE) + "\tfake - OS specific fake start without go\n" +#endif + "\tgo - start OS"; +#endif + +U_BOOT_CMD( + bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, + "boot application image from memory", bootm_help_text +); + +/*******************************************************************/ +/* bootd - boot default image */ +/*******************************************************************/ +#if defined(CONFIG_CMD_BOOTD) +int do_bootd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return run_command(env_get("bootcmd"), flag); +} + +U_BOOT_CMD( + boot, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); + +/* keep old command name "bootd" for backward compatibility */ +U_BOOT_CMD( + bootd, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); + +#endif + + +/*******************************************************************/ +/* iminfo - print header info for a requested image */ +/*******************************************************************/ +#if defined(CONFIG_CMD_IMI) +static int do_iminfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int arg; + ulong addr; + int rcode = 0; + + if (argc < 2) { + return image_info(image_load_addr); + } + + for (arg = 1; arg < argc; ++arg) { + addr = simple_strtoul(argv[arg], NULL, 16); + if (image_info(addr) != 0) + rcode = 1; + } + return rcode; +} + +static int image_info(ulong addr) +{ + void *hdr = (void *)map_sysmem(addr, 0); + + printf("\n## Checking Image at %08lx ...\n", addr); + + switch (genimg_get_format(hdr)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + puts(" Legacy image found\n"); + if (!image_check_magic(hdr)) { + puts(" Bad Magic Number\n"); + unmap_sysmem(hdr); + return 1; + } + + if (!image_check_hcrc(hdr)) { + puts(" Bad Header Checksum\n"); + unmap_sysmem(hdr); + return 1; + } + + image_print_contents(hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + puts(" Bad Data CRC\n"); + unmap_sysmem(hdr); + return 1; + } + puts("OK\n"); + unmap_sysmem(hdr); + return 0; +#endif +#if defined(CONFIG_ANDROID_BOOT_IMAGE) + case IMAGE_FORMAT_ANDROID: + puts(" Android image found\n"); + android_print_contents(hdr); + unmap_sysmem(hdr); + return 0; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + puts(" FIT image found\n"); + + if (fit_check_format(hdr, IMAGE_SIZE_INVAL)) { + puts("Bad FIT image format!\n"); + unmap_sysmem(hdr); + return 1; + } + + fit_print_contents(hdr); + + if (!fit_all_image_verify(hdr)) { + puts("Bad hash in FIT image!\n"); + unmap_sysmem(hdr); + return 1; + } + + unmap_sysmem(hdr); + return 0; +#endif + default: + puts("Unknown image format!\n"); + break; + } + + unmap_sysmem(hdr); + return 1; +} + +U_BOOT_CMD( + iminfo, CONFIG_SYS_MAXARGS, 1, do_iminfo, + "print header information for application image", + "addr [addr ...]\n" + " - print header information for application image starting at\n" + " address 'addr' in memory; this includes verification of the\n" + " image contents (magic number, header and payload checksums)" +); +#endif + + +/*******************************************************************/ +/* imls - list all images found in flash */ +/*******************************************************************/ +#if defined(CONFIG_CMD_IMLS) +static int do_imls_nor(void) +{ + flash_info_t *info; + int i, j; + void *hdr; + + for (i = 0, info = &flash_info[0]; + i < CONFIG_SYS_MAX_FLASH_BANKS; ++i, ++info) { + + if (info->flash_id == FLASH_UNKNOWN) + goto next_bank; + for (j = 0; j < info->sector_count; ++j) { + + hdr = (void *)info->start[j]; + if (!hdr) + goto next_sector; + + switch (genimg_get_format(hdr)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + if (!image_check_hcrc(hdr)) + goto next_sector; + + printf("Legacy Image at %08lX:\n", (ulong)hdr); + image_print_contents(hdr); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + puts("Bad Data CRC\n"); + } else { + puts("OK\n"); + } + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (fit_check_format(hdr, IMAGE_SIZE_INVAL)) + goto next_sector; + + printf("FIT Image at %08lX:\n", (ulong)hdr); + fit_print_contents(hdr); + break; +#endif + default: + goto next_sector; + } + +next_sector: ; + } +next_bank: ; + } + return 0; +} +#endif + +#if defined(CONFIG_CMD_IMLS_NAND) +static int nand_imls_legacyimage(struct mtd_info *mtd, int nand_dev, + loff_t off, size_t len) +{ + void *imgdata; + int ret; + + imgdata = malloc(len); + if (!imgdata) { + printf("May be a Legacy Image at NAND device %d offset %08llX:\n", + nand_dev, off); + printf(" Low memory(cannot allocate memory for image)\n"); + return -ENOMEM; + } + + ret = nand_read_skip_bad(mtd, off, &len, NULL, mtd->size, imgdata); + if (ret < 0 && ret != -EUCLEAN) { + free(imgdata); + return ret; + } + + if (!image_check_hcrc(imgdata)) { + free(imgdata); + return 0; + } + + printf("Legacy Image at NAND device %d offset %08llX:\n", + nand_dev, off); + image_print_contents(imgdata); + + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(imgdata)) + puts("Bad Data CRC\n"); + else + puts("OK\n"); + + free(imgdata); + + return 0; +} + +static int nand_imls_fitimage(struct mtd_info *mtd, int nand_dev, loff_t off, + size_t len) +{ + void *imgdata; + int ret; + + imgdata = malloc(len); + if (!imgdata) { + printf("May be a FIT Image at NAND device %d offset %08llX:\n", + nand_dev, off); + printf(" Low memory(cannot allocate memory for image)\n"); + return -ENOMEM; + } + + ret = nand_read_skip_bad(mtd, off, &len, NULL, mtd->size, imgdata); + if (ret < 0 && ret != -EUCLEAN) { + free(imgdata); + return ret; + } + + if (fit_check_format(imgdata, IMAGE_SIZE_INVAL)) { + free(imgdata); + return 0; + } + + printf("FIT Image at NAND device %d offset %08llX:\n", nand_dev, off); + + fit_print_contents(imgdata); + free(imgdata); + + return 0; +} + +static int do_imls_nand(void) +{ + struct mtd_info *mtd; + int nand_dev = nand_curr_device; + size_t len; + loff_t off; + u32 buffer[16]; + + if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) { + puts("\nNo NAND devices available\n"); + return -ENODEV; + } + + printf("\n"); + + for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) { + mtd = get_nand_dev_by_index(nand_dev); + if (!mtd->name || !mtd->size) + continue; + + for (off = 0; off < mtd->size; off += mtd->erasesize) { + const image_header_t *header; + int ret; + + if (nand_block_isbad(mtd, off)) + continue; + + len = sizeof(buffer); + + ret = nand_read(mtd, off, &len, (u8 *)buffer); + if (ret < 0 && ret != -EUCLEAN) { + printf("NAND read error %d at offset %08llX\n", + ret, off); + continue; + } + + switch (genimg_get_format(buffer)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + header = (const image_header_t *)buffer; + + len = image_get_image_size(header); + nand_imls_legacyimage(mtd, nand_dev, off, len); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + len = fit_get_size(buffer); + nand_imls_fitimage(mtd, nand_dev, off, len); + break; +#endif + } + } + } + + return 0; +} +#endif + +#if defined(CONFIG_CMD_IMLS) || defined(CONFIG_CMD_IMLS_NAND) +static int do_imls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret_nor = 0, ret_nand = 0; + +#if defined(CONFIG_CMD_IMLS) + ret_nor = do_imls_nor(); +#endif + +#if defined(CONFIG_CMD_IMLS_NAND) + ret_nand = do_imls_nand(); +#endif + + if (ret_nor) + return ret_nor; + + if (ret_nand) + return ret_nand; + + return (0); +} + +U_BOOT_CMD( + imls, 1, 1, do_imls, + "list all images found in flash", + "\n" + " - Prints information about all images found at sector/block\n" + " boundaries in nor/nand flash." +); +#endif diff --git a/roms/u-boot/cmd/bootmenu.c b/roms/u-boot/cmd/bootmenu.c new file mode 100644 index 000000000..409ef9a84 --- /dev/null +++ b/roms/u-boot/cmd/bootmenu.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org> + */ + +#include <common.h> +#include <command.h> +#include <ansi.h> +#include <env.h> +#include <log.h> +#include <menu.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/delay.h> +#include <linux/string.h> + +/* maximum bootmenu entries */ +#define MAX_COUNT 99 + +/* maximal size of bootmenu env + * 9 = strlen("bootmenu_") + * 2 = strlen(MAX_COUNT) + * 1 = NULL term + */ +#define MAX_ENV_SIZE (9 + 2 + 1) + +struct bootmenu_entry { + unsigned short int num; /* unique number 0 .. MAX_COUNT */ + char key[3]; /* key identifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +enum bootmenu_key { + KEY_NONE = 0, + KEY_UP, + KEY_DOWN, + KEY_SELECT, + KEY_QUIT, +}; + +static char *bootmenu_getoption(unsigned short int n) +{ + char name[MAX_ENV_SIZE]; + + if (n > MAX_COUNT) + return NULL; + + sprintf(name, "bootmenu_%d", n); + return env_get(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where the entry will be drown (entry->num) + * First 3 lines contain bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + puts(" "); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(entry->title); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static void bootmenu_autoboot_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int i, c; + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + printf(" Hit any key to stop autoboot: %2d ", menu->delay); + } + + while (menu->delay > 0) { + for (i = 0; i < 100; ++i) { + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + c = getchar(); + + switch (c) { + case '\e': + *esc = 1; + *key = KEY_NONE; + break; + case '\r': + *key = KEY_SELECT; + break; + case 0x3: /* ^C */ + *key = KEY_QUIT; + break; + default: + *key = KEY_NONE; + break; + } + + break; + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + } + + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + *key = KEY_SELECT; +} + +static void bootmenu_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int c; + + if (*esc == 1) { + if (tstc()) { + c = getchar(); + } else { + WATCHDOG_RESET(); + mdelay(10); + if (tstc()) + c = getchar(); + else + c = '\e'; + } + } else { + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + c = getchar(); + } + + switch (*esc) { + case 0: + /* First char of ANSI escape sequence '\e' */ + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; + } + break; + case 1: + /* Second char of ANSI '[' */ + if (c == '[') { + *esc = 2; + *key = KEY_NONE; + } else { + /* Alone ESC key was pressed */ + *key = KEY_QUIT; + *esc = (c == '\e') ? 1 : 0; + } + break; + case 2: + case 3: + /* Third char of ANSI (number '1') - optional */ + if (*esc == 2 && c == '1') { + *esc = 3; + *key = KEY_NONE; + break; + } + + *esc = 0; + + /* ANSI 'A' - key up was pressed */ + if (c == 'A') + *key = KEY_UP; + /* ANSI 'B' - key down was pressed */ + else if (c == 'B') + *key = KEY_DOWN; + /* other key was pressed */ + else + *key = KEY_NONE; + + break; + } + + /* enter key was pressed */ + if (c == '\r') + *key = KEY_SELECT; + + /* ^C was pressed */ + if (c == 0x3) + *key = KEY_QUIT; +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + struct bootmenu_entry *iter; + enum bootmenu_key key = KEY_NONE; + int esc = 0; + int i; + + while (1) { + if (menu->delay >= 0) { + /* Autoboot was not stopped */ + bootmenu_autoboot_loop(menu, &key, &esc); + } else { + /* Some key was pressed, so autoboot was stopped */ + bootmenu_loop(menu, &key, &esc); + } + + switch (key) { + case KEY_UP: + if (menu->active > 0) + --menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_DOWN: + if (menu->active < menu->count - 1) + ++menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + case KEY_QUIT: + /* Quit by choosing the last entry - U-Boot console */ + iter = menu->first; + while (iter->next) + iter = iter->next; + return iter->key; + default: + break; + } + } + + /* never happens */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + unsigned short int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + char *default_str; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + default_str = env_get("bootmenu_default"); + if (default_str) + menu->active = (int)simple_strtol(default_str, NULL, 10); + + while ((option = bootmenu_getoption(i))) { + sep = strchr(option, '='); + if (!sep) { + printf("Invalid bootmenu entry: %s\n", option); + break; + } + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len + 1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep + 1); + entry->command = malloc(len + 1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep + 1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i == MAX_COUNT - 1) + break; + } + + /* Add U-Boot console entry at the end */ + if (i <= MAX_COUNT - 1) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + } + + menu->count = i; + + if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu + printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1); + menu->active=0; + } + + return menu; + +cleanup: + bootmenu_destroy(menu); + return NULL; +} + +static void menu_display_statusline(struct menu *m) +{ + struct bootmenu_entry *entry; + struct bootmenu_data *menu; + + if (menu_default_choice(m, (void *)&entry) < 0) + return; + + menu = entry->menu; + + printf(ANSI_CURSOR_POSITION, 1, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, 2, 1); + puts(" *** U-Boot Boot Menu ***"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, 3, 1); + puts(ANSI_CLEAR_LINE); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) { + puts("bootmenu option 0 was not found\n"); + return; + } + sep = strchr(option, '='); + if (!sep) { + puts("bootmenu option 0 is invalid\n"); + return; + } + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline, + bootmenu_print_entry, bootmenu_choice_entry, + bootmenu); + if (!menu) { + bootmenu_destroy(bootmenu); + return; + } + + for (iter = bootmenu->first; iter; iter = iter->next) { + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + } + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + debug("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +#ifdef CONFIG_AUTOBOOT_MENU_SHOW +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} +#endif + +int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + delay = CONFIG_BOOTDELAY; +#endif + + if (argc >= 2) + delay_str = argv[1]; + + if (!delay_str) + delay_str = env_get("bootmenu_delay"); + + if (delay_str) + delay = (int)simple_strtol(delay_str, NULL, 10); + + bootmenu_show(delay); + return 0; +} + +U_BOOT_CMD( + bootmenu, 2, 1, do_bootmenu, + "ANSI terminal bootmenu", + "[delay]\n" + " - show ANSI terminal bootmenu with autoboot delay" +); diff --git a/roms/u-boot/cmd/bootstage.c b/roms/u-boot/cmd/bootstage.c new file mode 100644 index 000000000..0e623f216 --- /dev/null +++ b/roms/u-boot/cmd/bootstage.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012, Google Inc. All rights reserved. + */ + +#include <common.h> +#include <bootstage.h> +#include <command.h> + +static int do_bootstage_report(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bootstage_report(); + + return 0; +} + +static int get_base_size(int argc, char *const argv[], ulong *basep, + ulong *sizep) +{ + char *endp; + + *basep = CONFIG_BOOTSTAGE_STASH_ADDR; + *sizep = CONFIG_BOOTSTAGE_STASH_SIZE; + if (argc < 2) + return 0; + *basep = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + if (argc == 2) + return 0; + *sizep = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + + return 0; +} + +static int do_bootstage_stash(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong base, size; + int ret; + + if (get_base_size(argc, argv, &base, &size)) + return CMD_RET_USAGE; + if (base == -1UL) { + printf("No bootstage stash area defined\n"); + return 1; + } + + if (0 == strcmp(argv[0], "stash")) + ret = bootstage_stash((void *)base, size); + else + ret = bootstage_unstash((void *)base, size); + if (ret) + return 1; + + return 0; +} + +static struct cmd_tbl cmd_bootstage_sub[] = { + U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""), + U_BOOT_CMD_MKENT(stash, 4, 0, do_bootstage_stash, "", ""), + U_BOOT_CMD_MKENT(unstash, 4, 0, do_bootstage_stash, "", ""), +}; + +/* + * Process a bootstage sub-command + */ +static int do_boostage(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + /* Strip off leading 'bootstage' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_bootstage_sub, + ARRAY_SIZE(cmd_bootstage_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + + +U_BOOT_CMD(bootstage, 4, 1, do_boostage, + "Boot stage command", + " - check boot progress and timing\n" + "report - Print a report\n" + "stash [<start> [<size>]] - Stash data into memory\n" + "unstash [<start> [<size>]] - Unstash data from memory" +); diff --git a/roms/u-boot/cmd/bootz.c b/roms/u-boot/cmd/bootz.c new file mode 100644 index 000000000..7556cd275 --- /dev/null +++ b/roms/u-boot/cmd/bootz.c @@ -0,0 +1,111 @@ +// 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 <command.h> +#include <image.h> +#include <irq_func.h> +#include <lmb.h> +#include <log.h> +#include <linux/compiler.h> + +int __weak bootz_setup(ulong image, ulong *start, ulong *end) +{ + /* Please define bootz_setup() for your platform */ + + puts("Your platform's zImage format isn't supported yet!\n"); + return -1; +} + +/* + * zImage booting support + */ +static int bootz_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], bootm_headers_t *images) +{ + int ret; + ulong zi_start, zi_end; + + ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, + images, 1); + + /* Setup Linux kernel zImage entry point */ + if (!argc) { + images->ep = image_load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + image_load_addr); + } else { + images->ep = simple_strtoul(argv[0], NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", + images->ep); + } + + ret = bootz_setup(images->ep, &zi_start, &zi_end); + if (ret != 0) + return 1; + + lmb_reserve(&images->lmb, images->ep, zi_end - zi_start); + + /* + * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not + * have a header that provide this informaiton. + */ + if (bootm_find_images(flag, argc, argv, images->ep, zi_end - zi_start)) + return 1; + + return 0; +} + +int do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret; + + /* Consume 'bootz' */ + argc--; argv++; + + if (bootz_start(cmdtp, flag, argc, argv, &images)) + return 1; + + /* + * We are doing the BOOTM_STATE_LOADOS state ourselves, so must + * disable interrupts ourselves + */ + bootm_disable_interrupts(); + + images.os.os = IH_OS_LINUX; + ret = do_bootm_states(cmdtp, flag, argc, argv, +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + BOOTM_STATE_RAMDISK | +#endif + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO, + &images, 1); + + return ret; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootz_help_text[] = + "[addr [initrd[:size]] [fdt]]\n" + " - boot Linux zImage stored in memory\n" + "\tThe argument 'initrd' is optional and specifies the address\n" + "\tof the initrd in memory. The optional argument ':size' allows\n" + "\tspecifying the size of RAW initrd.\n" +#if defined(CONFIG_OF_LIBFDT) + "\tWhen booting a Linux kernel which requires a flat device-tree\n" + "\ta third argument is required which is the address of the\n" + "\tdevice-tree blob. To boot that kernel without an initrd image,\n" + "\tuse a '-' for the second argument. If you do not pass a third\n" + "\ta bd_info struct will be passed instead\n" +#endif + ""; +#endif + +U_BOOT_CMD( + bootz, CONFIG_SYS_MAXARGS, 1, do_bootz, + "boot Linux zImage image from memory", bootz_help_text +); diff --git a/roms/u-boot/cmd/broadcom/Makefile b/roms/u-boot/cmd/broadcom/Makefile new file mode 100644 index 000000000..62268d98d --- /dev/null +++ b/roms/u-boot/cmd/broadcom/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Broadcom + +obj-y += chimp_boot.o +obj-y += nitro_image_load.o +obj-y += chimp_handshake.o diff --git a/roms/u-boot/cmd/broadcom/chimp_boot.c b/roms/u-boot/cmd/broadcom/chimp_boot.c new file mode 100644 index 000000000..16f2b612c --- /dev/null +++ b/roms/u-boot/cmd/broadcom/chimp_boot.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Broadcom + */ + +#include <common.h> +#include <command.h> +#include <broadcom/chimp.h> + +static int do_chimp_fastboot_secure(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 health = 0; + + if (chimp_health_status_optee(&health)) { + pr_err("Chimp health command fail\n"); + return CMD_RET_FAILURE; + } + + if (health == BCM_CHIMP_RUNNIG_GOOD) { + printf("skip fastboot...\n"); + return CMD_RET_SUCCESS; + } + + if (chimp_fastboot_optee()) { + pr_err("Failed to load secure ChiMP image\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD + (chimp_ld_secure, 1, 0, do_chimp_fastboot_secure, + "Invoke chimp fw load via optee", + "chimp_ld_secure\n" +); diff --git a/roms/u-boot/cmd/broadcom/chimp_handshake.c b/roms/u-boot/cmd/broadcom/chimp_handshake.c new file mode 100644 index 000000000..a90a73a6d --- /dev/null +++ b/roms/u-boot/cmd/broadcom/chimp_handshake.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Broadcom + */ + +#include <common.h> +#include <command.h> +#include <broadcom/chimp.h> + +/* This command should be called after loading the nitro binaries */ +static int do_chimp_hs(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = CMD_RET_USAGE; + u32 hstatus; + + /* Returns 1, if handshake call is success */ + if (chimp_handshake_status_optee(0, &hstatus)) + ret = CMD_RET_SUCCESS; + + if (hstatus == CHIMP_HANDSHAKE_SUCCESS) + printf("ChiMP Handshake successful\n"); + else + printf("ERROR: ChiMP Handshake status 0x%x\n", hstatus); + + return ret; +} + +U_BOOT_CMD + (chimp_hs, 1, 1, do_chimp_hs, + "Verify the Chimp handshake", + "chimp_hs\n" +); diff --git a/roms/u-boot/cmd/broadcom/nitro_image_load.c b/roms/u-boot/cmd/broadcom/nitro_image_load.c new file mode 100644 index 000000000..4a36b300c --- /dev/null +++ b/roms/u-boot/cmd/broadcom/nitro_image_load.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Broadcom + */ + +#include <common.h> +#include <command.h> + +#define FW_IMAGE_SIG 0xff123456 +#define CFG_IMAGE_SIG 0xcf54321a + +/* + * structure for bin file + * signature: fw itb file + * size: fw itb file + * signature: NS3 config file + * size: NS3 config file + * Data: fw itb file + * ............................ + * ............................ + * Data: NS3 config file + * ............................ + * ............................ + */ + +static struct img_header { + u32 bin_sig; + u32 bin_size; + u32 cfg1_sig; + u32 cfg1_size; +} *img_header; + +static int env_set_val(const char *varname, ulong val) +{ + int ret; + + ret = env_set_hex(varname, val); + if (ret) + pr_err("Failed to %s env var\n", varname); + + return ret; +} + +static int do_spi_images_addr(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uintptr_t images_load_addr; + uintptr_t spi_load_addr; + u32 len; + u32 spi_data_offset = sizeof(struct img_header); + + if (argc != 3) + return CMD_RET_USAGE; + + /* convert command parameter to fastboot address (base 16), i.e. hex */ + images_load_addr = simple_strtoul(argv[1], NULL, 16); + if (!images_load_addr) { + pr_err("Invalid load address\n"); + return CMD_RET_USAGE; + } + + spi_load_addr = simple_strtoul(argv[2], NULL, 16); + if (!spi_load_addr) { + pr_err("Invalid spi load address\n"); + return CMD_RET_USAGE; + } + + img_header = (struct img_header *)images_load_addr; + + if (img_header->bin_sig != FW_IMAGE_SIG) { + pr_err("Invalid Nitro bin file\n"); + goto error; + } + + if (env_set_val("spi_nitro_fw_itb_start_addr", 0)) + goto error; + + if (env_set_val("spi_nitro_fw_itb_len", 0)) + goto error; + + if (env_set_val("spi_nitro_fw_ns3_cfg_start_addr", 0)) + goto error; + + if (env_set_val("spi_nitro_fw_ns3_cfg_len", 0)) + goto error; + + len = img_header->bin_size; + + if (env_set_val("spi_nitro_fw_itb_start_addr", + (spi_load_addr + spi_data_offset))) + goto error; + + if (env_set_val("spi_nitro_fw_itb_len", img_header->bin_size)) + goto error; + + spi_data_offset += len; + + if (img_header->cfg1_sig == CFG_IMAGE_SIG) { + len = img_header->cfg1_size; + + if (env_set_val("spi_nitro_fw_ns3_cfg_start_addr", + (spi_load_addr + spi_data_offset))) + goto error; + + if (env_set_val("spi_nitro_fw_ns3_cfg_len", len)) + goto error; + + spi_data_offset += len; + } + + /* disable secure boot */ + if (env_set_val("nitro_fastboot_secure", 0)) + goto error; + + return CMD_RET_SUCCESS; + +error: + return CMD_RET_FAILURE; +} + +U_BOOT_CMD + (spi_nitro_images_addr, 3, 1, do_spi_images_addr, + "Load the bnxt bin header and sets envs ", + "spi_nitro_images_addr <load_addr> <spi_base_addr>\n" +); diff --git a/roms/u-boot/cmd/btrfs.c b/roms/u-boot/cmd/btrfs.c new file mode 100644 index 000000000..7e3cbe12c --- /dev/null +++ b/roms/u-boot/cmd/btrfs.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 2017 by Marek Behun <marek.behun@nic.cz> + */ + +#include <common.h> +#include <command.h> +#include <btrfs.h> +#include <fs.h> + +int do_btrsubvol(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc != 3) + return CMD_RET_USAGE; + + if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_BTRFS)) + return 1; + + btrfs_list_subvols(); + return 0; +} + +U_BOOT_CMD(btrsubvol, 3, 1, do_btrsubvol, + "list subvolumes of a BTRFS filesystem", + "<interface> <dev[:part]>\n" + " - List subvolumes of a BTRFS filesystem." +) diff --git a/roms/u-boot/cmd/button.c b/roms/u-boot/cmd/button.c new file mode 100644 index 000000000..1b45d0a2a --- /dev/null +++ b/roms/u-boot/cmd/button.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com> + * + * Based on led.c + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <button.h> +#include <dm/uclass-internal.h> + +static const char *const state_label[] = { + [BUTTON_OFF] = "off", + [BUTTON_ON] = "on", +}; + +static int show_button_state(struct udevice *dev) +{ + int ret; + + ret = button_get_state(dev); + if (ret >= BUTTON_COUNT) + ret = -EINVAL; + if (ret >= 0) + printf("%s\n", state_label[ret]); + + return ret; +} + +static int list_buttons(void) +{ + struct udevice *dev; + int ret; + + for (uclass_find_first_device(UCLASS_BUTTON, &dev); + dev; + uclass_find_next_device(&dev)) { + struct button_uc_plat *plat = dev_get_uclass_plat(dev); + + if (!plat->label) + continue; + printf("%-15s ", plat->label); + if (device_active(dev)) { + ret = show_button_state(dev); + if (ret < 0) + printf("Error %d\n", ret); + } else { + printf("<inactive>\n"); + } + } + + return 0; +} + +int do_button(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *button_label; + struct udevice *dev; + int ret; + + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + button_label = argv[1]; + if (strncmp(button_label, "list", 4) == 0) + return list_buttons(); + + ret = button_get_by_label(button_label, &dev); + if (ret) { + printf("Button '%s' not found (err=%d)\n", button_label, ret); + return CMD_RET_FAILURE; + } + + ret = show_button_state(dev); + + return !ret; +} + +U_BOOT_CMD( + button, 2, 1, do_button, + "manage buttons", + "<button_label> \tGet button state\n" + "button list\t\tShow a list of buttons" +); diff --git a/roms/u-boot/cmd/cache.c b/roms/u-boot/cmd/cache.c new file mode 100644 index 000000000..b68d45b98 --- /dev/null +++ b/roms/u-boot/cmd/cache.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Cache support: switch on or off, get status + */ +#include <common.h> +#include <command.h> +#include <cpu_func.h> +#include <linux/compiler.h> + +static int parse_argv(const char *); + +void __weak invalidate_icache_all(void) +{ + /* please define arch specific invalidate_icache_all */ + puts("No arch specific invalidate_icache_all available!\n"); +} + +__weak void noncached_set_region(void) +{ +} + +static int do_icache(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + switch (argc) { + case 2: /* on / off / flush */ + switch (parse_argv(argv[1])) { + case 0: + icache_disable(); + break; + case 1: + icache_enable(); + break; + case 2: + invalidate_icache_all(); + break; + default: + return CMD_RET_USAGE; + } + break; + case 1: /* get status */ + printf("Instruction Cache is %s\n", + icache_status() ? "ON" : "OFF"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +void __weak flush_dcache_all(void) +{ + puts("No arch specific flush_dcache_all available!\n"); + /* please define arch specific flush_dcache_all */ +} + +static int do_dcache(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + switch (argc) { + case 2: /* on / off / flush */ + switch (parse_argv(argv[1])) { + case 0: + dcache_disable(); + break; + case 1: + dcache_enable(); + noncached_set_region(); + break; + case 2: + flush_dcache_all(); + break; + default: + return CMD_RET_USAGE; + } + break; + case 1: /* get status */ + printf("Data (writethrough) Cache is %s\n", + dcache_status() ? "ON" : "OFF"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +static int parse_argv(const char *s) +{ + if (strcmp(s, "flush") == 0) + return 2; + else if (strcmp(s, "on") == 0) + return 1; + else if (strcmp(s, "off") == 0) + return 0; + + return -1; +} + + +U_BOOT_CMD( + icache, 2, 1, do_icache, + "enable or disable instruction cache", + "[on, off, flush]\n" + " - enable, disable, or flush instruction cache" +); + +U_BOOT_CMD( + dcache, 2, 1, do_dcache, + "enable or disable data cache", + "[on, off, flush]\n" + " - enable, disable, or flush data (writethrough) cache" +); diff --git a/roms/u-boot/cmd/cbfs.c b/roms/u-boot/cmd/cbfs.c new file mode 100644 index 000000000..10c2c929c --- /dev/null +++ b/roms/u-boot/cmd/cbfs.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + */ + +/* + * CBFS commands + */ +#include <common.h> +#include <command.h> +#include <env.h> +#include <cbfs.h> + +static int do_cbfs_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uintptr_t end_of_rom = 0xffffffff; + char *ep; + + if (argc > 2) { + printf("usage: cbfsls [end of rom]>\n"); + return 0; + } + if (argc == 2) { + end_of_rom = simple_strtoul(argv[1], &ep, 16); + if (*ep) { + puts("\n** Invalid end of ROM **\n"); + return 1; + } + } + if (file_cbfs_init(end_of_rom)) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + return 0; +} + +U_BOOT_CMD( + cbfsinit, 2, 0, do_cbfs_init, + "initialize the cbfs driver", + "[end of rom]\n" + " - Initialize the cbfs driver. The optional 'end of rom'\n" + " parameter specifies where the end of the ROM is that the\n" + " CBFS is in. It defaults to 0xFFFFFFFF\n" +); + +static int do_cbfs_fsload(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct cbfs_cachenode *file; + unsigned long offset; + unsigned long count; + long size; + + if (argc < 3) { + printf("usage: cbfsload <addr> <filename> [bytes]\n"); + return 1; + } + + /* parse offset and count */ + offset = simple_strtoul(argv[1], NULL, 16); + if (argc == 4) + count = simple_strtoul(argv[3], NULL, 16); + else + count = 0; + + file = file_cbfs_find(argv[2]); + if (!file) { + if (cbfs_get_result() == CBFS_FILE_NOT_FOUND) + printf("%s: %s\n", file_cbfs_error(), argv[2]); + else + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf("reading %s\n", file_cbfs_name(file)); + + size = file_cbfs_read(file, (void *)offset, count); + + printf("\n%ld bytes read\n", size); + + env_set_hex("filesize", size); + + return 0; +} + +U_BOOT_CMD( + cbfsload, 4, 0, do_cbfs_fsload, + "load binary file from a cbfs filesystem", + "<addr> <filename> [bytes]\n" + " - load binary file 'filename' from the cbfs to address 'addr'\n" +); + +static int do_cbfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct cbfs_cachenode *file = file_cbfs_get_first(); + int files = 0; + + if (!file) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf(" size type name\n"); + printf("------------------------------------------\n"); + while (file) { + int type = file_cbfs_type(file); + char *type_name = NULL; + const char *filename = file_cbfs_name(file); + + printf(" %8d", file_cbfs_size(file)); + + switch (type) { + case CBFS_TYPE_BOOTBLOCK: + type_name = "bootblock"; + break; + case CBFS_TYPE_CBFSHEADER: + type_name = "cbfs header"; + break; + case CBFS_TYPE_STAGE: + type_name = "stage"; + break; + case CBFS_TYPE_PAYLOAD: + type_name = "payload"; + break; + case CBFS_TYPE_FIT: + type_name = "fit"; + break; + case CBFS_TYPE_OPTIONROM: + type_name = "option rom"; + break; + case CBFS_TYPE_BOOTSPLASH: + type_name = "boot splash"; + break; + case CBFS_TYPE_RAW: + type_name = "raw"; + break; + case CBFS_TYPE_VSA: + type_name = "vsa"; + break; + case CBFS_TYPE_MBI: + type_name = "mbi"; + break; + case CBFS_TYPE_MICROCODE: + type_name = "microcode"; + break; + case CBFS_TYPE_FSP: + type_name = "fsp"; + break; + case CBFS_TYPE_MRC: + type_name = "mrc"; + break; + case CBFS_TYPE_MMA: + type_name = "mma"; + break; + case CBFS_TYPE_EFI: + type_name = "efi"; + break; + case CBFS_TYPE_STRUCT: + type_name = "struct"; + break; + case CBFS_TYPE_CMOS_DEFAULT: + type_name = "cmos default"; + break; + case CBFS_TYPE_SPD: + type_name = "spd"; + break; + case CBFS_TYPE_MRC_CACHE: + type_name = "mrc cache"; + break; + case CBFS_TYPE_CMOS_LAYOUT: + type_name = "cmos layout"; + break; + case -1: + case 0: + type_name = "null"; + break; + } + if (type_name) + printf(" %16s", type_name); + else + printf(" %16d", type); + + if (filename[0]) + printf(" %s\n", filename); + else + printf(" %s\n", "(empty)"); + file_cbfs_get_next(&file); + files++; + } + + printf("\n%d file(s)\n\n", files); + return 0; +} + +U_BOOT_CMD( + cbfsls, 1, 1, do_cbfs_ls, + "list files", + " - list the files in the cbfs\n" +); + +static int do_cbfs_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct cbfs_header *header = file_cbfs_get_header(); + + if (!header) { + printf("%s.\n", file_cbfs_error()); + return 1; + } + + printf("\n"); + printf("CBFS version: %#x\n", header->version); + printf("ROM size: %#x\n", header->rom_size); + printf("Boot block size: %#x\n", header->boot_block_size); + printf("CBFS size: %#x\n", + header->rom_size - header->boot_block_size - header->offset); + printf("Alignment: %d\n", header->align); + printf("Offset: %#x\n", header->offset); + printf("\n"); + + return 0; +} + +U_BOOT_CMD( + cbfsinfo, 1, 1, do_cbfs_fsinfo, + "print information about filesystem", + " - print information about the cbfs filesystem\n" +); diff --git a/roms/u-boot/cmd/clk.c b/roms/u-boot/cmd/clk.c new file mode 100644 index 000000000..e3c3d2f9b --- /dev/null +++ b/roms/u-boot/cmd/clk.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Xilinx, Inc. + */ +#include <common.h> +#include <command.h> +#include <clk.h> +#if defined(CONFIG_DM) && defined(CONFIG_CLK) +#include <dm.h> +#include <dm/device.h> +#include <dm/root.h> +#include <dm/device-internal.h> +#include <linux/clk-provider.h> +#endif + +#if defined(CONFIG_DM) && defined(CONFIG_CLK) +static void show_clks(struct udevice *dev, int depth, int last_flag) +{ + int i, is_last; + struct udevice *child; + struct clk *clkp; + u32 rate; + + clkp = dev_get_clk_ptr(dev); + if (device_get_uclass_id(dev) == UCLASS_CLK && clkp) { + depth++; + rate = clk_get_rate(clkp); + + printf(" %-12u %8d ", rate, clkp->enable_count); + + for (i = depth; i >= 0; i--) { + is_last = (last_flag >> i) & 1; + if (i) { + if (is_last) + printf(" "); + else + printf("| "); + } else { + if (is_last) + printf("`-- "); + else + printf("|-- "); + } + } + + printf("%s\n", dev->name); + } + + list_for_each_entry(child, &dev->child_head, sibling_node) { + is_last = list_is_last(&child->sibling_node, &dev->child_head); + show_clks(child, depth, (last_flag << 1) | is_last); + } +} + +int __weak soc_clk_dump(void) +{ + struct udevice *root; + + root = dm_root(); + if (root) { + printf(" Rate Usecnt Name\n"); + printf("------------------------------------------\n"); + show_clks(root, -1, 0); + } + + return 0; +} +#else +int __weak soc_clk_dump(void) +{ + puts("Not implemented\n"); + return 1; +} +#endif + +static int do_clk_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + ret = soc_clk_dump(); + if (ret < 0) { + printf("Clock dump error %d\n", ret); + ret = CMD_RET_FAILURE; + } + + return ret; +} + +static struct cmd_tbl cmd_clk_sub[] = { + U_BOOT_CMD_MKENT(dump, 1, 1, do_clk_dump, "", ""), +}; + +static int do_clk(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'clk' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_clk_sub[0], ARRAY_SIZE(cmd_clk_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char clk_help_text[] = + "dump - Print clock frequencies"; +#endif + +U_BOOT_CMD(clk, 2, 1, do_clk, "CLK sub-system", clk_help_text); diff --git a/roms/u-boot/cmd/clone.c b/roms/u-boot/cmd/clone.c new file mode 100644 index 000000000..32473a032 --- /dev/null +++ b/roms/u-boot/cmd/clone.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 John Chau <john@harmon.hk> + * + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <part.h> +#include <blk.h> +#include <vsprintf.h> + +#define BUFSIZE (1 * 1024 * 1024) +static int do_clone(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + int srcdev, destdev; + struct blk_desc *srcdesc, *destdesc; + int srcbz, destbz, ret; + char *unit, *buf; + unsigned long wrcnt, rdcnt, requested, srcblk, destblk; + unsigned long timer; + const unsigned long buffersize = 1024 * 1024; + + if (argc < 6) + return CMD_RET_USAGE; + + srcdev = blk_get_device_by_str(argv[1], argv[2], &srcdesc); + destdev = blk_get_device_by_str(argv[3], argv[4], &destdesc); + if (srcdev < 0) { + printf("Unable to open source device\n"); + return 1; + } else if (destdev < 0) { + printf("Unable to open destination device\n"); + return 1; + } + requested = simple_strtoul(argv[5], &unit, 10); + srcbz = srcdesc->blksz; + destbz = destdesc->blksz; + + if ((srcbz * (buffersize / srcbz) != buffersize) || + (destbz * (buffersize / destbz) != buffersize)) { + printf("failed: cannot match device block sizes\n"); + return 1; + } + if (requested == 0) { + unsigned long a = srcdesc->lba * srcdesc->blksz; + unsigned long b = destdesc->lba * destdesc->blksz; + + if (a > b) + requested = a; + else + requested = b; + } else { + switch (unit[0]) { + case 'g': + case 'G': + requested *= 1024 * 1024 * 1024; + break; + case 'm': + case 'M': + requested *= 1024 * 1024; + break; + case 'k': + case 'K': + requested *= 1024; + break; + } + } + printf("Copying %ld bytes from %s:%s to %s:%s\n", + requested, argv[1], argv[2], argv[3], argv[4]); + wrcnt = 0; + rdcnt = 0; + buf = (char *)malloc(BUFSIZE); + srcblk = 0; + destblk = 0; + timer = get_timer(0); + while (wrcnt < requested) { + unsigned long toread = BUFSIZE / srcbz; + unsigned long towrite = BUFSIZE / destbz; + unsigned long offset = 0; + +read: + ret = blk_dread(srcdesc, srcblk, toread, buf + offset); + if (ret < 0) { + printf("Src read error @blk %ld\n", srcblk); + goto exit; + } + rdcnt += ret * srcbz; + srcblk += ret; + if (ret < toread) { + toread -= ret; + offset += ret * srcbz; + goto read; + } + offset = 0; +write: + ret = blk_dwrite(destdesc, destblk, towrite, buf + offset); + if (ret < 0) { + printf("Dest write error @blk %ld\n", srcblk); + goto exit; + } + wrcnt += ret * destbz; + destblk += ret; + if (ret < towrite) { + towrite -= ret; + offset += ret * destbz; + goto write; + } + } + +exit: + timer = get_timer(timer); + timer = 1000 * timer / CONFIG_SYS_HZ; + printf("%ld read\n", rdcnt); + printf("%ld written\n", wrcnt); + printf("%ldms, %ldkB/s\n", timer, wrcnt / timer); + free(buf); + + return 0; +} + +U_BOOT_CMD( + clone, 6, 1, do_clone, + "simple storage cloning", + "<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\n" + "clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)" +); diff --git a/roms/u-boot/cmd/cls.c b/roms/u-boot/cmd/cls.c new file mode 100644 index 000000000..eab4e6993 --- /dev/null +++ b/roms/u-boot/cmd/cls.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * DENX Software Engineering, Anatolij Gustschin <agust@denx.de> + * + * cls - clear screen command + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <lcd.h> +#include <video.h> + +static int do_video_clear(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#if defined(CONFIG_DM_VIDEO) + struct udevice *dev; + + if (uclass_first_device_err(UCLASS_VIDEO, &dev)) + return CMD_RET_FAILURE; + + if (video_clear(dev)) + return CMD_RET_FAILURE; +#elif defined(CONFIG_CFB_CONSOLE) + video_clear(); +#elif defined(CONFIG_LCD) + lcd_clear(); +#else + return CMD_RET_FAILURE; +#endif + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(cls, 1, 1, do_video_clear, "clear screen", ""); diff --git a/roms/u-boot/cmd/config.c b/roms/u-boot/cmd/config.c new file mode 100644 index 000000000..cf30841a3 --- /dev/null +++ b/roms/u-boot/cmd/config.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com> + */ + +#include <common.h> +#include <command.h> +#include <gzip.h> +#include <malloc.h> + +#include "config_data_gz.h" +#include "config_data_size.h" + +static int do_config(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *dst; + unsigned long len = data_size; + int ret = CMD_RET_SUCCESS; + + dst = malloc(data_size + 1); + if (!dst) + return CMD_RET_FAILURE; + + ret = gunzip(dst, data_size, (unsigned char *)data_gz, &len); + if (ret) { + printf("failed to uncompress .config data\n"); + ret = CMD_RET_FAILURE; + goto free; + } + + dst[data_size] = 0; + puts(dst); + +free: + free(dst); + + return ret; +} + +U_BOOT_CMD( + config, 1, 1, do_config, + "print .config", + "" +); diff --git a/roms/u-boot/cmd/conitrace.c b/roms/u-boot/cmd/conitrace.c new file mode 100644 index 000000000..d50f3bf3c --- /dev/null +++ b/roms/u-boot/cmd/conitrace.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'conitrace' command prints the codes received from the console input as + * hexadecimal numbers. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ +#include <common.h> +#include <command.h> +#include <linux/delay.h> + +static int do_conitrace(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool first = true; + + printf("Waiting for your input\n"); + printf("To terminate type 'x'\n"); + + /* Empty input buffer */ + while (tstc()) + getchar(); + + for (;;) { + int c = getchar(); + + if (first && (c == 'x' || c == 'X')) + break; + + printf("%02x ", c); + first = false; + + /* 10 ms delay - serves to detect separate keystrokes */ + udelay(10000); + if (!tstc()) { + printf("\n"); + first = true; + } + } + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_SYS_LONGHELP +static char conitrace_help_text[] = ""; +#endif + +U_BOOT_CMD_COMPLETE( + conitrace, 2, 0, do_conitrace, + "trace console input", + conitrace_help_text, NULL +); diff --git a/roms/u-boot/cmd/console.c b/roms/u-boot/cmd/console.c new file mode 100644 index 000000000..9a1db83c7 --- /dev/null +++ b/roms/u-boot/cmd/console.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <stdio_dev.h> + +extern void _do_coninfo (void); +static int do_coninfo(struct cmd_tbl *cmd, int flag, int argc, + char *const argv[]) +{ + int l; + struct list_head *list = stdio_get_list(); + struct list_head *pos; + struct stdio_dev *dev; + + /* Scan for valid output and input devices */ + + puts ("List of available devices:\n"); + + list_for_each(pos, list) { + dev = list_entry(pos, struct stdio_dev, list); + + printf ("%-8s %08x %c%c ", + dev->name, + dev->flags, + (dev->flags & DEV_FLAGS_INPUT) ? 'I' : '.', + (dev->flags & DEV_FLAGS_OUTPUT) ? 'O' : '.'); + + for (l = 0; l < MAX_FILES; l++) { + if (stdio_devices[l] == dev) { + printf ("%s ", stdio_names[l]); + } + } + putc ('\n'); + } + return 0; +} + + +/***************************************************/ + +U_BOOT_CMD( + coninfo, 3, 1, do_coninfo, + "print console devices and information", + "" +); diff --git a/roms/u-boot/cmd/cpu.c b/roms/u-boot/cmd/cpu.c new file mode 100644 index 000000000..67dbb044b --- /dev/null +++ b/roms/u-boot/cmd/cpu.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * Copyright (c) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <cpu.h> +#include <dm.h> +#include <errno.h> + +static const char *cpu_feature_name[CPU_FEAT_COUNT] = { + "L1 cache", + "MMU", + "Microcode", + "Device ID", +}; + +static int print_cpu_list(bool detail) +{ + struct udevice *dev; + char buf[100]; + + for (uclass_first_device(UCLASS_CPU, &dev); + dev; + uclass_next_device(&dev)) { + struct cpu_plat *plat = dev_get_parent_plat(dev); + struct cpu_info info; + bool first = true; + int ret, i; + + ret = cpu_get_desc(dev, buf, sizeof(buf)); + printf("%3d: %-10s %s\n", dev_seq(dev), dev->name, + ret ? "<no description>" : buf); + if (!detail) + continue; + ret = cpu_get_info(dev, &info); + if (ret) { + printf("\t(no detail available"); + if (ret != -ENOSYS) + printf(": err=%d", ret); + printf(")\n"); + continue; + } + printf("\tID = %d, freq = ", plat->cpu_id); + print_freq(info.cpu_freq, ""); + for (i = 0; i < CPU_FEAT_COUNT; i++) { + if (info.features & (1 << i)) { + printf("%s%s", first ? ": " : ", ", + cpu_feature_name[i]); + first = false; + } + } + printf("\n"); + if (info.features & (1 << CPU_FEAT_UCODE)) + printf("\tMicrocode version %#x\n", + plat->ucode_version); + if (info.features & (1 << CPU_FEAT_DEVICE_ID)) + printf("\tDevice ID %#lx\n", plat->device_id); + } + + return 0; +} + +static int do_cpu_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (print_cpu_list(false)) + return CMD_RET_FAILURE; + + return 0; +} + +static int do_cpu_detail(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (print_cpu_list(true)) + return CMD_RET_FAILURE; + + return 0; +} + +static struct cmd_tbl cmd_cpu_sub[] = { + U_BOOT_CMD_MKENT(list, 2, 1, do_cpu_list, "", ""), + U_BOOT_CMD_MKENT(detail, 4, 0, do_cpu_detail, "", ""), +}; + +/* + * Process a cpu sub-command + */ +static int do_cpu(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c = NULL; + + /* Strip off leading 'cpu' command argument */ + argc--; + argv++; + + if (argc) + c = find_cmd_tbl(argv[0], cmd_cpu_sub, + ARRAY_SIZE(cmd_cpu_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + cpu, 2, 1, do_cpu, + "display information about CPUs", + "list - list available CPUs\n" + "cpu detail - show CPU detail" +); diff --git a/roms/u-boot/cmd/cramfs.c b/roms/u-boot/cmd/cramfs.c new file mode 100644 index 000000000..1aeb56702 --- /dev/null +++ b/roms/u-boot/cmd/cramfs.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * + * based on: cmd_jffs2.c + * + * Add support for a CRAMFS located in RAM + */ + + +/* + * CRAMFS support + */ +#include <common.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <jffs2/jffs2.h> +#include <jffs2/load_kernel.h> +#include <cramfs/cramfs_fs.h> +#include <asm/io.h> + +/* enable/disable debugging messages */ +#define DEBUG_CRAMFS +#undef DEBUG_CRAMFS + +#ifdef DEBUG_CRAMFS +# define DEBUGF(fmt, args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt, args...) +#endif + +#include <flash.h> + +#ifndef CONFIG_MTD_NOR_FLASH +# define OFFSET_ADJUSTMENT 0 +#else +# define OFFSET_ADJUSTMENT (flash_info[id.num].start[0]) +#endif + +#ifndef CONFIG_FS_JFFS2 +#include <linux/stat.h> +char *mkmodestr(unsigned long mode, char *str) +{ + static const char *l = "xwr"; + int mask = 1, i; + char c; + + switch (mode & S_IFMT) { + case S_IFDIR: str[0] = 'd'; break; + case S_IFBLK: str[0] = 'b'; break; + case S_IFCHR: str[0] = 'c'; break; + case S_IFIFO: str[0] = 'f'; break; + case S_IFLNK: str[0] = 'l'; break; + case S_IFSOCK: str[0] = 's'; break; + case S_IFREG: str[0] = '-'; break; + default: str[0] = '?'; + } + + for(i = 0; i < 9; i++) { + c = l[i%3]; + str[9-i] = (mode & mask)?c:'-'; + mask = mask<<1; + } + + if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S'; + if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S'; + if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T'; + str[10] = '\0'; + return str; +} +#endif /* CONFIG_FS_JFFS2 */ + +extern int cramfs_check (struct part_info *info); +extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); +extern int cramfs_ls (struct part_info *info, char *filename); +extern int cramfs_info (struct part_info *info); + +/***************************************************/ +/* U-Boot commands */ +/***************************************************/ + +/** + * Routine implementing fsload u-boot command. This routine tries to load + * a requested file from cramfs filesystem at location 'cramfsaddr'. + * cramfsaddr is an evironment variable. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_cramfs_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *filename; + int size; + ulong offset = image_load_addr; + char *offset_virt; + + struct part_info part; + struct mtd_device dev; + struct mtdids id; + + ulong addr; + addr = simple_strtoul(env_get("cramfsaddr"), NULL, 16); + + /* hack! */ + /* cramfs_* only supports NOR flash chips */ + /* fake the device type */ + id.type = MTD_DEV_TYPE_NOR; + id.num = 0; + dev.id = &id; + part.dev = &dev; + /* fake the address offset */ + part.offset = (u64)(uintptr_t) map_sysmem(addr - OFFSET_ADJUSTMENT, 0); + + /* pre-set Boot file name */ + filename = env_get("bootfile"); + if (!filename) + filename = "uImage"; + + if (argc == 2) { + filename = argv[1]; + } + if (argc == 3) { + offset = simple_strtoul(argv[1], NULL, 0); + image_load_addr = offset; + filename = argv[2]; + } + + offset_virt = map_sysmem(offset, 0); + size = 0; + if (cramfs_check(&part)) + size = cramfs_load (offset_virt, &part, filename); + + if (size > 0) { + printf("### CRAMFS load complete: %d bytes loaded to 0x%lx\n", + size, offset); + env_set_hex("filesize", size); + } else { + printf("### CRAMFS LOAD ERROR<%x> for %s!\n", size, filename); + } + + unmap_sysmem(offset_virt); + unmap_sysmem((void *)(uintptr_t)part.offset); + + return !(size > 0); +} + +/** + * Routine implementing u-boot ls command which lists content of a given + * directory at location 'cramfsaddr'. + * cramfsaddr is an evironment variable. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_cramfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename = "/"; + int ret; + struct part_info part; + struct mtd_device dev; + struct mtdids id; + + ulong addr; + addr = simple_strtoul(env_get("cramfsaddr"), NULL, 16); + + /* hack! */ + /* cramfs_* only supports NOR flash chips */ + /* fake the device type */ + id.type = MTD_DEV_TYPE_NOR; + id.num = 0; + dev.id = &id; + part.dev = &dev; + /* fake the address offset */ + part.offset = (u64)(uintptr_t) map_sysmem(addr - OFFSET_ADJUSTMENT, 0); + + if (argc == 2) + filename = argv[1]; + + ret = 0; + if (cramfs_check(&part)) + ret = cramfs_ls (&part, filename); + unmap_sysmem((void *)(uintptr_t)part.offset); + + return ret ? 0 : 1; +} + +/* command line only */ + +/***************************************************/ +U_BOOT_CMD( + cramfsload, 3, 0, do_cramfs_load, + "load binary file from a filesystem image", + "[ off ] [ filename ]\n" + " - load binary file from address 'cramfsaddr'\n" + " with offset 'off'\n" +); +U_BOOT_CMD( + cramfsls, 2, 1, do_cramfs_ls, + "list files in a directory (default /)", + "[ directory ]\n" + " - list files in a directory.\n" +); diff --git a/roms/u-boot/cmd/cros_ec.c b/roms/u-boot/cmd/cros_ec.c new file mode 100644 index 000000000..eb5053d64 --- /dev/null +++ b/roms/u-boot/cmd/cros_ec.c @@ -0,0 +1,570 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2016 The Chromium OS Authors. + * Copyright (c) 2016 National Instruments Corp + */ + +#include <common.h> +#include <command.h> +#include <cros_ec.h> +#include <dm.h> +#include <flash.h> +#include <log.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* Note: depends on enum ec_current_image */ +static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; + +/** + * Decode a flash region parameter + * + * @param argc Number of params remaining + * @param argv List of remaining parameters + * @return flash region (EC_FLASH_REGION_...) or -1 on error + */ +static int cros_ec_decode_region(int argc, char *const argv[]) +{ + if (argc > 0) { + if (0 == strcmp(*argv, "rw")) + return EC_FLASH_REGION_ACTIVE; + else if (0 == strcmp(*argv, "ro")) + return EC_FLASH_REGION_RO; + + debug("%s: Invalid region '%s'\n", __func__, *argv); + } else { + debug("%s: Missing region parameter\n", __func__); + } + + return -1; +} + +/** + * Perform a flash read or write command + * + * @param dev CROS-EC device to read/write + * @param is_write 1 do to a write, 0 to do a read + * @param argc Number of arguments + * @param argv Arguments (2 is region, 3 is address) + * @return 0 for ok, 1 for a usage error or -ve for ec command error + * (negative EC_RES_...) + */ +static int do_read_write(struct udevice *dev, int is_write, int argc, + char *const argv[]) +{ + uint32_t offset, size = -1U, region_size; + unsigned long addr; + char *endp; + int region; + int ret; + + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == -1) + return 1; + if (argc < 4) + return 1; + addr = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return 1; + if (argc > 4) { + size = simple_strtoul(argv[4], &endp, 16); + if (*argv[4] == 0 || *endp != 0) + return 1; + } + + ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + return ret; + } + if (size == -1U) + size = region_size; + + ret = is_write ? + cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : + cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); + if (ret) { + debug("%s: Could not %s region\n", __func__, + is_write ? "write" : "read"); + return ret; + } + + return 0; +} + +static const char *const feat_name[64] = { + "limited", + "flash", + "pwm_fan", + "pwm_keyb", + "lightbar", + "led", + "motion_sense", + "keyb", + "pstore", + "port80", + "thermal", + "bklight_switch", + "wifi_switch", + "host_events", + "gpio", + "i2c", + "charger", + "battery", + "smart_battery", + "hang_detect", + "pmu", + "sub_mcu", + "usb_pd", + "usb_mux", + "motion_sense_fifo", + "vstore", + "usbc_ss_mux_virtual", + "rtc", + "fingerprint", + "touchpad", + "rwsig", + "device_event", + "unified_wake_masks", + "host_event64", + "exec_in_ram", + "cec", + "motion_sense_tight_timestamps", + "refined_tablet_mode_hysteresis", + "efs2", + "scp", + "ish", + "typec_cmd", + "typec_require_ap_mode_entry", + "typec_mux_require_ap_ack", +}; + +static int do_show_features(struct udevice *dev) +{ + u64 feat; + int ret; + uint i; + + ret = cros_ec_get_features(dev, &feat); + if (ret) + return ret; + for (i = 0; i < ARRAY_SIZE(feat_name); i++) { + if (feat & (1ULL << i)) { + if (feat_name[i]) + printf("%s\n", feat_name[i]); + else + printf("unknown %d\n", i); + } + } + + return 0; +} + +static const char *const switch_name[8] = { + "lid open", + "power button pressed", + "write-protect disabled", + NULL, + "dedicated recovery", + NULL, + NULL, + NULL, +}; + +static int do_show_switches(struct udevice *dev) +{ + uint switches; + int ret; + uint i; + + ret = cros_ec_get_switches(dev); + if (ret < 0) + return log_msg_ret("get", ret); + switches = ret; + for (i = 0; i < ARRAY_SIZE(switch_name); i++) { + uint mask = 1 << i; + + if (switches & mask) { + if (switch_name[i]) + printf("%s\n", switch_name[i]); + else + printf("unknown %02x\n", mask); + } + } + + return 0; +} + +static const char *const event_name[] = { + "lid_closed", + "lid_open", + "power_button", + "ac_connected", + "ac_disconnected", + "battery_low", + "battery_critical", + "battery", + "thermal_threshold", + "device", + "thermal", + "usb_charger", + "key_pressed", + "interface_ready", + "keyboard_recovery", + "thermal_shutdown", + "battery_shutdown", + "throttle_start", + "throttle_stop", + "hang_detect", + "hang_reboot", + "pd_mcu", + "battery_status", + "panic", + "keyboard_fastboot", + "rtc", + "mkbp", + "usb_mux", + "mode_change", + "keyboard_recovery_hw_reinit", + "extended", + "invalid", +}; + +static int do_show_events(struct udevice *dev) +{ + u32 events; + int ret; + uint i; + + ret = cros_ec_get_host_events(dev, &events); + if (ret) + return ret; + printf("%08x\n", events); + for (i = 0; i < ARRAY_SIZE(event_name); i++) { + enum host_event_code code = i + 1; + u64 mask = EC_HOST_EVENT_MASK(code); + + if (events & mask) { + if (event_name[i]) + printf("%s\n", event_name[i]); + else + printf("unknown code %#x\n", code); + } + } + + return 0; +} + +static int do_cros_ec(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + const char *cmd; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + if (0 == strcmp("init", cmd)) { + /* Remove any existing device */ + ret = uclass_find_device(UCLASS_CROS_EC, 0, &dev); + if (!ret) + device_remove(dev, DM_REMOVE_NORMAL); + ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev); + if (ret) { + printf("Could not init cros_ec device (err %d)\n", ret); + return 1; + } + return 0; + } + + ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev); + if (ret) { + printf("Cannot get cros-ec device (err=%d)\n", ret); + return 1; + } + if (0 == strcmp("id", cmd)) { + char id[MSG_BYTES]; + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return 1; + } + printf("%s\n", id); + } else if (0 == strcmp("info", cmd)) { + struct ec_response_mkbp_info info; + + if (cros_ec_info(dev, &info)) { + debug("%s: Could not read KBC info\n", __func__); + return 1; + } + printf("rows = %u\n", info.rows); + printf("cols = %u\n", info.cols); + } else if (!strcmp("features", cmd)) { + ret = do_show_features(dev); + + if (ret) + printf("Error: %d\n", ret); + } else if (!strcmp("switches", cmd)) { + ret = do_show_switches(dev); + + if (ret) + printf("Error: %d\n", ret); + } else if (0 == strcmp("curimage", cmd)) { + enum ec_current_image image; + + if (cros_ec_read_current_image(dev, &image)) { + debug("%s: Could not read KBC image\n", __func__); + return 1; + } + printf("%d\n", image); + } else if (0 == strcmp("hash", cmd)) { + struct ec_response_vboot_hash hash; + int i; + + if (cros_ec_read_hash(dev, EC_VBOOT_HASH_OFFSET_ACTIVE, &hash)) { + debug("%s: Could not read KBC hash\n", __func__); + return 1; + } + + if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) + printf("type: SHA-256\n"); + else + printf("type: %d\n", hash.hash_type); + + printf("offset: 0x%08x\n", hash.offset); + printf("size: 0x%08x\n", hash.size); + + printf("digest: "); + for (i = 0; i < hash.digest_size; i++) + printf("%02x", hash.hash_digest[i]); + printf("\n"); + } else if (0 == strcmp("reboot", cmd)) { + int region; + enum ec_reboot_cmd cmd; + + if (argc >= 3 && !strcmp(argv[2], "cold")) { + cmd = EC_REBOOT_COLD; + } else { + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == EC_FLASH_REGION_RO) + cmd = EC_REBOOT_JUMP_RO; + else if (region == EC_FLASH_REGION_ACTIVE) + cmd = EC_REBOOT_JUMP_RW; + else + return CMD_RET_USAGE; + } + + if (cros_ec_reboot(dev, cmd, 0)) { + debug("%s: Could not reboot KBC\n", __func__); + return 1; + } + } else if (0 == strcmp("events", cmd)) { + ret = do_show_events(dev); + + if (ret) + printf("Error: %d\n", ret); + } else if (0 == strcmp("clrevents", cmd)) { + uint32_t events = 0x7fffffff; + + if (argc >= 3) + events = simple_strtol(argv[2], NULL, 0); + + if (cros_ec_clear_host_events(dev, events)) { + debug("%s: Could not clear host events\n", __func__); + return 1; + } + } else if (0 == strcmp("read", cmd)) { + ret = do_read_write(dev, 0, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("write", cmd)) { + ret = do_read_write(dev, 1, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("erase", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + if (cros_ec_flash_offset(dev, region, &offset, &size)) { + debug("%s: Could not read region info\n", __func__); + ret = -1; + } else { + ret = cros_ec_flash_erase(dev, offset, size); + if (ret) { + debug("%s: Could not erase region\n", + __func__); + } + } + } else if (0 == strcmp("regioninfo", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + ret = cros_ec_flash_offset(dev, region, &offset, &size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + } else { + printf("Region: %s\n", region == EC_FLASH_REGION_RO ? + "RO" : "RW"); + printf("Offset: %x\n", offset); + printf("Size: %x\n", size); + } + } else if (0 == strcmp("flashinfo", cmd)) { + struct ec_response_flash_info p; + + ret = cros_ec_read_flashinfo(dev, &p); + if (!ret) { + printf("Flash size: %u\n", p.flash_size); + printf("Write block size: %u\n", p.write_block_size); + printf("Erase block size: %u\n", p.erase_block_size); + } + } else if (0 == strcmp("vbnvcontext", cmd)) { + uint8_t block[EC_VBNV_BLOCK_SIZE]; + char buf[3]; + int i, len; + unsigned long result; + + if (argc <= 2) { + ret = cros_ec_read_nvdata(dev, block, + EC_VBNV_BLOCK_SIZE); + if (!ret) { + printf("vbnv_block: "); + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) + printf("%02x", block[i]); + putc('\n'); + } + } else { + /* + * TODO(clchiou): Move this to a utility function as + * cmd_spi might want to call it. + */ + memset(block, 0, EC_VBNV_BLOCK_SIZE); + len = strlen(argv[2]); + buf[2] = '\0'; + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { + if (i * 2 >= len) + break; + buf[0] = argv[2][i * 2]; + if (i * 2 + 1 >= len) + buf[1] = '0'; + else + buf[1] = argv[2][i * 2 + 1]; + strict_strtoul(buf, 16, &result); + block[i] = result; + } + ret = cros_ec_write_nvdata(dev, block, + EC_VBNV_BLOCK_SIZE); + } + if (ret) { + debug("%s: Could not %s VbNvContext\n", __func__, + argc <= 2 ? "read" : "write"); + } + } else if (0 == strcmp("test", cmd)) { + int result = cros_ec_test(dev); + + if (result) + printf("Test failed with error %d\n", result); + else + puts("Test passed\n"); + } else if (0 == strcmp("version", cmd)) { + struct ec_response_get_version *p; + char *build_string; + + ret = cros_ec_read_version(dev, &p); + if (!ret) { + /* Print versions */ + printf("RO version: %1.*s\n", + (int)sizeof(p->version_string_ro), + p->version_string_ro); + printf("RW version: %1.*s\n", + (int)sizeof(p->version_string_rw), + p->version_string_rw); + printf("Firmware copy: %s\n", + (p->current_image < + ARRAY_SIZE(ec_current_image_name) ? + ec_current_image_name[p->current_image] : + "?")); + ret = cros_ec_read_build_info(dev, &build_string); + if (!ret) + printf("Build info: %s\n", build_string); + } + } else if (0 == strcmp("ldo", cmd)) { + uint8_t index, state; + char *endp; + + if (argc < 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], &endp, 10); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_USAGE; + if (argc > 3) { + state = simple_strtoul(argv[3], &endp, 10); + if (*argv[3] == 0 || *endp != 0) + return CMD_RET_USAGE; + ret = cros_ec_set_ldo(dev, index, state); + } else { + ret = cros_ec_get_ldo(dev, index, &state); + if (!ret) { + printf("LDO%d: %s\n", index, + state == EC_LDO_STATE_ON ? + "on" : "off"); + } + } + + if (ret) { + debug("%s: Could not access LDO%d\n", __func__, index); + return ret; + } + } else if (!strcmp("sku", cmd)) { + ret = cros_ec_get_sku_id(dev); + + if (ret >= 0) { + printf("%d\n", ret); + ret = 0; + } else { + printf("Error: %d\n", ret); + } + } else { + return CMD_RET_USAGE; + } + + if (ret < 0) { + printf("Error: CROS-EC command failed (error %d)\n", ret); + ret = 1; + } + + return ret; +} + +U_BOOT_CMD( + crosec, 6, 1, do_cros_ec, + "CROS-EC utility command", + "init Re-init CROS-EC (done on startup automatically)\n" + "crosec id Read CROS-EC ID\n" + "crosec info Read CROS-EC info\n" + "crosec features Read CROS-EC features\n" + "crosec switches Read CROS-EC switches\n" + "crosec curimage Read CROS-EC current image\n" + "crosec hash Read CROS-EC hash\n" + "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" + "crosec events Read CROS-EC host events\n" + "crosec eventsb Read CROS-EC host events_b\n" + "crosec clrevents [mask] Clear CROS-EC host events\n" + "crosec regioninfo <ro|rw> Read image info\n" + "crosec flashinfo Read flash info\n" + "crosec erase <ro|rw> Erase EC image\n" + "crosec read <ro|rw> <addr> [<size>] Read EC image\n" + "crosec write <ro|rw> <addr> [<size>] Write EC image\n" + "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" + "crosec ldo <idx> [<state>] Switch/Read LDO state\n" + "crosec sku Read board SKU ID\n" + "crosec test run tests on cros_ec\n" + "crosec version Read CROS-EC version" +); diff --git a/roms/u-boot/cmd/dataflash_mmc_mux.c b/roms/u-boot/cmd/dataflash_mmc_mux.c new file mode 100644 index 000000000..73876e946 --- /dev/null +++ b/roms/u-boot/cmd/dataflash_mmc_mux.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> + +static int mmc_nspi (const char *); + +int do_dataflash_mmc_mux(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + switch (argc) { + case 2: /* on / off */ + switch (mmc_nspi (argv[1])) { + case 0: AT91F_SelectSPI (); + break; + case 1: AT91F_SelectMMC (); + break; + } + case 1: /* get status */ + printf ("Mux is configured to be %s\n", + AT91F_GetMuxStatus () ? "MMC" : "SPI"); + return 0; + default: + return CMD_RET_USAGE; + } + return 0; +} + +static int mmc_nspi (const char *s) +{ + if (strcmp (s, "mmc") == 0) { + return 1; + } else if (strcmp (s, "spi") == 0) { + return 0; + } + return -1; +} + +U_BOOT_CMD( + dataflash_mmc_mux, 2, 1, do_dataflash_mmc_mux, + "enable or disable MMC or SPI\n", + "[mmc, spi]\n" + " - enable or disable MMC or SPI" +); diff --git a/roms/u-boot/cmd/date.c b/roms/u-boot/cmd/date.c new file mode 100644 index 000000000..0e1189475 --- /dev/null +++ b/roms/u-boot/cmd/date.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * RTC, Date & Time support: get and set date & time + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <rtc.h> +#include <i2c.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char * const weekdays[] = { + "Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +#define RELOC(a) ((typeof(a))((unsigned long)(a) + gd->reloc_off)) +#else +#define RELOC(a) a +#endif + +int mk_date (const char *, struct rtc_time *); + +static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 }; + +static int do_date(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct rtc_time tm; + int rcode = 0; + int old_bus __maybe_unused; + + /* switch to correct I2C bus */ +#ifdef CONFIG_DM_RTC + struct udevice *dev; + + rcode = uclass_get_device(UCLASS_RTC, 0, &dev); + if (rcode) { + printf("Cannot find RTC: err=%d\n", rcode); + return CMD_RET_FAILURE; + } +#elif defined(CONFIG_SYS_I2C) + old_bus = i2c_get_bus_num(); + i2c_set_bus_num(CONFIG_SYS_RTC_BUS_NUM); +#else + old_bus = I2C_GET_BUS(); + I2C_SET_BUS(CONFIG_SYS_RTC_BUS_NUM); +#endif + + switch (argc) { + case 2: /* set date & time */ + if (strcmp(argv[1],"reset") == 0) { + puts ("Reset RTC...\n"); +#ifdef CONFIG_DM_RTC + rcode = dm_rtc_reset(dev); + if (!rcode) + rcode = dm_rtc_set(dev, &default_tm); +#else + rtc_reset(); + rcode = rtc_set(&default_tm); +#endif + if (rcode) + puts("## Failed to set date after RTC reset\n"); + } else { + /* initialize tm with current time */ +#ifdef CONFIG_DM_RTC + rcode = dm_rtc_get(dev, &tm); +#else + rcode = rtc_get(&tm); +#endif + if (!rcode) { + /* insert new date & time */ + if (mk_date(argv[1], &tm) != 0) { + puts ("## Bad date format\n"); + break; + } + /* and write to RTC */ +#ifdef CONFIG_DM_RTC + rcode = dm_rtc_set(dev, &tm); +#else + rcode = rtc_set(&tm); +#endif + if (rcode) { + printf("## Set date failed: err=%d\n", + rcode); + } + } else { + puts("## Get date failed\n"); + } + } + /* FALL TROUGH */ + case 1: /* get date & time */ +#ifdef CONFIG_DM_RTC + rcode = dm_rtc_get(dev, &tm); +#else + rcode = rtc_get(&tm); +#endif + if (rcode) { + puts("## Get date failed\n"); + break; + } + + printf ("Date: %4d-%02d-%02d (%sday) Time: %2d:%02d:%02d\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + (tm.tm_wday<0 || tm.tm_wday>6) ? + "unknown " : RELOC(weekdays[tm.tm_wday]), + tm.tm_hour, tm.tm_min, tm.tm_sec); + + break; + default: + rcode = CMD_RET_USAGE; + } + + /* switch back to original I2C bus */ +#ifdef CONFIG_SYS_I2C + i2c_set_bus_num(old_bus); +#elif !defined(CONFIG_DM_RTC) + I2C_SET_BUS(old_bus); +#endif + + return rcode ? CMD_RET_FAILURE : 0; +} + +/* + * simple conversion of two-digit string with error checking + */ +static int cnvrt2 (const char *str, int *valp) +{ + int val; + + if ((*str < '0') || (*str > '9')) + return (-1); + + val = *str - '0'; + + ++str; + + if ((*str < '0') || (*str > '9')) + return (-1); + + *valp = 10 * val + (*str - '0'); + + return (0); +} + +/* + * Convert date string: MMDDhhmm[[CC]YY][.ss] + * + * Some basic checking for valid values is done, but this will not catch + * all possible error conditions. + */ +int mk_date (const char *datestr, struct rtc_time *tmp) +{ + int len, val; + char *ptr; + + ptr = strchr(datestr, '.'); + len = strlen(datestr); + + /* Set seconds */ + if (ptr) { + int sec; + + ptr++; + if ((len - (ptr - datestr)) != 2) + return (-1); + + len -= 3; + + if (cnvrt2 (ptr, &sec)) + return (-1); + + tmp->tm_sec = sec; + } else { + tmp->tm_sec = 0; + } + + if (len == 12) { /* MMDDhhmmCCYY */ + int year, century; + + if (cnvrt2 (datestr+ 8, ¢ury) || + cnvrt2 (datestr+10, &year) ) { + return (-1); + } + tmp->tm_year = 100 * century + year; + } else if (len == 10) { /* MMDDhhmmYY */ + int year, century; + + century = tmp->tm_year / 100; + if (cnvrt2 (datestr+ 8, &year)) + return (-1); + tmp->tm_year = 100 * century + year; + } + + switch (len) { + case 8: /* MMDDhhmm */ + /* fall thru */ + case 10: /* MMDDhhmmYY */ + /* fall thru */ + case 12: /* MMDDhhmmCCYY */ + if (cnvrt2 (datestr+0, &val) || + val > 12) { + break; + } + tmp->tm_mon = val; + if (cnvrt2 (datestr+2, &val) || + val > ((tmp->tm_mon==2) ? 29 : 31)) { + break; + } + tmp->tm_mday = val; + + if (cnvrt2 (datestr+4, &val) || + val > 23) { + break; + } + tmp->tm_hour = val; + + if (cnvrt2 (datestr+6, &val) || + val > 59) { + break; + } + tmp->tm_min = val; + + /* calculate day of week */ + rtc_calc_weekday(tmp); + + return (0); + default: + break; + } + + return (-1); +} + +/***************************************************/ + +U_BOOT_CMD( + date, 2, 1, do_date, + "get/set/reset date & time", + "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n" + " - without arguments: print date & time\n" + " - with numeric argument: set the system date & time\n" + " - with 'reset' argument: reset the RTC" +); diff --git a/roms/u-boot/cmd/demo.c b/roms/u-boot/cmd/demo.c new file mode 100644 index 000000000..78a55f72b --- /dev/null +++ b/roms/u-boot/cmd/demo.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <dm-demo.h> +#include <mapmem.h> +#include <asm/io.h> + +struct udevice *demo_dev; + +static int do_demo_hello(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ch = 0; + + if (argc) + ch = *argv[0]; + + return demo_hello(demo_dev, ch); +} + +static int do_demo_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int status; + int ret; + + ret = demo_status(demo_dev, &status); + if (ret) + return ret; + + printf("Status: %d\n", status); + + return 0; +} + +static int do_demo_light(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int light; + int ret; + + if (argc) { + light = simple_strtoul(argv[0], NULL, 16); + ret = demo_set_light(demo_dev, light); + } else { + ret = demo_get_light(demo_dev); + if (ret >= 0) { + printf("Light: %x\n", ret); + ret = 0; + } + } + + return ret; +} + +int do_demo_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev; + int i, ret; + + puts("Demo uclass entries:\n"); + + for (i = 0, ret = uclass_first_device(UCLASS_DEMO, &dev); + dev; + ret = uclass_next_device(&dev)) { + printf("entry %d - instance %08x, ops %08x, plat %08x\n", + i++, (uint)map_to_sysmem(dev), + (uint)map_to_sysmem(dev->driver->ops), + (uint)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl demo_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""), + U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""), + U_BOOT_CMD_MKENT(light, 2, 1, do_demo_light, "", ""), + U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""), +}; + +static int do_demo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *demo_cmd; + int devnum = 0; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + demo_cmd = find_cmd_tbl(argv[1], demo_commands, + ARRAY_SIZE(demo_commands)); + argc -= 2; + argv += 2; + + if ((!demo_cmd || argc > demo_cmd->maxargs) || + ((demo_cmd->name[0] != 'l') && (argc < 1))) + return CMD_RET_USAGE; + + if (argc) { + devnum = simple_strtoul(argv[0], NULL, 10); + ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); + if (ret) + return cmd_process_error(cmdtp, ret); + argc--; + argv++; + } else { + demo_dev = NULL; + if (demo_cmd->cmd != do_demo_list) + return CMD_RET_USAGE; + } + + ret = demo_cmd->cmd(demo_cmd, flag, argc, argv); + + return cmd_process_error(demo_cmd, ret); +} + +U_BOOT_CMD( + demo, 4, 1, do_demo, + "Driver model (dm) demo operations", + "list List available demo devices\n" + "demo hello <num> [<char>] Say hello\n" + "demo light [<num>] Set or get the lights\n" + "demo status <num> Get demo device status\n" +); diff --git a/roms/u-boot/cmd/dfu.c b/roms/u-boot/cmd/dfu.c new file mode 100644 index 000000000..4a288f74c --- /dev/null +++ b/roms/u-boot/cmd/dfu.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_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 <watchdog.h> +#include <dfu.h> +#include <console.h> +#include <g_dnl.h> +#include <usb.h> +#include <net.h> + +static int do_dfu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + + if (argc < 2) + return CMD_RET_USAGE; + +#ifdef CONFIG_DFU_OVER_USB + char *usb_controller = argv[1]; +#endif +#if defined(CONFIG_DFU_OVER_USB) || defined(CONFIG_DFU_OVER_TFTP) + char *interface = NULL; + char *devstring = NULL; +#if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP) + unsigned long value = 0; +#endif + if (argc >= 4) { + interface = argv[2]; + devstring = argv[3]; + } + +#if defined(CONFIG_DFU_TIMEOUT) || defined(CONFIG_DFU_OVER_TFTP) + if (argc == 5 || argc == 3) + value = simple_strtoul(argv[argc - 1], NULL, 0); +#endif +#endif + + int ret = 0; +#ifdef CONFIG_DFU_OVER_TFTP + if (!strcmp(argv[1], "tftp")) + return update_tftp(value, interface, devstring); +#endif +#ifdef CONFIG_DFU_OVER_USB + ret = dfu_init_env_entities(interface, devstring); + if (ret) + goto done; + +#ifdef CONFIG_DFU_TIMEOUT + dfu_set_timeout(value * 1000); +#endif + + ret = CMD_RET_SUCCESS; + if (strcmp(argv[argc - 1], "list") == 0) { + dfu_show_entities(); + goto done; + } + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + bool retry = false; + do { + ret = run_usb_dnl_gadget(controller_index, "usb_dnl_dfu"); + + if (dfu_reinit_needed) { + dfu_free_entities(); + ret = dfu_init_env_entities(interface, devstring); + if (ret) + goto done; + retry = true; + } + } while (retry); + +done: + dfu_free_entities(); +#endif + return ret; +} + +U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, + "Device Firmware Upgrade", + "" +#ifdef CONFIG_DFU_OVER_USB +#ifdef CONFIG_DFU_TIMEOUT + "<USB_controller> [<interface> <dev>] [<timeout>] [list]\n" +#else + "<USB_controller> [<interface> <dev>] [list]\n" +#endif + " - device firmware upgrade via <USB_controller>\n" + " on device <dev>, attached to interface\n" + " <interface>\n" +#ifdef CONFIG_DFU_TIMEOUT + " [<timeout>] - specify inactivity timeout in seconds\n" +#endif + " [list] - list available alt settings\n" +#endif +#ifdef CONFIG_DFU_OVER_TFTP +#ifdef CONFIG_DFU_OVER_USB + "dfu " +#endif + "tftp [<interface> <dev>] [<addr>]\n" + " - device firmware upgrade via TFTP\n" + " on device <dev>, attached to interface\n" + " <interface>\n" + " [<addr>] - address where FIT image has been stored\n" +#endif +); diff --git a/roms/u-boot/cmd/diag.c b/roms/u-boot/cmd/diag.c new file mode 100644 index 000000000..f51536dbf --- /dev/null +++ b/roms/u-boot/cmd/diag.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Diagnostics support + */ +#include <common.h> +#include <command.h> +#include <post.h> + +int do_diag(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned int i; + + if (argc == 1 || strcmp (argv[1], "run") != 0) { + /* List test info */ + if (argc == 1) { + puts ("Available hardware tests:\n"); + post_info (NULL); + puts ("Use 'diag [<test1> [<test2> ...]]'" + " to get more info.\n"); + puts ("Use 'diag run [<test1> [<test2> ...]]'" + " to run tests.\n"); + } else { + for (i = 1; i < argc; i++) { + if (post_info (argv[i]) != 0) + printf ("%s - no such test\n", argv[i]); + } + } + } else { + /* Run tests */ + if (argc == 2) { + post_run (NULL, POST_RAM | POST_MANUAL); + } else { + for (i = 2; i < argc; i++) { + if (post_run (argv[i], POST_RAM | POST_MANUAL) != 0) + printf ("%s - unable to execute the test\n", + argv[i]); + } + } + } + + return 0; +} +/***************************************************/ + +U_BOOT_CMD( + diag, CONFIG_SYS_MAXARGS, 0, do_diag, + "perform board diagnostics", + " - print list of available tests\n" + "diag [test1 [test2]]\n" + " - print information about specified tests\n" + "diag run - run all available tests\n" + "diag run [test1 [test2]]\n" + " - run specified tests" +); diff --git a/roms/u-boot/cmd/disk.c b/roms/u-boot/cmd/disk.c new file mode 100644 index 000000000..2726115e8 --- /dev/null +++ b/roms/u-boot/cmd/disk.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ +#include <common.h> +#include <bootstage.h> +#include <command.h> +#include <cpu_func.h> +#include <image.h> +#include <log.h> +#include <part.h> + +int common_diskboot(struct cmd_tbl *cmdtp, const char *intf, int argc, + char *const argv[]) +{ + __maybe_unused int dev; + int part; + ulong addr = CONFIG_SYS_LOAD_ADDR; + ulong cnt; + struct disk_partition info; +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + image_header_t *hdr; +#endif + struct blk_desc *dev_desc; + +#if CONFIG_IS_ENABLED(FIT) + const void *fit_hdr = NULL; +#endif + + bootstage_mark(BOOTSTAGE_ID_IDE_START); + if (argc > 3) { + bootstage_error(BOOTSTAGE_ID_IDE_ADDR); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_IDE_ADDR); + + if (argc > 1) + addr = simple_strtoul(argv[1], NULL, 16); + + bootstage_mark(BOOTSTAGE_ID_IDE_BOOT_DEVICE); + + part = blk_get_device_part_str(intf, (argc == 3) ? argv[2] : NULL, + &dev_desc, &info, 1); + if (part < 0) { + bootstage_error(BOOTSTAGE_ID_IDE_TYPE); + return 1; + } + + dev = dev_desc->devnum; + bootstage_mark(BOOTSTAGE_ID_IDE_TYPE); + + printf("\nLoading from %s device %d, partition %d: " + "Name: %.32s Type: %.32s\n", intf, dev, part, info.name, + info.type); + + debug("First Block: " LBAFU ", # of blocks: " LBAFU + ", Block Size: %ld\n", + info.start, info.size, info.blksz); + + if (blk_dread(dev_desc, info.start, 1, (ulong *)addr) != 1) { + printf("** Read error on %d:%d\n", dev, part); + bootstage_error(BOOTSTAGE_ID_IDE_PART_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_PART_READ); + + switch (genimg_get_format((void *) addr)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + hdr = (image_header_t *) addr; + + bootstage_mark(BOOTSTAGE_ID_IDE_FORMAT); + + if (!image_check_hcrc(hdr)) { + puts("\n** Bad Header Checksum **\n"); + bootstage_error(BOOTSTAGE_ID_IDE_CHECKSUM); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_CHECKSUM); + + image_print_contents(hdr); + + cnt = image_get_image_size(hdr); + break; +#endif +#if CONFIG_IS_ENABLED(FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = (const void *) addr; + puts("Fit image detected...\n"); + + cnt = fit_get_size(fit_hdr); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_IDE_FORMAT); + puts("** Unknown image type\n"); + return 1; + } + + cnt += info.blksz - 1; + cnt /= info.blksz; + cnt -= 1; + + if (blk_dread(dev_desc, info.start + 1, cnt, + (ulong *)(addr + info.blksz)) != cnt) { + printf("** Read error on %d:%d\n", dev, part); + bootstage_error(BOOTSTAGE_ID_IDE_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_READ); + +#if CONFIG_IS_ENABLED(FIT) + /* This cannot be done earlier, + * we need complete FIT image in RAM first */ + if (genimg_get_format((void *) addr) == IMAGE_FORMAT_FIT) { + if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) { + bootstage_error(BOOTSTAGE_ID_IDE_FIT_READ); + puts("** Bad FIT image format\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_IDE_FIT_READ_OK); + } +#endif + + flush_cache(addr, (cnt+1)*info.blksz); + + /* Loading ok, update default load address */ + image_load_addr = addr; + + return bootm_maybe_autostart(cmdtp, argv[0]); +} diff --git a/roms/u-boot/cmd/dm.c b/roms/u-boot/cmd/dm.c new file mode 100644 index 000000000..1dd19fe45 --- /dev/null +++ b/roms/u-boot/cmd/dm.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Marek Vasut <marex@denx.de> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <malloc.h> +#include <mapmem.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/root.h> +#include <dm/util.h> + +static int do_dm_dump_all(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + dm_dump_all(); + + return 0; +} + +static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + dm_dump_uclass(); + + return 0; +} + +static int do_dm_dump_devres(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + dm_dump_devres(); + + return 0; +} + +static int do_dm_dump_drivers(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + dm_dump_drivers(); + + return 0; +} + +static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + dm_dump_driver_compat(); + + return 0; +} + +static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + dm_dump_static_driver_info(); + + return 0; +} + +static struct cmd_tbl test_commands[] = { + U_BOOT_CMD_MKENT(tree, 0, 1, do_dm_dump_all, "", ""), + U_BOOT_CMD_MKENT(uclass, 1, 1, do_dm_dump_uclass, "", ""), + U_BOOT_CMD_MKENT(devres, 1, 1, do_dm_dump_devres, "", ""), + U_BOOT_CMD_MKENT(drivers, 1, 1, do_dm_dump_drivers, "", ""), + U_BOOT_CMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat, "", ""), + U_BOOT_CMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info, "", ""), +}; + +static __maybe_unused void dm_reloc(void) +{ + static int relocated; + + if (!relocated) { + fixup_cmdtable(test_commands, ARRAY_SIZE(test_commands)); + relocated = 1; + } +} + +static int do_dm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *test_cmd; + int ret; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + dm_reloc(); +#endif + + if (argc < 2) + return CMD_RET_USAGE; + test_cmd = find_cmd_tbl(argv[1], test_commands, + ARRAY_SIZE(test_commands)); + argc -= 2; + argv += 2; + if (!test_cmd || argc > test_cmd->maxargs) + return CMD_RET_USAGE; + + ret = test_cmd->cmd(test_cmd, flag, argc, argv); + + return cmd_process_error(test_cmd, ret); +} + +U_BOOT_CMD( + dm, 3, 1, do_dm, + "Driver model low level access", + "tree Dump driver model tree ('*' = activated)\n" + "dm uclass Dump list of instances for each uclass\n" + "dm devres Dump list of device resources for each device\n" + "dm drivers Dump list of drivers with uclass and instances\n" + "dm compat Dump list of drivers with compatibility strings\n" + "dm static Dump list of drivers with static platform data" +); diff --git a/roms/u-boot/cmd/echo.c b/roms/u-boot/cmd/echo.c new file mode 100644 index 000000000..fda844ee9 --- /dev/null +++ b/roms/u-boot/cmd/echo.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> + +static int do_echo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i = 1; + bool space = false; + bool newline = true; + + if (argc > 1) { + if (!strcmp(argv[1], "-n")) { + newline = false; + ++i; + } + } + + for (; i < argc; ++i) { + if (space) { + putc(' '); + } + puts(argv[i]); + space = true; + } + + if (newline) + putc('\n'); + + return 0; +} + +U_BOOT_CMD( + echo, CONFIG_SYS_MAXARGS, 1, do_echo, + "echo args to console", + "[-n] [args..]\n" + " - echo args to console; -n suppresses newline" +); diff --git a/roms/u-boot/cmd/eeprom.c b/roms/u-boot/cmd/eeprom.c new file mode 100644 index 000000000..b3fd37c82 --- /dev/null +++ b/roms/u-boot/cmd/eeprom.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000, 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Support for read and write access to EEPROM like memory devices. This + * includes regular EEPROM as well as FRAM (ferroelectic nonvolaile RAM). + * FRAM devices read and write data at bus speed. In particular, there is no + * write delay. Also, there is no limit imposed on the number of bytes that can + * be transferred with a single read or write. + * + * Use the following configuration options to ensure no unneeded performance + * degradation (typical for EEPROM) is incured for FRAM memory: + * + * #define CONFIG_SYS_I2C_FRAM + * #undef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS + * + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <eeprom.h> +#include <i2c.h> +#include <eeprom_layout.h> +#include <linux/delay.h> + +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +#ifndef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS +#define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 0 +#endif + +#ifndef CONFIG_SYS_EEPROM_PAGE_WRITE_BITS +#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 8 +#endif + +#ifndef I2C_RXTX_LEN +#define I2C_RXTX_LEN 128 +#endif + +#define EEPROM_PAGE_SIZE (1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS) +#define EEPROM_PAGE_OFFSET(x) ((x) & (EEPROM_PAGE_SIZE - 1)) + +/* + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is + * 0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM. + * + * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is + * 0x00000nxx for EEPROM address selectors and page number at n. + */ +#if !defined(CONFIG_SPI) || defined(CONFIG_ENV_EEPROM_IS_ON_I2C) +#if !defined(CONFIG_SYS_I2C_EEPROM_ADDR_LEN) || \ + (CONFIG_SYS_I2C_EEPROM_ADDR_LEN < 1) || \ + (CONFIG_SYS_I2C_EEPROM_ADDR_LEN > 2) +#error CONFIG_SYS_I2C_EEPROM_ADDR_LEN must be 1 or 2 +#endif +#endif + +#if CONFIG_IS_ENABLED(DM_I2C) +static int eeprom_i2c_bus; +#endif + +__weak int eeprom_write_enable(unsigned dev_addr, int state) +{ + return 0; +} + +void eeprom_init(int bus) +{ + /* I2C EEPROM */ +#if CONFIG_IS_ENABLED(DM_I2C) + eeprom_i2c_bus = bus; +#elif defined(CONFIG_SYS_I2C) + if (bus >= 0) + i2c_set_bus_num(bus); + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif +} + +static int eeprom_addr(unsigned dev_addr, unsigned offset, uchar *addr) +{ + unsigned blk_off; + int alen; + + blk_off = offset & 0xff; /* block offset */ +#if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 + addr[0] = offset >> 8; /* block number */ + addr[1] = blk_off; /* block offset */ + alen = 2; +#else + addr[0] = offset >> 16; /* block number */ + addr[1] = offset >> 8; /* upper address octet */ + addr[2] = blk_off; /* lower address octet */ + alen = 3; +#endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN */ + + addr[0] |= dev_addr; /* insert device address */ + + return alen; +} + +static int eeprom_len(unsigned offset, unsigned end) +{ + unsigned len = end - offset; + + /* + * For a FRAM device there is no limit on the number of the + * bytes that can be accessed with the single read or write + * operation. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + unsigned blk_off = offset & 0xff; + unsigned maxlen = EEPROM_PAGE_SIZE - EEPROM_PAGE_OFFSET(blk_off); + + if (maxlen > I2C_RXTX_LEN) + maxlen = I2C_RXTX_LEN; + + if (len > maxlen) + len = maxlen; +#endif + + return len; +} + +static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen, + uchar *buffer, unsigned len, bool read) +{ + int ret = 0; + +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; + + ret = i2c_get_chip_for_busnum(eeprom_i2c_bus, addr[0], + alen - 1, &dev); + if (ret) { + printf("%s: Cannot find udev for a bus %d\n", __func__, + eeprom_i2c_bus); + return CMD_RET_FAILURE; + } + + if (read) + ret = dm_i2c_read(dev, offset, buffer, len); + else + ret = dm_i2c_write(dev, offset, buffer, len); + +#else /* Non DM I2C support - will be removed */ + + if (read) + ret = i2c_read(addr[0], offset, alen - 1, buffer, len); + else + ret = i2c_write(addr[0], offset, alen - 1, buffer, len); +#endif /* CONFIG_DM_I2C */ + if (ret) + ret = CMD_RET_FAILURE; + + return ret; +} + +static int eeprom_rw(unsigned dev_addr, unsigned offset, uchar *buffer, + unsigned cnt, bool read) +{ + unsigned end = offset + cnt; + unsigned alen, len; + int rcode = 0; + uchar addr[3]; + +#if defined(CONFIG_SYS_I2C_EEPROM_BUS) + eeprom_init(CONFIG_SYS_I2C_EEPROM_BUS); +#endif + + while (offset < end) { + alen = eeprom_addr(dev_addr, offset, addr); + + len = eeprom_len(offset, end); + + rcode = eeprom_rw_block(offset, addr, alen, buffer, len, read); + + buffer += len; + offset += len; + + if (!read) + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); + } + + return rcode; +} + +int eeprom_read(unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt) +{ + /* + * Read data until done or would cross a page boundary. + * We must write the address again when changing pages + * because the next page may be in a different device. + */ + return eeprom_rw(dev_addr, offset, buffer, cnt, 1); +} + +int eeprom_write(unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt) +{ + int ret; + + eeprom_write_enable(dev_addr, 1); + + /* + * Write data until done or would cross a write page boundary. + * We must write the address again when changing pages + * because the address counter only increments within a page. + */ + ret = eeprom_rw(dev_addr, offset, buffer, cnt, 0); + + eeprom_write_enable(dev_addr, 0); + return ret; +} + +static int parse_numeric_param(char *str) +{ + char *endptr; + int value = simple_strtol(str, &endptr, 16); + + return (*endptr != '\0') ? -1 : value; +} + +/** + * parse_i2c_bus_addr - parse the i2c bus and i2c devaddr parameters + * + * @i2c_bus: address to store the i2c bus + * @i2c_addr: address to store the device i2c address + * @argc: count of command line arguments left to parse + * @argv: command line arguments left to parse + * @argc_no_bus_addr: argc value we expect to see when bus & addr aren't given + * + * @returns: number of arguments parsed or CMD_RET_USAGE if error + */ +static int parse_i2c_bus_addr(int *i2c_bus, ulong *i2c_addr, int argc, + char *const argv[], int argc_no_bus_addr) +{ + int argc_no_bus = argc_no_bus_addr + 1; + int argc_bus_addr = argc_no_bus_addr + 2; + +#ifdef CONFIG_SYS_DEF_EEPROM_ADDR + if (argc == argc_no_bus_addr) { + *i2c_bus = -1; + *i2c_addr = CONFIG_SYS_DEF_EEPROM_ADDR; + + return 0; + } +#endif + if (argc == argc_no_bus) { + *i2c_bus = -1; + *i2c_addr = parse_numeric_param(argv[0]); + + return 1; + } + + if (argc == argc_bus_addr) { + *i2c_bus = parse_numeric_param(argv[0]); + *i2c_addr = parse_numeric_param(argv[1]); + + return 2; + } + + return CMD_RET_USAGE; +} + +#ifdef CONFIG_CMD_EEPROM_LAYOUT + +__weak int eeprom_parse_layout_version(char *str) +{ + return LAYOUT_VERSION_UNRECOGNIZED; +} + +static unsigned char eeprom_buf[CONFIG_SYS_EEPROM_SIZE]; + +#endif + +enum eeprom_action { + EEPROM_READ, + EEPROM_WRITE, + EEPROM_PRINT, + EEPROM_UPDATE, + EEPROM_ACTION_INVALID, +}; + +static enum eeprom_action parse_action(char *cmd) +{ + if (!strncmp(cmd, "read", 4)) + return EEPROM_READ; + if (!strncmp(cmd, "write", 5)) + return EEPROM_WRITE; +#ifdef CONFIG_CMD_EEPROM_LAYOUT + if (!strncmp(cmd, "print", 5)) + return EEPROM_PRINT; + if (!strncmp(cmd, "update", 6)) + return EEPROM_UPDATE; +#endif + + return EEPROM_ACTION_INVALID; +} + +static int eeprom_execute_command(enum eeprom_action action, int i2c_bus, + ulong i2c_addr, int layout_ver, char *key, + char *value, ulong addr, ulong off, ulong cnt) +{ + int rcode = 0; + const char *const fmt = + "\nEEPROM @0x%lX %s: addr 0x%08lx off 0x%04lx count %ld ... "; +#ifdef CONFIG_CMD_EEPROM_LAYOUT + struct eeprom_layout layout; +#endif + + if (action == EEPROM_ACTION_INVALID) + return CMD_RET_USAGE; + + eeprom_init(i2c_bus); + if (action == EEPROM_READ) { + printf(fmt, i2c_addr, "read", addr, off, cnt); + + rcode = eeprom_read(i2c_addr, off, (uchar *)addr, cnt); + + puts("done\n"); + return rcode; + } else if (action == EEPROM_WRITE) { + printf(fmt, i2c_addr, "write", addr, off, cnt); + + rcode = eeprom_write(i2c_addr, off, (uchar *)addr, cnt); + + puts("done\n"); + return rcode; + } + +#ifdef CONFIG_CMD_EEPROM_LAYOUT + rcode = eeprom_read(i2c_addr, 0, eeprom_buf, CONFIG_SYS_EEPROM_SIZE); + if (rcode < 0) + return rcode; + + eeprom_layout_setup(&layout, eeprom_buf, CONFIG_SYS_EEPROM_SIZE, + layout_ver); + + if (action == EEPROM_PRINT) { + layout.print(&layout); + return 0; + } + + layout.update(&layout, key, value); + + rcode = eeprom_write(i2c_addr, 0, layout.data, CONFIG_SYS_EEPROM_SIZE); +#endif + + return rcode; +} + +#define NEXT_PARAM(argc, index) { (argc)--; (index)++; } +int do_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int layout_ver = LAYOUT_VERSION_AUTODETECT; + enum eeprom_action action = EEPROM_ACTION_INVALID; + int i2c_bus = -1, index = 0; + ulong i2c_addr = -1, addr = 0, cnt = 0, off = 0; + int ret; + char *field_name = ""; + char *field_value = ""; + + if (argc <= 1) + return CMD_RET_USAGE; + + NEXT_PARAM(argc, index); /* Skip program name */ + + action = parse_action(argv[index]); + NEXT_PARAM(argc, index); + + if (action == EEPROM_ACTION_INVALID) + return CMD_RET_USAGE; + +#ifdef CONFIG_CMD_EEPROM_LAYOUT + if (action == EEPROM_PRINT || action == EEPROM_UPDATE) { + if (!strcmp(argv[index], "-l")) { + NEXT_PARAM(argc, index); + layout_ver = eeprom_parse_layout_version(argv[index]); + NEXT_PARAM(argc, index); + } + } +#endif + + switch (action) { + case EEPROM_READ: + case EEPROM_WRITE: + ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc, + argv + index, 3); + break; + case EEPROM_PRINT: + ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc, + argv + index, 0); + break; + case EEPROM_UPDATE: + ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc, + argv + index, 2); + break; + default: + /* Get compiler to stop whining */ + return CMD_RET_USAGE; + } + + if (ret == CMD_RET_USAGE) + return ret; + + while (ret--) + NEXT_PARAM(argc, index); + + if (action == EEPROM_READ || action == EEPROM_WRITE) { + addr = parse_numeric_param(argv[index]); + NEXT_PARAM(argc, index); + off = parse_numeric_param(argv[index]); + NEXT_PARAM(argc, index); + cnt = parse_numeric_param(argv[index]); + } + +#ifdef CONFIG_CMD_EEPROM_LAYOUT + if (action == EEPROM_UPDATE) { + field_name = argv[index]; + NEXT_PARAM(argc, index); + field_value = argv[index]; + NEXT_PARAM(argc, index); + } +#endif + + return eeprom_execute_command(action, i2c_bus, i2c_addr, layout_ver, + field_name, field_value, addr, off, cnt); +} + +U_BOOT_CMD( + eeprom, 8, 1, do_eeprom, + "EEPROM sub-system", + "read <bus> <devaddr> addr off cnt\n" + "eeprom write <bus> <devaddr> addr off cnt\n" + " - read/write `cnt' bytes from `devaddr` EEPROM at offset `off'" +#ifdef CONFIG_CMD_EEPROM_LAYOUT + "\n" + "eeprom print [-l <layout_version>] <bus> <devaddr>\n" + " - Print layout fields and their data in human readable format\n" + "eeprom update [-l <layout_version>] <bus> <devaddr> field_name field_value\n" + " - Update a specific eeprom field with new data.\n" + " The new data must be written in the same human readable format as shown by the print command.\n" + "\n" + "LAYOUT VERSIONS\n" + "The -l option can be used to force the command to interpret the EEPROM data using the chosen layout.\n" + "If the -l option is omitted, the command will auto detect the layout based on the data in the EEPROM.\n" + "The values which can be provided with the -l option are:\n" + CONFIG_EEPROM_LAYOUT_HELP_STRING"\n" +#endif +) diff --git a/roms/u-boot/cmd/efi.c b/roms/u-boot/cmd/efi.c new file mode 100644 index 000000000..f2ed26bd4 --- /dev/null +++ b/roms/u-boot/cmd/efi.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <efi.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <sort.h> +#include <asm/global_data.h> + +static const char *const type_name[] = { + "reserved", + "loader_code", + "loader_data", + "bs_code", + "bs_data", + "rt_code", + "rt_data", + "conv", + "unusable", + "acpi_reclaim", + "acpi_nvs", + "io", + "io_port", + "pal_code", +}; + +static struct attr_info { + u64 val; + const char *name; +} mem_attr[] = { + { EFI_MEMORY_UC, "uncached" }, + { EFI_MEMORY_WC, "write-coalescing" }, + { EFI_MEMORY_WT, "write-through" }, + { EFI_MEMORY_WB, "write-back" }, + { EFI_MEMORY_UCE, "uncached & exported" }, + { EFI_MEMORY_WP, "write-protect" }, + { EFI_MEMORY_RP, "read-protect" }, + { EFI_MEMORY_XP, "execute-protect" }, + { EFI_MEMORY_NV, "non-volatile" }, + { EFI_MEMORY_MORE_RELIABLE, "higher reliability" }, + { EFI_MEMORY_RO, "read-only" }, + { EFI_MEMORY_SP, "specific purpose" }, + { EFI_MEMORY_RUNTIME, "needs runtime mapping" } +}; + +/* Maximum different attribute values we can track */ +#define ATTR_SEEN_MAX 30 + +static inline bool is_boot_services(int type) +{ + return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA || + type == EFI_BOOT_SERVICES_CODE || + type == EFI_BOOT_SERVICES_DATA; +} + +static int h_cmp_entry(const void *v1, const void *v2) +{ + const struct efi_mem_desc *desc1 = v1; + const struct efi_mem_desc *desc2 = v2; + int64_t diff = desc1->physical_start - desc2->physical_start; + + /* + * Manually calculate the difference to avoid sign loss in the 64-bit + * to 32-bit conversion + */ + return diff < 0 ? -1 : diff > 0 ? 1 : 0; +} + +/** + * efi_build_mem_table() - make a sorted copy of the memory table + * + * @map: Pointer to EFI memory map table + * @size: Size of table in bytes + * @skip_bs: True to skip boot-time memory and merge it with conventional + * memory. This will significantly reduce the number of table + * entries. + * Return: pointer to the new table. It should be freed with free() by the + * caller. + */ +static void *efi_build_mem_table(struct efi_entry_memmap *map, int size, + bool skip_bs) +{ + struct efi_mem_desc *desc, *end, *base, *dest, *prev; + int count; + u64 addr; + + base = malloc(size + sizeof(*desc)); + if (!base) { + debug("%s: Cannot allocate %#x bytes\n", __func__, size); + return NULL; + } + end = (struct efi_mem_desc *)((ulong)map + size); + count = ((ulong)end - (ulong)map->desc) / map->desc_size; + memcpy(base, map->desc, (ulong)end - (ulong)map->desc); + qsort(base, count, map->desc_size, h_cmp_entry); + prev = NULL; + addr = 0; + dest = base; + end = (struct efi_mem_desc *)((ulong)base + count * map->desc_size); + for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) { + bool merge = true; + u32 type = desc->type; + + if (type >= EFI_MAX_MEMORY_TYPE) { + printf("Memory map contains invalid entry type %u\n", + type); + continue; + } + + if (skip_bs && is_boot_services(desc->type)) + type = EFI_CONVENTIONAL_MEMORY; + + memcpy(dest, desc, map->desc_size); + dest->type = type; + if (!skip_bs || !prev) + merge = false; + else if (desc->physical_start != addr) + merge = false; + else if (type != EFI_CONVENTIONAL_MEMORY) + merge = false; + else if (prev->type != EFI_CONVENTIONAL_MEMORY) + merge = false; + + if (merge) { + prev->num_pages += desc->num_pages; + } else { + prev = dest; + dest = efi_get_next_mem_desc(map, dest); + } + addr = desc->physical_start + (desc->num_pages << + EFI_PAGE_SHIFT); + } + + /* Mark the end */ + dest->type = EFI_MAX_MEMORY_TYPE; + + return base; +} + +static void efi_print_mem_table(struct efi_entry_memmap *map, + struct efi_mem_desc *desc, bool skip_bs) +{ + u64 attr_seen[ATTR_SEEN_MAX]; + int attr_seen_count; + int upto, i; + u64 addr; + + printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical", + "Virtual", "Size", "Attributes"); + + /* Keep track of all the different attributes we have seen */ + attr_seen_count = 0; + addr = 0; + for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE; + upto++, desc = efi_get_next_mem_desc(map, desc)) { + const char *name; + u64 size; + + if (skip_bs && is_boot_services(desc->type)) + continue; + if (desc->physical_start != addr) { + printf(" %-14s %010llx %10s %010llx\n", "<gap>", + addr, "", desc->physical_start - addr); + } + size = desc->num_pages << EFI_PAGE_SHIFT; + + name = desc->type < ARRAY_SIZE(type_name) ? + type_name[desc->type] : "<invalid>"; + printf("%2d %x:%-12s %010llx %010llx %010llx ", upto, + desc->type, name, desc->physical_start, + desc->virtual_start, size); + if (desc->attribute & EFI_MEMORY_RUNTIME) + putc('r'); + printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME); + putc('\n'); + + for (i = 0; i < attr_seen_count; i++) { + if (attr_seen[i] == desc->attribute) + break; + } + if (i == attr_seen_count && i < ATTR_SEEN_MAX) + attr_seen[attr_seen_count++] = desc->attribute; + addr = desc->physical_start + size; + } + + printf("\nAttributes key:\n"); + for (i = 0; i < attr_seen_count; i++) { + u64 attr = attr_seen[i]; + bool first; + int j; + + printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ', + attr & ~EFI_MEMORY_RUNTIME); + for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) { + if (attr & mem_attr[j].val) { + if (first) + first = false; + else + printf(", "); + printf("%s", mem_attr[j].name); + } + } + putc('\n'); + } + if (skip_bs) + printf("*Some areas are merged (use 'all' to see)\n"); +} + +static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct efi_mem_desc *desc; + struct efi_entry_memmap *map; + int size, ret; + bool skip_bs; + + skip_bs = !argc || *argv[0] != 'a'; + ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); + switch (ret) { + case -ENOENT: + printf("No EFI table available\n"); + goto done; + case -EPROTONOSUPPORT: + printf("Incorrect EFI table version\n"); + goto done; + } + printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n", + gd->arch.table, map, size, map->version, map->desc_size); + if (map->version != EFI_MEM_DESC_VERSION) { + printf("Incorrect memory map version\n"); + ret = -EPROTONOSUPPORT; + goto done; + } + + desc = efi_build_mem_table(map, size, skip_bs); + if (!desc) { + ret = -ENOMEM; + goto done; + } + + efi_print_mem_table(map, desc, skip_bs); + free(desc); +done: + if (ret) + printf("Error: %d\n", ret); + + return ret ? CMD_RET_FAILURE : 0; +} + +static struct cmd_tbl efi_commands[] = { + U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""), +}; + +static int do_efi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *efi_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands)); + argc -= 2; + argv += 2; + if (!efi_cmd || argc > efi_cmd->maxargs) + return CMD_RET_USAGE; + + ret = efi_cmd->cmd(efi_cmd, flag, argc, argv); + + return cmd_process_error(efi_cmd, ret); +} + +U_BOOT_CMD( + efi, 3, 1, do_efi, + "EFI access", + "mem [all] Dump memory information [include boot services]" +); diff --git a/roms/u-boot/cmd/efidebug.c b/roms/u-boot/cmd/efidebug.c new file mode 100644 index 000000000..8211a589a --- /dev/null +++ b/roms/u-boot/cmd/efidebug.c @@ -0,0 +1,1787 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UEFI Shell-like command + * + * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited + */ + +#include <charset.h> +#include <common.h> +#include <command.h> +#include <efi_dt_fixup.h> +#include <efi_load_initrd.h> +#include <efi_loader.h> +#include <efi_rng.h> +#include <efi_variable.h> +#include <exports.h> +#include <hexdump.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <part.h> +#include <search.h> +#include <linux/ctype.h> +#include <linux/err.h> + +#define BS systab.boottime +#define RT systab.runtime + +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +/** + * do_efi_capsule_update() - process a capsule update + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule update" sub-command. + * process a capsule update. + * + * efidebug capsule update [-v] <capsule address> + */ +static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + int verbose = 0; + char *endp; + efi_status_t ret; + + if (argc != 2 && argc != 3) + return CMD_RET_USAGE; + + if (argc == 3) { + if (strcmp(argv[1], "-v")) + return CMD_RET_USAGE; + + verbose = 1; + argc--; + argv++; + } + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + if (verbose) { + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + } + + ret = EFI_CALL(RT->update_capsule(&capsule, 1, 0)); + if (ret) { + printf("Cannot handle a capsule at %p", capsule); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_efi_capsule_on_disk_update(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_status_t ret; + + ret = efi_launch_capsules(); + + return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +/** + * do_efi_capsule_show() - show capsule information + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule show" sub-command. + * show capsule information. + * + * efidebug capsule show <capsule address> + */ +static int do_efi_capsule_show(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + char *endp; + + if (argc != 2) + return CMD_RET_USAGE; + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_EFI_ESRT + +#define EFI_ESRT_FW_TYPE_NUM 4 +char *efi_fw_type_str[EFI_ESRT_FW_TYPE_NUM] = {"unknown", "system FW", "device FW", + "UEFI driver"}; + +#define EFI_ESRT_UPDATE_STATUS_NUM 9 +char *efi_update_status_str[EFI_ESRT_UPDATE_STATUS_NUM] = {"success", "unsuccessful", + "insufficient resources", "incorrect version", "invalid format", + "auth error", "power event (AC)", "power event (batt)", + "unsatisfied dependencies"}; + +#define EFI_FW_TYPE_STR_GET(idx) (\ +EFI_ESRT_FW_TYPE_NUM > (idx) ? efi_fw_type_str[(idx)] : "error"\ +) + +#define EFI_FW_STATUS_STR_GET(idx) (\ +EFI_ESRT_UPDATE_STATUS_NUM > (idx) ? efi_update_status_str[(idx)] : "error"\ +) + +/** + * do_efi_capsule_esrt() - manage UEFI capsules + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule esrt" sub-command. + * The prints the current ESRT table. + * + * efidebug capsule esrt + */ +static int do_efi_capsule_esrt(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_system_resource_table *esrt = NULL; + + if (argc != 1) + return CMD_RET_USAGE; + + for (int idx = 0; idx < systab.nr_tables; idx++) + if (!guidcmp(&efi_esrt_guid, &systab.tables[idx].guid)) + esrt = (struct efi_system_resource_table *)systab.tables[idx].table; + + if (!esrt) { + log_info("ESRT: table not present\n"); + return CMD_RET_SUCCESS; + } + + printf("========================================\n"); + printf("ESRT: fw_resource_count=%d\n", esrt->fw_resource_count); + printf("ESRT: fw_resource_count_max=%d\n", esrt->fw_resource_count_max); + printf("ESRT: fw_resource_version=%lld\n", esrt->fw_resource_version); + + for (int idx = 0; idx < esrt->fw_resource_count; idx++) { + printf("[entry %d]==============================\n", idx); + printf("ESRT: fw_class=%pUL\n", &esrt->entries[idx].fw_class); + printf("ESRT: fw_type=%s\n", EFI_FW_TYPE_STR_GET(esrt->entries[idx].fw_type)); + printf("ESRT: fw_version=%d\n", esrt->entries[idx].fw_version); + printf("ESRT: lowest_supported_fw_version=%d\n", + esrt->entries[idx].lowest_supported_fw_version); + printf("ESRT: capsule_flags=%d\n", + esrt->entries[idx].capsule_flags); + printf("ESRT: last_attempt_version=%d\n", + esrt->entries[idx].last_attempt_version); + printf("ESRT: last_attempt_status=%s\n", + EFI_FW_STATUS_STR_GET(esrt->entries[idx].last_attempt_status)); + } + printf("========================================\n"); + + return CMD_RET_SUCCESS; +} +#endif /* CONFIG_EFI_ESRT */ +/** + * do_efi_capsule_res() - show a capsule update result + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule result" sub-command. + * show a capsule update result. + * If result number is not specified, CapsuleLast will be shown. + * + * efidebug capsule result [<capsule result number>] + */ +static int do_efi_capsule_res(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + int capsule_id; + char *endp; + u16 var_name16[12]; + efi_guid_t guid; + struct efi_capsule_result_variable_header *result = NULL; + efi_uintn_t size; + efi_status_t ret; + + if (argc != 1 && argc != 2) + return CMD_RET_USAGE; + + guid = efi_guid_capsule_report; + if (argc == 1) { + size = sizeof(var_name16); + ret = efi_get_variable_int(L"CapsuleLast", &guid, NULL, + &size, var_name16, NULL); + + if (ret != EFI_SUCCESS) { + if (ret == EFI_NOT_FOUND) + printf("CapsuleLast doesn't exist\n"); + else + printf("Failed to get CapsuleLast\n"); + + return CMD_RET_FAILURE; + } + printf("CapsuleLast is %ls\n", var_name16); + } else { + argc--; + argv++; + + capsule_id = simple_strtoul(argv[0], &endp, 16); + if (capsule_id < 0 || capsule_id > 0xffff) + return CMD_RET_USAGE; + + efi_create_indexed_name(var_name16, sizeof(var_name16), + "Capsule", capsule_id); + } + + size = 0; + ret = efi_get_variable_int(var_name16, &guid, NULL, &size, NULL, NULL); + if (ret == EFI_BUFFER_TOO_SMALL) { + result = malloc(size); + if (!result) + return CMD_RET_FAILURE; + ret = efi_get_variable_int(var_name16, &guid, NULL, &size, + result, NULL); + } + if (ret != EFI_SUCCESS) { + free(result); + printf("Failed to get %ls\n", var_name16); + + return CMD_RET_FAILURE; + } + + printf("Result total size: 0x%x\n", result->variable_total_size); + printf("Capsule guid: %pUl\n", &result->capsule_guid); + printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n", + result->capsule_processed.year, result->capsule_processed.month, + result->capsule_processed.day, result->capsule_processed.hour, + result->capsule_processed.minute, + result->capsule_processed.second); + printf("Capsule status: 0x%lx\n", result->capsule_status); + + free(result); + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_efidebug_capsule_sub[] = { + U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update, + "", ""), + U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, + "", ""), +#ifdef CONFIG_EFI_ESRT + U_BOOT_CMD_MKENT(esrt, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_esrt, + "", ""), +#endif + U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update, + "", ""), + U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, + "", ""), +}; + +/** + * do_efi_capsule() - manage UEFI capsules + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule" sub-command. + */ +static int do_efi_capsule(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub, + ARRAY_SIZE(cmd_efidebug_capsule_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */ + +/** + * efi_get_device_path_text() - get device path text + * + * Return the text representation of the device path of a handle. + * + * @handle: handle of UEFI device + * Return: + * Pointer to the device path text or NULL. + * The caller is responsible for calling FreePool(). + */ +static u16 *efi_get_device_path_text(efi_handle_t handle) +{ + struct efi_handler *handler; + efi_status_t ret; + + ret = efi_search_protocol(handle, &efi_guid_device_path, &handler); + if (ret == EFI_SUCCESS && handler->protocol_interface) { + struct efi_device_path *dp = handler->protocol_interface; + + return efi_dp_str(dp); + } else { + return NULL; + } +} + +#define EFI_HANDLE_WIDTH ((int)sizeof(efi_handle_t) * 2) + +static const char spc[] = " "; +static const char sep[] = "================"; + +/** + * do_efi_show_devices() - show UEFI devices + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "devices" sub-command. + * Show all UEFI devices and their information. + */ +static int do_efi_show_devices(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_handle_t *handles; + efi_uintn_t num, i; + u16 *dev_path_text; + efi_status_t ret; + + ret = EFI_CALL(efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL, + &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Device%.*s Device Path\n", EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ====================\n", EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + dev_path_text = efi_get_device_path_text(handles[i]); + if (dev_path_text) { + printf("%p %ls\n", handles[i], dev_path_text); + efi_free_pool(dev_path_text); + } + } + + efi_free_pool(handles); + + return CMD_RET_SUCCESS; +} + +/** + * efi_get_driver_handle_info() - get information of UEFI driver + * + * @handle: Handle of UEFI device + * @driver_name: Driver name + * @image_path: Pointer to text of device path + * Return: 0 on success, -1 on failure + * + * Currently return no useful information as all UEFI drivers are + * built-in.. + */ +static int efi_get_driver_handle_info(efi_handle_t handle, u16 **driver_name, + u16 **image_path) +{ + struct efi_handler *handler; + struct efi_loaded_image *image; + efi_status_t ret; + + /* + * driver name + * TODO: support EFI_COMPONENT_NAME2_PROTOCOL + */ + *driver_name = NULL; + + /* image name */ + ret = efi_search_protocol(handle, &efi_guid_loaded_image, &handler); + if (ret != EFI_SUCCESS) { + *image_path = NULL; + return 0; + } + + image = handler->protocol_interface; + *image_path = efi_dp_str(image->file_path); + + return 0; +} + +/** + * do_efi_show_drivers() - show UEFI drivers + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "drivers" sub-command. + * Show all UEFI drivers and their information. + */ +static int do_efi_show_drivers(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_handle_t *handles; + efi_uintn_t num, i; + u16 *driver_name, *image_path_text; + efi_status_t ret; + + ret = EFI_CALL(efi_locate_handle_buffer( + BY_PROTOCOL, &efi_guid_driver_binding_protocol, + NULL, &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Driver%.*s Name Image Path\n", + EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ==================== ====================\n", + EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + if (!efi_get_driver_handle_info(handles[i], &driver_name, + &image_path_text)) { + if (image_path_text) + printf("%p %-20ls %ls\n", handles[i], + driver_name, image_path_text); + else + printf("%p %-20ls <built-in>\n", + handles[i], driver_name); + efi_free_pool(driver_name); + efi_free_pool(image_path_text); + } + } + + efi_free_pool(handles); + + return CMD_RET_SUCCESS; +} + +static const struct { + const char *text; + const efi_guid_t guid; +} guid_list[] = { + { + "Device Path", + EFI_DEVICE_PATH_PROTOCOL_GUID, + }, + { + "Device Path To Text", + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID, + }, + { + "Device Path Utilities", + EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID, + }, + { + "Unicode Collation 2", + EFI_UNICODE_COLLATION_PROTOCOL2_GUID, + }, + { + "Driver Binding", + EFI_DRIVER_BINDING_PROTOCOL_GUID, + }, + { + "Simple Text Input", + EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID, + }, + { + "Simple Text Input Ex", + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID, + }, + { + "Simple Text Output", + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID, + }, + { + "Block IO", + EFI_BLOCK_IO_PROTOCOL_GUID, + }, + { + "Simple File System", + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, + }, + { + "Loaded Image", + EFI_LOADED_IMAGE_PROTOCOL_GUID, + }, + { + "Graphics Output", + EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, + }, + { + "HII String", + EFI_HII_STRING_PROTOCOL_GUID, + }, + { + "HII Database", + EFI_HII_DATABASE_PROTOCOL_GUID, + }, + { + "HII Config Routing", + EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID, + }, + { + "Load File2", + EFI_LOAD_FILE2_PROTOCOL_GUID, + }, + { + "Random Number Generator", + EFI_RNG_PROTOCOL_GUID, + }, + { + "Simple Network", + EFI_SIMPLE_NETWORK_PROTOCOL_GUID, + }, + { + "PXE Base Code", + EFI_PXE_BASE_CODE_PROTOCOL_GUID, + }, + { + "Device-Tree Fixup", + EFI_DT_FIXUP_PROTOCOL_GUID, + }, + { + "System Partition", + PARTITION_SYSTEM_GUID + }, + { + "Firmware Management", + EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID + }, + /* Configuration table GUIDs */ + { + "ACPI table", + EFI_ACPI_TABLE_GUID, + }, + { + "EFI System Resource Table", + EFI_SYSTEM_RESOURCE_TABLE_GUID, + }, + { + "device tree", + EFI_FDT_GUID, + }, + { + "SMBIOS table", + SMBIOS_TABLE_GUID, + }, + { + "Runtime properties", + EFI_RT_PROPERTIES_TABLE_GUID, + }, + { + "TCG2 Final Events Table", + EFI_TCG2_FINAL_EVENTS_TABLE_GUID, + }, +}; + +/** + * get_guid_text - get string of GUID + * + * Return description of GUID. + * + * @guid: GUID + * Return: description of GUID or NULL + */ +static const char *get_guid_text(const void *guid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(guid_list); i++) { + /* + * As guidcmp uses memcmp() we can safely accept unaligned + * GUIDs. + */ + if (!guidcmp(&guid_list[i].guid, guid)) + return guid_list[i].text; + } + + return NULL; +} + +/** + * do_efi_show_handles() - show UEFI handles + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "dh" sub-command. + * Show all UEFI handles and their information, currently all protocols + * added to handle. + */ +static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_handle_t *handles; + efi_guid_t **guid; + efi_uintn_t num, count, i, j; + const char *guid_text; + efi_status_t ret; + + ret = EFI_CALL(efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL, + &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Handle%.*s Protocols\n", EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ====================\n", EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + printf("%p", handles[i]); + ret = EFI_CALL(BS->protocols_per_handle(handles[i], &guid, + &count)); + if (ret || !count) { + putc('\n'); + continue; + } + + for (j = 0; j < count; j++) { + if (j) + printf(", "); + else + putc(' '); + + guid_text = get_guid_text(guid[j]); + if (guid_text) + puts(guid_text); + else + printf("%pUl", guid[j]); + } + putc('\n'); + } + + efi_free_pool(handles); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_show_images() - show UEFI images + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "images" sub-command. + * Show all UEFI loaded images and their information. + */ +static int do_efi_show_images(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_print_image_infos(NULL); + + return CMD_RET_SUCCESS; +} + +static const char * const efi_mem_type_string[] = { + [EFI_RESERVED_MEMORY_TYPE] = "RESERVED", + [EFI_LOADER_CODE] = "LOADER CODE", + [EFI_LOADER_DATA] = "LOADER DATA", + [EFI_BOOT_SERVICES_CODE] = "BOOT CODE", + [EFI_BOOT_SERVICES_DATA] = "BOOT DATA", + [EFI_RUNTIME_SERVICES_CODE] = "RUNTIME CODE", + [EFI_RUNTIME_SERVICES_DATA] = "RUNTIME DATA", + [EFI_CONVENTIONAL_MEMORY] = "CONVENTIONAL", + [EFI_UNUSABLE_MEMORY] = "UNUSABLE MEM", + [EFI_ACPI_RECLAIM_MEMORY] = "ACPI RECLAIM MEM", + [EFI_ACPI_MEMORY_NVS] = "ACPI NVS", + [EFI_MMAP_IO] = "IO", + [EFI_MMAP_IO_PORT] = "IO PORT", + [EFI_PAL_CODE] = "PAL", + [EFI_PERSISTENT_MEMORY_TYPE] = "PERSISTENT", +}; + +static const struct efi_mem_attrs { + const u64 bit; + const char *text; +} efi_mem_attrs[] = { + {EFI_MEMORY_UC, "UC"}, + {EFI_MEMORY_UC, "UC"}, + {EFI_MEMORY_WC, "WC"}, + {EFI_MEMORY_WT, "WT"}, + {EFI_MEMORY_WB, "WB"}, + {EFI_MEMORY_UCE, "UCE"}, + {EFI_MEMORY_WP, "WP"}, + {EFI_MEMORY_RP, "RP"}, + {EFI_MEMORY_XP, "WP"}, + {EFI_MEMORY_NV, "NV"}, + {EFI_MEMORY_MORE_RELIABLE, "REL"}, + {EFI_MEMORY_RO, "RO"}, + {EFI_MEMORY_SP, "SP"}, + {EFI_MEMORY_RUNTIME, "RT"}, +}; + +/** + * print_memory_attributes() - print memory map attributes + * + * @attributes: Attribute value + * + * Print memory map attributes + */ +static void print_memory_attributes(u64 attributes) +{ + int sep, i; + + for (sep = 0, i = 0; i < ARRAY_SIZE(efi_mem_attrs); i++) + if (attributes & efi_mem_attrs[i].bit) { + if (sep) { + putc('|'); + } else { + putc(' '); + sep = 1; + } + puts(efi_mem_attrs[i].text); + } +} + +#define EFI_PHYS_ADDR_WIDTH (int)(sizeof(efi_physical_addr_t) * 2) + +/** + * do_efi_show_memmap() - show UEFI memory map + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "memmap" sub-command. + * Show UEFI memory map. + */ +static int do_efi_show_memmap(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct efi_mem_desc *memmap = NULL, *map; + efi_uintn_t map_size = 0; + const char *type; + int i; + efi_status_t ret; + + ret = efi_get_memory_map(&map_size, memmap, NULL, NULL, NULL); + if (ret == EFI_BUFFER_TOO_SMALL) { + map_size += sizeof(struct efi_mem_desc); /* for my own */ + ret = efi_allocate_pool(EFI_LOADER_DATA, map_size, + (void *)&memmap); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + ret = efi_get_memory_map(&map_size, memmap, NULL, NULL, NULL); + } + if (ret != EFI_SUCCESS) { + efi_free_pool(memmap); + return CMD_RET_FAILURE; + } + + printf("Type Start%.*s End%.*s Attributes\n", + EFI_PHYS_ADDR_WIDTH - 5, spc, EFI_PHYS_ADDR_WIDTH - 3, spc); + printf("================ %.*s %.*s ==========\n", + EFI_PHYS_ADDR_WIDTH, sep, EFI_PHYS_ADDR_WIDTH, sep); + /* + * Coverity check: dereferencing null pointer "map." + * This is a false positive as memmap will always be + * populated by allocate_pool() above. + */ + for (i = 0, map = memmap; i < map_size / sizeof(*map); map++, i++) { + if (map->type < ARRAY_SIZE(efi_mem_type_string)) + type = efi_mem_type_string[map->type]; + else + type = "(unknown)"; + + printf("%-16s %.*llx-%.*llx", type, + EFI_PHYS_ADDR_WIDTH, + (u64)map_to_sysmem((void *)(uintptr_t) + map->physical_start), + EFI_PHYS_ADDR_WIDTH, + (u64)map_to_sysmem((void *)(uintptr_t) + (map->physical_start + + map->num_pages * EFI_PAGE_SIZE))); + + print_memory_attributes(map->attribute); + putc('\n'); + } + + efi_free_pool(memmap); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_show_tables() - show UEFI configuration tables + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "tables" sub-command. + * Show UEFI configuration tables. + */ +static int do_efi_show_tables(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_uintn_t i; + const char *guid_str; + + for (i = 0; i < systab.nr_tables; ++i) { + guid_str = get_guid_text(&systab.tables[i].guid); + if (!guid_str) + guid_str = ""; + printf("%pUl %s\n", &systab.tables[i].guid, guid_str); + } + + return CMD_RET_SUCCESS; +} + +/** + * create_initrd_dp() - Create a special device for our Boot### option + * + * @dev: Device + * @part: Disk partition + * @file: Filename + * Return: Pointer to the device path or ERR_PTR + * + */ +static +struct efi_device_path *create_initrd_dp(const char *dev, const char *part, + const char *file) + +{ + struct efi_device_path *tmp_dp = NULL, *tmp_fp = NULL; + struct efi_device_path *initrd_dp = NULL; + efi_status_t ret; + const struct efi_initrd_dp id_dp = { + .vendor = { + { + DEVICE_PATH_TYPE_MEDIA_DEVICE, + DEVICE_PATH_SUB_TYPE_VENDOR_PATH, + sizeof(id_dp.vendor), + }, + EFI_INITRD_MEDIA_GUID, + }, + .end = { + DEVICE_PATH_TYPE_END, + DEVICE_PATH_SUB_TYPE_END, + sizeof(id_dp.end), + } + }; + + ret = efi_dp_from_name(dev, part, file, &tmp_dp, &tmp_fp); + if (ret != EFI_SUCCESS) { + printf("Cannot create device path for \"%s %s\"\n", part, file); + goto out; + } + + initrd_dp = efi_dp_append((const struct efi_device_path *)&id_dp, + tmp_fp); + +out: + efi_free_pool(tmp_dp); + efi_free_pool(tmp_fp); + return initrd_dp; +} + +/** + * do_efi_boot_add() - set UEFI load option + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot add" sub-command. Create or change UEFI load option. + * + * efidebug boot add -b <id> <label> <interface> <devnum>[:<part>] <file> + * -i <file> <interface2> <devnum2>[:<part>] <initrd> + * -s '<options>' + */ +static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int id; + char *endp; + u16 var_name16[9]; + efi_guid_t guid; + size_t label_len, label_len16; + u16 *label; + struct efi_device_path *device_path = NULL, *file_path = NULL; + struct efi_device_path *final_fp = NULL; + struct efi_device_path *initrd_dp = NULL; + struct efi_load_option lo; + void *data = NULL; + efi_uintn_t size; + efi_uintn_t fp_size = 0; + efi_status_t ret; + int r = CMD_RET_SUCCESS; + + guid = efi_global_variable_guid; + + /* attributes */ + lo.attributes = LOAD_OPTION_ACTIVE; /* always ACTIVE */ + lo.optional_data = NULL; + lo.label = NULL; + + argc--; + argv++; /* 'add' */ + for (; argc > 0; argc--, argv++) { + if (!strcmp(argv[0], "-b")) { + if (argc < 5 || lo.label) { + r = CMD_RET_USAGE; + goto out; + } + id = (int)simple_strtoul(argv[1], &endp, 16); + if (*endp != '\0' || id > 0xffff) + return CMD_RET_USAGE; + + efi_create_indexed_name(var_name16, sizeof(var_name16), + "Boot", id); + + /* label */ + label_len = strlen(argv[2]); + label_len16 = utf8_utf16_strnlen(argv[2], label_len); + label = malloc((label_len16 + 1) * sizeof(u16)); + if (!label) + return CMD_RET_FAILURE; + lo.label = label; /* label will be changed below */ + utf8_utf16_strncpy(&label, argv[2], label_len); + + /* file path */ + ret = efi_dp_from_name(argv[3], argv[4], argv[5], + &device_path, &file_path); + if (ret != EFI_SUCCESS) { + printf("Cannot create device path for \"%s %s\"\n", + argv[3], argv[4]); + r = CMD_RET_FAILURE; + goto out; + } + fp_size += efi_dp_size(file_path) + + sizeof(struct efi_device_path); + argc -= 5; + argv += 5; + } else if (!strcmp(argv[0], "-i")) { + if (argc < 3 || initrd_dp) { + r = CMD_RET_USAGE; + goto out; + } + + initrd_dp = create_initrd_dp(argv[1], argv[2], argv[3]); + if (!initrd_dp) { + printf("Cannot add an initrd\n"); + r = CMD_RET_FAILURE; + goto out; + } + argc -= 3; + argv += 3; + fp_size += efi_dp_size(initrd_dp) + + sizeof(struct efi_device_path); + } else if (!strcmp(argv[0], "-s")) { + if (argc < 1 || lo.optional_data) { + r = CMD_RET_USAGE; + goto out; + } + lo.optional_data = (const u8 *)argv[1]; + argc -= 1; + argv += 1; + } else { + r = CMD_RET_USAGE; + goto out; + } + } + + if (!file_path) { + printf("Missing binary\n"); + r = CMD_RET_USAGE; + goto out; + } + + final_fp = efi_dp_concat(file_path, initrd_dp); + if (!final_fp) { + printf("Cannot create final device path\n"); + r = CMD_RET_FAILURE; + goto out; + } + + lo.file_path = final_fp; + lo.file_path_length = fp_size; + + size = efi_serialize_load_option(&lo, (u8 **)&data); + if (!size) { + r = CMD_RET_FAILURE; + goto out; + } + + ret = efi_set_variable_int(var_name16, &guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, data, false); + if (ret != EFI_SUCCESS) { + printf("Cannot set %ls\n", var_name16); + r = CMD_RET_FAILURE; + } + +out: + free(data); + efi_free_pool(final_fp); + efi_free_pool(initrd_dp); + efi_free_pool(device_path); + efi_free_pool(file_path); + free(lo.label); + + return r; +} + +/** + * do_efi_boot_rm() - delete UEFI load options + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot rm" sub-command. + * Delete UEFI load options. + * + * efidebug boot rm <id> ... + */ +static int do_efi_boot_rm(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + efi_guid_t guid; + int id, i; + char *endp; + u16 var_name16[9]; + efi_status_t ret; + + if (argc == 1) + return CMD_RET_USAGE; + + guid = efi_global_variable_guid; + for (i = 1; i < argc; i++, argv++) { + id = (int)simple_strtoul(argv[1], &endp, 16); + if (*endp != '\0' || id > 0xffff) + return CMD_RET_FAILURE; + + efi_create_indexed_name(var_name16, sizeof(var_name16), + "Boot", id); + ret = efi_set_variable_int(var_name16, &guid, 0, 0, NULL, + false); + if (ret) { + printf("Cannot remove %ls\n", var_name16); + return CMD_RET_FAILURE; + } + } + + return CMD_RET_SUCCESS; +} + +/** + * show_efi_boot_opt_data() - dump UEFI load option + * + * @varname16: variable name + * @data: value of UEFI load option variable + * @size: size of the boot option + * + * Decode the value of UEFI load option variable and print information. + */ +static void show_efi_boot_opt_data(u16 *varname16, void *data, size_t *size) +{ + struct efi_device_path *initrd_path = NULL; + struct efi_load_option lo; + u16 *dp_str; + efi_status_t ret; + efi_uintn_t initrd_dp_size; + const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID; + + ret = efi_deserialize_load_option(&lo, data, size); + if (ret != EFI_SUCCESS) { + printf("%ls: invalid load option\n", varname16); + return; + } + + printf("%ls:\nattributes: %c%c%c (0x%08x)\n", + varname16, + /* ACTIVE */ + lo.attributes & LOAD_OPTION_ACTIVE ? 'A' : '-', + /* FORCE RECONNECT */ + lo.attributes & LOAD_OPTION_FORCE_RECONNECT ? 'R' : '-', + /* HIDDEN */ + lo.attributes & LOAD_OPTION_HIDDEN ? 'H' : '-', + lo.attributes); + printf(" label: %ls\n", lo.label); + + dp_str = efi_dp_str(lo.file_path); + printf(" file_path: %ls\n", dp_str); + efi_free_pool(dp_str); + + initrd_path = efi_dp_from_lo(&lo, &initrd_dp_size, lf2_initrd_guid); + if (initrd_path) { + dp_str = efi_dp_str(initrd_path); + printf(" initrd_path: %ls\n", dp_str); + efi_free_pool(dp_str); + efi_free_pool(initrd_path); + } + + printf(" data:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + lo.optional_data, *size, true); +} + +/** + * show_efi_boot_opt() - dump UEFI load option + * + * @varname16: variable name + * + * Dump information defined by UEFI load option. + */ +static void show_efi_boot_opt(u16 *varname16) +{ + void *data; + efi_uintn_t size; + efi_status_t ret; + + size = 0; + ret = EFI_CALL(efi_get_variable(varname16, &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + data = malloc(size); + if (!data) { + printf("ERROR: Out of memory\n"); + return; + } + ret = EFI_CALL(efi_get_variable(varname16, + &efi_global_variable_guid, + NULL, &size, data)); + if (ret == EFI_SUCCESS) + show_efi_boot_opt_data(varname16, data, &size); + free(data); + } +} + +static int u16_tohex(u16 c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + /* not hexadecimal */ + return -1; +} + +/** + * show_efi_boot_dump() - dump all UEFI load options + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot dump" sub-command. + * Dump information of all UEFI load options defined. + * + * efidebug boot dump + */ +static int do_efi_boot_dump(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u16 *var_name16, *p; + efi_uintn_t buf_size, size; + efi_guid_t guid; + int id, i, digit; + efi_status_t ret; + + if (argc > 1) + return CMD_RET_USAGE; + + buf_size = 128; + var_name16 = malloc(buf_size); + if (!var_name16) + return CMD_RET_FAILURE; + + var_name16[0] = 0; + for (;;) { + size = buf_size; + ret = EFI_CALL(efi_get_next_variable_name(&size, var_name16, + &guid)); + if (ret == EFI_NOT_FOUND) + break; + if (ret == EFI_BUFFER_TOO_SMALL) { + buf_size = size; + p = realloc(var_name16, buf_size); + if (!p) { + free(var_name16); + return CMD_RET_FAILURE; + } + var_name16 = p; + ret = EFI_CALL(efi_get_next_variable_name(&size, + var_name16, + &guid)); + } + if (ret != EFI_SUCCESS) { + free(var_name16); + return CMD_RET_FAILURE; + } + + if (memcmp(var_name16, L"Boot", 8)) + continue; + + for (id = 0, i = 0; i < 4; i++) { + digit = u16_tohex(var_name16[4 + i]); + if (digit < 0) + break; + id = (id << 4) + digit; + } + if (i == 4 && !var_name16[8]) + show_efi_boot_opt(var_name16); + } + + free(var_name16); + + return CMD_RET_SUCCESS; +} + +/** + * show_efi_boot_order() - show order of UEFI load options + * + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Show order of UEFI load options defined by BootOrder variable. + */ +static int show_efi_boot_order(void) +{ + u16 *bootorder; + efi_uintn_t size; + int num, i; + u16 var_name16[9]; + void *data; + struct efi_load_option lo; + efi_status_t ret; + + size = 0; + ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret != EFI_BUFFER_TOO_SMALL) { + if (ret == EFI_NOT_FOUND) { + printf("BootOrder not defined\n"); + return CMD_RET_SUCCESS; + } else { + return CMD_RET_FAILURE; + } + } + bootorder = malloc(size); + if (!bootorder) { + printf("ERROR: Out of memory\n"); + return CMD_RET_FAILURE; + } + ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid, + NULL, &size, bootorder)); + if (ret != EFI_SUCCESS) { + ret = CMD_RET_FAILURE; + goto out; + } + + num = size / sizeof(u16); + for (i = 0; i < num; i++) { + efi_create_indexed_name(var_name16, sizeof(var_name16), + "Boot", bootorder[i]); + + size = 0; + ret = EFI_CALL(efi_get_variable(var_name16, + &efi_global_variable_guid, NULL, + &size, NULL)); + if (ret != EFI_BUFFER_TOO_SMALL) { + printf("%2d: %ls: (not defined)\n", i + 1, var_name16); + continue; + } + + data = malloc(size); + if (!data) { + ret = CMD_RET_FAILURE; + goto out; + } + ret = EFI_CALL(efi_get_variable(var_name16, + &efi_global_variable_guid, NULL, + &size, data)); + if (ret != EFI_SUCCESS) { + free(data); + ret = CMD_RET_FAILURE; + goto out; + } + + ret = efi_deserialize_load_option(&lo, data, &size); + if (ret != EFI_SUCCESS) { + printf("%ls: invalid load option\n", var_name16); + ret = CMD_RET_FAILURE; + goto out; + } + + printf("%2d: %ls: %ls\n", i + 1, var_name16, lo.label); + + free(data); + } +out: + free(bootorder); + + return ret; +} + +/** + * do_efi_boot_next() - manage UEFI BootNext variable + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot next" sub-command. + * Set BootNext variable. + * + * efidebug boot next <id> + */ +static int do_efi_boot_next(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u16 bootnext; + efi_uintn_t size; + char *endp; + efi_guid_t guid; + efi_status_t ret; + int r = CMD_RET_SUCCESS; + + if (argc != 2) + return CMD_RET_USAGE; + + bootnext = (u16)simple_strtoul(argv[1], &endp, 16); + if (*endp) { + printf("invalid value: %s\n", argv[1]); + r = CMD_RET_FAILURE; + goto out; + } + + guid = efi_global_variable_guid; + size = sizeof(u16); + ret = efi_set_variable_int(L"BootNext", &guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, &bootnext, false); + if (ret != EFI_SUCCESS) { + printf("Cannot set BootNext\n"); + r = CMD_RET_FAILURE; + } +out: + return r; +} + +/** + * do_efi_boot_order() - manage UEFI BootOrder variable + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot order" sub-command. + * Show order of UEFI load options, or change it in BootOrder variable. + * + * efidebug boot order [<id> ...] + */ +static int do_efi_boot_order(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u16 *bootorder = NULL; + efi_uintn_t size; + int id, i; + char *endp; + efi_guid_t guid; + efi_status_t ret; + int r = CMD_RET_SUCCESS; + + if (argc == 1) + return show_efi_boot_order(); + + argc--; + argv++; + + size = argc * sizeof(u16); + bootorder = malloc(size); + if (!bootorder) + return CMD_RET_FAILURE; + + for (i = 0; i < argc; i++) { + id = (int)simple_strtoul(argv[i], &endp, 16); + if (*endp != '\0' || id > 0xffff) { + printf("invalid value: %s\n", argv[i]); + r = CMD_RET_FAILURE; + goto out; + } + + bootorder[i] = (u16)id; + } + + guid = efi_global_variable_guid; + ret = efi_set_variable_int(L"BootOrder", &guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, bootorder, true); + if (ret != EFI_SUCCESS) { + printf("Cannot set BootOrder\n"); + r = CMD_RET_FAILURE; + } +out: + free(bootorder); + + return r; +} + +static struct cmd_tbl cmd_efidebug_boot_sub[] = { + U_BOOT_CMD_MKENT(add, CONFIG_SYS_MAXARGS, 1, do_efi_boot_add, "", ""), + U_BOOT_CMD_MKENT(rm, CONFIG_SYS_MAXARGS, 1, do_efi_boot_rm, "", ""), + U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_efi_boot_dump, "", ""), + U_BOOT_CMD_MKENT(next, CONFIG_SYS_MAXARGS, 1, do_efi_boot_next, "", ""), + U_BOOT_CMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_efi_boot_order, + "", ""), +}; + +/** + * do_efi_boot_opt() - manage UEFI load options + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot" sub-command. + */ +static int do_efi_boot_opt(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_boot_sub, + ARRAY_SIZE(cmd_efidebug_boot_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +/** + * do_efi_test_bootmgr() - run simple bootmgr for test + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "test bootmgr" sub-command. + * Run simple bootmgr for test. + * + * efidebug test bootmgr + */ +static __maybe_unused int do_efi_test_bootmgr(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_handle_t image; + efi_uintn_t exit_data_size = 0; + u16 *exit_data = NULL; + efi_status_t ret; + void *load_options = NULL; + + ret = efi_bootmgr_load(&image, &load_options); + printf("efi_bootmgr_load() returned: %ld\n", ret & ~EFI_ERROR_MASK); + + /* We call efi_start_image() even if error for test purpose. */ + ret = EFI_CALL(efi_start_image(image, &exit_data_size, &exit_data)); + printf("efi_start_image() returned: %ld\n", ret & ~EFI_ERROR_MASK); + if (ret && exit_data) + efi_free_pool(exit_data); + + efi_restore_gd(); + + free(load_options); + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_efidebug_test_sub[] = { +#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR + U_BOOT_CMD_MKENT(bootmgr, CONFIG_SYS_MAXARGS, 1, do_efi_test_bootmgr, + "", ""), +#endif +}; + +/** + * do_efi_test() - manage UEFI load options + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "test" sub-command. + */ +static int do_efi_test(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_test_sub, + ARRAY_SIZE(cmd_efidebug_test_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +/** + * do_efi_query_info() - QueryVariableInfo EFI service + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_FAILURE on failure + * + * Implement efidebug "test" sub-command. + */ + +static int do_efi_query_info(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_status_t ret; + u32 attr = 0; + u64 max_variable_storage_size; + u64 remain_variable_storage_size; + u64 max_variable_size; + int i; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-bs")) + attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; + else if (!strcmp(argv[i], "-rt")) + attr |= EFI_VARIABLE_RUNTIME_ACCESS; + else if (!strcmp(argv[i], "-nv")) + attr |= EFI_VARIABLE_NON_VOLATILE; + else if (!strcmp(argv[i], "-at")) + attr |= + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + } + + ret = EFI_CALL(efi_query_variable_info(attr, + &max_variable_storage_size, + &remain_variable_storage_size, + &max_variable_size)); + if (ret != EFI_SUCCESS) { + printf("Error: Cannot query UEFI variables, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + printf("Max storage size %llu\n", max_variable_storage_size); + printf("Remaining storage size %llu\n", remain_variable_storage_size); + printf("Max variable size %llu\n", max_variable_size); + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_efidebug_sub[] = { + U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""), +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT + U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule, + "", ""), +#endif + U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices, + "", ""), + U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers, + "", ""), + U_BOOT_CMD_MKENT(dh, CONFIG_SYS_MAXARGS, 1, do_efi_show_handles, + "", ""), + U_BOOT_CMD_MKENT(images, CONFIG_SYS_MAXARGS, 1, do_efi_show_images, + "", ""), + U_BOOT_CMD_MKENT(memmap, CONFIG_SYS_MAXARGS, 1, do_efi_show_memmap, + "", ""), + U_BOOT_CMD_MKENT(tables, CONFIG_SYS_MAXARGS, 1, do_efi_show_tables, + "", ""), + U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_efi_test, + "", ""), + U_BOOT_CMD_MKENT(query, CONFIG_SYS_MAXARGS, 1, do_efi_query_info, + "", ""), +}; + +/** + * do_efidebug() - display and configure UEFI environment + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug command which allows us to display and + * configure UEFI environment. + */ +static int do_efidebug(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + efi_status_t r; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + /* Initialize UEFI drivers */ + r = efi_init_obj_list(); + if (r != EFI_SUCCESS) { + printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", + r & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + cp = find_cmd_tbl(argv[0], cmd_efidebug_sub, + ARRAY_SIZE(cmd_efidebug_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +#ifdef CONFIG_SYS_LONGHELP +static char efidebug_help_text[] = + " - UEFI Shell-like interface to configure UEFI environment\n" + "\n" + "efidebug boot add " + "-b <bootid> <label> <interface> <devnum>[:<part>] <file path> " + "-i <interface> <devnum>[:<part>] <initrd file path> " + "-s '<optional data>'\n" + " - set UEFI BootXXXX variable\n" + " <load options> will be passed to UEFI application\n" + "efidebug boot rm <bootid#1> [<bootid#2> [<bootid#3> [...]]]\n" + " - delete UEFI BootXXXX variables\n" + "efidebug boot dump\n" + " - dump all UEFI BootXXXX variables\n" + "efidebug boot next <bootid>\n" + " - set UEFI BootNext variable\n" + "efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n" + " - set/show UEFI boot order\n" + "\n" +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT + "efidebug capsule update [-v] <capsule address>\n" + " - process a capsule\n" + "efidebug capsule disk-update\n" + " - update a capsule from disk\n" + "efidebug capsule show <capsule address>\n" + " - show capsule information\n" + "efidebug capsule result [<capsule result var>]\n" + " - show a capsule update result\n" +#ifdef CONFIG_EFI_ESRT + "efidebug capsule esrt\n" + " - print the ESRT\n" +#endif + "\n" +#endif + "efidebug devices\n" + " - show UEFI devices\n" + "efidebug drivers\n" + " - show UEFI drivers\n" + "efidebug dh\n" + " - show UEFI handles\n" + "efidebug images\n" + " - show loaded images\n" + "efidebug memmap\n" + " - show UEFI memory map\n" + "efidebug tables\n" + " - show UEFI configuration tables\n" +#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR + "efidebug test bootmgr\n" + " - run simple bootmgr for test\n" +#endif + "efidebug query [-nv][-bs][-rt][-at]\n" + " - show size of UEFI variables store\n"; +#endif + +U_BOOT_CMD( + efidebug, CONFIG_SYS_MAXARGS, 0, do_efidebug, + "Configure UEFI environment", + efidebug_help_text +); diff --git a/roms/u-boot/cmd/elf.c b/roms/u-boot/cmd/elf.c new file mode 100644 index 000000000..d44b95d90 --- /dev/null +++ b/roms/u-boot/cmd/elf.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2001 William L. Pitts + * All rights reserved. + */ + +#include <common.h> +#include <command.h> +#include <cpu_func.h> +#include <elf.h> +#include <env.h> +#include <image.h> +#include <log.h> +#include <net.h> +#include <vxworks.h> +#ifdef CONFIG_X86 +#include <vbe.h> +#include <asm/cache.h> +#include <asm/e820.h> +#include <linux/linkage.h> +#endif + +/* Allow ports to override the default behavior */ +static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]), + int argc, char *const argv[]) +{ + unsigned long ret; + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + ret = entry(argc, argv); + + return ret; +} + +/* Interpreter command to boot an arbitrary ELF image from memory */ +int do_bootelf(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long addr; /* Address of the ELF image */ + unsigned long rc; /* Return value from user code */ + char *sload = NULL; + const char *ep = env_get("autostart"); + int rcode = 0; + + /* Consume 'bootelf' */ + argc--; argv++; + + /* Check for flag. */ + if (argc >= 1 && (argv[0][0] == '-' && \ + (argv[0][1] == 'p' || argv[0][1] == 's'))) { + sload = argv[0]; + /* Consume flag. */ + argc--; argv++; + } + /* Check for address. */ + if (argc >= 1 && strict_strtoul(argv[0], 16, &addr) != -EINVAL) { + /* Consume address */ + argc--; argv++; + } else + addr = image_load_addr; + + if (!valid_elf_image(addr)) + return 1; + + if (sload && sload[1] == 'p') + addr = load_elf_image_phdr(addr); + else + addr = load_elf_image_shdr(addr); + + if (ep && !strcmp(ep, "no")) + return rcode; + + printf("## Starting application at 0x%08lx ...\n", addr); + + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args + */ + rc = do_bootelf_exec((void *)addr, argc, argv); + if (rc != 0) + rcode = 1; + + printf("## Application terminated, rc = 0x%lx\n", rc); + + return rcode; +} + +/* + * Interpreter command to boot VxWorks from a memory image. The image can + * be either an ELF image or a raw binary. Will attempt to setup the + * bootline and other parameters correctly. + */ +int do_bootvx(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long addr; /* Address of image */ + unsigned long bootaddr = 0; /* Address to put the bootline */ + char *bootline; /* Text of the bootline */ + char *tmp; /* Temporary char pointer */ + char build_buf[128]; /* Buffer for building the bootline */ + int ptr = 0; +#ifdef CONFIG_X86 + ulong base; + struct e820_info *info; + struct e820_entry *data; + struct efi_gop_info *gop; + struct vesa_mode_info *vesa = &mode_info.vesa; +#endif + + /* + * Check the loadaddr variable. + * If we don't know where the image is then we're done. + */ + if (argc < 2) + addr = image_load_addr; + else + addr = simple_strtoul(argv[1], NULL, 16); + +#if defined(CONFIG_CMD_NET) + /* + * Check to see if we need to tftp the image ourselves + * before starting + */ + if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) { + if (net_loop(TFTPGET) <= 0) + return 1; + printf("Automatic boot of VxWorks image at address 0x%08lx ...\n", + addr); + } +#endif + + /* + * This should equate to + * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET + * from the VxWorks BSP header files. + * This will vary from board to board + */ +#if defined(CONFIG_SYS_VXWORKS_MAC_PTR) + tmp = (char *)CONFIG_SYS_VXWORKS_MAC_PTR; + eth_env_get_enetaddr("ethaddr", (uchar *)build_buf); + memcpy(tmp, build_buf, 6); +#else + puts("## Ethernet MAC address not copied to NV RAM\n"); +#endif + +#ifdef CONFIG_X86 + /* + * Get VxWorks's physical memory base address from environment, + * if we don't specify it in the environment, use a default one. + */ + base = env_get_hex("vx_phys_mem_base", VXWORKS_PHYS_MEM_BASE); + data = (struct e820_entry *)(base + E820_DATA_OFFSET); + info = (struct e820_info *)(base + E820_INFO_OFFSET); + + memset(info, 0, sizeof(struct e820_info)); + info->sign = E820_SIGNATURE; + info->entries = install_e820_map(E820MAX, data); + info->addr = (info->entries - 1) * sizeof(struct e820_entry) + + E820_DATA_OFFSET; + + /* + * Explicitly clear the bootloader image size otherwise if memory + * at this offset happens to contain some garbage data, the final + * available memory size for the kernel is insane. + */ + *(u32 *)(base + BOOT_IMAGE_SIZE_OFFSET) = 0; + + /* + * Prepare compatible framebuffer information block. + * The VESA mode has to be 32-bit RGBA. + */ + if (vesa->x_resolution && vesa->y_resolution) { + gop = (struct efi_gop_info *)(base + EFI_GOP_INFO_OFFSET); + gop->magic = EFI_GOP_INFO_MAGIC; + gop->info.version = 0; + gop->info.width = vesa->x_resolution; + gop->info.height = vesa->y_resolution; + gop->info.pixel_format = EFI_GOT_RGBA8; + gop->info.pixels_per_scanline = vesa->bytes_per_scanline / 4; + gop->fb_base = vesa->phys_base_ptr; + gop->fb_size = vesa->bytes_per_scanline * vesa->y_resolution; + } +#endif + + /* + * Use bootaddr to find the location in memory that VxWorks + * will look for the bootline string. The default value is + * (LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET) as defined by + * VxWorks BSP. For example, on PowerPC it defaults to 0x4200. + */ + tmp = env_get("bootaddr"); + if (!tmp) { +#ifdef CONFIG_X86 + bootaddr = base + X86_BOOT_LINE_OFFSET; +#else + printf("## VxWorks bootline address not specified\n"); + return 1; +#endif + } + + if (!bootaddr) + bootaddr = simple_strtoul(tmp, NULL, 16); + + /* + * Check to see if the bootline is defined in the 'bootargs' parameter. + * If it is not defined, we may be able to construct the info. + */ + bootline = env_get("bootargs"); + if (!bootline) { + tmp = env_get("bootdev"); + if (tmp) { + strcpy(build_buf, tmp); + ptr = strlen(tmp); + } else { + printf("## VxWorks boot device not specified\n"); + } + + tmp = env_get("bootfile"); + if (tmp) + ptr += sprintf(build_buf + ptr, "host:%s ", tmp); + else + ptr += sprintf(build_buf + ptr, "host:vxWorks "); + + /* + * The following parameters are only needed if 'bootdev' + * is an ethernet device, otherwise they are optional. + */ + tmp = env_get("ipaddr"); + if (tmp) { + ptr += sprintf(build_buf + ptr, "e=%s", tmp); + tmp = env_get("netmask"); + if (tmp) { + u32 mask = env_get_ip("netmask").s_addr; + ptr += sprintf(build_buf + ptr, + ":%08x ", ntohl(mask)); + } else { + ptr += sprintf(build_buf + ptr, " "); + } + } + + tmp = env_get("serverip"); + if (tmp) + ptr += sprintf(build_buf + ptr, "h=%s ", tmp); + + tmp = env_get("gatewayip"); + if (tmp) + ptr += sprintf(build_buf + ptr, "g=%s ", tmp); + + tmp = env_get("hostname"); + if (tmp) + ptr += sprintf(build_buf + ptr, "tn=%s ", tmp); + + tmp = env_get("othbootargs"); + if (tmp) { + strcpy(build_buf + ptr, tmp); + ptr += strlen(tmp); + } + + bootline = build_buf; + } + + memcpy((void *)bootaddr, bootline, max(strlen(bootline), (size_t)255)); + flush_cache(bootaddr, max(strlen(bootline), (size_t)255)); + printf("## Using bootline (@ 0x%lx): %s\n", bootaddr, (char *)bootaddr); + + /* + * If the data at the load address is an elf image, then + * treat it like an elf image. Otherwise, assume that it is a + * binary image. + */ + if (valid_elf_image(addr)) + addr = load_elf_image_phdr(addr); + else + puts("## Not an ELF image, assuming binary\n"); + + printf("## Starting vxWorks at 0x%08lx ...\n", addr); + + dcache_disable(); +#if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI) + armv8_setup_psci(); + smp_kick_all_cpus(); +#endif + +#ifdef CONFIG_X86 + /* VxWorks on x86 uses stack to pass parameters */ + ((asmlinkage void (*)(int))addr)(0); +#else + ((void (*)(int))addr)(0); +#endif + + puts("## vxWorks terminated\n"); + + return 1; +} + +U_BOOT_CMD( + bootelf, CONFIG_SYS_MAXARGS, 0, do_bootelf, + "Boot from an ELF image in memory", + "[-p|-s] [address]\n" + "\t- load ELF image at [address] via program headers (-p)\n" + "\t or via section headers (-s)" +); + +U_BOOT_CMD( + bootvx, 2, 0, do_bootvx, + "Boot vxWorks from an ELF image", + " [address] - load address of vxWorks ELF image." +); diff --git a/roms/u-boot/cmd/ethsw.c b/roms/u-boot/cmd/ethsw.c new file mode 100644 index 000000000..f8b8a798b --- /dev/null +++ b/roms/u-boot/cmd/ethsw.c @@ -0,0 +1,1103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * Ethernet Switch commands + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <errno.h> +#include <env_flags.h> +#include <ethsw.h> +#include <net.h> + +static const char *ethsw_name; + +#define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \ +"{ [help] | [clear] } - show an l2 switch port's statistics" + +static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_STATS_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_LEARN_HELP "ethsw [port <port_no>] learning " \ +"{ [help] | show | auto | disable } " \ +"- enable/disable/show learning configuration on a port" + +static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_LEARN_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_FDB_HELP "ethsw [port <port_no>] [vlan <vid>] fdb " \ +"{ [help] | show | flush | { add | del } <mac> } " \ +"- Add/delete a mac entry in FDB; use show to see FDB entries; " \ +"if vlan <vid> is missing, VID 1 will be used" + +static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_FDB_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_PVID_HELP "ethsw [port <port_no>] " \ +"pvid { [help] | show | <pvid> } " \ +"- set/show PVID (ingress and egress VLAN tagging) for a port" + +static int ethsw_pvid_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PVID_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_VLAN_HELP "ethsw [port <port_no>] vlan " \ +"{ [help] | show | add <vid> | del <vid> } " \ +"- add a VLAN to a port (VLAN members)" + +static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_VLAN_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_PORT_UNTAG_HELP "ethsw [port <port_no>] untagged " \ +"{ [help] | show | all | none | pvid } " \ +" - set egress tagging mode for a port" + +static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_UNTAG_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_EGR_VLAN_TAG_HELP "ethsw [port <port_no>] egress tag " \ +"{ [help] | show | pvid | classified } " \ +"- Configure VID source for egress tag. " \ +"Tag's VID could be the frame's classified VID or the PVID of the port" + +static int ethsw_egr_tag_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_EGR_VLAN_TAG_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_VLAN_FDB_HELP "ethsw vlan fdb " \ +"{ [help] | show | shared | private } " \ +"- make VLAN learning shared or private" + +static int ethsw_vlan_learn_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_VLAN_FDB_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_PORT_INGR_FLTR_HELP "ethsw [port <port_no>] ingress filtering" \ +" { [help] | show | enable | disable } " \ +"- enable/disable VLAN ingress filtering on port" + +static int ethsw_ingr_fltr_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_INGR_FLTR_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +#define ETHSW_PORT_AGGR_HELP "ethsw [port <port_no>] aggr" \ +" { [help] | show | <lag_group_no> } " \ +"- get/set LAG group for a port" + +static int ethsw_port_aggr_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_AGGR_HELP"\n"); + + return CMD_RET_SUCCESS; +} + +static struct keywords_to_function { + enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; + int cmd_func_offset; + int (*keyword_function)(struct ethsw_command_def *parsed_cmd); +} ethsw_cmd_def[] = { + { + .cmd_keyword = { + ethsw_id_enable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_enable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_disable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_stats_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_stats), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_clear, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_stats_clear), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_auto, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_fdb_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_fdb_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_flush, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_flush), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_add, + ethsw_id_add_del_mac, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_entry_add), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_del, + ethsw_id_add_del_mac, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_entry_del), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_pvid, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_pvid_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_pvid, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_pvid_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_pvid, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + pvid_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_pvid, + ethsw_id_pvid_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + pvid_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_add, + ethsw_id_add_del_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_del, + ethsw_id_add_del_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_untag_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_untag_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_untag_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_all, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_untag_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_none, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_untag_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_pvid, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_untag_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_egress, + ethsw_id_tag, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_egr_tag_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_egress, + ethsw_id_tag, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_egr_tag_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_egress, + ethsw_id_tag, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_egr_vlan_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_egress, + ethsw_id_tag, + ethsw_id_pvid, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_egr_vlan_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_egress, + ethsw_id_tag, + ethsw_id_classified, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_egr_vlan_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_shared, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_private, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_ingr_fltr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_ingr_fltr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_enable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_aggr_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_set), + .keyword_function = NULL, + }, +}; + +struct keywords_optional { + int cmd_keyword[ETHSW_MAX_CMD_PARAMS]; +} cmd_opt_def[] = { + { + .cmd_keyword = { + ethsw_id_port, + ethsw_id_port_no, + ethsw_id_key_end, + }, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_vlan_no, + ethsw_id_key_end, + }, + }, { + .cmd_keyword = { + ethsw_id_port, + ethsw_id_port_no, + ethsw_id_vlan, + ethsw_id_vlan_no, + ethsw_id_key_end, + }, + }, +}; + +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char + *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); + +/* + * Define properties for each keyword; + * keep the order synced with enum ethsw_keyword_id + */ +struct keyword_def { + const char *keyword_name; + int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[], + int *argc_nr, struct ethsw_command_def *parsed_cmd); +} keyword[] = { + { + .keyword_name = "help", + .match = &keyword_match_gen, + }, { + .keyword_name = "show", + .match = &keyword_match_gen, + }, { + .keyword_name = "port", + .match = &keyword_match_port + }, { + .keyword_name = "enable", + .match = &keyword_match_gen, + }, { + .keyword_name = "disable", + .match = &keyword_match_gen, + }, { + .keyword_name = "statistics", + .match = &keyword_match_gen, + }, { + .keyword_name = "clear", + .match = &keyword_match_gen, + }, { + .keyword_name = "learning", + .match = &keyword_match_gen, + }, { + .keyword_name = "auto", + .match = &keyword_match_gen, + }, { + .keyword_name = "vlan", + .match = &keyword_match_vlan, + }, { + .keyword_name = "fdb", + .match = &keyword_match_gen, + }, { + .keyword_name = "add", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "del", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "flush", + .match = &keyword_match_gen, + }, { + .keyword_name = "pvid", + .match = &keyword_match_pvid, + }, { + .keyword_name = "untagged", + .match = &keyword_match_gen, + }, { + .keyword_name = "all", + .match = &keyword_match_gen, + }, { + .keyword_name = "none", + .match = &keyword_match_gen, + }, { + .keyword_name = "egress", + .match = &keyword_match_gen, + }, { + .keyword_name = "tag", + .match = &keyword_match_gen, + }, { + .keyword_name = "classified", + .match = &keyword_match_gen, + }, { + .keyword_name = "shared", + .match = &keyword_match_gen, + }, { + .keyword_name = "private", + .match = &keyword_match_gen, + }, { + .keyword_name = "ingress", + .match = &keyword_match_gen, + }, { + .keyword_name = "filtering", + .match = &keyword_match_gen, + }, { + .keyword_name = "aggr", + .match = &keyword_match_aggr, + }, +}; + +/* + * Function used by an Ethernet Switch driver to set the functions + * that must be called by the parser when an ethsw command is given + */ +int ethsw_define_functions(const struct ethsw_command_func *cmd_func) +{ + int i; + void **aux_p; + int (*cmd_func_aux)(struct ethsw_command_def *); + + if (!cmd_func->ethsw_name) + return -EINVAL; + + ethsw_name = cmd_func->ethsw_name; + + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + /* + * get the pointer to the function send by the Ethernet Switch + * driver that corresponds to the proper ethsw command + */ + if (ethsw_cmd_def[i].keyword_function) + continue; + + aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset; + + cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p; + ethsw_cmd_def[i].keyword_function = cmd_func_aux; + } + + return 0; +} + +/* Generic function used to match a keyword only by a string */ +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) { + parsed_cmd->cmd_to_keywords[*argc_nr] = key_id; + + return 1; + } + return 0; +} + +/* Function used to match the command's port */ +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->port = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no; + return 1; + } + + return 0; +} + +/* Function used to match the command's vlan */ +static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + int aux; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->vid = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_vlan_no; + return 1; + } + + aux = *argc_nr + 1; + + if (keyword_match_gen(ethsw_id_add, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add; + else if (keyword_match_gen(ethsw_id_del, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_del; + else + return 0; + + if (*argc_nr + 2 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) { + parsed_cmd->vid = val; + (*argc_nr) += 2; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_add_del_no; + return 1; + } + + return 0; +} + +/* Function used to match the command's pvid */ +static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 1; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->vid = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_pvid_no; + } + + return 1; +} + +/* Function used to match the command's MAC address */ +static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if ((*argc_nr + 1 >= argc) || + !is_broadcast_ethaddr(parsed_cmd->ethaddr)) + return 1; + + if (eth_validate_ethaddr_str(argv[*argc_nr + 1])) { + printf("Invalid MAC address: %s\n", argv[*argc_nr + 1]); + return 0; + } + + string_to_enetaddr(argv[*argc_nr + 1], parsed_cmd->ethaddr); + + if (is_broadcast_ethaddr(parsed_cmd->ethaddr)) { + memset(parsed_cmd->ethaddr, 0xFF, sizeof(parsed_cmd->ethaddr)); + return 0; + } + + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add_del_mac; + + return 1; +} + +/* Function used to match the command's aggregation number */ +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 1; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->aggr_grp = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_aggr_no; + } + + return 1; +} + +/* Finds optional keywords and modifies *argc_va to skip them */ +static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, + int *argc_val) +{ + int i; + int keyw_opt_matched; + int argc_val_max; + int const *cmd_keyw_p; + int const *cmd_keyw_opt_p; + + /* remember the best match */ + argc_val_max = *argc_val; + + /* + * check if our command's optional keywords match the optional + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(cmd_opt_def); i++) { + keyw_opt_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched]; + cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_opt_matched + *argc_val < + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) { + keyw_opt_matched++; + cmd_keyw_p++; + cmd_keyw_opt_p++; + } + + /* + * if all our optional command's keywords perfectly match an + * optional pattern, then we can move to the next defined + * keywords in our command; remember the one that matched the + * greatest number of keywords + */ + if (keyw_opt_matched + *argc_val <= + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p == ethsw_id_key_end && + *argc_val + keyw_opt_matched > argc_val_max) + argc_val_max = *argc_val + keyw_opt_matched; + } + + *argc_val = argc_val_max; +} + +/* + * Finds the function to call based on keywords and + * modifies *argc_va to skip them + */ +static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd, + int *argc_val) +{ + int i; + int keyw_matched; + int *cmd_keyw_p; + int *cmd_keyw_def_p; + + /* + * check if our command's keywords match the + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + keyw_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched]; + cmd_keyw_def_p = ðsw_cmd_def[i].cmd_keyword[keyw_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) { + keyw_matched++; + cmd_keyw_p++; + cmd_keyw_def_p++; + } + + /* + * if all our command's keywords perfectly match an + * available command, then we get the function we need to call + * to configure the Ethernet Switch + */ + if (keyw_matched && keyw_matched + *argc_val == + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p == ethsw_id_key_end) { + *argc_val += keyw_matched; + parsed_cmd->cmd_function = + ethsw_cmd_def[i].keyword_function; + return; + } + } +} + +/* find all the keywords in the command */ +static int keywords_find(int argc, char *const argv[], + struct ethsw_command_def *parsed_cmd) +{ + int i; + int j; + int argc_val; + int rc = CMD_RET_SUCCESS; + + for (i = 1; i < argc; i++) { + for (j = 0; j < ethsw_id_count; j++) { + if (keyword[j].match(j, argc, argv, &i, parsed_cmd)) + break; + } + } + + /* if there is no keyword match for a word, the command is invalid */ + for (i = 1; i < argc; i++) + if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end) + rc = CMD_RET_USAGE; + + parsed_cmd->cmd_keywords_nr = argc; + argc_val = 1; + + /* get optional parameters first */ + cmd_keywords_opt_check(parsed_cmd, &argc_val); + + if (argc_val == parsed_cmd->cmd_keywords_nr) + return CMD_RET_USAGE; + + /* + * check the keywords and if a match is found, + * get the function to call + */ + cmd_keywords_check(parsed_cmd, &argc_val); + + /* error if not all commands' parameters were matched */ + if (argc_val == parsed_cmd->cmd_keywords_nr) { + if (!parsed_cmd->cmd_function) { + printf("Command not available for: %s\n", ethsw_name); + rc = CMD_RET_FAILURE; + } + } else { + rc = CMD_RET_USAGE; + } + + return rc; +} + +static void command_def_init(struct ethsw_command_def *parsed_cmd) +{ + int i; + + for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++) + parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; + + parsed_cmd->port = ETHSW_CMD_PORT_ALL; + parsed_cmd->vid = ETHSW_CMD_VLAN_ALL; + parsed_cmd->aggr_grp = ETHSW_CMD_AGGR_GRP_NONE; + parsed_cmd->cmd_function = NULL; + + /* We initialize the MAC address with the Broadcast address */ + memset(parsed_cmd->ethaddr, 0xff, sizeof(parsed_cmd->ethaddr)); +} + +/* function to interpret commands starting with "ethsw " */ +static int do_ethsw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ethsw_command_def parsed_cmd; + int rc = CMD_RET_SUCCESS; + + if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS) + return CMD_RET_USAGE; + + command_def_init(&parsed_cmd); + + rc = keywords_find(argc, argv, &parsed_cmd); + + if (rc == CMD_RET_SUCCESS) + rc = parsed_cmd.cmd_function(&parsed_cmd); + + return rc; +} + +#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \ +"- enable/disable a port; show a port's configuration" + +U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, + "Ethernet l2 switch commands", + ETHSW_PORT_CONF_HELP"\n" + ETHSW_PORT_STATS_HELP"\n" + ETHSW_LEARN_HELP"\n" + ETHSW_FDB_HELP"\n" + ETHSW_PVID_HELP"\n" + ETHSW_VLAN_HELP"\n" + ETHSW_PORT_UNTAG_HELP"\n" + ETHSW_EGR_VLAN_TAG_HELP"\n" + ETHSW_VLAN_FDB_HELP"\n" + ETHSW_PORT_INGR_FLTR_HELP"\n" + ETHSW_PORT_AGGR_HELP"\n" +); diff --git a/roms/u-boot/cmd/exit.c b/roms/u-boot/cmd/exit.c new file mode 100644 index 000000000..923f0870f --- /dev/null +++ b/roms/u-boot/cmd/exit.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> + +static int do_exit(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc > 1) + return simple_strtoul(argv[1], NULL, 10); + + return 0; +} + +U_BOOT_CMD( + exit, 2, 1, do_exit, + "exit script", + "" +); diff --git a/roms/u-boot/cmd/ext2.c b/roms/u-boot/cmd/ext2.c new file mode 100644 index 000000000..57a99516a --- /dev/null +++ b/roms/u-boot/cmd/ext2.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + */ + +/* + * Ext2fs support + */ +#include <common.h> +#include <command.h> +#include <fs.h> + +static int do_ext2ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +/****************************************************************************** + * Ext2fs boot command intepreter. Derived from diskboot + */ +int do_ext2load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +U_BOOT_CMD( + ext2ls, 4, 1, do_ext2ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +) + +U_BOOT_CMD( + ext2load, 6, 0, do_ext2load, + "load binary file from a Ext2 filesystem", + "<interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from ext2 filesystem." +) diff --git a/roms/u-boot/cmd/ext4.c b/roms/u-boot/cmd/ext4.c new file mode 100644 index 000000000..4791b69fd --- /dev/null +++ b/roms/u-boot/cmd/ext4.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 - 2012 Samsung Electronics + * EXT4 filesystem implementation in Uboot by + * Uma Shankar <uma.shankar@samsung.com> + * Manjunatha C Achar <a.manjunatha@samsung.com> + * + * Ext4fs support + * made from existing cmd_ext2.c file of Uboot + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + */ + +/* + * Changelog: + * 0.1 - Newly created file for ext4fs support. Taken from cmd_ext2.c + * file in uboot. Added ext4fs ls load and write support. + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <ext4fs.h> +#include <linux/stat.h> +#include <malloc.h> +#include <fs.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +int do_ext4_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +int do_ext4_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +int do_ext4_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +#if defined(CONFIG_CMD_EXT4_WRITE) +int do_ext4_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_save(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + +U_BOOT_CMD(ext4write, 7, 1, do_ext4_write, + "create a file in the root directory", + "<interface> <dev[:part]> <addr> <absolute filename path>\n" + " [sizebytes] [file offset]\n" + " - create a file in / directory"); + +#endif + +U_BOOT_CMD( + ext4size, 4, 0, do_ext4_size, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + +U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'"); + +U_BOOT_CMD(ext4load, 7, 0, do_ext4_load, + "load binary file from a Ext4 filesystem", + "<interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from ext4 filesystem"); diff --git a/roms/u-boot/cmd/extension_board.c b/roms/u-boot/cmd/extension_board.c new file mode 100644 index 000000000..bbb4812ff --- /dev/null +++ b/roms/u-boot/cmd/extension_board.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021 + * Köry Maincent, Bootlin, <kory.maincent@bootlin.com> + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <extension_board.h> +#include <mapmem.h> +#include <linux/libfdt.h> +#include <fdt_support.h> + +static LIST_HEAD(extension_list); + +static int extension_apply(struct extension *extension) +{ + char *overlay_cmd; + ulong extrasize, overlay_addr; + struct fdt_header *blob; + + if (!working_fdt) { + printf("No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr <address>\" command.\n"); + return CMD_RET_FAILURE; + } + + overlay_cmd = env_get("extension_overlay_cmd"); + if (!overlay_cmd) { + printf("Environment extension_overlay_cmd is missing\n"); + return CMD_RET_FAILURE; + } + + overlay_addr = env_get_hex("extension_overlay_addr", 0); + if (!overlay_addr) { + printf("Environment extension_overlay_addr is missing\n"); + return CMD_RET_FAILURE; + } + + env_set("extension_overlay_name", extension->overlay); + if (run_command(overlay_cmd, 0) != 0) + return CMD_RET_FAILURE; + + extrasize = env_get_hex("filesize", 0); + if (!extrasize) + return CMD_RET_FAILURE; + + fdt_shrink_to_minimum(working_fdt, extrasize); + + blob = map_sysmem(overlay_addr, 0); + if (!fdt_valid(&blob)) + return CMD_RET_FAILURE; + + /* apply method prints messages on error */ + if (fdt_overlay_apply_verbose(working_fdt, blob)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int do_extension_list(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int i = 0; + struct extension *extension; + + if (list_empty(&extension_list)) { + printf("No extension registered - Please run \"extension scan\"\n"); + return CMD_RET_SUCCESS; + } + + list_for_each_entry(extension, &extension_list, list) { + printf("Extension %d: %s\n", i++, extension->name); + printf("\tManufacturer: \t\t%s\n", extension->owner); + printf("\tVersion: \t\t%s\n", extension->version); + printf("\tDevicetree overlay: \t%s\n", extension->overlay); + printf("\tOther information: \t%s\n", extension->other); + } + return CMD_RET_SUCCESS; +} + +static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct extension *extension, *next; + int extension_num; + + list_for_each_entry_safe(extension, next, &extension_list, list) { + list_del(&extension->list); + free(extension); + } + extension_num = extension_board_scan(&extension_list); + + if (extension_num < 0) + return CMD_RET_FAILURE; + + printf("Found %d extension board(s).\n", extension_num); + + return CMD_RET_SUCCESS; +} + +static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct extension *extension = NULL; + struct list_head *entry; + int i = 0, extension_id, ret; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "all") == 0) { + list_for_each_entry(extension, &extension_list, list) { + ret = extension_apply(extension); + if (ret != CMD_RET_SUCCESS) + break; + } + } else { + extension_id = simple_strtol(argv[1], NULL, 10); + list_for_each(entry, &extension_list) { + if (i == extension_id) { + extension = list_entry(entry, struct extension, list); + break; + } + i++; + } + + if (!extension) { + printf("Wrong extension number\n"); + return CMD_RET_FAILURE; + } + + ret = extension_apply(extension); + } + + return ret; +} + +static struct cmd_tbl cmd_extension[] = { + U_BOOT_CMD_MKENT(scan, 1, 1, do_extension_scan, "", ""), + U_BOOT_CMD_MKENT(list, 1, 0, do_extension_list, "", ""), + U_BOOT_CMD_MKENT(apply, 2, 0, do_extension_apply, "", ""), +}; + +static int do_extensionops(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + /* Drop the extension command */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_extension, ARRAY_SIZE(cmd_extension)); + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(extension, 3, 1, do_extensionops, + "Extension board management sub system", + "scan - scan plugged extension(s) board(s)\n" + "extension list - lists available extension(s) board(s)\n" + "extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n" +); diff --git a/roms/u-boot/cmd/fastboot.c b/roms/u-boot/cmd/fastboot.c new file mode 100644 index 000000000..ad5c06491 --- /dev/null +++ b/roms/u-boot/cmd/fastboot.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008 - 2009 Windriver, <www.windriver.com> + * Author: Tom Rix <Tom.Rix@windriver.com> + * + * (C) Copyright 2014 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + */ +#include <common.h> +#include <command.h> +#include <console.h> +#include <g_dnl.h> +#include <fastboot.h> +#include <net.h> +#include <usb.h> +#include <watchdog.h> +#include <linux/stringify.h> + +static int do_fastboot_udp(int argc, char *const argv[], + uintptr_t buf_addr, size_t buf_size) +{ +#if CONFIG_IS_ENABLED(UDP_FUNCTION_FASTBOOT) + int err = net_loop(FASTBOOT); + + if (err < 0) { + printf("fastboot udp error: %d\n", err); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +#else + pr_err("Fastboot UDP not enabled\n"); + return CMD_RET_FAILURE; +#endif +} + +static int do_fastboot_usb(int argc, char *const argv[], + uintptr_t buf_addr, size_t buf_size) +{ +#if CONFIG_IS_ENABLED(USB_FUNCTION_FASTBOOT) + int controller_index; + char *usb_controller; + char *endp; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + usb_controller = argv[1]; + controller_index = simple_strtoul(usb_controller, &endp, 0); + if (*endp != '\0') { + pr_err("Error: Wrong USB controller index format\n"); + return CMD_RET_FAILURE; + } + + ret = usb_gadget_initialize(controller_index); + if (ret) { + pr_err("USB init failed: %d\n", ret); + return CMD_RET_FAILURE; + } + + g_dnl_clear_detach(); + ret = g_dnl_register("usb_dnl_fastboot"); + if (ret) + return ret; + + if (!g_dnl_board_usb_cable_connected()) { + puts("\rUSB cable not detected.\n" \ + "Command exit.\n"); + ret = CMD_RET_FAILURE; + goto exit; + } + + while (1) { + if (g_dnl_detach()) + break; + if (ctrlc()) + break; + WATCHDOG_RESET(); + usb_gadget_handle_interrupts(controller_index); + } + + ret = CMD_RET_SUCCESS; + +exit: + g_dnl_unregister(); + g_dnl_clear_detach(); + usb_gadget_release(controller_index); + + return ret; +#else + pr_err("Fastboot USB not enabled\n"); + return CMD_RET_FAILURE; +#endif +} + +static int do_fastboot(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uintptr_t buf_addr = (uintptr_t)NULL; + size_t buf_size = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'l': + if (--argc <= 0) + return CMD_RET_USAGE; + buf_addr = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + + case 's': + if (--argc <= 0) + return CMD_RET_USAGE; + buf_size = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + + default: + return CMD_RET_USAGE; + } + } +NXTARG: + ; + } + + /* Handle case when USB controller param is just '-' */ + if (argc == 1) { + pr_err("Error: Incorrect USB controller index\n"); + return CMD_RET_USAGE; + } + + fastboot_init((void *)buf_addr, buf_size); + + if (!strcmp(argv[1], "udp")) + return do_fastboot_udp(argc, argv, buf_addr, buf_size); + + if (!strcmp(argv[1], "usb")) { + argv++; + argc--; + } + + return do_fastboot_usb(argc, argv, buf_addr, buf_size); +} + +#ifdef CONFIG_SYS_LONGHELP +static char fastboot_help_text[] = + "[-l addr] [-s size] usb <controller> | udp\n" + "\taddr - address of buffer used during data transfers (" + __stringify(CONFIG_FASTBOOT_BUF_ADDR) ")\n" + "\tsize - size of buffer used during data transfers (" + __stringify(CONFIG_FASTBOOT_BUF_SIZE) ")" + ; +#endif + +U_BOOT_CMD( + fastboot, CONFIG_SYS_MAXARGS, 1, do_fastboot, + "run as a fastboot usb or udp device", fastboot_help_text +); diff --git a/roms/u-boot/cmd/fat.c b/roms/u-boot/cmd/fat.c new file mode 100644 index 000000000..69ce1fa53 --- /dev/null +++ b/roms/u-boot/cmd/fat.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Richard Jones, rjones@nexus-tech.net + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <mapmem.h> +#include <fat.h> +#include <fs.h> +#include <part.h> +#include <asm/cache.h> + +int do_fat_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatsize, 4, 0, do_fat_size, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + +int do_fat_fsload(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + + +U_BOOT_CMD( + fatload, 7, 0, do_fat_fsload, + "load binary file from a dos filesystem", + "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n" + " - Load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem.\n" + " 'pos' gives the file position to start loading from.\n" + " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n" + " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n" + " the load stops on end of file.\n" + " If either 'pos' or 'bytes' are not aligned to\n" + " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n" + " be printed and performance will suffer for the load." +); + +static int do_fat_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatls, 4, 1, do_fat_ls, + "list files in a directory (default /)", + "<interface> [<dev[:part]>] [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +); + +static int do_fat_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int dev, part; + struct blk_desc *dev_desc; + struct disk_partition info; + + if (argc < 2) { + printf("usage: fatinfo <interface> [<dev[:part]>]\n"); + return 0; + } + + part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->devnum; + if (fat_set_blk_dev(dev_desc, &info) != 0) { + printf("\n** Unable to use %s %d:%d for fatinfo **\n", + argv[1], dev, part); + return 1; + } + return file_fat_detectfs(); +} + +U_BOOT_CMD( + fatinfo, 3, 1, do_fat_fsinfo, + "print information about filesystem", + "<interface> [<dev[:part]>]\n" + " - print information about filesystem from 'dev' on 'interface'" +); + +#ifdef CONFIG_FAT_WRITE +static int do_fat_fswrite(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_save(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatwrite, 7, 0, do_fat_fswrite, + "write file into a dos filesystem", + "<interface> <dev[:part]> <addr> <filename> [<bytes> [<offset>]]\n" + " - write file 'filename' from the address 'addr' in RAM\n" + " to 'dev' on 'interface'" +); + +static int do_fat_rm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_rm(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatrm, 4, 1, do_fat_rm, + "delete a file", + "<interface> [<dev[:part]>] <filename>\n" + " - delete a file from 'dev' on 'interface'" +); + +static int do_fat_mkdir(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_mkdir(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatmkdir, 4, 1, do_fat_mkdir, + "create a directory", + "<interface> [<dev[:part]>] <directory>\n" + " - create a directory in 'dev' on 'interface'" +); +#endif diff --git a/roms/u-boot/cmd/fdt.c b/roms/u-boot/cmd/fdt.c new file mode 100644 index 000000000..f1e2fc2fd --- /dev/null +++ b/roms/u-boot/cmd/fdt.c @@ -0,0 +1,1109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * Based on code written by: + * Pantelis Antoniou <pantelis.antoniou@gmail.com> and + * Matthew McClintock <msm@freescale.com> + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <fdt_support.h> +#include <mapmem.h> +#include <asm/io.h> + +#define MAX_LEVEL 32 /* how deeply nested we will go */ +#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ + +/* + * Global data (for the gd->bd) + */ +DECLARE_GLOBAL_DATA_PTR; + +static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); +static int fdt_print(const char *pathp, char *prop, int depth); +static int is_printable_string(const void *data, int len); + +/* + * The working_fdt points to our working flattened device tree. + */ +struct fdt_header *working_fdt; + +void set_working_fdt_addr(ulong addr) +{ + void *buf; + + buf = map_sysmem(addr, 0); + working_fdt = buf; + env_set_hex("fdtaddr", addr); +} + +/* + * Get a value from the fdt and format it to be set in the environment + */ +static int fdt_value_env_set(const void *nodep, int len, const char *var) +{ + if (is_printable_string(nodep, len)) + env_set(var, (void *)nodep); + else if (len == 4) { + char buf[11]; + + sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep)); + env_set(var, buf); + } else if (len%4 == 0 && len <= 20) { + /* Needed to print things like sha1 hashes. */ + char buf[41]; + int i; + + for (i = 0; i < len; i += sizeof(unsigned int)) + sprintf(buf + (i * 2), "%08x", + *(unsigned int *)(nodep + i)); + env_set(var, buf); + } else { + printf("error: unprintable value\n"); + return 1; + } + return 0; +} + +static const char * const fdt_member_table[] = { + "magic", + "totalsize", + "off_dt_struct", + "off_dt_strings", + "off_mem_rsvmap", + "version", + "last_comp_version", + "boot_cpuid_phys", + "size_dt_strings", + "size_dt_struct", +}; + +static int fdt_get_header_value(int argc, char *const argv[]) +{ + fdt32_t *fdtp = (fdt32_t *)working_fdt; + ulong val; + int i; + + if (argv[2][0] != 'g') + return CMD_RET_FAILURE; + + for (i = 0; i < ARRAY_SIZE(fdt_member_table); i++) { + if (strcmp(fdt_member_table[i], argv[4])) + continue; + + val = fdt32_to_cpu(fdtp[i]); + env_set_hex(argv[3], val); + return CMD_RET_SUCCESS; + } + + return CMD_RET_FAILURE; +} + +/* + * Flattened Device Tree command, see the help for parameter definitions. + */ +static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + /* + * Set the address of the fdt + */ + if (strncmp(argv[1], "ad", 2) == 0) { + unsigned long addr; + int control = 0; + struct fdt_header *blob; + /* + * Set the address [and length] of the fdt. + */ + argc -= 2; + argv += 2; +/* Temporary #ifdef - some archs don't have fdt_blob yet */ +#ifdef CONFIG_OF_CONTROL + if (argc && !strcmp(*argv, "-c")) { + control = 1; + argc--; + argv++; + } +#endif + if (argc == 0) { + if (control) + blob = (struct fdt_header *)gd->fdt_blob; + else + blob = working_fdt; + if (!blob || !fdt_valid(&blob)) + return 1; + printf("The address of the fdt is %#08lx\n", + control ? (ulong)map_to_sysmem(blob) : + env_get_hex("fdtaddr", 0)); + return 0; + } + + addr = simple_strtoul(argv[0], NULL, 16); + blob = map_sysmem(addr, 0); + if (!fdt_valid(&blob)) + return 1; + if (control) + gd->fdt_blob = blob; + else + set_working_fdt_addr(addr); + + if (argc >= 2) { + int len; + int err; + /* + * Optional new length + */ + len = simple_strtoul(argv[1], NULL, 16); + if (len < fdt_totalsize(blob)) { + printf ("New length %d < existing length %d, " + "ignoring.\n", + len, fdt_totalsize(blob)); + } else { + /* + * Open in place with a new length. + */ + err = fdt_open_into(blob, blob, len); + if (err != 0) { + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); + } + } + } + + return CMD_RET_SUCCESS; + } + + if (!working_fdt) { + puts( + "No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr <address>\" command.\n" + "Aborting!\n"); + return CMD_RET_FAILURE; + } + + /* + * Move the working_fdt + */ + if (strncmp(argv[1], "mo", 2) == 0) { + struct fdt_header *newaddr; + int len; + int err; + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Set the address and length of the fdt. + */ + working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); + if (!fdt_valid(&working_fdt)) + return 1; + + newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); + + /* + * If the user specifies a length, use that. Otherwise use the + * current length. + */ + if (argc <= 4) { + len = fdt_totalsize(working_fdt); + } else { + len = simple_strtoul(argv[4], NULL, 16); + if (len < fdt_totalsize(working_fdt)) { + printf ("New length 0x%X < existing length " + "0x%X, aborting.\n", + len, fdt_totalsize(working_fdt)); + return 1; + } + } + + /* + * Copy to the new location. + */ + err = fdt_open_into(working_fdt, newaddr, len); + if (err != 0) { + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); + return 1; + } + set_working_fdt_addr((ulong)newaddr); +#ifdef CONFIG_OF_SYSTEM_SETUP + /* Call the board-specific fixup routine */ + } else if (strncmp(argv[1], "sys", 3) == 0) { + int err = ft_system_setup(working_fdt, gd->bd); + + if (err) { + printf("Failed to add system information to FDT: %s\n", + fdt_strerror(err)); + return CMD_RET_FAILURE; + } +#endif + /* + * Make a new node + */ + } else if (strncmp(argv[1], "mk", 2) == 0) { + char *pathp; /* path */ + char *nodep; /* new node to add */ + int nodeoffset; /* node offset from libfdt */ + int err; + + /* + * Parameters: Node path, new node to be appended to the path. + */ + if (argc < 4) + return CMD_RET_USAGE; + + pathp = argv[2]; + nodep = argv[3]; + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + err = fdt_add_subnode(working_fdt, nodeoffset, nodep); + if (err < 0) { + printf ("libfdt fdt_add_subnode(): %s\n", + fdt_strerror(err)); + return 1; + } + + /* + * Set the value of a property in the working_fdt. + */ + } else if (strncmp(argv[1], "se", 2) == 0) { + char *pathp; /* path */ + char *prop; /* property */ + int nodeoffset; /* node offset from libfdt */ + static char data[SCRATCHPAD] __aligned(4);/* property storage */ + const void *ptmp; + int len; /* new length of the property */ + int ret; /* return value */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 4) + return CMD_RET_USAGE; + + pathp = argv[2]; + prop = argv[3]; + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + + if (argc == 4) { + len = 0; + } else { + ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len); + if (len > SCRATCHPAD) { + printf("prop (%d) doesn't fit in scratchpad!\n", + len); + return 1; + } + if (ptmp != NULL) + memcpy(data, ptmp, len); + + ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); + if (ret != 0) + return ret; + } + + ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); + if (ret < 0) { + printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); + return 1; + } + + /******************************************************************** + * Get the value of a property in the working_fdt. + ********************************************************************/ + } else if (argv[1][0] == 'g') { + char *subcmd; /* sub-command */ + char *pathp; /* path */ + char *prop; /* property */ + char *var; /* variable to store result */ + int nodeoffset; /* node offset from libfdt */ + const void *nodep; /* property node pointer */ + int len = 0; /* new length of the property */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 5) + return CMD_RET_USAGE; + + subcmd = argv[2]; + + if (argc < 6 && subcmd[0] != 's') + return CMD_RET_USAGE; + + var = argv[3]; + pathp = argv[4]; + prop = argv[5]; + + nodeoffset = fdt_path_offset(working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + + if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { + int reqIndex = -1; + int startDepth = fdt_node_depth( + working_fdt, nodeoffset); + int curDepth = startDepth; + int curIndex = -1; + int nextNodeOffset = fdt_next_node( + working_fdt, nodeoffset, &curDepth); + + if (subcmd[0] == 'n') + reqIndex = simple_strtoul(argv[5], NULL, 16); + + while (curDepth > startDepth) { + if (curDepth == startDepth + 1) + curIndex++; + if (subcmd[0] == 'n' && curIndex == reqIndex) { + const char *node_name; + + node_name = fdt_get_name(working_fdt, + nextNodeOffset, + NULL); + env_set(var, node_name); + return 0; + } + nextNodeOffset = fdt_next_node( + working_fdt, nextNodeOffset, &curDepth); + if (nextNodeOffset < 0) + break; + } + if (subcmd[0] == 's') { + /* get the num nodes at this level */ + env_set_ulong(var, curIndex + 1); + } else { + /* node index not found */ + printf("libfdt node not found\n"); + return 1; + } + } else { + nodep = fdt_getprop( + working_fdt, nodeoffset, prop, &len); + if (len == 0) { + /* no property value */ + env_set(var, ""); + return 0; + } else if (nodep && len > 0) { + if (subcmd[0] == 'v') { + int ret; + + ret = fdt_value_env_set(nodep, len, + var); + if (ret != 0) + return ret; + } else if (subcmd[0] == 'a') { + /* Get address */ + char buf[11]; + + sprintf(buf, "0x%p", nodep); + env_set(var, buf); + } else if (subcmd[0] == 's') { + /* Get size */ + char buf[11]; + + sprintf(buf, "0x%08X", len); + env_set(var, buf); + } else + return CMD_RET_USAGE; + return 0; + } else { + printf("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } + } + + /* + * Print (recursive) / List (single level) + */ + } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { + int depth = MAX_LEVEL; /* how deep to print */ + char *pathp; /* path */ + char *prop; /* property */ + int ret; /* return value */ + static char root[2] = "/"; + + /* + * list is an alias for print, but limited to 1 level + */ + if (argv[1][0] == 'l') { + depth = 1; + } + + /* + * Get the starting path. The root node is an oddball, + * the offset is zero and has no name. + */ + if (argc == 2) + pathp = root; + else + pathp = argv[2]; + if (argc > 3) + prop = argv[3]; + else + prop = NULL; + + ret = fdt_print(pathp, prop, depth); + if (ret != 0) + return ret; + + /* + * Remove a property/node + */ + } else if (strncmp(argv[1], "rm", 2) == 0) { + int nodeoffset; /* node offset from libfdt */ + int err; + + /* + * Get the path. The root node is an oddball, the offset + * is zero and has no name. + */ + nodeoffset = fdt_path_offset (working_fdt, argv[2]); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + /* + * Do the delete. A fourth parameter means delete a property, + * otherwise delete the node. + */ + if (argc > 3) { + err = fdt_delprop(working_fdt, nodeoffset, argv[3]); + if (err < 0) { + printf("libfdt fdt_delprop(): %s\n", + fdt_strerror(err)); + return err; + } + } else { + err = fdt_del_node(working_fdt, nodeoffset); + if (err < 0) { + printf("libfdt fdt_del_node(): %s\n", + fdt_strerror(err)); + return err; + } + } + + /* + * Display header info + */ + } else if (argv[1][0] == 'h') { + if (argc == 5) + return fdt_get_header_value(argc, argv); + + u32 version = fdt_version(working_fdt); + printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt)); + printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt), + fdt_totalsize(working_fdt)); + printf("off_dt_struct:\t\t0x%x\n", + fdt_off_dt_struct(working_fdt)); + printf("off_dt_strings:\t\t0x%x\n", + fdt_off_dt_strings(working_fdt)); + printf("off_mem_rsvmap:\t\t0x%x\n", + fdt_off_mem_rsvmap(working_fdt)); + printf("version:\t\t%d\n", version); + printf("last_comp_version:\t%d\n", + fdt_last_comp_version(working_fdt)); + if (version >= 2) + printf("boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(working_fdt)); + if (version >= 3) + printf("size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(working_fdt)); + if (version >= 17) + printf("size_dt_struct:\t\t0x%x\n", + fdt_size_dt_struct(working_fdt)); + printf("number mem_rsv:\t\t0x%x\n", + fdt_num_mem_rsv(working_fdt)); + printf("\n"); + + /* + * Set boot cpu id + */ + } else if (strncmp(argv[1], "boo", 3) == 0) { + unsigned long tmp = simple_strtoul(argv[2], NULL, 16); + fdt_set_boot_cpuid_phys(working_fdt, tmp); + + /* + * memory command + */ + } else if (strncmp(argv[1], "me", 2) == 0) { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[2], NULL, 16); + size = simple_strtoull(argv[3], NULL, 16); + err = fdt_fixup_memory(working_fdt, addr, size); + if (err < 0) + return err; + + /* + * mem reserve commands + */ + } else if (strncmp(argv[1], "rs", 2) == 0) { + if (argv[2][0] == 'p') { + uint64_t addr, size; + int total = fdt_num_mem_rsv(working_fdt); + int j, err; + printf("index\t\t start\t\t size\n"); + printf("-------------------------------" + "-----------------\n"); + for (j = 0; j < total; j++) { + err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); + if (err < 0) { + printf("libfdt fdt_get_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + printf(" %x\t%08x%08x\t%08x%08x\n", j, + (u32)(addr >> 32), + (u32)(addr & 0xffffffff), + (u32)(size >> 32), + (u32)(size & 0xffffffff)); + } + } else if (argv[2][0] == 'a') { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[3], NULL, 16); + size = simple_strtoull(argv[4], NULL, 16); + err = fdt_add_mem_rsv(working_fdt, addr, size); + + if (err < 0) { + printf("libfdt fdt_add_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else if (argv[2][0] == 'd') { + unsigned long idx = simple_strtoul(argv[3], NULL, 16); + int err = fdt_del_mem_rsv(working_fdt, idx); + + if (err < 0) { + printf("libfdt fdt_del_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + } +#ifdef CONFIG_OF_BOARD_SETUP + /* Call the board-specific fixup routine */ + else if (strncmp(argv[1], "boa", 3) == 0) { + int err = ft_board_setup(working_fdt, gd->bd); + + if (err) { + printf("Failed to update board information in FDT: %s\n", + fdt_strerror(err)); + return CMD_RET_FAILURE; + } +#ifdef CONFIG_SOC_KEYSTONE + ft_board_setup_ex(working_fdt, gd->bd); +#endif + } +#endif + /* Create a chosen node */ + else if (strncmp(argv[1], "cho", 3) == 0) { + unsigned long initrd_start = 0, initrd_end = 0; + + if ((argc != 2) && (argc != 4)) + return CMD_RET_USAGE; + + if (argc == 4) { + initrd_start = simple_strtoul(argv[2], NULL, 16); + initrd_end = simple_strtoul(argv[3], NULL, 16); + } + + fdt_chosen(working_fdt); + fdt_initrd(working_fdt, initrd_start, initrd_end); + +#if defined(CONFIG_FIT_SIGNATURE) + } else if (strncmp(argv[1], "che", 3) == 0) { + int cfg_noffset; + int ret; + unsigned long addr; + struct fdt_header *blob; + + if (!working_fdt) + return CMD_RET_FAILURE; + + if (argc > 2) { + addr = simple_strtoul(argv[2], NULL, 16); + blob = map_sysmem(addr, 0); + } else { + blob = (struct fdt_header *)gd->fdt_blob; + } + if (!fdt_valid(&blob)) + return 1; + + gd->fdt_blob = blob; + cfg_noffset = fit_conf_get_node(working_fdt, NULL); + if (!cfg_noffset) { + printf("Could not find configuration node: %s\n", + fdt_strerror(cfg_noffset)); + return CMD_RET_FAILURE; + } + + ret = fit_config_verify(working_fdt, cfg_noffset); + if (ret == 0) + return CMD_RET_SUCCESS; + else + return CMD_RET_FAILURE; +#endif + + } +#ifdef CONFIG_OF_LIBFDT_OVERLAY + /* apply an overlay */ + else if (strncmp(argv[1], "ap", 2) == 0) { + unsigned long addr; + struct fdt_header *blob; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + if (!working_fdt) + return CMD_RET_FAILURE; + + addr = simple_strtoul(argv[2], NULL, 16); + blob = map_sysmem(addr, 0); + if (!fdt_valid(&blob)) + return CMD_RET_FAILURE; + + /* apply method prints messages on error */ + ret = fdt_overlay_apply_verbose(working_fdt, blob); + if (ret) + return CMD_RET_FAILURE; + } +#endif + /* resize the fdt */ + else if (strncmp(argv[1], "re", 2) == 0) { + uint extrasize; + if (argc > 2) + extrasize = simple_strtoul(argv[2], NULL, 16); + else + extrasize = 0; + fdt_shrink_to_minimum(working_fdt, extrasize); + } + else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + + return 0; +} + +/****************************************************************************/ + +/* + * Parse the user's input, partially heuristic. Valid formats: + * <0x00112233 4 05> - an array of cells. Numbers follow standard + * C conventions. + * [00 11 22 .. nn] - byte stream + * "string" - If the the value doesn't start with "<" or "[", it is + * treated as a string. Note that the quotes are + * stripped by the parser before we get the string. + * newval: An array of strings containing the new property as specified + * on the command line + * count: The number of strings in the array + * data: A bytestream to be placed in the property + * len: The length of the resulting bytestream + */ +static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) +{ + char *cp; /* temporary char pointer */ + char *newp; /* temporary newval char pointer */ + unsigned long tmp; /* holds converted values */ + int stridx = 0; + + *len = 0; + newp = newval[0]; + + /* An array of cells */ + if (*newp == '<') { + newp++; + while ((*newp != '>') && (stridx < count)) { + /* + * Keep searching until we find that last ">" + * That way users don't have to escape the spaces + */ + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + + cp = newp; + tmp = simple_strtoul(cp, &newp, 0); + if (*cp != '?') + *(fdt32_t *)data = cpu_to_fdt32(tmp); + else + newp++; + + data += 4; + *len += 4; + + /* If the ptr didn't advance, something went wrong */ + if ((newp - cp) <= 0) { + printf("Sorry, I could not convert \"%s\"\n", + cp); + return 1; + } + + while (*newp == ' ') + newp++; + } + + if (*newp != '>') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else if (*newp == '[') { + /* + * Byte stream. Convert the values. + */ + newp++; + while ((stridx < count) && (*newp != ']')) { + while (*newp == ' ') + newp++; + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + if (!isxdigit(*newp)) + break; + tmp = simple_strtoul(newp, &newp, 16); + *data++ = tmp & 0xFF; + *len = *len + 1; + } + if (*newp != ']') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else { + /* + * Assume it is one or more strings. Copy it into our + * data area for convenience (including the + * terminating '\0's). + */ + while (stridx < count) { + size_t length = strlen(newp) + 1; + strcpy(data, newp); + data += length; + *len += length; + newp = newval[++stridx]; + } + } + return 0; +} + +/****************************************************************************/ + +/* + * Heuristic to guess if this is a string or concatenated strings. + */ + +static int is_printable_string(const void *data, int len) +{ + const char *s = data; + + /* zero length is not */ + if (len == 0) + return 0; + + /* must terminate with zero or '\n' */ + if (s[len - 1] != '\0' && s[len - 1] != '\n') + return 0; + + /* printable or a null byte (concatenated strings) */ + while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) { + /* + * If we see a null, there are three possibilities: + * 1) If len == 1, it is the end of the string, printable + * 2) Next character also a null, not printable. + * 3) Next character not a null, continue to check. + */ + if (s[0] == '\0') { + if (len == 1) + return 1; + if (s[1] == '\0') + return 0; + } + s++; + len--; + } + + /* Not the null termination, or not done yet: not printable */ + if (*s != '\0' || (len != 0)) + return 0; + + return 1; +} + + +/* + * Print the property in the best format, a heuristic guess. Print as + * a string, concatenated strings, a byte, word, double word, or (if all + * else fails) it is printed as a stream of bytes. + */ +static void print_data(const void *data, int len) +{ + int j; + const char *env_max_dump; + ulong max_dump = ULONG_MAX; + + /* no data, don't print */ + if (len == 0) + return; + + env_max_dump = env_get("fdt_max_dump"); + if (env_max_dump) + max_dump = simple_strtoul(env_max_dump, NULL, 16); + + /* + * It is a string, but it may have multiple strings (embedded '\0's). + */ + if (is_printable_string(data, len)) { + puts("\""); + j = 0; + while (j < len) { + if (j > 0) + puts("\", \""); + puts(data); + j += strlen(data) + 1; + data += strlen(data) + 1; + } + puts("\""); + return; + } + + if ((len %4) == 0) { + if (len > max_dump) + printf("* 0x%p [0x%08x]", data, len); + else { + const __be32 *p; + + printf("<"); + for (j = 0, p = data; j < len/4; j++) + printf("0x%08x%s", fdt32_to_cpu(p[j]), + j < (len/4 - 1) ? " " : ""); + printf(">"); + } + } else { /* anything else... hexdump */ + if (len > max_dump) + printf("* 0x%p [0x%08x]", data, len); + else { + const u8 *s; + + printf("["); + for (j = 0, s = data; j < len; j++) + printf("%02x%s", s[j], j < len - 1 ? " " : ""); + printf("]"); + } + } +} + +/****************************************************************************/ + +/* + * Recursively print (a portion of) the working_fdt. The depth parameter + * determines how deeply nested the fdt is printed. + */ +static int fdt_print(const char *pathp, char *prop, int depth) +{ + static char tabs[MAX_LEVEL+1] = + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + const void *nodep; /* property node pointer */ + int nodeoffset; /* node offset from libfdt */ + int nextoffset; /* next node offset from libfdt */ + uint32_t tag; /* tag */ + int len; /* length of the property */ + int level = 0; /* keep track of nesting level */ + const struct fdt_property *fdt_prop; + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + /* + * The user passed in a property as well as node path. + * Print only the given property and then return. + */ + if (prop) { + nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len); + if (len == 0) { + /* no property value */ + printf("%s %s\n", pathp, prop); + return 0; + } else if (nodep && len > 0) { + printf("%s = ", prop); + print_data (nodep, len); + printf("\n"); + return 0; + } else { + printf ("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } + } + + /* + * The user passed in a node path and no property, + * print the node and all subnodes. + */ + while(level >= 0) { + tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset); + switch(tag) { + case FDT_BEGIN_NODE: + pathp = fdt_get_name(working_fdt, nodeoffset, NULL); + if (level <= depth) { + if (pathp == NULL) + pathp = "/* NULL pointer error */"; + if (*pathp == '\0') + pathp = "/"; /* root is nameless */ + printf("%s%s {\n", + &tabs[MAX_LEVEL - level], pathp); + } + level++; + if (level >= MAX_LEVEL) { + printf("Nested too deep, aborting.\n"); + return 1; + } + break; + case FDT_END_NODE: + level--; + if (level <= depth) + printf("%s};\n", &tabs[MAX_LEVEL - level]); + if (level == 0) { + level = -1; /* exit the loop */ + } + break; + case FDT_PROP: + fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset, + sizeof(*fdt_prop)); + pathp = fdt_string(working_fdt, + fdt32_to_cpu(fdt_prop->nameoff)); + len = fdt32_to_cpu(fdt_prop->len); + nodep = fdt_prop->data; + if (len < 0) { + printf ("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); + return 1; + } else if (len == 0) { + /* the property has no value */ + if (level <= depth) + printf("%s%s;\n", + &tabs[MAX_LEVEL - level], + pathp); + } else { + if (level <= depth) { + printf("%s%s = ", + &tabs[MAX_LEVEL - level], + pathp); + print_data (nodep, len); + printf(";\n"); + } + } + break; + case FDT_NOP: + printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]); + break; + case FDT_END: + return 1; + default: + if (level <= depth) + printf("Unknown tag 0x%08X\n", tag); + return 1; + } + nodeoffset = nextoffset; + } + return 0; +} + +/********************************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char fdt_help_text[] = + "addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>\n" +#ifdef CONFIG_OF_LIBFDT_OVERLAY + "fdt apply <addr> - Apply overlay to the DT\n" +#endif +#ifdef CONFIG_OF_BOARD_SETUP + "fdt boardsetup - Do board-specific set up\n" +#endif +#ifdef CONFIG_OF_SYSTEM_SETUP + "fdt systemsetup - Do system-specific set up\n" +#endif + "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" + "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n" + "fdt print <path> [<prop>] - Recursive print starting at <path>\n" + "fdt list <path> [<prop>] - Print one level starting at <path>\n" + "fdt get value <var> <path> <prop> - Get <property> and store in <var>\n" + "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n" + "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n" + "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n" + "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" + "fdt mknode <path> <node> - Create a new node after <path>\n" + "fdt rm <path> [<prop>] - Delete the node or <property>\n" + "fdt header [get <var> <member>] - Display header info\n" + " get - get header member <member> and store it in <var>\n" + "fdt bootcpu <id> - Set boot cpuid\n" + "fdt memory <addr> <size> - Add/Update memory node\n" + "fdt rsvmem print - Show current mem reserves\n" + "fdt rsvmem add <addr> <size> - Add a mem reserve\n" + "fdt rsvmem delete <index> - Delete a mem reserves\n" + "fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n" + " <start>/<end> - initrd start/end addr\n" +#if defined(CONFIG_FIT_SIGNATURE) + "fdt checksign [<addr>] - check FIT signature\n" + " <start> - addr of key blob\n" + " default gd->fdt_blob\n" +#endif + "NOTE: Dereference aliases by omitting the leading '/', " + "e.g. fdt print ethernet0."; +#endif + +U_BOOT_CMD( + fdt, 255, 0, do_fdt, + "flattened device tree utility commands", fdt_help_text +); diff --git a/roms/u-boot/cmd/flash.c b/roms/u-boot/cmd/flash.c new file mode 100644 index 000000000..240871e80 --- /dev/null +++ b/roms/u-boot/cmd/flash.c @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * FLASH support + */ +#include <common.h> +#include <command.h> +#include <log.h> +#include <uuid.h> + +#if defined(CONFIG_CMD_MTDPARTS) +#include <jffs2/jffs2.h> + +/* partition handling routines */ +int mtdparts_init(void); +int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); +#endif + +#ifdef CONFIG_MTD_NOR_FLASH +#include <flash.h> +#include <mtd/cfi_flash.h> +extern flash_info_t flash_info[]; /* info for FLASH chips */ + +/* + * The user interface starts numbering for Flash banks with 1 + * for historical reasons. + */ + +/* + * this routine looks for an abbreviated flash range specification. + * the syntax is B:SF[-SL], where B is the bank number, SF is the first + * sector to erase, and SL is the last sector to erase (defaults to SF). + * bank numbers start at 1 to be consistent with other specs, sector numbers + * start at zero. + * + * returns: 1 - correct spec; *pinfo, *psf and *psl are + * set appropriately + * 0 - doesn't look like an abbreviated spec + * -1 - looks like an abbreviated spec, but got + * a parsing error, a number out of range, + * or an invalid flash bank. + */ +static int +abbrev_spec (char *str, flash_info_t ** pinfo, int *psf, int *psl) +{ + flash_info_t *fp; + int bank, first, last; + char *p, *ep; + + if ((p = strchr (str, ':')) == NULL) + return 0; + *p++ = '\0'; + + bank = simple_strtoul (str, &ep, 10); + if (ep == str || *ep != '\0' || + bank < 1 || bank > CONFIG_SYS_MAX_FLASH_BANKS || + (fp = &flash_info[bank - 1])->flash_id == FLASH_UNKNOWN) + return -1; + + str = p; + if ((p = strchr (str, '-')) != NULL) + *p++ = '\0'; + + first = simple_strtoul (str, &ep, 10); + if (ep == str || *ep != '\0' || first >= fp->sector_count) + return -1; + + if (p != NULL) { + last = simple_strtoul (p, &ep, 10); + if (ep == p || *ep != '\0' || + last < first || last >= fp->sector_count) + return -1; + } else { + last = first; + } + + *pinfo = fp; + *psf = first; + *psl = last; + + return 1; +} + +/* + * Take *addr in Flash and adjust it to fall on the end of its sector + */ +int flash_sect_roundb(ulong *addr) +{ + flash_info_t *info; + ulong bank, sector_end_addr; + char found; + int i; + + /* find the end addr of the sector where the *addr is */ + found = 0; + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS && !found; ++bank) { + info = &flash_info[bank]; + for (i = 0; i < info->sector_count && !found; ++i) { + /* get the end address of the sector */ + if (i == info->sector_count - 1) { + sector_end_addr = info->start[0] + + info->size - 1; + } else { + sector_end_addr = info->start[i+1] - 1; + } + + if (*addr <= sector_end_addr && + *addr >= info->start[i]) { + found = 1; + /* adjust *addr if necessary */ + if (*addr < sector_end_addr) + *addr = sector_end_addr; + } /* sector */ + } /* bank */ + } + if (!found) { + /* error, address not in flash */ + printf("Error: end address (0x%08lx) not in flash!\n", *addr); + return 1; + } + + return 0; +} + +/* + * This function computes the start and end addresses for both + * erase and protect commands. The range of the addresses on which + * either of the commands is to operate can be given in two forms: + * 1. <cmd> start end - operate on <'start', 'end') + * 2. <cmd> start +length - operate on <'start', start + length) + * If the second form is used and the end address doesn't fall on the + * sector boundary, than it will be adjusted to the next sector boundary. + * If it isn't in the flash, the function will fail (return -1). + * Input: + * arg1, arg2: address specification (i.e. both command arguments) + * Output: + * addr_first, addr_last: computed address range + * Return: + * 1: success + * -1: failure (bad format, bad address). +*/ +static int +addr_spec(char *arg1, char *arg2, ulong *addr_first, ulong *addr_last) +{ + char *ep; + char len_used; /* indicates if the "start +length" form used */ + + *addr_first = simple_strtoul(arg1, &ep, 16); + if (ep == arg1 || *ep != '\0') + return -1; + + len_used = 0; + if (arg2 && *arg2 == '+'){ + len_used = 1; + ++arg2; + } + + *addr_last = simple_strtoul(arg2, &ep, 16); + if (ep == arg2 || *ep != '\0') + return -1; + + if (len_used){ + /* + * *addr_last has the length, compute correct *addr_last + * XXX watch out for the integer overflow! Right now it is + * checked for in both the callers. + */ + *addr_last = *addr_first + *addr_last - 1; + + /* + * It may happen that *addr_last doesn't fall on the sector + * boundary. We want to round such an address to the next + * sector boundary, so that the commands don't fail later on. + */ + + if (flash_sect_roundb(addr_last) > 0) + return -1; + } /* "start +length" from used */ + + return 1; +} + +static int +flash_fill_sect_ranges (ulong addr_first, ulong addr_last, + int *s_first, int *s_last, + int *s_count ) +{ + flash_info_t *info; + ulong bank; + int rcode = 0; + + *s_count = 0; + + for (bank=0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + s_first[bank] = -1; /* first sector to erase */ + s_last [bank] = -1; /* last sector to erase */ + } + + for (bank=0,info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first <= addr_last); + ++bank, ++info) { + ulong b_end; + int sect; + short s_end; + + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + + b_end = info->start[0] + info->size - 1; /* bank end addr */ + s_end = info->sector_count - 1; /* last sector */ + + + for (sect=0; sect < info->sector_count; ++sect) { + ulong end; /* last address in current sect */ + + end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; + + if (addr_first > end) + continue; + if (addr_last < info->start[sect]) + continue; + + if (addr_first == info->start[sect]) { + s_first[bank] = sect; + } + if (addr_last == end) { + s_last[bank] = sect; + } + } + if (s_first[bank] >= 0) { + if (s_last[bank] < 0) { + if (addr_last > b_end) { + s_last[bank] = s_end; + } else { + puts ("Error: end address" + " not on sector boundary\n"); + rcode = 1; + break; + } + } + if (s_last[bank] < s_first[bank]) { + puts ("Error: end sector" + " precedes start sector\n"); + rcode = 1; + break; + } + sect = s_last[bank]; + addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1]; + (*s_count) += s_last[bank] - s_first[bank] + 1; + } else if (addr_first >= info->start[0] && addr_first < b_end) { + puts ("Error: start address not on sector boundary\n"); + rcode = 1; + break; + } else if (s_last[bank] >= 0) { + puts ("Error: cannot span across banks when they are" + " mapped in reverse order\n"); + rcode = 1; + break; + } + } + + return rcode; +} +#endif /* CONFIG_MTD_NOR_FLASH */ + +static int do_flinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#ifdef CONFIG_MTD_NOR_FLASH + ulong bank; +#endif + +#ifdef CONFIG_MTD_NOR_FLASH + if (argc == 1) { /* print info for all FLASH banks */ + for (bank=0; bank <CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + printf ("\nBank # %ld: ", bank+1); + + flash_print_info(&flash_info[bank]); + } + return 0; + } + + bank = simple_strtoul(argv[1], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("\nBank # %ld: ", bank); + flash_print_info(&flash_info[bank - 1]); +#endif /* CONFIG_MTD_NOR_FLASH */ + return 0; +} + +static int do_flerase(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#ifdef CONFIG_MTD_NOR_FLASH + flash_info_t *info = NULL; + ulong bank, addr_first, addr_last; + int n, sect_first = 0, sect_last = 0; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 dev_type, dev_num, pnum; +#endif + int rcode = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "all") == 0) { + for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + printf ("Erase Flash Bank # %ld ", bank); + info = &flash_info[bank-1]; + rcode = flash_erase(info, 0, info->sector_count - 1); + } + return rcode; + } + + if ((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0) { + if (n < 0) { + puts ("Bad sector specification\n"); + return 1; + } + printf ("Erase Flash Sectors %d-%d in Bank # %zu ", + sect_first, sect_last, (info-flash_info)+1); + rcode = flash_erase(info, sect_first, sect_last); + return rcode; + } + +#if defined(CONFIG_CMD_MTDPARTS) + /* erase <part-id> - erase partition */ + if ((argc == 2) && (mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0)) { + mtdparts_init(); + if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) { + if (dev->id->type == MTD_DEV_TYPE_NOR) { + bank = dev->id->num; + info = &flash_info[bank]; + addr_first = part->offset + info->start[0]; + addr_last = addr_first + part->size - 1; + + printf ("Erase Flash Partition %s, " + "bank %ld, 0x%08lx - 0x%08lx ", + argv[1], bank, addr_first, + addr_last); + + rcode = flash_sect_erase(addr_first, addr_last); + return rcode; + } + + printf("cannot erase, not a NOR device\n"); + return 1; + } + } +#endif + + if (argc != 3) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "bank") == 0) { + bank = simple_strtoul(argv[2], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("Erase Flash Bank # %ld ", bank); + info = &flash_info[bank-1]; + rcode = flash_erase(info, 0, info->sector_count - 1); + return rcode; + } + + if (addr_spec(argv[1], argv[2], &addr_first, &addr_last) < 0){ + printf ("Bad address format\n"); + return 1; + } + + if (addr_first >= addr_last) + return CMD_RET_USAGE; + + rcode = flash_sect_erase(addr_first, addr_last); + return rcode; +#else + return 0; +#endif /* CONFIG_MTD_NOR_FLASH */ +} + +#ifdef CONFIG_MTD_NOR_FLASH +int flash_sect_erase(ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int erased = 0; + int planned; + int rcode = 0; + + rcode = flash_fill_sect_ranges (addr_first, addr_last, + s_first, s_last, &planned ); + + if (planned && (rcode == 0)) { + for (bank=0,info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (rcode == 0); + ++bank, ++info) { + if (s_first[bank]>=0) { + erased += s_last[bank] - s_first[bank] + 1; + debug("Erase Flash from 0x%08lx to 0x%08lx in Bank # %ld ", + info->start[s_first[bank]], + (s_last[bank] == info->sector_count) ? + info->start[0] + info->size - 1 : + info->start[s_last[bank] + 1] - 1, + bank + 1); + rcode = flash_erase(info, s_first[bank], + s_last[bank]); + } + } + if (rcode == 0) + printf("Erased %d sectors\n", erased); + } else if (rcode == 0) { + puts ("Error: start and/or end address" + " not on sector boundary\n"); + rcode = 1; + } + return rcode; +} +#endif /* CONFIG_MTD_NOR_FLASH */ + +static int do_protect(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int rcode = 0; +#ifdef CONFIG_MTD_NOR_FLASH + flash_info_t *info = NULL; + ulong bank; + int i, n, sect_first = 0, sect_last = 0; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 dev_type, dev_num, pnum; +#endif +#endif /* CONFIG_MTD_NOR_FLASH */ +#if defined(CONFIG_MTD_NOR_FLASH) + int p; + ulong addr_first, addr_last; +#endif + + if (argc < 3) + return CMD_RET_USAGE; + +#if defined(CONFIG_MTD_NOR_FLASH) + if (strcmp(argv[1], "off") == 0) + p = 0; + else if (strcmp(argv[1], "on") == 0) + p = 1; + else + return CMD_RET_USAGE; +#endif + +#ifdef CONFIG_MTD_NOR_FLASH + if (strcmp(argv[2], "all") == 0) { + for (bank=1; bank<=CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + info = &flash_info[bank-1]; + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + printf ("%sProtect Flash Bank # %ld\n", + p ? "" : "Un-", bank); + + for (i=0; i<info->sector_count; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + return rcode; + } + + if ((n = abbrev_spec(argv[2], &info, §_first, §_last)) != 0) { + if (n < 0) { + puts ("Bad sector specification\n"); + return 1; + } + printf("%sProtect Flash Sectors %d-%d in Bank # %zu\n", + p ? "" : "Un-", sect_first, sect_last, + (info-flash_info)+1); + for (i = sect_first; i <= sect_last; i++) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + return rcode; + } + +#if defined(CONFIG_CMD_MTDPARTS) + /* protect on/off <part-id> */ + if ((argc == 3) && (mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0)) { + mtdparts_init(); + if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) { + if (dev->id->type == MTD_DEV_TYPE_NOR) { + bank = dev->id->num; + info = &flash_info[bank]; + addr_first = part->offset + info->start[0]; + addr_last = addr_first + part->size - 1; + + printf ("%sProtect Flash Partition %s, " + "bank %ld, 0x%08lx - 0x%08lx\n", + p ? "" : "Un", argv[1], + bank, addr_first, addr_last); + + rcode = flash_sect_protect(p, addr_first, + addr_last); + return rcode; + } + + printf("cannot %sprotect, not a NOR device\n", + p ? "" : "un"); + return 1; + } + } +#endif + + if (argc != 4) + return CMD_RET_USAGE; + + if (strcmp(argv[2], "bank") == 0) { + bank = simple_strtoul(argv[3], NULL, 16); + if ((bank < 1) || (bank > CONFIG_SYS_MAX_FLASH_BANKS)) { + printf ("Only FLASH Banks # 1 ... # %d supported\n", + CONFIG_SYS_MAX_FLASH_BANKS); + return 1; + } + printf ("%sProtect Flash Bank # %ld\n", + p ? "" : "Un-", bank); + info = &flash_info[bank-1]; + + if (info->flash_id == FLASH_UNKNOWN) { + puts ("missing or unknown FLASH type\n"); + return 1; + } + for (i=0; i<info->sector_count; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (!rcode) puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + return rcode; + } + + if (addr_spec(argv[2], argv[3], &addr_first, &addr_last) < 0){ + printf("Bad address format\n"); + return 1; + } + + if (addr_first >= addr_last) + return CMD_RET_USAGE; + + rcode = flash_sect_protect(p, addr_first, addr_last); +#endif /* CONFIG_MTD_NOR_FLASH */ + return rcode; +} + +#ifdef CONFIG_MTD_NOR_FLASH +int flash_sect_protect(int p, ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int protected, i; + int planned; + int rcode; + + rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned ); + + protected = 0; + + if (planned && (rcode == 0)) { + for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) { + if (info->flash_id == FLASH_UNKNOWN) { + continue; + } + + if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) { + debug("%sProtecting sectors %d..%d in bank %ld\n", + p ? "" : "Un-", s_first[bank], + s_last[bank], bank + 1); + protected += s_last[bank] - s_first[bank] + 1; + for (i=s_first[bank]; i<=s_last[bank]; ++i) { +#if defined(CONFIG_SYS_FLASH_PROTECTION) + if (flash_real_protect(info, i, p)) + rcode = 1; + putc ('.'); +#else + info->protect[i] = p; +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + } + } + } +#if defined(CONFIG_SYS_FLASH_PROTECTION) + puts (" done\n"); +#endif /* CONFIG_SYS_FLASH_PROTECTION */ + + printf ("%sProtected %d sectors\n", + p ? "" : "Un-", protected); + } else if (rcode == 0) { + puts ("Error: start and/or end address" + " not on sector boundary\n"); + rcode = 1; + } + return rcode; +} +#endif /* CONFIG_MTD_NOR_FLASH */ + + +/**************************************************/ +#if defined(CONFIG_CMD_MTDPARTS) +# define TMP_ERASE "erase <part-id>\n - erase partition\n" +# define TMP_PROT_ON "protect on <part-id>\n - protect partition\n" +# define TMP_PROT_OFF "protect off <part-id>\n - make partition writable\n" +#else +# define TMP_ERASE /* empty */ +# define TMP_PROT_ON /* empty */ +# define TMP_PROT_OFF /* empty */ +#endif + +U_BOOT_CMD( + flinfo, 2, 1, do_flinfo, + "print FLASH memory information", + "\n - print information for all FLASH memory banks\n" + "flinfo N\n - print information for FLASH memory bank # N" +); + +U_BOOT_CMD( + erase, 3, 0, do_flerase, + "erase FLASH memory", + "start end\n" + " - erase FLASH from addr 'start' to addr 'end'\n" + "erase start +len\n" + " - erase FLASH from addr 'start' to the end of sect " + "w/addr 'start'+'len'-1\n" + "erase N:SF[-SL]\n - erase sectors SF-SL in FLASH bank # N\n" + "erase bank N\n - erase FLASH bank # N\n" + TMP_ERASE + "erase all\n - erase all FLASH banks" +); + +U_BOOT_CMD( + protect, 4, 0, do_protect, + "enable or disable FLASH write protection", + "on start end\n" + " - protect FLASH from addr 'start' to addr 'end'\n" + "protect on start +len\n" + " - protect FLASH from addr 'start' to end of sect " + "w/addr 'start'+'len'-1\n" + "protect on N:SF[-SL]\n" + " - protect sectors SF-SL in FLASH bank # N\n" + "protect on bank N\n - protect FLASH bank # N\n" + TMP_PROT_ON + "protect on all\n - protect all FLASH banks\n" + "protect off start end\n" + " - make FLASH from addr 'start' to addr 'end' writable\n" + "protect off start +len\n" + " - make FLASH from addr 'start' to end of sect " + "w/addr 'start'+'len'-1 wrtable\n" + "protect off N:SF[-SL]\n" + " - make sectors SF-SL writable in FLASH bank # N\n" + "protect off bank N\n - make FLASH bank # N writable\n" + TMP_PROT_OFF + "protect off all\n - make all FLASH banks writable" +); + +#undef TMP_ERASE +#undef TMP_PROT_ON +#undef TMP_PROT_OFF diff --git a/roms/u-boot/cmd/fpga.c b/roms/u-boot/cmd/fpga.c new file mode 100644 index 000000000..51410a8e4 --- /dev/null +++ b/roms/u-boot/cmd/fpga.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000, 2001 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + */ + +/* + * FPGA support + */ +#include <common.h> +#include <command.h> +#include <env.h> +#include <fpga.h> +#include <fs.h> +#include <gzip.h> +#include <image.h> +#include <log.h> +#include <malloc.h> + +static long do_fpga_get_device(char *arg) +{ + long dev = FPGA_INVALID_DEVICE; + char *devstr = env_get("fpga"); + + if (devstr) + /* Should be strtol to handle -1 cases */ + dev = simple_strtol(devstr, NULL, 16); + + if (dev == FPGA_INVALID_DEVICE && arg) + dev = simple_strtol(arg, NULL, 16); + + debug("%s: device = %ld\n", __func__, dev); + + return dev; +} + +static int do_fpga_check_params(long *dev, long *fpga_data, size_t *data_size, + struct cmd_tbl *cmdtp, int argc, + char *const argv[]) +{ + size_t local_data_size; + long local_fpga_data; + + debug("%s %d, %d\n", __func__, argc, cmdtp->maxargs); + + if (argc != cmdtp->maxargs) { + debug("fpga: incorrect parameters passed\n"); + return CMD_RET_USAGE; + } + + *dev = do_fpga_get_device(argv[0]); + + local_fpga_data = simple_strtol(argv[1], NULL, 16); + if (!local_fpga_data) { + debug("fpga: zero fpga_data address\n"); + return CMD_RET_USAGE; + } + *fpga_data = local_fpga_data; + + local_data_size = simple_strtoul(argv[2], NULL, 16); + if (!local_data_size) { + debug("fpga: zero size\n"); + return CMD_RET_USAGE; + } + *data_size = local_data_size; + + return 0; +} + +#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) +int do_fpga_loads(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + struct fpga_secure_info fpga_sec_info; + + memset(&fpga_sec_info, 0, sizeof(fpga_sec_info)); + + if (argc < 5) { + debug("fpga: incorrect parameters passed\n"); + return CMD_RET_USAGE; + } + + if (argc == 6) + fpga_sec_info.userkey_addr = (u8 *)(uintptr_t) + simple_strtoull(argv[5], + NULL, 16); + else + /* + * If 6th parameter is not passed then do_fpga_check_params + * will get 5 instead of expected 6 which means that function + * return CMD_RET_USAGE. Increase number of params +1 to pass + * this. + */ + argc++; + + fpga_sec_info.encflag = (u8)simple_strtoul(argv[4], NULL, 16); + fpga_sec_info.authflag = (u8)simple_strtoul(argv[3], NULL, 16); + + if (fpga_sec_info.authflag >= FPGA_NO_ENC_OR_NO_AUTH && + fpga_sec_info.encflag >= FPGA_NO_ENC_OR_NO_AUTH) { + debug("fpga: Use <fpga load> for NonSecure bitstream\n"); + return CMD_RET_USAGE; + } + + if (fpga_sec_info.encflag == FPGA_ENC_USR_KEY && + !fpga_sec_info.userkey_addr) { + debug("fpga: User key not provided\n"); + return CMD_RET_USAGE; + } + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_loads(dev, (void *)fpga_data, data_size, &fpga_sec_info); +} +#endif + +#if defined(CONFIG_CMD_FPGA_LOADFS) +static int do_fpga_loadfs(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + fpga_fs_info fpga_fsinfo; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + fpga_fsinfo.fstype = FS_TYPE_ANY; + fpga_fsinfo.blocksize = (unsigned int)simple_strtoul(argv[3], NULL, 16); + fpga_fsinfo.interface = argv[4]; + fpga_fsinfo.dev_part = argv[5]; + fpga_fsinfo.filename = argv[6]; + + return fpga_fsload(dev, (void *)fpga_data, data_size, &fpga_fsinfo); +} +#endif + +static int do_fpga_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + long dev = do_fpga_get_device(argv[0]); + + return fpga_info(dev); +} + +static int do_fpga_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_dump(dev, (void *)fpga_data, data_size); +} + +static int do_fpga_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_load(dev, (void *)fpga_data, data_size, BIT_FULL); +} + +static int do_fpga_loadb(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_loadbitstream(dev, (void *)fpga_data, data_size, BIT_FULL); +} + +#if defined(CONFIG_CMD_FPGA_LOADP) +static int do_fpga_loadp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_load(dev, (void *)fpga_data, data_size, BIT_PARTIAL); +} +#endif + +#if defined(CONFIG_CMD_FPGA_LOADBP) +static int do_fpga_loadbp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_loadbitstream(dev, (void *)fpga_data, data_size, + BIT_PARTIAL); +} +#endif + +#if defined(CONFIG_CMD_FPGA_LOADMK) +static int do_fpga_loadmk(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + void *fpga_data = NULL; +#if defined(CONFIG_FIT) + const char *fit_uname = NULL; + ulong fit_addr; +#endif + ulong dev = do_fpga_get_device(argv[0]); + char *datastr = env_get("fpgadata"); + + debug("fpga: argc %x, dev %lx, datastr %s\n", argc, dev, datastr); + + if (dev == FPGA_INVALID_DEVICE) { + debug("fpga: Invalid fpga device\n"); + return CMD_RET_USAGE; + } + + if (argc == 0 && !datastr) { + debug("fpga: No datastr passed\n"); + return CMD_RET_USAGE; + } + + if (argc == 2) { + datastr = argv[1]; + debug("fpga: Full command with two args\n"); + } else if (argc == 1 && !datastr) { + debug("fpga: Dev is setup - fpgadata passed\n"); + datastr = argv[0]; + } + +#if defined(CONFIG_FIT) + if (fit_parse_subimage(datastr, (ulong)fpga_data, + &fit_addr, &fit_uname)) { + fpga_data = (void *)fit_addr; + debug("* fpga: subimage '%s' from FIT image ", + fit_uname); + debug("at 0x%08lx\n", fit_addr); + } else +#endif + { + fpga_data = (void *)simple_strtoul(datastr, NULL, 16); + debug("* fpga: cmdline image address = 0x%08lx\n", + (ulong)fpga_data); + } + debug("%s: fpga_data = 0x%lx\n", __func__, (ulong)fpga_data); + if (!fpga_data) { + puts("Zero fpga_data address\n"); + return CMD_RET_USAGE; + } + + switch (genimg_get_format(fpga_data)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + { + image_header_t *hdr = (image_header_t *)fpga_data; + ulong data; + u8 comp; + + comp = image_get_comp(hdr); + if (comp == IH_COMP_GZIP) { +#if defined(CONFIG_GZIP) + ulong image_buf = image_get_data(hdr); + ulong image_size = ~0UL; + + data = image_get_load(hdr); + + if (gunzip((void *)data, ~0UL, (void *)image_buf, + &image_size) != 0) { + puts("GUNZIP: error\n"); + return CMD_RET_FAILURE; + } + data_size = image_size; +#else + puts("Gunzip image is not supported\n"); + return 1; +#endif + } else { + data = (ulong)image_get_data(hdr); + data_size = image_get_data_size(hdr); + } + return fpga_load(dev, (void *)data, data_size, + BIT_FULL); + } +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + { + const void *fit_hdr = (const void *)fpga_data; + int noffset; + const void *fit_data; + + if (!fit_uname) { + puts("No FIT subimage unit name\n"); + return CMD_RET_FAILURE; + } + + if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) { + puts("Bad FIT image format\n"); + return CMD_RET_FAILURE; + } + + /* get fpga component image node offset */ + noffset = fit_image_get_node(fit_hdr, fit_uname); + if (noffset < 0) { + printf("Can't find '%s' FIT subimage\n", fit_uname); + return CMD_RET_FAILURE; + } + + /* verify integrity */ + if (!fit_image_verify(fit_hdr, noffset)) { + puts("Bad Data Hash\n"); + return CMD_RET_FAILURE; + } + + /* get fpga subimage/external data address and length */ + if (fit_image_get_data_and_size(fit_hdr, noffset, + &fit_data, &data_size)) { + puts("Fpga subimage data not found\n"); + return CMD_RET_FAILURE; + } + + return fpga_load(dev, fit_data, data_size, BIT_FULL); + } +#endif + default: + puts("** Unknown image type\n"); + return CMD_RET_FAILURE; + } +} +#endif + +static struct cmd_tbl fpga_commands[] = { + U_BOOT_CMD_MKENT(info, 1, 1, do_fpga_info, "", ""), + U_BOOT_CMD_MKENT(dump, 3, 1, do_fpga_dump, "", ""), + U_BOOT_CMD_MKENT(load, 3, 1, do_fpga_load, "", ""), + U_BOOT_CMD_MKENT(loadb, 3, 1, do_fpga_loadb, "", ""), +#if defined(CONFIG_CMD_FPGA_LOADP) + U_BOOT_CMD_MKENT(loadp, 3, 1, do_fpga_loadp, "", ""), +#endif +#if defined(CONFIG_CMD_FPGA_LOADBP) + U_BOOT_CMD_MKENT(loadbp, 3, 1, do_fpga_loadbp, "", ""), +#endif +#if defined(CONFIG_CMD_FPGA_LOADFS) + U_BOOT_CMD_MKENT(loadfs, 7, 1, do_fpga_loadfs, "", ""), +#endif +#if defined(CONFIG_CMD_FPGA_LOADMK) + U_BOOT_CMD_MKENT(loadmk, 2, 1, do_fpga_loadmk, "", ""), +#endif +#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) + U_BOOT_CMD_MKENT(loads, 6, 1, do_fpga_loads, "", ""), +#endif +}; + +static int do_fpga_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *fpga_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + fpga_cmd = find_cmd_tbl(argv[1], fpga_commands, + ARRAY_SIZE(fpga_commands)); + if (!fpga_cmd) { + debug("fpga: non existing command\n"); + return CMD_RET_USAGE; + } + + argc -= 2; + argv += 2; + + if (argc > fpga_cmd->maxargs) { + debug("fpga: more parameters passed\n"); + return CMD_RET_USAGE; + } + + ret = fpga_cmd->cmd(fpga_cmd, flag, argc, argv); + + return cmd_process_error(fpga_cmd, ret); +} + +#if defined(CONFIG_CMD_FPGA_LOADFS) || defined(CONFIG_CMD_FPGA_LOAD_SECURE) +U_BOOT_CMD(fpga, 9, 1, do_fpga_wrapper, +#else +U_BOOT_CMD(fpga, 6, 1, do_fpga_wrapper, +#endif + "loadable FPGA image support", + "[operation type] [device number] [image address] [image size]\n" + "fpga operations:\n" + " dump\t[dev] [address] [size]\tLoad device to memory buffer\n" + " info\t[dev]\t\t\tlist known device information\n" + " load\t[dev] [address] [size]\tLoad device from memory buffer\n" +#if defined(CONFIG_CMD_FPGA_LOADP) + " loadp\t[dev] [address] [size]\t" + "Load device from memory buffer with partial bitstream\n" +#endif + " loadb\t[dev] [address] [size]\t" + "Load device from bitstream buffer (Xilinx only)\n" +#if defined(CONFIG_CMD_FPGA_LOADBP) + " loadbp\t[dev] [address] [size]\t" + "Load device from bitstream buffer with partial bitstream" + "(Xilinx only)\n" +#endif +#if defined(CONFIG_CMD_FPGA_LOADFS) + "Load device from filesystem (FAT by default) (Xilinx only)\n" + " loadfs [dev] [address] [image size] [blocksize] <interface>\n" + " [<dev[:part]>] <filename>\n" +#endif +#if defined(CONFIG_CMD_FPGA_LOADMK) + " loadmk [dev] [address]\tLoad device generated with mkimage" +#if defined(CONFIG_FIT) + "\n" + "\tFor loadmk operating on FIT format uImage address must include\n" + "\tsubimage unit name in the form of addr:<subimg_uname>" +#endif +#endif +#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) + "Load encrypted bitstream (Xilinx only)\n" + " loads [dev] [address] [size] [auth-OCM-0/DDR-1/noauth-2]\n" + " [enc-devkey(0)/userkey(1)/nenc(2) [Userkey address]\n" + "Loads the secure bistreams(authenticated/encrypted/both\n" + "authenticated and encrypted) of [size] from [address].\n" + "The auth-OCM/DDR flag specifies to perform authentication\n" + "in OCM or in DDR. 0 for OCM, 1 for DDR, 2 for no authentication.\n" + "The enc flag specifies which key to be used for decryption\n" + "0-device key, 1-user key, 2-no encryption.\n" + "The optional Userkey address specifies from which address key\n" + "has to be used for decryption if user key is selected.\n" + "NOTE: the secure bitstream has to be created using Xilinx\n" + "bootgen tool only.\n" +#endif +); diff --git a/roms/u-boot/cmd/fpgad.c b/roms/u-boot/cmd/fpgad.c new file mode 100644 index 000000000..fb2fe63e7 --- /dev/null +++ b/roms/u-boot/cmd/fpgad.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2013 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * based on cmd_mem.c + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <console.h> + +#include <gdsys_fpga.h> + +static uint dp_last_fpga; +static uint dp_last_addr; +static uint dp_last_length = 0x40; + +/* + * FPGA Memory Display + * + * Syntax: + * fpgad {fpga} {addr} {len} + */ +#define DISP_LINE_LEN 16 +int do_fpga_md(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned int k; + unsigned int fpga; + ulong addr, length; + int rc = 0; + u16 linebuf[DISP_LINE_LEN/sizeof(u16)]; + ulong nbytes; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + fpga = dp_last_fpga; + addr = dp_last_addr; + length = dp_last_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * FPGA is specified since argc > 2 + */ + fpga = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is specified since argc > 2 + */ + addr = simple_strtoul(argv[2], NULL, 16); + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 3) + length = simple_strtoul(argv[3], NULL, 16); + } + + nbytes = length * sizeof(u16); + do { + ulong linebytes = (nbytes > DISP_LINE_LEN) ? + DISP_LINE_LEN : nbytes; + + for (k = 0; k < linebytes / sizeof(u16); ++k) + fpga_get_reg(fpga, + (u16 *)fpga_ptr[fpga] + addr + / sizeof(u16) + k, + addr + k * sizeof(u16), + &linebuf[k]); + print_buffer(addr, (void *)linebuf, sizeof(u16), + linebytes / sizeof(u16), + DISP_LINE_LEN / sizeof(u16)); + + nbytes -= linebytes; + addr += linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + + dp_last_fpga = fpga; + dp_last_addr = addr; + dp_last_length = length; + return rc; +} + +U_BOOT_CMD( + fpgad, 4, 1, do_fpga_md, + "fpga register display", + "fpga address [# of objects]" +); diff --git a/roms/u-boot/cmd/fs.c b/roms/u-boot/cmd/fs.c new file mode 100644 index 000000000..5ad11647c --- /dev/null +++ b/roms/u-boot/cmd/fs.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Inspired by cmd_ext_common.c, cmd_fat.c. + */ + +#include <common.h> +#include <command.h> +#include <fs.h> + +static int do_size_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + size, 4, 0, do_size_wrapper, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + +static int do_load_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + load, 7, 0, do_load_wrapper, + "load binary file from a filesystem", + "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n" + " - Load binary file 'filename' from partition 'part' on device\n" + " type 'interface' instance 'dev' to address 'addr' in memory.\n" + " 'bytes' gives the size to load in bytes.\n" + " If 'bytes' is 0 or omitted, the file is read until the end.\n" + " 'pos' gives the file byte position to start reading from.\n" + " If 'pos' is 0 or omitted, the file is read from the start." +) + +static int do_save_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_save(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + save, 7, 0, do_save_wrapper, + "save file to a filesystem", + "<interface> <dev[:part]> <addr> <filename> bytes [pos]\n" + " - Save binary file 'filename' to partition 'part' on device\n" + " type 'interface' instance 'dev' from addr 'addr' in memory.\n" + " 'bytes' gives the size to save in bytes and is mandatory.\n" + " 'pos' gives the file byte position to start writing to.\n" + " If 'pos' is 0 or omitted, the file is written from the start." +) + +static int do_ls_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + ls, 4, 1, do_ls_wrapper, + "list files in a directory (default /)", + "<interface> [<dev[:part]> [directory]]\n" + " - List files in directory 'directory' of partition 'part' on\n" + " device type 'interface' instance 'dev'." +) + +static int do_ln_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_ln(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + ln, 5, 1, do_ln_wrapper, + "Create a symbolic link", + "<interface> <dev[:part]> target linkname\n" + " - create a symbolic link to 'target' with the name 'linkname' on\n" + " device type 'interface' instance 'dev'." +) + +static int do_fstype_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_fs_type(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + fstype, 4, 1, do_fstype_wrapper, + "Look up a filesystem type", + "<interface> <dev>:<part>\n" + "- print filesystem type\n" + "fstype <interface> <dev>:<part> <varname>\n" + "- set environment variable to filesystem type\n" +); + +static int do_fstypes_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + return do_fs_types(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + fstypes, 1, 1, do_fstypes_wrapper, + "List supported filesystem types", "" +); diff --git a/roms/u-boot/cmd/fs_uuid.c b/roms/u-boot/cmd/fs_uuid.c new file mode 100644 index 000000000..5dc94aa64 --- /dev/null +++ b/roms/u-boot/cmd/fs_uuid.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_fs_uuid.c -- fsuuid command + * + * Copyright (C) 2014, Bachmann electronic GmbH + */ + +#include <common.h> +#include <command.h> +#include <fs.h> + +static int do_fs_uuid_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_fs_uuid(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + fsuuid, 4, 1, do_fs_uuid_wrapper, + "Look up a filesystem UUID", + "<interface> <dev>:<part>\n" + " - print filesystem UUID\n" + "fsuuid <interface> <dev>:<part> <varname>\n" + " - set environment variable to filesystem UUID\n" +); diff --git a/roms/u-boot/cmd/fuse.c b/roms/u-boot/cmd/fuse.c new file mode 100644 index 000000000..e001619d1 --- /dev/null +++ b/roms/u-boot/cmd/fuse.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009-2013 ADVANSEE + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on the mpc512x iim code: + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx <mmarx@silicontkx.com> + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <fuse.h> +#include <linux/errno.h> + +static int strtou32(const char *str, unsigned int base, u32 *result) +{ + char *ep; + + *result = simple_strtoul(str, &ep, base); + if (ep == str || *ep != '\0') + return -EINVAL; + + return 0; +} + +static int confirm_prog(void) +{ + puts("Warning: Programming fuses is an irreversible operation!\n" + " This may brick your system.\n" + " Use this command only if you are sure of " + "what you are doing!\n" + "\nReally perform this fuse programming? <y/N>\n"); + + if (confirm_yesno()) + return 1; + + puts("Fuse programming aborted\n"); + return 0; +} + +static int do_fuse(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *op = argc >= 2 ? argv[1] : NULL; + int confirmed = argc >= 3 && !strcmp(argv[2], "-y"); + u32 bank, word, cnt, val; + int ret, i; + + argc -= 2 + confirmed; + argv += 2 + confirmed; + + if (argc < 2 || strtou32(argv[0], 0, &bank) || + strtou32(argv[1], 0, &word)) + return CMD_RET_USAGE; + + if (!strcmp(op, "read")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Reading bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_read(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "sense")) { + if (argc == 2) + cnt = 1; + else if (argc != 3 || strtou32(argv[2], 0, &cnt)) + return CMD_RET_USAGE; + + printf("Sensing bank %u:\n", bank); + for (i = 0; i < cnt; i++, word++) { + if (!(i % 4)) + printf("\nWord 0x%.8x:", word); + + ret = fuse_sense(bank, word, &val); + if (ret) + goto err; + + printf(" %.8x", val); + } + putc('\n'); + } else if (!strcmp(op, "prog")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Programming bank %u word 0x%.8x to 0x%.8x...\n", + bank, word, val); + if (!confirmed && !confirm_prog()) + return CMD_RET_FAILURE; + ret = fuse_prog(bank, word, val); + if (ret) + goto err; + } + } else if (!strcmp(op, "override")) { + if (argc < 3) + return CMD_RET_USAGE; + + for (i = 2; i < argc; i++, word++) { + if (strtou32(argv[i], 16, &val)) + return CMD_RET_USAGE; + + printf("Overriding bank %u word 0x%.8x with " + "0x%.8x...\n", bank, word, val); + ret = fuse_override(bank, word, val); + if (ret) + goto err; + } + } else { + return CMD_RET_USAGE; + } + + return 0; + +err: + puts("ERROR\n"); + return CMD_RET_FAILURE; +} + +U_BOOT_CMD( + fuse, CONFIG_SYS_MAXARGS, 0, do_fuse, + "Fuse sub-system", + "read <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse sense <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,\n" + " starting at 'word'\n" + "fuse prog [-y] <bank> <word> <hexval> [<hexval>...] - program 1 or\n" + " several fuse words, starting at 'word' (PERMANENT)\n" + "fuse override <bank> <word> <hexval> [<hexval>...] - override 1 or\n" + " several fuse words, starting at 'word'" +); diff --git a/roms/u-boot/cmd/gettime.c b/roms/u-boot/cmd/gettime.c new file mode 100644 index 000000000..2e74e02b4 --- /dev/null +++ b/roms/u-boot/cmd/gettime.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Get Timer overflows after 2^32 / CONFIG_SYS_HZ (32Khz) = 131072 sec + */ +#include <common.h> +#include <command.h> + +static int do_gettime(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long int val = get_timer(0); + +#ifdef CONFIG_SYS_HZ + printf("Timer val: %lu\n", val); + printf("Seconds : %lu\n", val / CONFIG_SYS_HZ); + printf("Remainder : %lu\n", val % CONFIG_SYS_HZ); + printf("sys_hz = %lu\n", (unsigned long int)CONFIG_SYS_HZ); +#else + printf("CONFIG_SYS_HZ not defined"); + printf("Timer Val %lu", val); +#endif + + return 0; +} + +U_BOOT_CMD( + gettime, 1, 1, do_gettime, + "get timer val elapsed", + "get time elapsed from uboot start" +); diff --git a/roms/u-boot/cmd/gpio.c b/roms/u-boot/cmd/gpio.c new file mode 100644 index 000000000..4fdb3135f --- /dev/null +++ b/roms/u-boot/cmd/gpio.c @@ -0,0 +1,270 @@ +/* + * Control GPIO pins on the fly + * + * Copyright (c) 2008-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <asm/gpio.h> +#include <linux/err.h> + +__weak int name_to_gpio(const char *name) +{ + return simple_strtoul(name, NULL, 10); +} + +enum gpio_cmd { + GPIOC_INPUT, + GPIOC_SET, + GPIOC_CLEAR, + GPIOC_TOGGLE, +}; + +#if defined(CONFIG_DM_GPIO) && !defined(gpio_status) + +/* A few flags used by show_gpio() */ +enum { + FLAG_SHOW_ALL = 1 << 0, + FLAG_SHOW_BANK = 1 << 1, + FLAG_SHOW_NEWLINE = 1 << 2, +}; + +static void gpio_get_description(struct udevice *dev, const char *bank_name, + int offset, int *flagsp, bool show_all) +{ + char buf[80]; + int ret; + + ret = gpio_get_function(dev, offset, NULL); + if (ret < 0) + goto err; + if (!show_all && !(*flagsp & FLAG_SHOW_ALL) && ret == GPIOF_UNUSED) + return; + if ((*flagsp & FLAG_SHOW_BANK) && bank_name) { + if (*flagsp & FLAG_SHOW_NEWLINE) { + putc('\n'); + *flagsp &= ~FLAG_SHOW_NEWLINE; + } + printf("Bank %s:\n", bank_name); + *flagsp &= ~FLAG_SHOW_BANK; + } + + ret = gpio_get_status(dev, offset, buf, sizeof(buf)); + if (ret) + goto err; + + printf("%s\n", buf); + return; +err: + printf("Error %d\n", ret); +} + +static int do_gpio_status(bool all, const char *gpio_name) +{ + struct udevice *dev; + int banklen; + int flags; + int ret; + + flags = 0; + if (gpio_name && !*gpio_name) + gpio_name = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + const char *bank_name; + int num_bits; + + flags |= FLAG_SHOW_BANK; + if (all) + flags |= FLAG_SHOW_ALL; + bank_name = gpio_get_bank_info(dev, &num_bits); + if (!num_bits) { + debug("GPIO device %s has no bits\n", dev->name); + continue; + } + banklen = bank_name ? strlen(bank_name) : 0; + + if (!gpio_name || !bank_name || + !strncasecmp(gpio_name, bank_name, banklen)) { + const char *p; + int offset; + + p = gpio_name + banklen; + if (gpio_name && *p) { + offset = simple_strtoul(p, NULL, 10); + gpio_get_description(dev, bank_name, offset, + &flags, true); + } else { + for (offset = 0; offset < num_bits; offset++) { + gpio_get_description(dev, bank_name, + offset, &flags, false); + } + } + } + /* Add a newline between bank names */ + if (!(flags & FLAG_SHOW_BANK)) + flags |= FLAG_SHOW_NEWLINE; + } + + return ret; +} +#endif + +static int do_gpio(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned int gpio; + enum gpio_cmd sub_cmd; + int value; + const char *str_cmd, *str_gpio = NULL; + int ret; +#ifdef CONFIG_DM_GPIO + bool all = false; +#endif + + if (argc < 2) + show_usage: + return CMD_RET_USAGE; + str_cmd = argv[1]; + argc -= 2; + argv += 2; +#ifdef CONFIG_DM_GPIO + if (argc > 0 && !strcmp(*argv, "-a")) { + all = true; + argc--; + argv++; + } +#endif + if (argc > 0) + str_gpio = *argv; + if (!strncmp(str_cmd, "status", 2)) { + /* Support deprecated gpio_status() */ +#ifdef gpio_status + gpio_status(); + return 0; +#elif defined(CONFIG_DM_GPIO) + return cmd_process_error(cmdtp, do_gpio_status(all, str_gpio)); +#else + goto show_usage; +#endif + } + + if (!str_gpio) + goto show_usage; + + /* parse the behavior */ + switch (*str_cmd) { + case 'i': + sub_cmd = GPIOC_INPUT; + break; + case 's': + sub_cmd = GPIOC_SET; + break; + case 'c': + sub_cmd = GPIOC_CLEAR; + break; + case 't': + sub_cmd = GPIOC_TOGGLE; + break; + default: + goto show_usage; + } + +#if defined(CONFIG_DM_GPIO) + /* + * TODO(sjg@chromium.org): For now we must fit into the existing GPIO + * framework, so we look up the name here and convert it to a GPIO number. + * Once all GPIO drivers are converted to driver model, we can change the + * code here to use the GPIO uclass interface instead of the numbered + * GPIO compatibility layer. + */ + ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio); + if (ret) { + printf("GPIO: '%s' not found\n", str_gpio); + return cmd_process_error(cmdtp, ret); + } +#else + /* turn the gpio name into a gpio number */ + gpio = name_to_gpio(str_gpio); + if (gpio < 0) + goto show_usage; +#endif + /* grab the pin before we tweak it */ + ret = gpio_request(gpio, "cmd_gpio"); + if (ret && ret != -EBUSY) { + printf("gpio: requesting pin %u failed\n", gpio); + return -1; + } + + /* finally, let's do it: set direction and exec command */ + if (sub_cmd == GPIOC_INPUT) { + gpio_direction_input(gpio); + value = gpio_get_value(gpio); + } else { + switch (sub_cmd) { + case GPIOC_SET: + value = 1; + break; + case GPIOC_CLEAR: + value = 0; + break; + case GPIOC_TOGGLE: + value = gpio_get_value(gpio); + if (!IS_ERR_VALUE(value)) + value = !value; + break; + default: + goto show_usage; + } + gpio_direction_output(gpio, value); + } + printf("gpio: pin %s (gpio %u) value is ", str_gpio, gpio); + + if (IS_ERR_VALUE(value)) { + printf("unknown (ret=%d)\n", value); + goto err; + } else { + printf("%d\n", value); + } + + if (sub_cmd != GPIOC_INPUT && !IS_ERR_VALUE(value)) { + int nval = gpio_get_value(gpio); + + if (IS_ERR_VALUE(nval)) { + printf(" Warning: no access to GPIO output value\n"); + goto err; + } else if (nval != value) { + printf(" Warning: value of pin is still %d\n", nval); + goto err; + } + } + + if (ret != -EBUSY) + gpio_free(gpio); + + /* + * Whilst wrong, the legacy gpio input command returns the pin + * value, or CMD_RET_FAILURE (which is indistinguishable from a + * valid pin value). + */ + return (sub_cmd == GPIOC_INPUT) ? value : CMD_RET_SUCCESS; + +err: + if (ret != -EBUSY) + gpio_free(gpio); + return CMD_RET_FAILURE; +} + +U_BOOT_CMD(gpio, 4, 0, do_gpio, + "query and control gpio pins", + "<input|set|clear|toggle> <pin>\n" + " - input/set/clear/toggle the specified pin\n" + "gpio status [-a] [<bank> | <pin>] - show [all/claimed] GPIOs"); diff --git a/roms/u-boot/cmd/gpt.c b/roms/u-boot/cmd/gpt.c new file mode 100644 index 000000000..17f2b839d --- /dev/null +++ b/roms/u-boot/cmd/gpt.c @@ -0,0 +1,1072 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_gpt.c -- GPT (GUID Partition Table) handling command + * + * Copyright (C) 2015 + * Lukasz Majewski <l.majewski@majess.pl> + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * author: Piotr Wilczek <p.wilczek@samsung.com> + */ + +#include <common.h> +#include <blk.h> +#include <env.h> +#include <log.h> +#include <malloc.h> +#include <command.h> +#include <part.h> +#include <part_efi.h> +#include <part.h> +#include <exports.h> +#include <uuid.h> +#include <linux/ctype.h> +#include <div64.h> +#include <memalign.h> +#include <linux/compat.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <stdlib.h> + +static LIST_HEAD(disk_partitions); + +/** + * extract_env(): Expand env name from string format '&{env_name}' + * and return pointer to the env (if the env is set) + * + * @param str - pointer to string + * @param env - pointer to pointer to extracted env + * + * @return - zero on successful expand and env is set + */ +static int extract_env(const char *str, char **env) +{ + int ret = -1; + char *e, *s; +#ifdef CONFIG_RANDOM_UUID + char uuid_str[UUID_STR_LEN + 1]; +#endif + + if (!str || strlen(str) < 4) + return -1; + + if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}'))) + return -1; + + s = strdup(str); + if (s == NULL) + return -1; + + memset(s + strlen(s) - 1, '\0', 1); + memmove(s, s + 2, strlen(s) - 1); + + e = env_get(s); + if (e == NULL) { +#ifdef CONFIG_RANDOM_UUID + debug("%s unset. ", str); + gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID); + env_set(s, uuid_str); + + e = env_get(s); + if (e) { + debug("Set to random.\n"); + ret = 0; + } else { + debug("Can't get random UUID.\n"); + } +#else + debug("%s unset.\n", str); +#endif + } else { + debug("%s get from environment.\n", str); + ret = 0; + } + + *env = e; + free(s); + + return ret; +} + +/** + * extract_val(): Extract value from a key=value pair list (comma separated). + * Only value for the given key is returend. + * Function allocates memory for the value, remember to free! + * + * @param str - pointer to string with key=values pairs + * @param key - pointer to the key to search for + * + * @return - pointer to allocated string with the value + */ +static char *extract_val(const char *str, const char *key) +{ + char *v, *k; + char *s, *strcopy; + char *new = NULL; + + strcopy = strdup(str); + if (strcopy == NULL) + return NULL; + + s = strcopy; + while (s) { + v = strsep(&s, ","); + if (!v) + break; + k = strsep(&v, "="); + if (!k) + break; + if (strcmp(k, key) == 0) { + new = strdup(v); + break; + } + } + + free(strcopy); + + return new; +} + +/** + * found_key(): Found key without value in parameter list (comma separated). + * + * @param str - pointer to string with key + * @param key - pointer to the key to search for + * + * @return - true on found key + */ +static bool found_key(const char *str, const char *key) +{ + char *k; + char *s, *strcopy; + bool result = false; + + strcopy = strdup(str); + if (!strcopy) + return NULL; + + s = strcopy; + while (s) { + k = strsep(&s, ","); + if (!k) + break; + if (strcmp(k, key) == 0) { + result = true; + break; + } + } + + free(strcopy); + + return result; +} + +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +#ifdef CONFIG_CMD_GPT_RENAME +static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(struct disk_partition *info, + int partnum) +{ + struct disk_part *newpart; + newpart = calloc(1, sizeof(struct disk_part)); + if (!newpart) + return ERR_PTR(-ENOMEM); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void prettyprint_part_size(char *sizestr, lbaint_t partsize, + lbaint_t blksize) +{ + unsigned long long partbytes, partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%lluMiB", partmegabytes); +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16]; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, curr->gpt_part_info.start, + curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, curr->gpt_part_info.size, + curr->gpt_part_info.blksz); + + printf("Partition %d:\n", curr->partnum); + printf("Start %s, size %s\n", partstartstr, partsizestr); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable & PART_BOOTABLE); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, + char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -EINVAL; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, + PART_NAME_LEN + 1); + sprintf(partstr, ",start=0x%llx", + (unsigned long long)curr->gpt_part_info.start * + curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + sprintf(partstr, ",size=0x%llx", + (unsigned long long)curr->gpt_part_info.size * + curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 0, p, ret; + struct disk_partition info; + struct disk_part *new_disk_part; + + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + /* Add 1 here because counter is zero-based but p1 is + the first partition */ + new_disk_part = allocate_disk_part(&info, valid_parts+1); + if (IS_ERR(new_disk_part)) + goto out; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (valid_parts == 0) { + printf("** No valid partitions found **\n"); + goto out; + } + return valid_parts; + out: + if (valid_parts >= 1) + del_gpt_info(); + return -ENODEV; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc, char * const namestr) +{ + int numparts; + + numparts = get_gpt_info(dev_desc); + + if (numparts > 0) { + if (namestr) { + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list; + int partlistlen; + int ret = -1; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + if (!partitions_list) { + del_gpt_info(); + return -ENOMEM; + } + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, disk_guid, + partitions_list); + if (ret < 0) + printf("Error: Could not create partition list string!\n"); + else + env_set(namestr, partitions_list); + + free(partitions_list); + } else { + print_gpt_info(); + } + del_gpt_info(); + return 0; + } + return numparts; +} +#endif + +/** + * set_gpt_info(): Fill partition information from string + * function allocates memory, remember to free! + * + * @param dev_desc - pointer block device descriptor + * @param str_part - pointer to string with partition information + * @param str_disk_guid - pointer to pointer to allocated string with disk guid + * @param partitions - pointer to pointer to allocated partitions array + * @param parts_count - number of partitions + * + * @return - zero on success, otherwise error + * + */ +static int set_gpt_info(struct blk_desc *dev_desc, + const char *str_part, + char **str_disk_guid, + struct disk_partition **partitions, + u8 *parts_count) +{ + char *tok, *str, *s; + int i; + char *val, *p; + int p_count; + struct disk_partition *parts; + int errno = 0; + uint64_t size_ll, start_ll; + lbaint_t offset = 0; + int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS); + + debug("%s: lba num: 0x%x %d\n", __func__, + (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); + + if (str_part == NULL) + return -1; + + str = strdup(str_part); + if (str == NULL) + return -ENOMEM; + + /* extract disk guid */ + s = str; + val = extract_val(str, "uuid_disk"); + if (!val) { +#ifdef CONFIG_RANDOM_UUID + *str_disk_guid = malloc(UUID_STR_LEN + 1); + if (*str_disk_guid == NULL) + return -ENOMEM; + gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD); +#else + free(str); + return -2; +#endif + } else { + val = strsep(&val, ";"); + if (extract_env(val, &p)) + p = val; + *str_disk_guid = strdup(p); + free(val); + /* Move s to first partition */ + strsep(&s, ";"); + } + if (s == NULL) { + printf("Error: is the partitions string NULL-terminated?\n"); + return -EINVAL; + } + if (strnlen(s, max_str_part) == 0) + return -3; + + i = strnlen(s, max_str_part) - 1; + if (s[i] == ';') + s[i] = '\0'; + + /* calculate expected number of partitions */ + p_count = 1; + p = s; + while (*p) { + if (*p++ == ';') + p_count++; + } + + /* allocate memory for partitions */ + parts = calloc(sizeof(struct disk_partition), p_count); + if (parts == NULL) + return -ENOMEM; + + /* retrieve partitions data from string */ + for (i = 0; i < p_count; i++) { + tok = strsep(&s, ";"); + + if (tok == NULL) + break; + + /* uuid */ + val = extract_val(tok, "uuid"); + if (!val) { + /* 'uuid' is optional if random uuid's are enabled */ +#ifdef CONFIG_RANDOM_UUID + gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD); +#else + errno = -4; + goto err; +#endif + } else { + if (extract_env(val, &p)) + p = val; + if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) { + printf("Wrong uuid format for partition %d\n", i); + errno = -4; + goto err; + } + strncpy((char *)parts[i].uuid, p, max_str_part); + free(val); + } +#ifdef CONFIG_PARTITION_TYPE_GUID + /* guid */ + val = extract_val(tok, "type"); + if (val) { + /* 'type' is optional */ + if (extract_env(val, &p)) + p = val; + if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) { + printf("Wrong type guid format for partition %d\n", + i); + errno = -4; + goto err; + } + strncpy((char *)parts[i].type_guid, p, max_str_part); + free(val); + } +#endif + /* name */ + val = extract_val(tok, "name"); + if (!val) { /* name is mandatory */ + errno = -4; + goto err; + } + if (extract_env(val, &p)) + p = val; + if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) { + errno = -4; + goto err; + } + strncpy((char *)parts[i].name, p, max_str_part); + free(val); + + /* size */ + val = extract_val(tok, "size"); + if (!val) { /* 'size' is mandatory */ + errno = -4; + goto err; + } + if (extract_env(val, &p)) + p = val; + if ((strcmp(p, "-") == 0)) { + /* Let part efi module to auto extend the size */ + parts[i].size = 0; + } else { + size_ll = ustrtoull(p, &p, 0); + parts[i].size = lldiv(size_ll, dev_desc->blksz); + } + + free(val); + + /* start address */ + val = extract_val(tok, "start"); + if (val) { /* start address is optional */ + if (extract_env(val, &p)) + p = val; + start_ll = ustrtoull(p, &p, 0); + parts[i].start = lldiv(start_ll, dev_desc->blksz); + free(val); + } + + offset += parts[i].size + parts[i].start; + + /* bootable */ + if (found_key(tok, "bootable")) + parts[i].bootable = PART_BOOTABLE; + } + + *parts_count = p_count; + *partitions = parts; + free(str); + + return 0; +err: + free(str); + free(*str_disk_guid); + free(parts); + + return errno; +} + +static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part) +{ + int ret; + char *str_disk_guid; + u8 part_count = 0; + struct disk_partition *partitions = NULL; + + /* fill partitions */ + ret = set_gpt_info(blk_dev_desc, str_part, + &str_disk_guid, &partitions, &part_count); + if (ret) { + if (ret == -1) + printf("No partition list provided\n"); + if (ret == -2) + printf("Missing disk guid\n"); + if ((ret == -3) || (ret == -4)) + printf("Partition list incomplete\n"); + return -1; + } + + /* save partitions layout to disk */ + ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count); + free(str_disk_guid); + free(partitions); + + return ret; +} + +static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, + blk_dev_desc->blksz); + struct disk_partition *partitions = NULL; + gpt_entry *gpt_pte = NULL; + char *str_disk_guid; + u8 part_count = 0; + int ret = 0; + + /* fill partitions */ + ret = set_gpt_info(blk_dev_desc, str_part, + &str_disk_guid, &partitions, &part_count); + if (ret) { + if (ret == -1) { + printf("No partition list provided - only basic check\n"); + ret = gpt_verify_headers(blk_dev_desc, gpt_head, + &gpt_pte); + goto out; + } + if (ret == -2) + printf("Missing disk guid\n"); + if ((ret == -3) || (ret == -4)) + printf("Partition list incomplete\n"); + return -1; + } + + /* Check partition layout with provided pattern */ + ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count, + gpt_head, &gpt_pte); + free(str_disk_guid); + free(partitions); + out: + free(gpt_pte); + return ret; +} + +/** + * gpt_enumerate() - Enumerate partition names into environment variable. + * + * Enumerate partition names. Partition names are stored in gpt_partition_list + * environment variable. Each partition name is delimited by space. + * + * @desc: block device descriptor + * + * @Return: '0' on success and -ve error on failure + */ +static int gpt_enumerate(struct blk_desc *desc) +{ + struct part_driver *first_drv, *part_drv; + int str_len = 0, tmp_len; + char part_list[2048]; + int n_drvs; + char *ptr; + + part_list[0] = 0; + n_drvs = part_driver_get_count(); + if (!n_drvs) { + printf("Failed to get partition driver count\n"); + return -ENOENT; + } + + first_drv = part_driver_get_first(); + for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { + struct disk_partition pinfo; + int ret; + int i; + + for (i = 1; i < part_drv->max_entries; i++) { + ret = part_drv->get_info(desc, i, &pinfo); + if (ret) { + /* no more entries in table */ + break; + } + + ptr = &part_list[str_len]; + tmp_len = strlen((const char *)pinfo.name); + str_len += tmp_len; + /* +1 for space */ + str_len++; + if (str_len > sizeof(part_list)) { + printf("Error insufficient memory\n"); + return -ENOMEM; + } + strcpy(ptr, (const char *)pinfo.name); + /* One byte for space(" ") delimiter */ + ptr[tmp_len] = ' '; + } + } + if (*part_list) + part_list[strlen(part_list) - 1] = 0; + debug("setenv gpt_partition_list %s\n", part_list); + + return env_set("gpt_partition_list", part_list); +} + +/** + * gpt_setenv_part_variables() - setup partition environmental variables + * + * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr + * and gpt_partition_size environment variables. + * + * @pinfo: pointer to disk partition + * @i: partition entry + * + * @Return: '0' on success and -ENOENT on failure + */ +static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i) +{ + int ret; + + ret = env_set_hex("gpt_partition_addr", pinfo->start); + if (ret) + goto fail; + + ret = env_set_hex("gpt_partition_size", pinfo->size); + if (ret) + goto fail; + + ret = env_set_ulong("gpt_partition_entry", i); + if (ret) + goto fail; + + ret = env_set("gpt_partition_name", (const char *)pinfo->name); + if (ret) + goto fail; + + return 0; + +fail: + return -ENOENT; +} + +/** + * gpt_setenv() - Dynamically setup environment variables. + * + * Dynamically setup environment variables for name, index, offset and size + * for partition in GPT table after running "gpt setenv" for a partition name. + * + * @desc: block device descriptor + * @name: partition name + * + * @Return: '0' on success and -ve err on failure + */ +static int gpt_setenv(struct blk_desc *desc, const char *name) +{ + struct part_driver *first_drv, *part_drv; + int n_drvs; + int ret = -1; + + n_drvs = part_driver_get_count(); + if (!n_drvs) { + printf("Failed to get partition driver count\n"); + goto fail; + } + + first_drv = part_driver_get_first(); + for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { + struct disk_partition pinfo; + int i; + + for (i = 1; i < part_drv->max_entries; i++) { + ret = part_drv->get_info(desc, i, &pinfo); + if (ret) { + /* no more entries in table */ + break; + } + + if (!strcmp(name, (const char *)pinfo.name)) { + /* match found, setup environment variables */ + ret = gpt_setenv_part_variables(&pinfo, i); + if (ret) + goto fail; + + return 0; + } + } + } + +fail: + return ret; +} + +static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return CMD_RET_FAILURE; + + if (namestr) + env_set(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return ret; +} + +#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + struct disk_partition *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid = NULL; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + /* + * Allocates disk_partitions, requiring matching call to del_gpt_info() + * if successful. + */ + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + if (!partitions_list) { + del_gpt_info(); + return -ENOMEM; + } + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) { + free(partitions_list); + return ret; + } + /* + * Uncomment the following line to print a string that 'gpt write' + * or 'gpt verify' will accept as input. + */ + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + /* set_gpt_info allocates new_partitions and str_disk_guid */ + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + goto out; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + ret = -EINVAL; + goto out; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + ret = -EINVAL; + goto out; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + ret = -EINVAL; + goto out; + } + partnum = (int)simple_strtol(name1, NULL, 10); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + ret = -EINVAL; + goto out; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + goto out; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) + goto out; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + /* + * Even though valid pointers are here passed into set_gpt_info(), + * it mallocs again, and there's no way to tell which failed. + */ + if (ret < 0) + goto out; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + goto out; + } + + debug("Reading back new partition table\n"); + /* + * Empty the existing disk_partitions list, as otherwise the memory in + * the original list is unreachable. + */ + del_gpt_info(); + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) { + ret = numparts ? numparts : -ENODEV; + goto out; + } + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + out: + del_gpt_info(); +#ifdef CONFIG_RANDOM_UUID + free(str_disk_guid); +#endif + free(new_partitions); + free(partitions_list); + return ret; +} +#endif + +/** + * do_gpt(): Perform GPT operations + * + * @param cmdtp - command name + * @param flag + * @param argc + * @param argv + * + * @return zero on success; otherwise error + */ +static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = CMD_RET_SUCCESS; + int dev = 0; + char *ep; + struct blk_desc *blk_dev_desc = NULL; + +#ifndef CONFIG_CMD_GPT_RENAME + if (argc < 4 || argc > 5) +#else + if (argc < 4 || argc > 6) +#endif + return CMD_RET_USAGE; + + dev = (int)simple_strtoul(argv[3], &ep, 10); + if (!ep || ep[0] != '\0') { + printf("'%s' is not a number\n", argv[3]); + return CMD_RET_USAGE; + } + blk_dev_desc = blk_get_dev(argv[2], dev); + if (!blk_dev_desc) { + printf("%s: %s dev %d NOT available\n", + __func__, argv[2], dev); + return CMD_RET_FAILURE; + } + + if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { + printf("Writing GPT: "); + ret = gpt_default(blk_dev_desc, argv[4]); + } else if ((strcmp(argv[1], "verify") == 0)) { + ret = gpt_verify(blk_dev_desc, argv[4]); + printf("Verify GPT: "); + } else if ((strcmp(argv[1], "setenv") == 0)) { + ret = gpt_setenv(blk_dev_desc, argv[4]); + } else if ((strcmp(argv[1], "enumerate") == 0)) { + ret = gpt_enumerate(blk_dev_desc); + } else if (strcmp(argv[1], "guid") == 0) { + ret = do_disk_guid(blk_dev_desc, argv[4]); +#ifdef CONFIG_CMD_GPT_RENAME + } else if (strcmp(argv[1], "read") == 0) { + ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL); + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif + } else { + return CMD_RET_USAGE; + } + + if (ret) { + printf("error!\n"); + return CMD_RET_FAILURE; + } + + printf("success!\n"); + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, + "GUID Partition Table", + "<command> <interface> <dev> <partitions_list>\n" + " - GUID partition table restoration and validity check\n" + " Restore or verify GPT information on a device connected\n" + " to interface\n" + " Example usage:\n" + " gpt write mmc 0 $partitions\n" + " - write the GPT to device\n" + " gpt verify mmc 0 $partitions\n" + " - verify the GPT on device against $partitions\n" + " gpt setenv mmc 0 $name\n" + " - setup environment variables for partition $name:\n" + " gpt_partition_addr, gpt_partition_size,\n" + " gpt_partition_name, gpt_partition_entry\n" + " gpt enumerate mmc 0\n" + " - store list of partitions to gpt_partition_list environment variable\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" + " gpt guid <interface> <dev>\n" + " - print disk GUID\n" + " gpt guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + " gpt read <interface> <dev> [<varname>]\n" + " - read GPT into a data structure for manipulation\n" + " - read GPT partitions into environment variable\n" + " gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + " gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif +); diff --git a/roms/u-boot/cmd/hash.c b/roms/u-boot/cmd/hash.c new file mode 100644 index 000000000..e163cd677 --- /dev/null +++ b/roms/u-boot/cmd/hash.c @@ -0,0 +1,56 @@ +// 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. + */ + +#include <common.h> +#include <command.h> +#include <hash.h> +#include <linux/ctype.h> + +static int do_hash(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *s; + int flags = HASH_FLAG_ENV; + +#ifdef CONFIG_HASH_VERIFY + if (argc < 4) + return CMD_RET_USAGE; + if (!strcmp(argv[1], "-v")) { + flags |= HASH_FLAG_VERIFY; + argc--; + argv++; + } +#endif + /* Move forward to 'algorithm' parameter */ + argc--; + argv++; + for (s = *argv; *s; s++) + *s = tolower(*s); + return hash_command(*argv, flags, cmdtp, flag, argc - 1, argv + 1); +} + +#ifdef CONFIG_HASH_VERIFY +#define HARGS 6 +#else +#define HARGS 5 +#endif + +U_BOOT_CMD( + hash, HARGS, 1, do_hash, + "compute hash message digest", + "algorithm address count [[*]hash_dest]\n" + " - compute message digest [save to env var / *address]" +#ifdef CONFIG_HASH_VERIFY + "\nhash -v algorithm address count [*]hash\n" + " - verify message digest of memory area to immediate value, \n" + " env var or *address" +#endif +); diff --git a/roms/u-boot/cmd/help.c b/roms/u-boot/cmd/help.c new file mode 100644 index 000000000..8d67d977b --- /dev/null +++ b/roms/u-boot/cmd/help.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> + +static int do_help(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#ifdef CONFIG_CMDLINE + struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd); + const int len = ll_entry_count(struct cmd_tbl, cmd); + return _do_help(start, len, cmdtp, flag, argc, argv); +#else + return 0; +#endif +} + +U_BOOT_CMD( + help, CONFIG_SYS_MAXARGS, 1, do_help, + "print command description/usage", + "\n" + " - print brief description of all commands\n" + "help command ...\n" + " - print detailed usage of 'command'" +); + +#ifdef CONFIG_CMDLINE +/* This does not use the U_BOOT_CMD macro as ? can't be used in symbol names */ +ll_entry_declare(struct cmd_tbl, question_mark, cmd) = { + "?", CONFIG_SYS_MAXARGS, cmd_always_repeatable, do_help, + "alias for 'help'", +#ifdef CONFIG_SYS_LONGHELP + "" +#endif /* CONFIG_SYS_LONGHELP */ +}; +#endif diff --git a/roms/u-boot/cmd/host.c b/roms/u-boot/cmd/host.c new file mode 100644 index 000000000..6aa3d9167 --- /dev/null +++ b/roms/u-boot/cmd/host.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012, Google Inc. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <fs.h> +#include <part.h> +#include <sandboxblockdev.h> +#include <dm/device_compat.h> +#include <linux/errno.h> + +static int host_curr_device = -1; + +static int do_host_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_host_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_host_size(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_host_save(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_save(cmdtp, flag, argc, argv, FS_TYPE_SANDBOX); +} + +static int do_host_bind(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool removable = false; + const char *dev_str; + char *file; + char *ep; + int dev; + + /* Skip 'bind' */ + argc--; + argv++; + if (argc < 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[0], "-r")) { + removable = true; + argc--; + argv++; + } + + if (argc > 2) + return CMD_RET_USAGE; + dev_str = argv[0]; + dev = simple_strtoul(dev_str, &ep, 16); + if (*ep) { + printf("** Bad device specification %s **\n", dev_str); + return CMD_RET_USAGE; + } + file = argc > 1 ? argv[1] : NULL; + + return !!host_dev_bind(dev, file, removable); +} + +static int do_host_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc < 1 || argc > 2) + return CMD_RET_USAGE; + int min_dev = 0; + int max_dev = CONFIG_HOST_MAX_DEVICES - 1; + if (argc >= 2) { + char *ep; + char *dev_str = argv[1]; + int dev = simple_strtoul(dev_str, &ep, 16); + if (*ep) { + printf("** Bad device specification %s **\n", dev_str); + return CMD_RET_USAGE; + } + min_dev = dev; + max_dev = dev; + } + int dev; + printf("%3s %12s %s\n", "dev", "blocks", "path"); + for (dev = min_dev; dev <= max_dev; dev++) { + struct blk_desc *blk_dev; + int ret; + + printf("%3d ", dev); + ret = host_get_dev_err(dev, &blk_dev); + if (ret) { + if (ret == -ENOENT) + puts("Not bound to a backing file\n"); + else if (ret == -ENODEV) + puts("Invalid host device number\n"); + + continue; + } + struct host_block_dev *host_dev; + +#ifdef CONFIG_BLK + host_dev = dev_get_plat(blk_dev->bdev); +#else + host_dev = blk_dev->priv; +#endif + printf("%12lu %s\n", (unsigned long)blk_dev->lba, + host_dev->filename); + } + return 0; +} + +static int do_host_dev(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int dev; + char *ep; + struct blk_desc *blk_dev; + int ret; + + if (argc < 1 || argc > 3) + return CMD_RET_USAGE; + + if (argc == 1) { + if (host_curr_device < 0) { + printf("No current host device\n"); + return 1; + } + printf("Current host device %d\n", host_curr_device); + return 0; + } + + dev = simple_strtoul(argv[1], &ep, 16); + if (*ep) { + printf("** Bad device specification %s **\n", argv[2]); + return CMD_RET_USAGE; + } + + ret = host_get_dev_err(dev, &blk_dev); + if (ret) { + if (ret == -ENOENT) + puts("Not bound to a backing file\n"); + else if (ret == -ENODEV) + puts("Invalid host device number\n"); + + return 1; + } + + host_curr_device = dev; + return 0; +} + +static struct cmd_tbl cmd_host_sub[] = { + U_BOOT_CMD_MKENT(load, 7, 0, do_host_load, "", ""), + U_BOOT_CMD_MKENT(ls, 3, 0, do_host_ls, "", ""), + U_BOOT_CMD_MKENT(save, 6, 0, do_host_save, "", ""), + U_BOOT_CMD_MKENT(size, 3, 0, do_host_size, "", ""), + U_BOOT_CMD_MKENT(bind, 4, 0, do_host_bind, "", ""), + U_BOOT_CMD_MKENT(info, 3, 0, do_host_info, "", ""), + U_BOOT_CMD_MKENT(dev, 0, 1, do_host_dev, "", ""), +}; + +static int do_host(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + /* Skip past 'host' */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_host_sub, + ARRAY_SIZE(cmd_host_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + host, 8, 1, do_host, + "Miscellaneous host commands", + "load hostfs - <addr> <filename> [<bytes> <offset>] - " + "load a file from host\n" + "host ls hostfs - <filename> - list files on host\n" + "host save hostfs - <addr> <filename> <bytes> [<offset>] - " + "save a file to host\n" + "host size hostfs - <filename> - determine size of file on host\n" + "host bind [-r] <dev> [<filename>] - bind \"host\" device to file\n" + " -r = mark as removable\n" + "host info [<dev>] - show device binding & info\n" + "host dev [<dev>] - Set or retrieve the current host device\n" + "host commands use the \"hostfs\" device. The \"host\" device is used\n" + "with standard IO commands such as fatls or ext2load" +); diff --git a/roms/u-boot/cmd/i2c.c b/roms/u-boot/cmd/i2c.c new file mode 100644 index 000000000..5d0e20787 --- /dev/null +++ b/roms/u-boot/cmd/i2c.c @@ -0,0 +1,2075 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 + * Sergey Kubushyn, himself, ksi@koi8.net + * + * Changes for unified multibus/multiadapter I2C support. + * + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + */ + +/* + * I2C Functions similar to the standard memory functions. + * + * There are several parameters in many of the commands that bear further + * explanations: + * + * {i2c_chip} is the I2C chip address (the first byte sent on the bus). + * Each I2C chip on the bus has a unique address. On the I2C data bus, + * the address is the upper seven bits and the LSB is the "read/write" + * bit. Note that the {i2c_chip} address specified on the command + * line is not shifted up: e.g. a typical EEPROM memory chip may have + * an I2C address of 0x50, but the data put on the bus will be 0xA0 + * for write and 0xA1 for read. This "non shifted" address notation + * matches at least half of the data sheets :-/. + * + * {addr} is the address (or offset) within the chip. Small memory + * chips have 8 bit addresses. Large memory chips have 16 bit + * addresses. Other memory chips have 9, 10, or 11 bit addresses. + * Many non-memory chips have multiple registers and {addr} is used + * as the register index. Some non-memory chips have only one register + * and therefore don't need any {addr} parameter. + * + * The default {addr} parameter is one byte (.1) which works well for + * memories and registers with 8 bits of address space. + * + * You can specify the length of the {addr} field with the optional .0, + * .1, or .2 modifier (similar to the .b, .w, .l modifier). If you are + * manipulating a single register device which doesn't use an address + * field, use "0.0" for the address and the ".0" length field will + * suppress the address in the I2C data stream. This also works for + * successive reads using the I2C auto-incrementing memory pointer. + * + * If you are manipulating a large memory with 2-byte addresses, use + * the .2 address modifier, e.g. 210.2 addresses location 528 (decimal). + * + * Then there are the unfortunate memory chips that spill the most + * significant 1, 2, or 3 bits of address into the chip address byte. + * This effectively makes one chip (logically) look like 2, 4, or + * 8 chips. This is handled (awkwardly) by #defining + * CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW and using the .1 modifier on the + * {addr} field (since .1 is the default, it doesn't actually have to + * be specified). Examples: given a memory chip at I2C chip address + * 0x50, the following would happen... + * i2c md 50 0 10 display 16 bytes starting at 0x000 + * On the bus: <S> A0 00 <E> <S> A1 <rd> ... <rd> + * i2c md 50 100 10 display 16 bytes starting at 0x100 + * On the bus: <S> A2 00 <E> <S> A3 <rd> ... <rd> + * i2c md 50 210 10 display 16 bytes starting at 0x210 + * On the bus: <S> A4 10 <E> <S> A5 <rd> ... <rd> + * This is awfully ugly. It would be nice if someone would think up + * a better way of handling this. + * + * Adapted from cmd_mem.c which is copyright Wolfgang Denk (wd@denx.de). + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <edid.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <u-boot/crc.h> + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static uint i2c_dp_last_chip; +static uint i2c_dp_last_addr; +static uint i2c_dp_last_alen; +static uint i2c_dp_last_length = 0x10; + +static uint i2c_mm_last_chip; +static uint i2c_mm_last_addr; +static uint i2c_mm_last_alen; + +/* If only one I2C bus is present, the list of devices to ignore when + * the probe command is issued is represented by a 1D array of addresses. + * When multiple buses are present, the list is an array of bus-address + * pairs. The following macros take care of this */ + +#if defined(CONFIG_SYS_I2C_NOPROBES) +#if defined(CONFIG_SYS_I2C) || defined(CONFIG_I2C_MULTI_BUS) +static struct +{ + uchar bus; + uchar addr; +} i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; +#define GET_BUS_NUM i2c_get_bus_num() +#define COMPARE_BUS(b,i) (i2c_no_probes[(i)].bus == (b)) +#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)].addr == (a)) +#define NO_PROBE_ADDR(i) i2c_no_probes[(i)].addr +#else /* single bus */ +static uchar i2c_no_probes[] = CONFIG_SYS_I2C_NOPROBES; +#define GET_BUS_NUM 0 +#define COMPARE_BUS(b,i) ((b) == 0) /* Make compiler happy */ +#define COMPARE_ADDR(a,i) (i2c_no_probes[(i)] == (a)) +#define NO_PROBE_ADDR(i) i2c_no_probes[(i)] +#endif /* defined(CONFIG_SYS_I2C) */ +#endif + +#define DISP_LINE_LEN 16 + +/* + * Default for driver model is to use the chip's existing address length. + * For legacy code, this is not stored, so we need to use a suitable + * default. + */ +#if CONFIG_IS_ENABLED(DM_I2C) +#define DEFAULT_ADDR_LEN (-1) +#else +#define DEFAULT_ADDR_LEN 1 +#endif + +#if CONFIG_IS_ENABLED(DM_I2C) +static struct udevice *i2c_cur_bus; + +static int cmd_i2c_set_bus_num(unsigned int busnum) +{ + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus); + if (ret) { + debug("%s: No bus %d\n", __func__, busnum); + return ret; + } + i2c_cur_bus = bus; + + return 0; +} + +static int i2c_get_cur_bus(struct udevice **busp) +{ +#ifdef CONFIG_I2C_SET_DEFAULT_BUS_NUM + if (!i2c_cur_bus) { + if (cmd_i2c_set_bus_num(CONFIG_I2C_DEFAULT_BUS_NUMBER)) { + printf("Default I2C bus %d not found\n", + CONFIG_I2C_DEFAULT_BUS_NUMBER); + return -ENODEV; + } + } +#endif + + if (!i2c_cur_bus) { + puts("No I2C bus selected\n"); + return -ENODEV; + } + *busp = i2c_cur_bus; + + return 0; +} + +static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp) +{ + struct udevice *bus; + int ret; + + ret = i2c_get_cur_bus(&bus); + if (ret) + return ret; + + return i2c_get_chip(bus, chip_addr, 1, devp); +} + +#endif + +/** + * i2c_init_board() - Board-specific I2C bus init + * + * This function is the default no-op implementation of I2C bus + * initialization. This function can be overridden by board-specific + * implementation if needed. + */ +__weak +void i2c_init_board(void) +{ +} + +/* TODO: Implement architecture-specific get/set functions */ + +/** + * i2c_get_bus_speed() - Return I2C bus speed + * + * This function is the default implementation of function for retrieveing + * the current I2C bus speed in Hz. + * + * A driver implementing runtime switching of I2C bus speed must override + * this function to report the speed correctly. Simple or legacy drivers + * can use this fallback. + * + * Returns I2C bus speed in Hz. + */ +#if !defined(CONFIG_SYS_I2C) && !CONFIG_IS_ENABLED(DM_I2C) +/* + * TODO: Implement architecture-specific get/set functions + * Should go away, if we switched completely to new multibus support + */ +__weak +unsigned int i2c_get_bus_speed(void) +{ + return CONFIG_SYS_I2C_SPEED; +} + +/** + * i2c_set_bus_speed() - Configure I2C bus speed + * @speed: Newly set speed of the I2C bus in Hz + * + * This function is the default implementation of function for setting + * the I2C bus speed in Hz. + * + * A driver implementing runtime switching of I2C bus speed must override + * this function to report the speed correctly. Simple or legacy drivers + * can use this fallback. + * + * Returns zero on success, negative value on error. + */ +__weak +int i2c_set_bus_speed(unsigned int speed) +{ + if (speed != CONFIG_SYS_I2C_SPEED) + return -1; + + return 0; +} +#endif + +/** + * get_alen() - Small parser helper function to get address length + * + * Returns the address length. + */ +static uint get_alen(char *arg, int default_len) +{ + int j; + int alen; + + alen = default_len; + for (j = 0; j < 8; j++) { + if (arg[j] == '.') { + alen = arg[j+1] - '0'; + break; + } else if (arg[j] == '\0') + break; + } + return alen; +} + +enum i2c_err_op { + I2C_ERR_READ, + I2C_ERR_WRITE, +}; + +static int i2c_report_err(int ret, enum i2c_err_op op) +{ + printf("Error %s the chip: %d\n", + op == I2C_ERR_READ ? "reading" : "writing", ret); + + return CMD_RET_FAILURE; +} + +/** + * do_i2c_read() - Handle the "i2c read" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c read {i2c_chip} {devaddr}{.0, .1, .2} {len} {memaddr} + */ +static int do_i2c_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + uint devaddr, length; + int alen; + u_char *memaddr; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if (argc != 5) + return CMD_RET_USAGE; + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + devaddr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[3], NULL, 16); + + /* + * memaddr is the address where to store things in memory + */ + memaddr = (u_char *)simple_strtoul(argv[4], NULL, 16); + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (!ret) + ret = dm_i2c_read(dev, devaddr, memaddr, length); +#else + ret = i2c_read(chip, devaddr, alen, memaddr, length); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + return 0; +} + +static int do_i2c_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + uint devaddr, length; + int alen; + u_char *memaddr; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; + struct dm_i2c_chip *i2c_chip; +#endif + + if ((argc < 5) || (argc > 6)) + return cmd_usage(cmdtp); + + /* + * memaddr is the address where to store things in memory + */ + memaddr = (u_char *)simple_strtoul(argv[1], NULL, 16); + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[2], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + devaddr = simple_strtoul(argv[3], NULL, 16); + alen = get_alen(argv[3], DEFAULT_ADDR_LEN); + if (alen > 3) + return cmd_usage(cmdtp); + + /* + * Length is the number of bytes. + */ + length = simple_strtoul(argv[4], NULL, 16); + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); + i2c_chip = dev_get_parent_plat(dev); + if (!i2c_chip) + return i2c_report_err(ret, I2C_ERR_WRITE); +#endif + + if (argc == 6 && !strcmp(argv[5], "-s")) { + /* + * Write all bytes in a single I2C transaction. If the target + * device is an EEPROM, it is your responsibility to not cross + * a page boundary. No write delay upon completion, take this + * into account if linking commands. + */ +#if CONFIG_IS_ENABLED(DM_I2C) + i2c_chip->flags &= ~DM_I2C_CHIP_WR_ADDRESS; + ret = dm_i2c_write(dev, devaddr, memaddr, length); +#else + ret = i2c_write(chip, devaddr, alen, memaddr, length); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); + } else { + /* + * Repeated addressing - perform <length> separate + * write transactions of one byte each + */ + while (length-- > 0) { +#if CONFIG_IS_ENABLED(DM_I2C) + i2c_chip->flags |= DM_I2C_CHIP_WR_ADDRESS; + ret = dm_i2c_write(dev, devaddr++, memaddr++, 1); +#else + ret = i2c_write(chip, devaddr++, alen, memaddr++, 1); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); +/* + * No write delay with FRAM devices. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + udelay(11000); +#endif + } + } + return 0; +} + +#if CONFIG_IS_ENABLED(DM_I2C) +static int do_i2c_flags(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + uint flags; + int chip; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + chip = simple_strtoul(argv[1], NULL, 16); + ret = i2c_get_cur_bus_chip(chip, &dev); + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + if (argc > 2) { + flags = simple_strtoul(argv[2], NULL, 16); + ret = i2c_set_chip_flags(dev, flags); + } else { + ret = i2c_get_chip_flags(dev, &flags); + if (!ret) + printf("%x\n", flags); + } + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + return 0; +} + +static int do_i2c_olen(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + uint olen; + int chip; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + chip = simple_strtoul(argv[1], NULL, 16); + ret = i2c_get_cur_bus_chip(chip, &dev); + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + if (argc > 2) { + olen = simple_strtoul(argv[2], NULL, 16); + ret = i2c_set_chip_offset_len(dev, olen); + } else { + ret = i2c_get_chip_offset_len(dev); + if (ret >= 0) { + printf("%x\n", ret); + ret = 0; + } + } + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + return 0; +} +#endif + +/** + * do_i2c_md() - Handle the "i2c md" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c md {i2c_chip} {addr}{.0, .1, .2} {len} + */ +static int do_i2c_md(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + uint addr, length; + int alen; + int j, nbytes, linebytes; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + /* We use the last specified parameters, unless new ones are + * entered. + */ + chip = i2c_dp_last_chip; + addr = i2c_dp_last_addr; + alen = i2c_dp_last_alen; + length = i2c_dp_last_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. + */ + + /* + * I2C chip address + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * I2C data address within the chip. This can be 1 or + * 2 bytes long. Some day it might be 3 bytes long :-). + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 3) + length = simple_strtoul(argv[3], NULL, 16); + } + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); +#endif + + /* + * Print the lines. + * + * We buffer all read data, so we can make sure data is read only + * once. + */ + nbytes = length; + do { + unsigned char linebuf[DISP_LINE_LEN]; + unsigned char *cp; + + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_read(dev, addr, linebuf, linebytes); +#else + ret = i2c_read(chip, addr, alen, linebuf, linebytes); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + else { + printf("%04x:", addr); + cp = linebuf; + for (j=0; j<linebytes; j++) { + printf(" %02x", *cp++); + addr++; + } + puts (" "); + cp = linebuf; + for (j=0; j<linebytes; j++) { + if ((*cp < 0x20) || (*cp > 0x7e)) + puts ("."); + else + printf("%c", *cp); + cp++; + } + putc ('\n'); + } + nbytes -= linebytes; + } while (nbytes > 0); + + i2c_dp_last_chip = chip; + i2c_dp_last_addr = addr; + i2c_dp_last_alen = alen; + i2c_dp_last_length = length; + + return 0; +} + +/** + * do_i2c_mw() - Handle the "i2c mw" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c mw {i2c_chip} {addr}{.0, .1, .2} {data} [{count}] + */ +static int do_i2c_mw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + ulong addr; + int alen; + uchar byte; + int count; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if ((argc < 4) || (argc > 5)) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); +#endif + /* + * Value to write is always specified. + */ + byte = simple_strtoul(argv[3], NULL, 16); + + /* + * Optional count + */ + if (argc == 5) + count = simple_strtoul(argv[4], NULL, 16); + else + count = 1; + + while (count-- > 0) { +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_write(dev, addr++, &byte, 1); +#else + ret = i2c_write(chip, addr++, alen, &byte, 1); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); + /* + * Wait for the write to complete. The write can take + * up to 10mSec (we allow a little more time). + */ +/* + * No write delay with FRAM devices. + */ +#if !defined(CONFIG_SYS_I2C_FRAM) + udelay(11000); +#endif + } + + return 0; +} + +/** + * do_i2c_crc() - Handle the "i2c crc32" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Calculate a CRC on memory + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c crc32 {i2c_chip} {addr}{.0, .1, .2} {count} + */ +static int do_i2c_crc(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + ulong addr; + int alen; + int count; + uchar byte; + ulong crc; + ulong err; + int ret = 0; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); +#endif + /* + * Count is always specified + */ + count = simple_strtoul(argv[3], NULL, 16); + + printf ("CRC32 for %08lx ... %08lx ==> ", addr, addr + count - 1); + /* + * CRC a byte at a time. This is going to be slooow, but hey, the + * memories are small and slow too so hopefully nobody notices. + */ + crc = 0; + err = 0; + while (count-- > 0) { +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_read(dev, addr, &byte, 1); +#else + ret = i2c_read(chip, addr, alen, &byte, 1); +#endif + if (ret) + err++; + crc = crc32(crc, &byte, 1); + addr++; + } + if (err > 0) + i2c_report_err(ret, I2C_ERR_READ); + else + printf ("%08lx\n", crc); + + return 0; +} + +/** + * mod_i2c_mem() - Handle the "i2c mm" and "i2c nm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Modify memory. + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c mm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2} + * i2c nm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2} + */ +static int mod_i2c_mem(struct cmd_tbl *cmdtp, int incrflag, int flag, int argc, + char *const argv[]) +{ + uint chip; + ulong addr; + int alen; + ulong data; + int size = 1; + int nbytes; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if (argc != 3) + return CMD_RET_USAGE; + + bootretry_reset_cmd_timeout(); /* got a good command to get here */ + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + chip = i2c_mm_last_chip; + addr = i2c_mm_last_addr; + alen = i2c_mm_last_alen; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. Check for a size specification. + * Defaults to byte if no or incorrect specification. + */ + size = cmd_get_data_size(argv[0], 1); + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; + } + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); +#endif + + /* + * Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + printf("%08lx:", addr); +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_read(dev, addr, (uchar *)&data, size); +#else + ret = i2c_read(chip, addr, alen, (uchar *)&data, size); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + data = cpu_to_be32(data); + if (size == 1) + printf(" %02lx", (data >> 24) & 0x000000FF); + else if (size == 2) + printf(" %04lx", (data >> 16) & 0x0000FFFF); + else + printf(" %08lx", data); + + nbytes = cli_readline(" ? "); + if (nbytes == 0) { + /* + * <CR> pressed as only input, don't modify current + * location and move to next. + */ + if (incrflag) + addr += size; + nbytes = size; + /* good enough to not time out */ + bootretry_reset_cmd_timeout(); + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) + break; /* timed out, exit the command */ +#endif + else { + char *endp; + + data = simple_strtoul(console_buffer, &endp, 16); + if (size == 1) + data = data << 24; + else if (size == 2) + data = data << 16; + data = be32_to_cpu(data); + nbytes = endp - console_buffer; + if (nbytes) { + /* + * good enough to not time out + */ + bootretry_reset_cmd_timeout(); +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_write(dev, addr, (uchar *)&data, + size); +#else + ret = i2c_write(chip, addr, alen, + (uchar *)&data, size); +#endif + if (ret) + return i2c_report_err(ret, + I2C_ERR_WRITE); +#ifdef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + if (incrflag) + addr += size; + } + } + } while (nbytes); + + i2c_mm_last_chip = chip; + i2c_mm_last_addr = addr; + i2c_mm_last_alen = alen; + + return 0; +} + +/** + * do_i2c_probe() - Handle the "i2c probe" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c probe {addr} + * + * Returns zero (success) if one or more I2C devices was found + */ +static int do_i2c_probe(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int j; + int addr = -1; + int found = 0; +#if defined(CONFIG_SYS_I2C_NOPROBES) + int k, skip; + unsigned int bus = GET_BUS_NUM; +#endif /* NOPROBES */ + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus, *dev; + + if (i2c_get_cur_bus(&bus)) + return CMD_RET_FAILURE; +#endif + + if (argc == 2) + addr = simple_strtol(argv[1], 0, 16); + + puts ("Valid chip addresses:"); + for (j = 0; j < 128; j++) { + if ((0 <= addr) && (j != addr)) + continue; + +#if defined(CONFIG_SYS_I2C_NOPROBES) + skip = 0; + for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) { + if (COMPARE_BUS(bus, k) && COMPARE_ADDR(j, k)) { + skip = 1; + break; + } + } + if (skip) + continue; +#endif +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_probe(bus, j, 0, &dev); +#else + ret = i2c_probe(j); +#endif + if (ret == 0) { + printf(" %02X", j); + found++; + } + } + putc ('\n'); + +#if defined(CONFIG_SYS_I2C_NOPROBES) + puts ("Excluded chip addresses:"); + for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) { + if (COMPARE_BUS(bus,k)) + printf(" %02X", NO_PROBE_ADDR(k)); + } + putc ('\n'); +#endif + + return (0 == found); +} + +/** + * do_i2c_loop() - Handle the "i2c loop" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * + * Syntax: + * i2c loop {i2c_chip} {addr}{.0, .1, .2} [{length}] [{delay}] + * {length} - Number of bytes to read + * {delay} - A DECIMAL number and defaults to 1000 uSec + */ +static int do_i2c_loop(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint chip; + int alen; + uint addr; + uint length; + u_char bytes[16]; + int delay; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if (argc < 3) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul(argv[1], NULL, 16); + + /* + * Address is always specified. + */ + addr = simple_strtoul(argv[2], NULL, 16); + alen = get_alen(argv[2], DEFAULT_ADDR_LEN); + if (alen > 3) + return CMD_RET_USAGE; +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret && alen != -1) + ret = i2c_set_chip_offset_len(dev, alen); + if (ret) + return i2c_report_err(ret, I2C_ERR_WRITE); +#endif + + /* + * Length is the number of objects, not number of bytes. + */ + length = 1; + length = simple_strtoul(argv[3], NULL, 16); + if (length > sizeof(bytes)) + length = sizeof(bytes); + + /* + * The delay time (uSec) is optional. + */ + delay = 1000; + if (argc > 3) + delay = simple_strtoul(argv[4], NULL, 10); + /* + * Run the loop... + */ + while (1) { +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_read(dev, addr, bytes, length); +#else + ret = i2c_read(chip, addr, alen, bytes, length); +#endif + if (ret) + i2c_report_err(ret, I2C_ERR_READ); + udelay(delay); + } + + /* NOTREACHED */ + return 0; +} + +/* + * The SDRAM command is separately configured because many + * (most?) embedded boards don't use SDRAM DIMMs. + * + * FIXME: Document and probably move elsewhere! + */ +#if defined(CONFIG_CMD_SDRAM) +static void print_ddr2_tcyc (u_char const b) +{ + printf ("%d.", (b >> 4) & 0x0F); + switch (b & 0x0F) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + printf ("%d ns\n", b & 0x0F); + break; + case 0xA: + puts ("25 ns\n"); + break; + case 0xB: + puts ("33 ns\n"); + break; + case 0xC: + puts ("66 ns\n"); + break; + case 0xD: + puts ("75 ns\n"); + break; + default: + puts ("?? ns\n"); + break; + } +} + +static void decode_bits (u_char const b, char const *str[], int const do_once) +{ + u_char mask; + + for (mask = 0x80; mask != 0x00; mask >>= 1, ++str) { + if (b & mask) { + puts (*str); + if (do_once) + return; + } + } +} + +/* + * Syntax: + * i2c sdram {i2c_chip} + */ +static int do_sdram(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum { unknown, EDO, SDRAM, DDR, DDR2, DDR3, DDR4 } type; + + uint chip; + u_char data[128]; + u_char cksum; + int j, ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + static const char *decode_CAS_DDR2[] = { + " TBD", " 6", " 5", " 4", " 3", " 2", " TBD", " TBD" + }; + + static const char *decode_CAS_default[] = { + " TBD", " 7", " 6", " 5", " 4", " 3", " 2", " 1" + }; + + static const char *decode_CS_WE_default[] = { + " TBD", " 6", " 5", " 4", " 3", " 2", " 1", " 0" + }; + + static const char *decode_byte21_default[] = { + " TBD (bit 7)\n", + " Redundant row address\n", + " Differential clock input\n", + " Registerd DQMB inputs\n", + " Buffered DQMB inputs\n", + " On-card PLL\n", + " Registered address/control lines\n", + " Buffered address/control lines\n" + }; + + static const char *decode_byte22_DDR2[] = { + " TBD (bit 7)\n", + " TBD (bit 6)\n", + " TBD (bit 5)\n", + " TBD (bit 4)\n", + " TBD (bit 3)\n", + " Supports partial array self refresh\n", + " Supports 50 ohm ODT\n", + " Supports weak driver\n" + }; + + static const char *decode_row_density_DDR2[] = { + "512 MiB", "256 MiB", "128 MiB", "16 GiB", + "8 GiB", "4 GiB", "2 GiB", "1 GiB" + }; + + static const char *decode_row_density_default[] = { + "512 MiB", "256 MiB", "128 MiB", "64 MiB", + "32 MiB", "16 MiB", "8 MiB", "4 MiB" + }; + + if (argc < 2) + return CMD_RET_USAGE; + + /* + * Chip is always specified. + */ + chip = simple_strtoul (argv[1], NULL, 16); + +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret) + ret = dm_i2c_read(dev, 0, data, sizeof(data)); +#else + ret = i2c_read(chip, 0, 1, data, sizeof(data)); +#endif + if (ret) { + puts ("No SDRAM Serial Presence Detect found.\n"); + return 1; + } + + cksum = 0; + for (j = 0; j < 63; j++) { + cksum += data[j]; + } + if (cksum != data[63]) { + printf ("WARNING: Configuration data checksum failure:\n" + " is 0x%02x, calculated 0x%02x\n", data[63], cksum); + } + printf ("SPD data revision %d.%d\n", + (data[62] >> 4) & 0x0F, data[62] & 0x0F); + printf ("Bytes used 0x%02X\n", data[0]); + printf ("Serial memory size 0x%02X\n", 1 << data[1]); + + puts ("Memory type "); + switch (data[2]) { + case 2: + type = EDO; + puts ("EDO\n"); + break; + case 4: + type = SDRAM; + puts ("SDRAM\n"); + break; + case 7: + type = DDR; + puts("DDR\n"); + break; + case 8: + type = DDR2; + puts ("DDR2\n"); + break; + case 11: + type = DDR3; + puts("DDR3\n"); + break; + case 12: + type = DDR4; + puts("DDR4\n"); + break; + default: + type = unknown; + puts ("unknown\n"); + break; + } + + puts ("Row address bits "); + if ((data[3] & 0x00F0) == 0) + printf ("%d\n", data[3] & 0x0F); + else + printf ("%d/%d\n", data[3] & 0x0F, (data[3] >> 4) & 0x0F); + + puts ("Column address bits "); + if ((data[4] & 0x00F0) == 0) + printf ("%d\n", data[4] & 0x0F); + else + printf ("%d/%d\n", data[4] & 0x0F, (data[4] >> 4) & 0x0F); + + switch (type) { + case DDR2: + printf ("Number of ranks %d\n", + (data[5] & 0x07) + 1); + break; + default: + printf ("Module rows %d\n", data[5]); + break; + } + + switch (type) { + case DDR2: + printf ("Module data width %d bits\n", data[6]); + break; + default: + printf ("Module data width %d bits\n", + (data[7] << 8) | data[6]); + break; + } + + puts ("Interface signal levels "); + switch(data[8]) { + case 0: puts ("TTL 5.0 V\n"); break; + case 1: puts ("LVTTL\n"); break; + case 2: puts ("HSTL 1.5 V\n"); break; + case 3: puts ("SSTL 3.3 V\n"); break; + case 4: puts ("SSTL 2.5 V\n"); break; + case 5: puts ("SSTL 1.8 V\n"); break; + default: puts ("unknown\n"); break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time "); + print_ddr2_tcyc (data[9]); + break; + default: + printf ("SDRAM cycle time %d.%d ns\n", + (data[9] >> 4) & 0x0F, data[9] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access time 0.%d%d ns\n", + (data[10] >> 4) & 0x0F, data[10] & 0x0F); + break; + default: + printf ("SDRAM access time %d.%d ns\n", + (data[10] >> 4) & 0x0F, data[10] & 0x0F); + break; + } + + puts ("EDC configuration "); + switch (data[11]) { + case 0: puts ("None\n"); break; + case 1: puts ("Parity\n"); break; + case 2: puts ("ECC\n"); break; + default: puts ("unknown\n"); break; + } + + if ((data[12] & 0x80) == 0) + puts ("No self refresh, rate "); + else + puts ("Self refresh, rate "); + + switch(data[12] & 0x7F) { + case 0: puts ("15.625 us\n"); break; + case 1: puts ("3.9 us\n"); break; + case 2: puts ("7.8 us\n"); break; + case 3: puts ("31.3 us\n"); break; + case 4: puts ("62.5 us\n"); break; + case 5: puts ("125 us\n"); break; + default: puts ("unknown\n"); break; + } + + switch (type) { + case DDR2: + printf ("SDRAM width (primary) %d\n", data[13]); + break; + default: + printf ("SDRAM width (primary) %d\n", data[13] & 0x7F); + if ((data[13] & 0x80) != 0) { + printf (" (second bank) %d\n", + 2 * (data[13] & 0x7F)); + } + break; + } + + switch (type) { + case DDR2: + if (data[14] != 0) + printf ("EDC width %d\n", data[14]); + break; + default: + if (data[14] != 0) { + printf ("EDC width %d\n", + data[14] & 0x7F); + + if ((data[14] & 0x80) != 0) { + printf (" (second bank) %d\n", + 2 * (data[14] & 0x7F)); + } + } + break; + } + + if (DDR2 != type) { + printf ("Min clock delay, back-to-back random column addresses " + "%d\n", data[15]); + } + + puts ("Burst length(s) "); + if (data[16] & 0x80) puts (" Page"); + if (data[16] & 0x08) puts (" 8"); + if (data[16] & 0x04) puts (" 4"); + if (data[16] & 0x02) puts (" 2"); + if (data[16] & 0x01) puts (" 1"); + putc ('\n'); + printf ("Number of banks %d\n", data[17]); + + switch (type) { + case DDR2: + puts ("CAS latency(s) "); + decode_bits (data[18], decode_CAS_DDR2, 0); + putc ('\n'); + break; + default: + puts ("CAS latency(s) "); + decode_bits (data[18], decode_CAS_default, 0); + putc ('\n'); + break; + } + + if (DDR2 != type) { + puts ("CS latency(s) "); + decode_bits (data[19], decode_CS_WE_default, 0); + putc ('\n'); + } + + if (DDR2 != type) { + puts ("WE latency(s) "); + decode_bits (data[20], decode_CS_WE_default, 0); + putc ('\n'); + } + + switch (type) { + case DDR2: + puts ("Module attributes:\n"); + if (data[21] & 0x80) + puts (" TBD (bit 7)\n"); + if (data[21] & 0x40) + puts (" Analysis probe installed\n"); + if (data[21] & 0x20) + puts (" TBD (bit 5)\n"); + if (data[21] & 0x10) + puts (" FET switch external enable\n"); + printf (" %d PLLs on DIMM\n", (data[21] >> 2) & 0x03); + if (data[20] & 0x11) { + printf (" %d active registers on DIMM\n", + (data[21] & 0x03) + 1); + } + break; + default: + puts ("Module attributes:\n"); + if (!data[21]) + puts (" (none)\n"); + else + decode_bits (data[21], decode_byte21_default, 0); + break; + } + + switch (type) { + case DDR2: + decode_bits (data[22], decode_byte22_DDR2, 0); + break; + default: + puts ("Device attributes:\n"); + if (data[22] & 0x80) puts (" TBD (bit 7)\n"); + if (data[22] & 0x40) puts (" TBD (bit 6)\n"); + if (data[22] & 0x20) puts (" Upper Vcc tolerance 5%\n"); + else puts (" Upper Vcc tolerance 10%\n"); + if (data[22] & 0x10) puts (" Lower Vcc tolerance 5%\n"); + else puts (" Lower Vcc tolerance 10%\n"); + if (data[22] & 0x08) puts (" Supports write1/read burst\n"); + if (data[22] & 0x04) puts (" Supports precharge all\n"); + if (data[22] & 0x02) puts (" Supports auto precharge\n"); + if (data[22] & 0x01) puts (" Supports early RAS# precharge\n"); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time (2nd highest CAS latency) "); + print_ddr2_tcyc (data[23]); + break; + default: + printf ("SDRAM cycle time (2nd highest CAS latency) %d." + "%d ns\n", (data[23] >> 4) & 0x0F, data[23] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access from clock (2nd highest CAS latency) 0." + "%d%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F); + break; + default: + printf ("SDRAM access from clock (2nd highest CAS latency) %d." + "%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM cycle time (3rd highest CAS latency) "); + print_ddr2_tcyc (data[25]); + break; + default: + printf ("SDRAM cycle time (3rd highest CAS latency) %d." + "%d ns\n", (data[25] >> 4) & 0x0F, data[25] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("SDRAM access from clock (3rd highest CAS latency) 0." + "%d%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F); + break; + default: + printf ("SDRAM access from clock (3rd highest CAS latency) %d." + "%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Minimum row precharge %d.%02d ns\n", + (data[27] >> 2) & 0x3F, 25 * (data[27] & 0x03)); + break; + default: + printf ("Minimum row precharge %d ns\n", data[27]); + break; + } + + switch (type) { + case DDR2: + printf ("Row active to row active min %d.%02d ns\n", + (data[28] >> 2) & 0x3F, 25 * (data[28] & 0x03)); + break; + default: + printf ("Row active to row active min %d ns\n", data[28]); + break; + } + + switch (type) { + case DDR2: + printf ("RAS to CAS delay min %d.%02d ns\n", + (data[29] >> 2) & 0x3F, 25 * (data[29] & 0x03)); + break; + default: + printf ("RAS to CAS delay min %d ns\n", data[29]); + break; + } + + printf ("Minimum RAS pulse width %d ns\n", data[30]); + + switch (type) { + case DDR2: + puts ("Density of each row "); + decode_bits (data[31], decode_row_density_DDR2, 1); + putc ('\n'); + break; + default: + puts ("Density of each row "); + decode_bits (data[31], decode_row_density_default, 1); + putc ('\n'); + break; + } + + switch (type) { + case DDR2: + puts ("Command and Address setup "); + if (data[32] >= 0xA0) { + printf ("1.%d%d ns\n", + ((data[32] >> 4) & 0x0F) - 10, data[32] & 0x0F); + } else { + printf ("0.%d%d ns\n", + ((data[32] >> 4) & 0x0F), data[32] & 0x0F); + } + break; + default: + printf ("Command and Address setup %c%d.%d ns\n", + (data[32] & 0x80) ? '-' : '+', + (data[32] >> 4) & 0x07, data[32] & 0x0F); + break; + } + + switch (type) { + case DDR2: + puts ("Command and Address hold "); + if (data[33] >= 0xA0) { + printf ("1.%d%d ns\n", + ((data[33] >> 4) & 0x0F) - 10, data[33] & 0x0F); + } else { + printf ("0.%d%d ns\n", + ((data[33] >> 4) & 0x0F), data[33] & 0x0F); + } + break; + default: + printf ("Command and Address hold %c%d.%d ns\n", + (data[33] & 0x80) ? '-' : '+', + (data[33] >> 4) & 0x07, data[33] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Data signal input setup 0.%d%d ns\n", + (data[34] >> 4) & 0x0F, data[34] & 0x0F); + break; + default: + printf ("Data signal input setup %c%d.%d ns\n", + (data[34] & 0x80) ? '-' : '+', + (data[34] >> 4) & 0x07, data[34] & 0x0F); + break; + } + + switch (type) { + case DDR2: + printf ("Data signal input hold 0.%d%d ns\n", + (data[35] >> 4) & 0x0F, data[35] & 0x0F); + break; + default: + printf ("Data signal input hold %c%d.%d ns\n", + (data[35] & 0x80) ? '-' : '+', + (data[35] >> 4) & 0x07, data[35] & 0x0F); + break; + } + + puts ("Manufacturer's JEDEC ID "); + for (j = 64; j <= 71; j++) + printf ("%02X ", data[j]); + putc ('\n'); + printf ("Manufacturing Location %02X\n", data[72]); + puts ("Manufacturer's Part Number "); + for (j = 73; j <= 90; j++) + printf ("%02X ", data[j]); + putc ('\n'); + printf ("Revision Code %02X %02X\n", data[91], data[92]); + printf ("Manufacturing Date %02X %02X\n", data[93], data[94]); + puts ("Assembly Serial Number "); + for (j = 95; j <= 98; j++) + printf ("%02X ", data[j]); + putc ('\n'); + + if (DDR2 != type) { + printf ("Speed rating PC%d\n", + data[126] == 0x66 ? 66 : data[126]); + } + return 0; +} +#endif + +/* + * Syntax: + * i2c edid {i2c_chip} + */ +#if defined(CONFIG_I2C_EDID) +int do_edid(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + uint chip; + struct edid1_info edid; + int ret; +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *dev; +#endif + + if (argc < 2) { + cmd_usage(cmdtp); + return 1; + } + + chip = simple_strtoul(argv[1], NULL, 16); +#if CONFIG_IS_ENABLED(DM_I2C) + ret = i2c_get_cur_bus_chip(chip, &dev); + if (!ret) + ret = dm_i2c_read(dev, 0, (uchar *)&edid, sizeof(edid)); +#else + ret = i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid)); +#endif + if (ret) + return i2c_report_err(ret, I2C_ERR_READ); + + if (edid_check_info(&edid)) { + puts("Content isn't valid EDID.\n"); + return 1; + } + + edid_print_info(&edid); + return 0; + +} +#endif /* CONFIG_I2C_EDID */ + +#if CONFIG_IS_ENABLED(DM_I2C) +static void show_bus(struct udevice *bus) +{ + struct udevice *dev; + + printf("Bus %d:\t%s", dev_seq(bus), bus->name); + if (device_active(bus)) + printf(" (active %d)", dev_seq(bus)); + printf("\n"); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + struct dm_i2c_chip *chip = dev_get_parent_plat(dev); + + printf(" %02x: %s, offset len %x, flags %x\n", + chip->chip_addr, dev->name, chip->offset_len, + chip->flags); + } +} +#endif + +/** + * do_i2c_show_bus() - Handle the "i2c bus" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero always. + */ +#if defined(CONFIG_SYS_I2C) || CONFIG_IS_ENABLED(DM_I2C) +static int do_i2c_show_bus(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc == 1) { + /* show all busses */ +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_I2C, &uc); + if (ret) + return CMD_RET_FAILURE; + uclass_foreach_dev(bus, uc) + show_bus(bus); +#else + int i; + + for (i = 0; i < CONFIG_SYS_NUM_I2C_BUSES; i++) { + printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name); +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + int j; + + for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) { + if (i2c_bus[i].next_hop[j].chip == 0) + break; + printf("->%s@0x%2x:%d", + i2c_bus[i].next_hop[j].mux.name, + i2c_bus[i].next_hop[j].chip, + i2c_bus[i].next_hop[j].channel); + } +#endif + printf("\n"); + } +#endif + } else { + int i; + + /* show specific bus */ + i = simple_strtoul(argv[1], NULL, 10); +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, i, &bus); + if (ret) { + printf("Invalid bus %d: err=%d\n", i, ret); + return CMD_RET_FAILURE; + } + show_bus(bus); +#else + if (i >= CONFIG_SYS_NUM_I2C_BUSES) { + printf("Invalid bus %d\n", i); + return -1; + } + printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name); +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + int j; + for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) { + if (i2c_bus[i].next_hop[j].chip == 0) + break; + printf("->%s@0x%2x:%d", + i2c_bus[i].next_hop[j].mux.name, + i2c_bus[i].next_hop[j].chip, + i2c_bus[i].next_hop[j].channel); + } +#endif + printf("\n"); +#endif + } + + return 0; +} +#endif + +/** + * do_i2c_bus_num() - Handle the "i2c dev" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +#if defined(CONFIG_SYS_I2C) || defined(CONFIG_I2C_MULTI_BUS) || \ + CONFIG_IS_ENABLED(DM_I2C) +static int do_i2c_bus_num(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0; + int bus_no; + + if (argc == 1) { + /* querying current setting */ +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus; + + if (!i2c_get_cur_bus(&bus)) + bus_no = dev_seq(bus); + else + bus_no = -1; +#else + bus_no = i2c_get_bus_num(); +#endif + printf("Current bus is %d\n", bus_no); + } else { + bus_no = simple_strtoul(argv[1], NULL, 10); +#if defined(CONFIG_SYS_I2C) + if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) { + printf("Invalid bus %d\n", bus_no); + return -1; + } +#endif + printf("Setting bus to %d\n", bus_no); +#if CONFIG_IS_ENABLED(DM_I2C) + ret = cmd_i2c_set_bus_num(bus_no); +#else + ret = i2c_set_bus_num(bus_no); +#endif + if (ret) + printf("Failure changing bus number (%d)\n", ret); + } + + return ret ? CMD_RET_FAILURE : 0; +} +#endif /* defined(CONFIG_SYS_I2C) */ + +/** + * do_i2c_bus_speed() - Handle the "i2c speed" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_i2c_bus_speed(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int speed, ret=0; + +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus; + + if (i2c_get_cur_bus(&bus)) + return 1; +#endif + if (argc == 1) { +#if CONFIG_IS_ENABLED(DM_I2C) + speed = dm_i2c_get_bus_speed(bus); +#else + speed = i2c_get_bus_speed(); +#endif + /* querying current speed */ + printf("Current bus speed=%d\n", speed); + } else { + speed = simple_strtoul(argv[1], NULL, 10); + printf("Setting bus speed to %d Hz\n", speed); +#if CONFIG_IS_ENABLED(DM_I2C) + ret = dm_i2c_set_bus_speed(bus, speed); +#else + ret = i2c_set_bus_speed(speed); +#endif + if (ret) + printf("Failure changing bus speed (%d)\n", ret); + } + + return ret ? CMD_RET_FAILURE : 0; +} + +/** + * do_i2c_mm() - Handle the "i2c mm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_i2c_mm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return mod_i2c_mem (cmdtp, 1, flag, argc, argv); +} + +/** + * do_i2c_nm() - Handle the "i2c nm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_i2c_nm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return mod_i2c_mem (cmdtp, 0, flag, argc, argv); +} + +/** + * do_i2c_reset() - Handle the "i2c reset" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero always. + */ +static int do_i2c_reset(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#if CONFIG_IS_ENABLED(DM_I2C) + struct udevice *bus; + + if (i2c_get_cur_bus(&bus)) + return CMD_RET_FAILURE; + if (i2c_deblock(bus)) { + printf("Error: Not supported by the driver\n"); + return CMD_RET_FAILURE; + } +#elif defined(CONFIG_SYS_I2C) + i2c_init(I2C_ADAP->speed, I2C_ADAP->slaveaddr); +#else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + return 0; +} + +static struct cmd_tbl cmd_i2c_sub[] = { +#if defined(CONFIG_SYS_I2C) || CONFIG_IS_ENABLED(DM_I2C) + U_BOOT_CMD_MKENT(bus, 1, 1, do_i2c_show_bus, "", ""), +#endif + U_BOOT_CMD_MKENT(crc32, 3, 1, do_i2c_crc, "", ""), +#if defined(CONFIG_SYS_I2C) || \ + defined(CONFIG_I2C_MULTI_BUS) || CONFIG_IS_ENABLED(DM_I2C) + U_BOOT_CMD_MKENT(dev, 1, 1, do_i2c_bus_num, "", ""), +#endif /* CONFIG_I2C_MULTI_BUS */ +#if defined(CONFIG_I2C_EDID) + U_BOOT_CMD_MKENT(edid, 1, 1, do_edid, "", ""), +#endif /* CONFIG_I2C_EDID */ + U_BOOT_CMD_MKENT(loop, 3, 1, do_i2c_loop, "", ""), + U_BOOT_CMD_MKENT(md, 3, 1, do_i2c_md, "", ""), + U_BOOT_CMD_MKENT(mm, 2, 1, do_i2c_mm, "", ""), + U_BOOT_CMD_MKENT(mw, 3, 1, do_i2c_mw, "", ""), + U_BOOT_CMD_MKENT(nm, 2, 1, do_i2c_nm, "", ""), + U_BOOT_CMD_MKENT(probe, 0, 1, do_i2c_probe, "", ""), + U_BOOT_CMD_MKENT(read, 5, 1, do_i2c_read, "", ""), + U_BOOT_CMD_MKENT(write, 6, 0, do_i2c_write, "", ""), +#if CONFIG_IS_ENABLED(DM_I2C) + U_BOOT_CMD_MKENT(flags, 2, 1, do_i2c_flags, "", ""), + U_BOOT_CMD_MKENT(olen, 2, 1, do_i2c_olen, "", ""), +#endif + U_BOOT_CMD_MKENT(reset, 0, 1, do_i2c_reset, "", ""), +#if defined(CONFIG_CMD_SDRAM) + U_BOOT_CMD_MKENT(sdram, 1, 1, do_sdram, "", ""), +#endif + U_BOOT_CMD_MKENT(speed, 1, 1, do_i2c_bus_speed, "", ""), +}; + +static __maybe_unused void i2c_reloc(void) +{ + static int relocated; + + if (!relocated) { + fixup_cmdtable(cmd_i2c_sub, ARRAY_SIZE(cmd_i2c_sub)); + relocated = 1; + }; +} + +/** + * do_i2c() - Handle the "i2c" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int do_i2c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *c; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC + i2c_reloc(); +#endif + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'i2c' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_i2c_sub[0], ARRAY_SIZE(cmd_i2c_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +/***************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char i2c_help_text[] = +#if defined(CONFIG_SYS_I2C) || CONFIG_IS_ENABLED(DM_I2C) + "bus [muxtype:muxaddr:muxchannel] - show I2C bus info\n" + "i2c " /* That's the prefix for the crc32 command below. */ +#endif + "crc32 chip address[.0, .1, .2] count - compute CRC32 checksum\n" +#if defined(CONFIG_SYS_I2C) || \ + defined(CONFIG_I2C_MULTI_BUS) || CONFIG_IS_ENABLED(DM_I2C) + "i2c dev [dev] - show or set current I2C bus\n" +#endif /* CONFIG_I2C_MULTI_BUS */ +#if defined(CONFIG_I2C_EDID) + "i2c edid chip - print EDID configuration information\n" +#endif /* CONFIG_I2C_EDID */ + "i2c loop chip address[.0, .1, .2] [# of objects] - looping read of device\n" + "i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device\n" + "i2c mm chip address[.0, .1, .2] - write to I2C device (auto-incrementing)\n" + "i2c mw chip address[.0, .1, .2] value [count] - write to I2C device (fill)\n" + "i2c nm chip address[.0, .1, .2] - write to I2C device (constant address)\n" + "i2c probe [address] - test for and show device(s) on the I2C bus\n" + "i2c read chip address[.0, .1, .2] length memaddress - read to memory\n" + "i2c write memaddress chip address[.0, .1, .2] length [-s] - write memory\n" + " to I2C; the -s option selects bulk write in a single transaction\n" +#if CONFIG_IS_ENABLED(DM_I2C) + "i2c flags chip [flags] - set or get chip flags\n" + "i2c olen chip [offset_length] - set or get chip offset length\n" +#endif + "i2c reset - re-init the I2C Controller\n" +#if defined(CONFIG_CMD_SDRAM) + "i2c sdram chip - print SDRAM configuration information\n" +#endif + "i2c speed [speed] - show or set I2C bus speed"; +#endif + +U_BOOT_CMD( + i2c, 7, 1, do_i2c, + "I2C sub-system", + i2c_help_text +); diff --git a/roms/u-boot/cmd/ide.c b/roms/u-boot/cmd/ide.c new file mode 100644 index 000000000..b78c38e15 --- /dev/null +++ b/roms/u-boot/cmd/ide.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * IDE support + */ + +#include <common.h> +#include <blk.h> +#include <config.h> +#include <watchdog.h> +#include <command.h> +#include <image.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +#include <ide.h> +#include <ata.h> + +#ifdef CONFIG_LED_STATUS +# include <status_led.h> +#endif + +/* Current I/O Device */ +static int curr_device; + +int do_ide(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc == 2) { + if (strncmp(argv[1], "res", 3) == 0) { + puts("\nReset IDE: "); + ide_init(); + return 0; + } + } + + return blk_common_cmd(argc, argv, IF_TYPE_IDE, &curr_device); +} + +int do_diskboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return common_diskboot(cmdtp, "ide", argc, argv); +} + +U_BOOT_CMD(ide, 5, 1, do_ide, + "IDE sub-system", + "reset - reset IDE controller\n" + "ide info - show available IDE devices\n" + "ide device [dev] - show or set current device\n" + "ide part [dev] - print partition table of one or all IDE devices\n" + "ide read addr blk# cnt\n" + "ide write addr blk# cnt - read/write `cnt'" + " blocks starting at block `blk#'\n" + " to/from memory address `addr'"); + +U_BOOT_CMD(diskboot, 3, 1, do_diskboot, + "boot from IDE device", "loadAddr dev:part"); diff --git a/roms/u-boot/cmd/ini.c b/roms/u-boot/cmd/ini.c new file mode 100644 index 000000000..c075be623 --- /dev/null +++ b/roms/u-boot/cmd/ini.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * inih -- simple .INI file parser + * + * Copyright (c) 2009, Brush Technology + * Copyright (c) 2012: + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * All rights reserved. + * + * Go to the project home page for more info: + * http://code.google.com/p/inih/ + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <linux/ctype.h> +#include <linux/string.h> + +#ifdef CONFIG_INI_MAX_LINE +#define MAX_LINE CONFIG_INI_MAX_LINE +#else +#define MAX_LINE 200 +#endif + +#ifdef CONFIG_INI_MAX_SECTION +#define MAX_SECTION CONFIG_INI_MAX_SECTION +#else +#define MAX_SECTION 50 +#endif + +#ifdef CONFIG_INI_MAX_NAME +#define MAX_NAME CONFIG_INI_MAX_NAME +#else +#define MAX_NAME 50 +#endif + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char *rstrip(char *s) +{ + char *p = s + strlen(s); + + while (p > s && isspace(*--p)) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char *lskip(const char *s) +{ + while (*s && isspace(*s)) + s++; + return (char *)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char *find_char_or_comment(const char *s, char c) +{ + int was_whitespace = 0; + + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace(*s); + s++; + } + return (char *)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char *strncpy0(char *dest, const char *src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* Emulate the behavior of fgets but on memory */ +static char *memgets(char *str, int num, char **mem, size_t *memsize) +{ + char *end; + int len; + int newline = 1; + + end = memchr(*mem, '\n', *memsize); + if (end == NULL) { + if (*memsize == 0) + return NULL; + end = *mem + *memsize; + newline = 0; + } + len = min((end - *mem) + newline, num); + memcpy(str, *mem, len); + if (len < num) + str[len] = '\0'; + + /* prepare the mem vars for the next call */ + *memsize -= (end - *mem) + newline; + *mem += (end - *mem) + newline; + + return str; +} + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error). +*/ +static int ini_parse(char *filestart, size_t filelen, + int (*handler)(void *, char *, char *, char *), void *user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ + char line[MAX_LINE]; + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char *curmem = filestart; + char *start; + char *end; + char *name; + char *value; + size_t memleft = filelen; + int lineno = 0; + int error = 0; + + /* Scan through file line by line */ + while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) { + lineno++; + start = lskip(rstrip(line)); + + if (*start == ';' || *start == '#') { + /* + * Per Python ConfigParser, allow '#' comments at start + * of line + */ + } +#if CONFIG_INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* + * Non-blank line with leading whitespace, treat as + * continuation of previous name's value (as per Python + * ConfigParser). + */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') + end = find_char_or_comment(start, ':'); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + /* Strip double-quotes */ + if (value[0] == '"' && + value[strlen(value)-1] == '"') { + value[strlen(value)-1] = '\0'; + value += 1; + } + + /* + * Valid name[=:]value pair found, call handler + */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && + !error) + error = lineno; + } else if (!error) + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + + return error; +} + +static int ini_handler(void *user, char *section, char *name, char *value) +{ + char *requested_section = (char *)user; +#ifdef CONFIG_INI_CASE_INSENSITIVE + int i; + + for (i = 0; i < strlen(requested_section); i++) + requested_section[i] = tolower(requested_section[i]); + for (i = 0; i < strlen(section); i++) + section[i] = tolower(section[i]); +#endif + + if (!strcmp(section, requested_section)) { +#ifdef CONFIG_INI_CASE_INSENSITIVE + for (i = 0; i < strlen(name); i++) + name[i] = tolower(name[i]); + for (i = 0; i < strlen(value); i++) + value[i] = tolower(value[i]); +#endif + env_set(name, value); + printf("ini: Imported %s as %s\n", name, value); + } + + /* success */ + return 1; +} + +static int do_ini(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *section; + char *file_address; + size_t file_size; + + if (argc == 1) + return CMD_RET_USAGE; + + section = argv[1]; + file_address = (char *)simple_strtoul( + argc < 3 ? env_get("loadaddr") : argv[2], NULL, 16); + file_size = (size_t)simple_strtoul( + argc < 4 ? env_get("filesize") : argv[3], NULL, 16); + + return ini_parse(file_address, file_size, ini_handler, (void *)section); +} + +U_BOOT_CMD( + ini, 4, 0, do_ini, + "parse an ini file in memory and merge the specified section into the env", + "section [[file-address] file-size]" +); diff --git a/roms/u-boot/cmd/io.c b/roms/u-boot/cmd/io.c new file mode 100644 index 000000000..c7e964153 --- /dev/null +++ b/roms/u-boot/cmd/io.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012 The Chromium OS Authors. + */ + +/* + * IO space access commands. + */ + +#include <common.h> +#include <command.h> +#include <asm/io.h> + +/* Display values from last command */ +static ulong last_addr, last_size; +static ulong last_length = 0x40; +static ulong base_address; + +#define DISP_LINE_LEN 16 + +/* + * IO Display + * + * Syntax: + * iod{.b, .w, .l} {addr} + */ +int do_io_iod(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr, length, bytes; + u8 buf[DISP_LINE_LEN]; + int size, todo; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + addr = last_addr; + size = last_size; + length = last_length; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* + * New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + size = cmd_get_data_size(argv[0], 4); + if (size < 0) + return 1; + + /* Address is specified since argc > 1 */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + /* + * If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 2) + length = simple_strtoul(argv[2], NULL, 16); + } + + bytes = size * length; + + /* Print the lines */ + for (; bytes > 0; addr += todo) { + u8 *ptr = buf; + int i; + + todo = min(bytes, (ulong)DISP_LINE_LEN); + for (i = 0; i < todo; i += size, ptr += size) { + if (size == 4) + *(u32 *)ptr = inl(addr + i); + else if (size == 2) + *(u16 *)ptr = inw(addr + i); + else + *ptr = inb(addr + i); + } + print_buffer(addr, buf, size, todo / size, + DISP_LINE_LEN / size); + bytes -= todo; + } + + last_addr = addr; + last_length = length; + last_size = size; + + return 0; +} + +int do_io_iow(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr, val; + int size; + + if (argc != 3) + return CMD_RET_USAGE; + + size = cmd_get_data_size(argv[0], 4); + if (size < 0) + return 1; + + addr = simple_strtoul(argv[1], NULL, 16); + val = simple_strtoul(argv[2], NULL, 16); + + if (size == 4) + outl((u32) val, addr); + else if (size == 2) + outw((u16) val, addr); + else + outb((u8) val, addr); + + return 0; +} + +/**************************************************/ +U_BOOT_CMD(iod, 3, 1, do_io_iod, + "IO space display", "[.b, .w, .l] address"); + +U_BOOT_CMD(iow, 3, 0, do_io_iow, + "IO space modify", + "[.b, .w, .l] address value"); diff --git a/roms/u-boot/cmd/iotrace.c b/roms/u-boot/cmd/iotrace.c new file mode 100644 index 000000000..652ebefb8 --- /dev/null +++ b/roms/u-boot/cmd/iotrace.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc + */ + +#include <common.h> +#include <command.h> +#include <iotrace.h> + +static void do_print_stats(void) +{ + ulong start, size, needed_size, offset, count; + + printf("iotrace is %sabled\n", iotrace_get_enabled() ? "en" : "dis"); + iotrace_get_buffer(&start, &size, &needed_size, &offset, &count); + printf("Start: %08lx\n", start); + printf("Actual Size: %08lx\n", size); + printf("Needed Size: %08lx\n", needed_size); + iotrace_get_region(&start, &size); + printf("Region: %08lx\n", start); + printf("Size: %08lx\n", size); + printf("Offset: %08lx\n", offset); + printf("Output: %08lx\n", start + offset); + printf("Count: %08lx\n", count); + printf("CRC32: %08lx\n", (ulong)iotrace_get_checksum()); +} + +static void do_print_trace(void) +{ + ulong start, size, needed_size, offset, count; + + struct iotrace_record *cur_record; + + iotrace_get_buffer(&start, &size, &needed_size, &offset, &count); + + if (!start || !size || !count) + return; + + printf("Timestamp Value Address\n"); + + cur_record = (struct iotrace_record *)start; + for (int i = 0; i < count; i++) { + if (cur_record->flags & IOT_WRITE) + printf("%08llu: 0x%08lx --> 0x%08llx\n", + cur_record->timestamp, + cur_record->value, + (unsigned long long)cur_record->addr); + else + printf("%08llu: 0x%08lx <-- 0x%08llx\n", + cur_record->timestamp, + cur_record->value, + (unsigned long long)cur_record->addr); + + cur_record++; + } +} + +static int do_set_buffer(int argc, char *const argv[]) +{ + ulong addr = 0, size = 0; + + if (argc == 2) { + addr = simple_strtoul(*argv++, NULL, 16); + size = simple_strtoul(*argv++, NULL, 16); + } else if (argc != 0) { + return CMD_RET_USAGE; + } + + iotrace_set_buffer(addr, size); + + return 0; +} + +static int do_set_region(int argc, char *const argv[]) +{ + ulong addr = 0, size = 0; + + if (argc == 2) { + addr = simple_strtoul(*argv++, NULL, 16); + size = simple_strtoul(*argv++, NULL, 16); + } else if (argc != 0) { + return CMD_RET_USAGE; + } + + iotrace_set_region(addr, size); + + return 0; +} + +int do_iotrace(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *cmd = argc < 2 ? NULL : argv[1]; + + if (!cmd) + return cmd_usage(cmdtp); + switch (*cmd) { + case 'b': + return do_set_buffer(argc - 2, argv + 2); + case 'l': + return do_set_region(argc - 2, argv + 2); + case 'p': + iotrace_set_enabled(0); + break; + case 'r': + iotrace_set_enabled(1); + break; + case 's': + do_print_stats(); + break; + case 'd': + do_print_trace(); + break; + default: + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + iotrace, 4, 1, do_iotrace, + "iotrace utility commands", + "stats - display iotrace stats\n" + "iotrace buffer <address> <size> - set iotrace buffer\n" + "iotrace limit <address> <size> - set iotrace region limit\n" + "iotrace pause - pause tracing\n" + "iotrace resume - resume tracing\n" + "iotrace dump - dump iotrace buffer" +); diff --git a/roms/u-boot/cmd/irq.c b/roms/u-boot/cmd/irq.c new file mode 100644 index 000000000..1d3e28cb3 --- /dev/null +++ b/roms/u-boot/cmd/irq.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <irq_func.h> + +static int do_interrupts(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + + if (argc != 2) + return CMD_RET_USAGE; + + /* on */ + if (strncmp(argv[1], "on", 2) == 0) + enable_interrupts(); + else + disable_interrupts(); + + return 0; +} + +U_BOOT_CMD( + interrupts, 5, 0, do_interrupts, + "enable or disable interrupts", + "[on, off]" +); + +/* Implemented in $(CPU)/interrupts.c */ +int do_irqinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); + +U_BOOT_CMD( + irqinfo, 1, 1, do_irqinfo, + "print information about IRQs", + "" +); diff --git a/roms/u-boot/cmd/itest.c b/roms/u-boot/cmd/itest.c new file mode 100644 index 000000000..9a441ce9b --- /dev/null +++ b/roms/u-boot/cmd/itest.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2003 + * Tait Electronics Limited, Christchurch, New Zealand + */ + +/* + * This file provides a shell like 'test' function to return + * true/false from an integer or string compare of two memory + * locations or a location and a scalar/literal. + * A few parts were lifted from bash 'test' command + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <env.h> +#include <mapmem.h> + +#include <asm/io.h> + +#define EQ 0 +#define NE 1 +#define LT 2 +#define GT 3 +#define LE 4 +#define GE 5 + +struct op_tbl_s { + char *op; /* operator string */ + int opcode; /* internal representation of opcode */ +}; + +typedef struct op_tbl_s op_tbl_t; + +static const op_tbl_t op_table [] = { + { "-lt", LT }, + { "<" , LT }, + { "-gt", GT }, + { ">" , GT }, + { "-eq", EQ }, + { "==" , EQ }, + { "-ne", NE }, + { "!=" , NE }, + { "<>" , NE }, + { "-ge", GE }, + { ">=" , GE }, + { "-le", LE }, + { "<=" , LE }, +}; + +static long evalexp(char *s, int w) +{ + long l = 0; + unsigned long addr; + void *buf; + + /* if the parameter starts with a * then assume is a pointer to the value we want */ + if (s[0] == '*') { + addr = simple_strtoul(&s[1], NULL, 16); + buf = map_physmem(addr, w, MAP_WRBACK); + if (!buf && addr) { + puts("Failed to map physical memory\n"); + return 0; + } + switch (w) { + case 1: + l = (long)(*(u8 *)buf); + break; + case 2: + l = (long)(*(u16 *)buf); + break; + case 4: + l = (long)(*(u32 *)buf); + break; +#ifdef CONFIG_PHYS_64BIT + case 8: + l = (long)(*(unsigned long *)buf); + break; +#endif + } + unmap_physmem(buf, w); + return l; + } else { + l = simple_strtoul(s, NULL, 16); + } + + /* avoid overflow on mask calculus */ + return (w >= sizeof(long)) ? l : (l & ((1UL << (w * 8)) - 1)); +} + +static char * evalstr(char *s) +{ + /* if the parameter starts with a * then assume a string pointer else its a literal */ + if (s[0] == '*') { + return (char *)simple_strtoul(&s[1], NULL, 16); + } else if (s[0] == '$') { + int i = 2; + + if (s[1] != '{') + return NULL; + + while (s[i] != '}') { + if (s[i] == 0) + return NULL; + i++; + } + s[i] = 0; + return env_get((const char *)&s[2]); + } else { + return s; + } +} + +static int stringcomp(char *s, char *t, int op) +{ + int p; + char *l, *r; + + l = evalstr(s); + r = evalstr(t); + + p = strcmp(l, r); + switch (op) { + case EQ: return (p == 0); + case NE: return (p != 0); + case LT: return (p < 0); + case GT: return (p > 0); + case LE: return (p <= 0); + case GE: return (p >= 0); + } + return (0); +} + +static int arithcomp (char *s, char *t, int op, int w) +{ + long l, r; + + l = evalexp (s, w); + r = evalexp (t, w); + + switch (op) { + case EQ: return (l == r); + case NE: return (l != r); + case LT: return (l < r); + case GT: return (l > r); + case LE: return (l <= r); + case GE: return (l >= r); + } + return (0); +} + +static int binary_test(char *op, char *arg1, char *arg2, int w) +{ + int len, i; + const op_tbl_t *optp; + + len = strlen(op); + + for (optp = (op_tbl_t *)&op_table, i = 0; + i < ARRAY_SIZE(op_table); + optp++, i++) { + + if ((strncmp (op, optp->op, len) == 0) && (len == strlen (optp->op))) { + if (w == 0) { + return (stringcomp(arg1, arg2, optp->opcode)); + } else { + return (arithcomp (arg1, arg2, optp->opcode, w)); + } + } + } + + printf("Unknown operator '%s'\n", op); + return 0; /* op code not found */ +} + +/* command line interface to the shell test */ +static int do_itest(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int value, w; + + /* Validate arguments */ + if ((argc != 4)) + return CMD_RET_USAGE; + + /* Check for a data width specification. + * Defaults to long (4) if no specification. + * Uses -2 as 'width' for .s (string) so as not to upset existing code + */ + switch (w = cmd_get_data_size(argv[0], 4)) { + case 1: + case 2: + case 4: +#ifdef CONFIG_PHYS_64BIT + case 8: +#endif + value = binary_test (argv[2], argv[1], argv[3], w); + break; + case CMD_DATA_SIZE_STR: + value = binary_test (argv[2], argv[1], argv[3], 0); + break; + case CMD_DATA_SIZE_ERR: + default: + puts("Invalid data width specifier\n"); + value = 0; + break; + } + + return !value; +} + +U_BOOT_CMD( + itest, 4, 0, do_itest, + "return true/false on integer compare", +#ifdef CONFIG_PHYS_64BIT + "[.b, .w, .l, .q, .s] [*]value1 <op> [*]value2" +#else + "[.b, .w, .l, .s] [*]value1 <op> [*]value2" +#endif +); diff --git a/roms/u-boot/cmd/jffs2.c b/roms/u-boot/cmd/jffs2.c new file mode 100644 index 000000000..9540988e3 --- /dev/null +++ b/roms/u-boot/cmd/jffs2.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + * + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Added support for reading flash partition table from environment. + * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 + * kernel tree. + * + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ + * Copyright 2002 SYSGO Real-Time Solutions GmbH + */ + +/* + * Three environment variables are used by the parsing routines: + * + * 'partition' - keeps current partition identifier + * + * partition := <part-id> + * <part-id> := <dev-id>,part_num + * + * + * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping + * + * mtdids=<idmap>[,<idmap>,...] + * + * <idmap> := <dev-id>=<mtd-id> + * <dev-id> := 'nand'|'nor'|'onenand'<dev-num> + * <dev-num> := mtd device number, 0... + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * + * + * 'mtdparts' - partition list + * + * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...] + * + * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * <part-def> := <size>[@<offset>][<name>][<ro-flag>] + * <size> := standard linux memsize OR '-' to denote all remaining space + * <offset> := partition start offset within the device + * <name> := '(' NAME ')' + * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) + * + * Notes: + * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping + * - if the above variables are not set defaults for a given target are used + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * mtdids=nor0=edb7312-nor + * mtdparts=mtdparts=edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * mtdids=nor0=edb7312-nor,nand0=edb7312-nand + * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * + */ + +/* + * JFFS2/CRAMFS support + */ +#include <common.h> +#include <command.h> +#include <env.h> +#include <flash.h> +#include <image.h> +#include <malloc.h> +#include <jffs2/jffs2.h> +#include <linux/bug.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <cramfs/cramfs_fs.h> + +#if defined(CONFIG_CMD_NAND) +#include <linux/mtd/rawnand.h> +#include <nand.h> +#endif + +#if defined(CONFIG_CMD_ONENAND) +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <onenand_uboot.h> +#endif + +/* enable/disable debugging messages */ +#define DEBUG_JFFS +#undef DEBUG_JFFS + +#ifdef DEBUG_JFFS +# define DEBUGF(fmt, args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt, args...) +#endif + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING 0xFFFFFFFF + +/* special offset value, it is used when not provided by user + * + * this value is used temporarily during parsing, later such offests + * are recalculated */ +#define OFFSET_NOT_SPECIFIED 0xFFFFFFFF + +/* minimum partition size */ +#define MIN_PART_SIZE 4096 + +/* this flag needs to be set in part_info struct mask_flags + * field for read-only partitions */ +#define MTD_WRITEABLE_CMD 1 + +/* current active device and partition number */ +#ifdef CONFIG_CMD_MTDPARTS +/* Use the ones declared in cmd_mtdparts.c */ +extern struct mtd_device *current_mtd_dev; +extern u8 current_mtd_partnum; +#else +/* Use local ones */ +struct mtd_device *current_mtd_dev = NULL; +u8 current_mtd_partnum = 0; +#endif + +#if defined(CONFIG_CMD_CRAMFS) +extern int cramfs_check (struct part_info *info); +extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename); +extern int cramfs_ls (struct part_info *info, char *filename); +extern int cramfs_info (struct part_info *info); +#else +/* defining empty macros for function names is ugly but avoids ifdef clutter + * all over the code */ +#define cramfs_check(x) (0) +#define cramfs_load(x,y,z) (-1) +#define cramfs_ls(x,y) (0) +#define cramfs_info(x) (0) +#endif + +#ifndef CONFIG_CMD_MTDPARTS +/** + * Check device number to be within valid range for given device type. + * + * @param dev device to validate + * @return 0 if device is valid, 1 otherwise + */ +static int mtd_device_validate(u8 type, u8 num, u32 *size) +{ + if (type == MTD_DEV_TYPE_NOR) { +#if defined(CONFIG_CMD_FLASH) + if (num < CONFIG_SYS_MAX_FLASH_BANKS) { + extern flash_info_t flash_info[]; + *size = flash_info[num].size; + + return 0; + } + + printf("no such FLASH device: %s%d (valid range 0 ... %d\n", + MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1); +#else + printf("support for FLASH devices not present\n"); +#endif + } else if (type == MTD_DEV_TYPE_NAND) { +#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND) + struct mtd_info *mtd = get_nand_dev_by_index(num); + if (mtd) { + *size = mtd->size; + return 0; + } + + printf("no such NAND device: %s%d (valid range 0 ... %d)\n", + MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1); +#else + printf("support for NAND devices not present\n"); +#endif + } else if (type == MTD_DEV_TYPE_ONENAND) { +#if defined(CONFIG_CMD_ONENAND) + *size = onenand_mtd.size; + return 0; +#else + printf("support for OneNAND devices not present\n"); +#endif + } else + printf("Unknown defice type %d\n", type); + + return 1; +} + +/** + * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>, + * return device type and number. + * + * @param id string describing device id + * @param ret_id output pointer to next char after parse completes (output) + * @param dev_type parsed device type (output) + * @param dev_num parsed device number (output) + * @return 0 on success, 1 otherwise + */ +static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) +{ + const char *p = id; + + *dev_type = 0; + if (strncmp(p, "nand", 4) == 0) { + *dev_type = MTD_DEV_TYPE_NAND; + p += 4; + } else if (strncmp(p, "nor", 3) == 0) { + *dev_type = MTD_DEV_TYPE_NOR; + p += 3; + } else if (strncmp(p, "onenand", 7) == 0) { + *dev_type = MTD_DEV_TYPE_ONENAND; + p += 7; + } else { + printf("incorrect device type in %s\n", id); + return 1; + } + + if (!isdigit(*p)) { + printf("incorrect device number in %s\n", id); + return 1; + } + + *dev_num = simple_strtoul(p, (char **)&p, 0); + if (ret_id) + *ret_id = p; + return 0; +} + +/* + * 'Static' version of command line mtdparts_init() routine. Single partition on + * a single device configuration. + */ + +/** + * Calculate sector size. + * + * @return sector size + */ +static inline u32 get_part_sector_size_nand(struct mtdids *id) +{ +#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND) + struct mtd_info *mtd; + + mtd = get_nand_dev_by_index(id->num); + + return mtd->erasesize; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size_nor(struct mtdids *id, struct part_info *part) +{ +#if defined(CONFIG_CMD_FLASH) + extern flash_info_t flash_info[]; + + u32 end_phys, start_phys, sector_size = 0, size = 0; + int i; + flash_info_t *flash; + + flash = &flash_info[id->num]; + + start_phys = flash->start[0] + part->offset; + end_phys = start_phys + part->size - 1; + + for (i = 0; i < flash->sector_count; i++) { + if (flash->start[i] >= end_phys) + break; + + if (flash->start[i] >= start_phys) { + if (i == flash->sector_count - 1) { + size = flash->start[0] + flash->size - flash->start[i]; + } else { + size = flash->start[i+1] - flash->start[i]; + } + + if (sector_size < size) + sector_size = size; + } + } + + return sector_size; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size_onenand(void) +{ +#if defined(CONFIG_CMD_ONENAND) + struct mtd_info *mtd; + + mtd = &onenand_mtd; + + return mtd->erasesize; +#else + BUG(); + return 0; +#endif +} + +static inline u32 get_part_sector_size(struct mtdids *id, struct part_info *part) +{ + if (id->type == MTD_DEV_TYPE_NAND) + return get_part_sector_size_nand(id); + else if (id->type == MTD_DEV_TYPE_NOR) + return get_part_sector_size_nor(id, part); + else if (id->type == MTD_DEV_TYPE_ONENAND) + return get_part_sector_size_onenand(); + else + DEBUGF("Error: Unknown device type.\n"); + + return 0; +} + +/** + * Parse and initialize global mtdids mapping and create global + * device/partition list. + * + * 'Static' version of command line mtdparts_init() routine. Single partition on + * a single device configuration. + * + * @return 0 on success, 1 otherwise + */ +int mtdparts_init(void) +{ + static int initialized = 0; + u32 size; + char *dev_name; + + DEBUGF("\n---mtdparts_init---\n"); + if (!initialized) { + struct mtdids *id; + struct part_info *part; + + initialized = 1; + current_mtd_dev = (struct mtd_device *) + malloc(sizeof(struct mtd_device) + + sizeof(struct part_info) + + sizeof(struct mtdids)); + if (!current_mtd_dev) { + printf("out of memory\n"); + return 1; + } + memset(current_mtd_dev, 0, sizeof(struct mtd_device) + + sizeof(struct part_info) + sizeof(struct mtdids)); + + id = (struct mtdids *)(current_mtd_dev + 1); + part = (struct part_info *)(id + 1); + + /* id */ + id->mtd_id = "single part"; + +#if defined(CONFIG_JFFS2_DEV) + dev_name = CONFIG_JFFS2_DEV; +#else + dev_name = "nor0"; +#endif + + if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) || + (mtd_device_validate(id->type, id->num, &size) != 0)) { + printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num); + free(current_mtd_dev); + return 1; + } + id->size = size; + INIT_LIST_HEAD(&id->link); + + DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n", + id->type, id->num, id->size, id->mtd_id); + + /* partition */ + part->name = "static"; + part->auto_name = 0; + +#if defined(CONFIG_JFFS2_PART_SIZE) + part->size = CONFIG_JFFS2_PART_SIZE; +#else + part->size = SIZE_REMAINING; +#endif + +#if defined(CONFIG_JFFS2_PART_OFFSET) + part->offset = CONFIG_JFFS2_PART_OFFSET; +#else + part->offset = 0x00000000; +#endif + + part->dev = current_mtd_dev; + INIT_LIST_HEAD(&part->link); + + /* recalculate size if needed */ + if (part->size == SIZE_REMAINING) + part->size = id->size - part->offset; + + part->sector_size = get_part_sector_size(id, part); + + DEBUGF("part : name = %s, size = 0x%08lx, offset = 0x%08lx\n", + part->name, part->size, part->offset); + + /* device */ + current_mtd_dev->id = id; + INIT_LIST_HEAD(¤t_mtd_dev->link); + current_mtd_dev->num_parts = 1; + INIT_LIST_HEAD(¤t_mtd_dev->parts); + list_add(&part->link, ¤t_mtd_dev->parts); + } + + return 0; +} +#endif /* #ifndef CONFIG_CMD_MTDPARTS */ + +/** + * Return pointer to the partition of a requested number from a requested + * device. + * + * @param dev device that is to be searched for a partition + * @param part_num requested partition number + * @return pointer to the part_info, NULL otherwise + */ +static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num) +{ + struct list_head *entry; + struct part_info *part; + int num; + + if (!dev) + return NULL; + + DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + if (part_num >= dev->num_parts) { + printf("invalid partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + return NULL; + } + + /* locate partition number, return it */ + num = 0; + list_for_each(entry, &dev->parts) { + part = list_entry(entry, struct part_info, link); + + if (part_num == num++) { + return part; + } + } + + return NULL; +} + +/***************************************************/ +/* U-Boot commands */ +/***************************************************/ + +/** + * Routine implementing fsload u-boot command. This routine tries to load + * a requested file from jffs2/cramfs filesystem on a current partition. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_fsload(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *fsname; + char *filename; + int size; + struct part_info *part; + ulong offset = image_load_addr; + + /* pre-set Boot file name */ + filename = env_get("bootfile"); + if (!filename) + filename = "uImage"; + + if (argc == 2) { + filename = argv[1]; + } + if (argc == 3) { + offset = simple_strtoul(argv[1], NULL, 16); + image_load_addr = offset; + filename = argv[2]; + } + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); + printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset); + + if (cramfs_check(part)) { + size = cramfs_load ((char *) offset, part, filename); + } else { + /* if this is not cramfs assume jffs2 */ + size = jffs2_1pass_load((char *)offset, part, filename); + } + + if (size > 0) { + printf("### %s load complete: %d bytes loaded to 0x%lx\n", + fsname, size, offset); + env_set_hex("filesize", size); + } else { + printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename); + } + + return !(size > 0); + } + return 1; +} + +/** + * Routine implementing u-boot ls command which lists content of a given + * directory on a current partition. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename = "/"; + int ret; + struct part_info *part; + + if (argc == 2) + filename = argv[1]; + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + if (cramfs_check(part)) { + ret = cramfs_ls (part, filename); + } else { + /* if this is not cramfs assume jffs2 */ + ret = jffs2_1pass_ls(part, filename); + } + + return ret ? 0 : 1; + } + return 1; +} + +/** + * Routine implementing u-boot fsinfo command. This routine prints out + * miscellaneous filesystem informations/statistics. + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +int do_jffs2_fsinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct part_info *part; + char *fsname; + int ret; + + /* make sure we are in sync with env variables */ + if (mtdparts_init() !=0) + return 1; + + if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){ + + /* check partition type for cramfs */ + fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2"); + printf("### filesystem type is %s\n", fsname); + + if (cramfs_check(part)) { + ret = cramfs_info (part); + } else { + /* if this is not cramfs assume jffs2 */ + ret = jffs2_1pass_info(part); + } + + return ret ? 0 : 1; + } + return 1; +} + +/***************************************************/ +U_BOOT_CMD( + fsload, 3, 0, do_jffs2_fsload, + "load binary file from a filesystem image", + "[ off ] [ filename ]\n" + " - load binary file from flash bank\n" + " with offset 'off'" +); +U_BOOT_CMD( + fsls, 2, 1, do_jffs2_ls, + "list files in a directory (default /)", + "[ directory ]" +); + +U_BOOT_CMD( + fsinfo, 1, 1, do_jffs2_fsinfo, + "print information about filesystems", + "" +); +/***************************************************/ diff --git a/roms/u-boot/cmd/led.c b/roms/u-boot/cmd/led.c new file mode 100644 index 000000000..aa7751933 --- /dev/null +++ b/roms/u-boot/cmd/led.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <led.h> +#include <dm/uclass-internal.h> + +#define LED_TOGGLE LEDST_COUNT + +static const char *const state_label[] = { + [LEDST_OFF] = "off", + [LEDST_ON] = "on", + [LEDST_TOGGLE] = "toggle", +#ifdef CONFIG_LED_BLINK + [LEDST_BLINK] = "blink", +#endif +}; + +enum led_state_t get_led_cmd(char *var) +{ + int i; + + for (i = 0; i < LEDST_COUNT; i++) { + if (!strncmp(var, state_label[i], strlen(var))) + return i; + } + + return -1; +} + +static int show_led_state(struct udevice *dev) +{ + int ret; + + ret = led_get_state(dev); + if (ret >= LEDST_COUNT) + ret = -EINVAL; + if (ret >= 0) + printf("%s\n", state_label[ret]); + + return ret; +} + +static int list_leds(void) +{ + struct udevice *dev; + int ret; + + for (uclass_find_first_device(UCLASS_LED, &dev); + dev; + uclass_find_next_device(&dev)) { + struct led_uc_plat *plat = dev_get_uclass_plat(dev); + + if (!plat->label) + continue; + printf("%-15s ", plat->label); + if (device_active(dev)) { + ret = show_led_state(dev); + if (ret < 0) + printf("Error %d\n", ret); + } else { + printf("<inactive>\n"); + } + } + + return 0; +} + +int do_led(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + enum led_state_t cmd; + const char *led_label; + struct udevice *dev; +#ifdef CONFIG_LED_BLINK + int freq_ms = 0; +#endif + int ret; + + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + led_label = argv[1]; + if (strncmp(led_label, "list", 4) == 0) + return list_leds(); + + cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT; +#ifdef CONFIG_LED_BLINK + if (cmd == LEDST_BLINK) { + if (argc < 4) + return CMD_RET_USAGE; + freq_ms = simple_strtoul(argv[3], NULL, 10); + } +#endif + ret = led_get_by_label(led_label, &dev); + if (ret) { + printf("LED '%s' not found (err=%d)\n", led_label, ret); + return CMD_RET_FAILURE; + } + switch (cmd) { + case LEDST_OFF: + case LEDST_ON: + case LEDST_TOGGLE: + ret = led_set_state(dev, cmd); + break; +#ifdef CONFIG_LED_BLINK + case LEDST_BLINK: + ret = led_set_period(dev, freq_ms); + if (!ret) + ret = led_set_state(dev, LEDST_BLINK); + break; +#endif + case LEDST_COUNT: + printf("LED '%s': ", led_label); + ret = show_led_state(dev); + break; + } + if (ret < 0) { + printf("LED '%s' operation failed (err=%d)\n", led_label, ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +#ifdef CONFIG_LED_BLINK +#define BLINK "|blink [blink-freq in ms]" +#else +#define BLINK "" +#endif + +U_BOOT_CMD( + led, 4, 1, do_led, + "manage LEDs", + "<led_label> on|off|toggle" BLINK "\tChange LED state\n" + "led <led_label>\tGet LED state\n" + "led list\t\tshow a list of LEDs" +); diff --git a/roms/u-boot/cmd/legacy-mtd-utils.c b/roms/u-boot/cmd/legacy-mtd-utils.c new file mode 100644 index 000000000..ac7139f84 --- /dev/null +++ b/roms/u-boot/cmd/legacy-mtd-utils.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <jffs2/jffs2.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/string.h> +#include <mtd.h> + +static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize, int devtype) +{ +#ifdef CONFIG_CMD_MTDPARTS + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + int ret; + + ret = mtdparts_init(); + if (ret) + return ret; + + ret = find_dev_and_part(partname, &dev, &pnum, &part); + if (ret) + return ret; + + if (dev->id->type != devtype) { + printf("not same typ %d != %d\n", dev->id->type, devtype); + return -1; + } + + *off = part->offset; + *size = part->size; + *maxsize = part->size; + *idx = dev->id->num; + + return 0; +#else + puts("mtdparts support missing.\n"); + return -1; +#endif +} + +int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize, int devtype, uint64_t chipsize) +{ + if (!str2off(arg, off)) + return get_part(arg, idx, off, size, maxsize, devtype); + + if (*off >= chipsize) { + puts("Offset exceeds device limit\n"); + return -1; + } + + *maxsize = chipsize - *off; + *size = *maxsize; + return 0; +} + +int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, + loff_t *size, loff_t *maxsize, int devtype, + uint64_t chipsize) +{ + int ret; + + if (argc == 0) { + *off = 0; + *size = chipsize; + *maxsize = *size; + goto print; + } + + ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, + chipsize); + if (ret) + return ret; + + if (argc == 1) + goto print; + + if (!str2off(argv[1], size)) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + + if (*size > *maxsize) { + puts("Size exceeds partition or device limit\n"); + return -1; + } + +print: + printf("device %d ", *idx); + if (*size == chipsize) + puts("whole chip\n"); + else + printf("offset 0x%llx, size 0x%llx\n", + (unsigned long long)*off, (unsigned long long)*size); + return 0; +} diff --git a/roms/u-boot/cmd/legacy-mtd-utils.h b/roms/u-boot/cmd/legacy-mtd-utils.h new file mode 100644 index 000000000..ac441d555 --- /dev/null +++ b/roms/u-boot/cmd/legacy-mtd-utils.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __LEGACY_MTD_UTILS_H +#define __LEGACY_MTD_UTILS_H + +int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, + loff_t *maxsize, int devtype, uint64_t chipsize); +int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, + loff_t *size, loff_t *maxsize, int devtype, + uint64_t chipsize); + +#endif /* LEGACY_MTD_UTILS_H */ diff --git a/roms/u-boot/cmd/legacy_led.c b/roms/u-boot/cmd/legacy_led.c new file mode 100644 index 000000000..86cd969e4 --- /dev/null +++ b/roms/u-boot/cmd/legacy_led.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Jason Kridner <jkridner@beagleboard.org> + * + * Based on cmd_led.c patch from: + * http://www.mail-archive.com/u-boot@lists.denx.de/msg06873.html + * (C) Copyright 2008 + * Ulf Samuelsson <ulf.samuelsson@atmel.com> + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <status_led.h> + +struct led_tbl_s { + char *string; /* String for use in the command */ + led_id_t mask; /* Mask used for calling __led_set() */ + void (*off)(void); /* Optional function for turning LED off */ + void (*on)(void); /* Optional function for turning LED on */ + void (*toggle)(void);/* Optional function for toggling LED */ +}; + +typedef struct led_tbl_s led_tbl_t; + +static const led_tbl_t led_commands[] = { +#ifdef CONFIG_LED_STATUS_BOARD_SPECIFIC +#ifdef CONFIG_LED_STATUS0 + { "0", CONFIG_LED_STATUS_BIT, NULL, NULL, NULL }, +#endif +#ifdef CONFIG_LED_STATUS1 + { "1", CONFIG_LED_STATUS_BIT1, NULL, NULL, NULL }, +#endif +#ifdef CONFIG_LED_STATUS2 + { "2", CONFIG_LED_STATUS_BIT2, NULL, NULL, NULL }, +#endif +#ifdef CONFIG_LED_STATUS3 + { "3", CONFIG_LED_STATUS_BIT3, NULL, NULL, NULL }, +#endif +#ifdef CONFIG_LED_STATUS4 + { "4", CONFIG_LED_STATUS_BIT4, NULL, NULL, NULL }, +#endif +#ifdef CONFIG_LED_STATUS5 + { "5", CONFIG_LED_STATUS_BIT5, NULL, NULL, NULL }, +#endif +#endif +#ifdef CONFIG_LED_STATUS_GREEN + { "green", CONFIG_LED_STATUS_GREEN, green_led_off, green_led_on, NULL }, +#endif +#ifdef CONFIG_LED_STATUS_YELLOW + { "yellow", CONFIG_LED_STATUS_YELLOW, yellow_led_off, yellow_led_on, + NULL }, +#endif +#ifdef CONFIG_LED_STATUS_RED + { "red", CONFIG_LED_STATUS_RED, red_led_off, red_led_on, NULL }, +#endif +#ifdef CONFIG_LED_STATUS_BLUE + { "blue", CONFIG_LED_STATUS_BLUE, blue_led_off, blue_led_on, NULL }, +#endif + { NULL, 0, NULL, NULL, NULL } +}; + +enum led_cmd { LED_ON, LED_OFF, LED_TOGGLE, LED_BLINK }; + +enum led_cmd get_led_cmd(char *var) +{ + if (strcmp(var, "off") == 0) + return LED_OFF; + if (strcmp(var, "on") == 0) + return LED_ON; + if (strcmp(var, "toggle") == 0) + return LED_TOGGLE; + if (strcmp(var, "blink") == 0) + return LED_BLINK; + + return -1; +} + +/* + * LED drivers providing a blinking LED functionality, like the + * PCA9551, can override this empty weak function + */ +void __weak __led_blink(led_id_t mask, int freq) +{ +} + +int do_legacy_led(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int i, match = 0; + enum led_cmd cmd; + int freq; + + /* Validate arguments */ + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + cmd = get_led_cmd(argv[2]); + if (cmd < 0) { + return CMD_RET_USAGE; + } + + for (i = 0; led_commands[i].string; i++) { + if ((strcmp("all", argv[1]) == 0) || + (strcmp(led_commands[i].string, argv[1]) == 0)) { + match = 1; + switch (cmd) { + case LED_ON: + if (led_commands[i].on) + led_commands[i].on(); + else + __led_set(led_commands[i].mask, + CONFIG_LED_STATUS_ON); + break; + case LED_OFF: + if (led_commands[i].off) + led_commands[i].off(); + else + __led_set(led_commands[i].mask, + CONFIG_LED_STATUS_OFF); + break; + case LED_TOGGLE: + if (led_commands[i].toggle) + led_commands[i].toggle(); + else + __led_toggle(led_commands[i].mask); + break; + case LED_BLINK: + if (argc != 4) + return CMD_RET_USAGE; + + freq = simple_strtoul(argv[3], NULL, 10); + __led_blink(led_commands[i].mask, freq); + } + /* Need to set only 1 led if led_name wasn't 'all' */ + if (strcmp("all", argv[1]) != 0) + break; + } + } + + /* If we ran out of matches, print Usage */ + if (!match) { + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + led, 4, 1, do_legacy_led, + "[" +#ifdef CONFIG_LED_STATUS_BOARD_SPECIFIC +#ifdef CONFIG_LED_STATUS0 + "0|" +#endif +#ifdef CONFIG_LED_STATUS1 + "1|" +#endif +#ifdef CONFIG_LED_STATUS2 + "2|" +#endif +#ifdef CONFIG_LED_STATUS3 + "3|" +#endif +#ifdef CONFIG_LED_STATUS4 + "4|" +#endif +#ifdef CONFIG_LED_STATUS5 + "5|" +#endif +#endif +#ifdef CONFIG_LED_STATUS_GREEN + "green|" +#endif +#ifdef CONFIG_LED_STATUS_YELLOW + "yellow|" +#endif +#ifdef CONFIG_LED_STATUS_RED + "red|" +#endif +#ifdef CONFIG_LED_STATUS_BLUE + "blue|" +#endif + "all] [on|off|toggle|blink] [blink-freq in ms]", + "[led_name] [on|off|toggle|blink] sets or clears led(s)" +); diff --git a/roms/u-boot/cmd/license.c b/roms/u-boot/cmd/license.c new file mode 100644 index 000000000..15411b5a9 --- /dev/null +++ b/roms/u-boot/cmd/license.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 by OpenMoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + */ + +#include <common.h> +#include <command.h> +#include <gzip.h> +#include <malloc.h> + +#include "license_data_gz.h" +#include "license_data_size.h" + +static int do_license(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *dst; + unsigned long len = data_size; + int ret = CMD_RET_SUCCESS; + + dst = malloc(data_size + 1); + if (!dst) + return CMD_RET_FAILURE; + + ret = gunzip(dst, data_size, (unsigned char *)data_gz, &len); + if (ret) { + printf("Error uncompressing license text\n"); + ret = CMD_RET_FAILURE; + goto free; + } + + dst[data_size] = 0; + puts(dst); + +free: + free(dst); + + return ret; +} + +U_BOOT_CMD( + license, 1, 1, do_license, + "print GPL license text", + "" +); diff --git a/roms/u-boot/cmd/load.c b/roms/u-boot/cmd/load.c new file mode 100644 index 000000000..b7894d7db --- /dev/null +++ b/roms/u-boot/cmd/load.c @@ -0,0 +1,1096 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Serial up- and download support + */ +#include <common.h> +#include <command.h> +#include <console.h> +#include <cpu_func.h> +#include <efi_loader.h> +#include <env.h> +#include <exports.h> +#include <flash.h> +#include <image.h> +#include <mapmem.h> +#include <net.h> +#include <s_record.h> +#include <serial.h> +#include <xyzModem.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_LOADB) +static ulong load_serial_ymodem(ulong offset, int mode); +#endif + +#if defined(CONFIG_CMD_LOADS) +static ulong load_serial(long offset); +static int read_record(char *buf, ulong len); +# if defined(CONFIG_CMD_SAVES) +static int save_serial(ulong offset, ulong size); +static int write_record(char *buf); +#endif + +static int do_echo = 1; +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_LOADS) +static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + long offset = 0; + ulong addr; + int i; + char *env_echo; + int rcode = 0; +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + int load_baudrate, current_baudrate; + + load_baudrate = current_baudrate = gd->baudrate; +#endif + + env_echo = env_get("loads_echo"); + if (env_echo && *env_echo == '1') + do_echo = 1; + else + do_echo = 0; + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (argc >= 2) { + offset = simple_strtol(argv[1], NULL, 16); + } + if (argc == 3) { + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if (load_baudrate == 0) + load_baudrate = current_baudrate; + } + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + load_baudrate); + udelay(50000); + gd->baudrate = load_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == '\r') + break; + } + } +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ + if (argc == 2) { + offset = simple_strtol(argv[1], NULL, 16); + } +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + + printf("## Ready for S-Record download ...\n"); + + addr = load_serial(offset); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for (i=0; i<100; ++i) { + if (tstc()) { + getchar(); + } + udelay(1000); + } + + if (addr == ~0) { + printf("## S-Record download aborted\n"); + rcode = 1; + } else { + printf("## Start Addr = 0x%08lX\n", addr); + image_load_addr = addr; + } + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == 0x1B) /* ESC */ + break; + } + } +#endif + return rcode; +} + +static ulong load_serial(long offset) +{ + char record[SREC_MAXRECLEN + 1]; /* buffer for one S-Record */ + char binbuf[SREC_MAXBINLEN]; /* buffer for binary data */ + int binlen; /* no. of data bytes in S-Rec. */ + int type; /* return code for record type */ + ulong addr; /* load address from S-Record */ + ulong size; /* number of bytes transferred */ + ulong store_addr; + ulong start_addr = ~0; + ulong end_addr = 0; + int line_count = 0; + + while (read_record(record, SREC_MAXRECLEN + 1) >= 0) { + type = srec_decode(record, &binlen, &addr, binbuf); + + if (type < 0) { + return (~0); /* Invalid S-Record */ + } + + switch (type) { + case SREC_DATA2: + case SREC_DATA3: + case SREC_DATA4: + store_addr = addr + offset; +#ifdef CONFIG_MTD_NOR_FLASH + if (addr2info(store_addr)) { + int rc; + + rc = flash_write((char *)binbuf,store_addr,binlen); + if (rc != 0) { + flash_perror(rc); + return (~0); + } + } else +#endif + { + memcpy((char *)(store_addr), binbuf, binlen); + } + if ((store_addr) < start_addr) + start_addr = store_addr; + if ((store_addr + binlen - 1) > end_addr) + end_addr = store_addr + binlen - 1; + break; + case SREC_END2: + case SREC_END3: + case SREC_END4: + udelay(10000); + size = end_addr - start_addr + 1; + printf("\n" + "## First Load Addr = 0x%08lX\n" + "## Last Load Addr = 0x%08lX\n" + "## Total Size = 0x%08lX = %ld Bytes\n", + start_addr, end_addr, size, size + ); + flush_cache(start_addr, size); + env_set_hex("filesize", size); + return (addr); + case SREC_START: + break; + default: + break; + } + if (!do_echo) { /* print a '.' every 100 lines */ + if ((++line_count % 100) == 0) + putc('.'); + } + } + + return (~0); /* Download aborted */ +} + +static int read_record(char *buf, ulong len) +{ + char *p; + char c; + + --len; /* always leave room for terminating '\0' byte */ + + for (p=buf; p < buf+len; ++p) { + c = getchar(); /* read character */ + if (do_echo) + putc(c); /* ... and echo it */ + + switch (c) { + case '\r': + case '\n': + *p = '\0'; + return (p - buf); + case '\0': + case 0x03: /* ^C - Control C */ + return (-1); + default: + *p = c; + } + + /* Check for the console hangup (if any different from serial) */ + if (gd->jt->getc != getchar) { + if (ctrlc()) + return (-1); + } + } + + /* line too long - truncate */ + *p = '\0'; + return (p - buf); +} + +#if defined(CONFIG_CMD_SAVES) + +int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong offset = 0; + ulong size = 0; +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + int save_baudrate, current_baudrate; + + save_baudrate = current_baudrate = gd->baudrate; +#endif + + if (argc >= 2) { + offset = simple_strtoul(argv[1], NULL, 16); + } +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (argc >= 3) { + size = simple_strtoul(argv[2], NULL, 16); + } + if (argc == 4) { + save_baudrate = (int)simple_strtoul(argv[3], NULL, 10); + + /* default to current baudrate */ + if (save_baudrate == 0) + save_baudrate = current_baudrate; + } + if (save_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + save_baudrate); + udelay(50000); + gd->baudrate = save_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == '\r') + break; + } + } +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ + if (argc == 3) { + size = simple_strtoul(argv[2], NULL, 16); + } +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + + printf("## Ready for S-Record upload, press ENTER to proceed ...\n"); + for (;;) { + if (getchar() == '\r') + break; + } + if (save_serial(offset, size)) { + printf("## S-Record upload aborted\n"); + } else { + printf("## S-Record upload complete\n"); + } +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE + if (save_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + (int)current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == 0x1B) /* ESC */ + break; + } + } +#endif + return 0; +} + +#define SREC3_START "S0030000FC\n" +#define SREC3_FORMAT "S3%02X%08lX%s%02X\n" +#define SREC3_END "S70500000000FA\n" +#define SREC_BYTES_PER_RECORD 16 + +static int save_serial(ulong address, ulong count) +{ + int i, c, reclen, checksum, length; + char *hex = "0123456789ABCDEF"; + char record[2*SREC_BYTES_PER_RECORD+16]; /* buffer for one S-Record */ + char data[2*SREC_BYTES_PER_RECORD+1]; /* buffer for hex data */ + + reclen = 0; + checksum = 0; + + if(write_record(SREC3_START)) /* write the header */ + return (-1); + do { + if(count) { /* collect hex data in the buffer */ + c = *(volatile uchar*)(address + reclen); /* get one byte */ + checksum += c; /* accumulate checksum */ + data[2*reclen] = hex[(c>>4)&0x0f]; + data[2*reclen+1] = hex[c & 0x0f]; + data[2*reclen+2] = '\0'; + ++reclen; + --count; + } + if(reclen == SREC_BYTES_PER_RECORD || count == 0) { + /* enough data collected for one record: dump it */ + if(reclen) { /* build & write a data record: */ + /* address + data + checksum */ + length = 4 + reclen + 1; + + /* accumulate length bytes into checksum */ + for(i = 0; i < 2; i++) + checksum += (length >> (8*i)) & 0xff; + + /* accumulate address bytes into checksum: */ + for(i = 0; i < 4; i++) + checksum += (address >> (8*i)) & 0xff; + + /* make proper checksum byte: */ + checksum = ~checksum & 0xff; + + /* output one record: */ + sprintf(record, SREC3_FORMAT, length, address, data, checksum); + if(write_record(record)) + return (-1); + } + address += reclen; /* increment address */ + checksum = 0; + reclen = 0; + } + } + while(count); + if(write_record(SREC3_END)) /* write the final record */ + return (-1); + return(0); +} + +static int write_record(char *buf) +{ + char c; + + while((c = *buf++)) + putc(c); + + /* Check for the console hangup (if any different from serial) */ + + if (ctrlc()) { + return (-1); + } + return (0); +} +# endif + +#endif + + +#if defined(CONFIG_CMD_LOADB) +/* + * loadb command (load binary) included + */ +#define XON_CHAR 17 +#define XOFF_CHAR 19 +#define START_CHAR 0x01 +#define ETX_CHAR 0x03 +#define END_CHAR 0x0D +#define SPACE 0x20 +#define K_ESCAPE 0x23 +#define SEND_TYPE 'S' +#define DATA_TYPE 'D' +#define ACK_TYPE 'Y' +#define NACK_TYPE 'N' +#define BREAK_TYPE 'B' +#define tochar(x) ((char) (((x) + SPACE) & 0xff)) +#define untochar(x) ((int) (((x) - SPACE) & 0xff)) + +static void set_kerm_bin_mode(unsigned long *); +static int k_recv(void); +static ulong load_serial_bin(ulong offset); + + +static char his_eol; /* character he needs at end of packet */ +static int his_pad_count; /* number of pad chars he needs */ +static char his_pad_char; /* pad chars he needs */ +static char his_quote; /* quote chars he'll use */ + +static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong offset = 0; + ulong addr; + int load_baudrate, current_baudrate; + int rcode = 0; + char *s; + + /* pre-set offset from CONFIG_SYS_LOAD_ADDR */ + offset = CONFIG_SYS_LOAD_ADDR; + + /* pre-set offset from $loadaddr */ + s = env_get("loadaddr"); + if (s) + offset = simple_strtoul(s, NULL, 16); + + load_baudrate = current_baudrate = gd->baudrate; + + if (argc >= 2) { + offset = simple_strtoul(argv[1], NULL, 16); + } + if (argc == 3) { + load_baudrate = (int)simple_strtoul(argv[2], NULL, 10); + + /* default to current baudrate */ + if (load_baudrate == 0) + load_baudrate = current_baudrate; + } + + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + load_baudrate); + udelay(50000); + gd->baudrate = load_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == '\r') + break; + } + } + + if (strcmp(argv[0],"loady")==0) { + printf("## Ready for binary (ymodem) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + + addr = load_serial_ymodem(offset, xyzModem_ymodem); + + } else if (strcmp(argv[0],"loadx")==0) { + printf("## Ready for binary (xmodem) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + + addr = load_serial_ymodem(offset, xyzModem_xmodem); + + } else { + + printf("## Ready for binary (kermit) download " + "to 0x%08lX at %d bps...\n", + offset, + load_baudrate); + addr = load_serial_bin(offset); + + if (addr == ~0) { + image_load_addr = 0; + printf("## Binary (kermit) download aborted\n"); + rcode = 1; + } else { + printf("## Start Addr = 0x%08lX\n", addr); + image_load_addr = addr; + } + } + if (load_baudrate != current_baudrate) { + printf("## Switch baudrate to %d bps and press ESC ...\n", + current_baudrate); + udelay(50000); + gd->baudrate = current_baudrate; + serial_setbrg(); + udelay(50000); + for (;;) { + if (getchar() == 0x1B) /* ESC */ + break; + } + } + + return rcode; +} + + +static ulong load_serial_bin(ulong offset) +{ + int size, i; + + set_kerm_bin_mode((ulong *) offset); + size = k_recv(); + + /* + * Gather any trailing characters (for instance, the ^D which + * is sent by 'cu' after sending a file), and give the + * box some time (100 * 1 ms) + */ + for (i=0; i<100; ++i) { + if (tstc()) { + getchar(); + } + udelay(1000); + } + + flush_cache(offset, size); + + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + env_set_hex("filesize", size); + + return offset; +} + +static void send_pad(void) +{ + int count = his_pad_count; + + while (count-- > 0) + putc(his_pad_char); +} + +/* converts escaped kermit char to binary char */ +static char ktrans(char in) +{ + if ((in & 0x60) == 0x40) { + return (char) (in & ~0x40); + } else if ((in & 0x7f) == 0x3f) { + return (char) (in | 0x40); + } else + return in; +} + +static int chk1(char *buffer) +{ + int total = 0; + + while (*buffer) { + total += *buffer++; + } + return (int) ((total + ((total >> 6) & 0x03)) & 0x3f); +} + +static void s1_sendpacket(char *packet) +{ + send_pad(); + while (*packet) { + putc(*packet++); + } +} + +static char a_b[24]; +static void send_ack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + s1_sendpacket(a_b); +} + +static void send_nack(int n) +{ + a_b[0] = START_CHAR; + a_b[1] = tochar(3); + a_b[2] = tochar(n); + a_b[3] = NACK_TYPE; + a_b[4] = '\0'; + a_b[4] = tochar(chk1(&a_b[1])); + a_b[5] = his_eol; + a_b[6] = '\0'; + s1_sendpacket(a_b); +} + + +static void (*os_data_init)(void); +static void (*os_data_char)(char new_char); +static int os_data_state, os_data_state_saved; +static char *os_data_addr, *os_data_addr_saved; +static char *bin_start_address; + +static void bin_data_init(void) +{ + os_data_state = 0; + os_data_addr = bin_start_address; +} + +static void os_data_save(void) +{ + os_data_state_saved = os_data_state; + os_data_addr_saved = os_data_addr; +} + +static void os_data_restore(void) +{ + os_data_state = os_data_state_saved; + os_data_addr = os_data_addr_saved; +} + +static void bin_data_char(char new_char) +{ + switch (os_data_state) { + case 0: /* data */ + *os_data_addr++ = new_char; + break; + } +} + +static void set_kerm_bin_mode(unsigned long *addr) +{ + bin_start_address = (char *) addr; + os_data_init = bin_data_init; + os_data_char = bin_data_char; +} + + +/* k_data_* simply handles the kermit escape translations */ +static int k_data_escape, k_data_escape_saved; +static void k_data_init(void) +{ + k_data_escape = 0; + os_data_init(); +} + +static void k_data_save(void) +{ + k_data_escape_saved = k_data_escape; + os_data_save(); +} + +static void k_data_restore(void) +{ + k_data_escape = k_data_escape_saved; + os_data_restore(); +} + +static void k_data_char(char new_char) +{ + if (k_data_escape) { + /* last char was escape - translate this character */ + os_data_char(ktrans(new_char)); + k_data_escape = 0; + } else { + if (new_char == his_quote) { + /* this char is escape - remember */ + k_data_escape = 1; + } else { + /* otherwise send this char as-is */ + os_data_char(new_char); + } + } +} + +#define SEND_DATA_SIZE 20 +static char send_parms[SEND_DATA_SIZE]; +static char *send_ptr; + +/* handle_send_packet interprits the protocol info and builds and + sends an appropriate ack for what we can do */ +static void handle_send_packet(int n) +{ + int length = 3; + int bytes; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* ignore last character if it filled the buffer */ + if (send_ptr == &send_parms[SEND_DATA_SIZE - 1]) + --send_ptr; + bytes = send_ptr - send_parms; /* how many bytes we'll process */ + do { + if (bytes-- <= 0) + break; + /* handle MAXL - max length */ + /* ignore what he says - most I'll take (here) is 94 */ + a_b[++length] = tochar(94); + if (bytes-- <= 0) + break; + /* handle TIME - time you should wait for my packets */ + /* ignore what he says - don't wait for my ack longer than 1 second */ + a_b[++length] = tochar(1); + if (bytes-- <= 0) + break; + /* handle NPAD - number of pad chars I need */ + /* remember what he says - I need none */ + his_pad_count = untochar(send_parms[2]); + a_b[++length] = tochar(0); + if (bytes-- <= 0) + break; + /* handle PADC - pad chars I need */ + /* remember what he says - I need none */ + his_pad_char = ktrans(send_parms[3]); + a_b[++length] = 0x40; /* He should ignore this */ + if (bytes-- <= 0) + break; + /* handle EOL - end of line he needs */ + /* remember what he says - I need CR */ + his_eol = untochar(send_parms[4]); + a_b[++length] = tochar(END_CHAR); + if (bytes-- <= 0) + break; + /* handle QCTL - quote control char he'll use */ + /* remember what he says - I'll use '#' */ + his_quote = send_parms[5]; + a_b[++length] = '#'; + if (bytes-- <= 0) + break; + /* handle QBIN - 8-th bit prefixing */ + /* ignore what he says - I refuse */ + a_b[++length] = 'N'; + if (bytes-- <= 0) + break; + /* handle CHKT - the clock check type */ + /* ignore what he says - I do type 1 (for now) */ + a_b[++length] = '1'; + if (bytes-- <= 0) + break; + /* handle REPT - the repeat prefix */ + /* ignore what he says - I refuse (for now) */ + a_b[++length] = 'N'; + if (bytes-- <= 0) + break; + /* handle CAPAS - the capabilities mask */ + /* ignore what he says - I only do long packets - I don't do windows */ + a_b[++length] = tochar(2); /* only long packets */ + a_b[++length] = tochar(0); /* no windows */ + a_b[++length] = tochar(94); /* large packet msb */ + a_b[++length] = tochar(94); /* large packet lsb */ + } while (0); + + a_b[0] = START_CHAR; + a_b[1] = tochar(length); + a_b[2] = tochar(n); + a_b[3] = ACK_TYPE; + a_b[++length] = '\0'; + a_b[length] = tochar(chk1(&a_b[1])); + a_b[++length] = his_eol; + a_b[++length] = '\0'; + s1_sendpacket(a_b); +} + +/* k_recv receives a OS Open image file over kermit line */ +static int k_recv(void) +{ + char new_char; + char k_state, k_state_saved; + int sum; + int done; + int length; + int n, last_n; + int len_lo, len_hi; + + /* initialize some protocol parameters */ + his_eol = END_CHAR; /* default end of line character */ + his_pad_count = 0; + his_pad_char = '\0'; + his_quote = K_ESCAPE; + + /* initialize the k_recv and k_data state machine */ + done = 0; + k_state = 0; + k_data_init(); + k_state_saved = k_state; + k_data_save(); + n = 0; /* just to get rid of a warning */ + last_n = -1; + + /* expect this "type" sequence (but don't check): + S: send initiate + F: file header + D: data (multiple) + Z: end of file + B: break transmission + */ + + /* enter main loop */ + while (!done) { + /* set the send packet pointer to begining of send packet parms */ + send_ptr = send_parms; + + /* With each packet, start summing the bytes starting with the length. + Save the current sequence number. + Note the type of the packet. + If a character less than SPACE (0x20) is received - error. + */ + +#if 0 + /* OLD CODE, Prior to checking sequence numbers */ + /* first have all state machines save current states */ + k_state_saved = k_state; + k_data_save (); +#endif + + /* get a packet */ + /* wait for the starting character or ^C */ + for (;;) { + switch (getchar()) { + case START_CHAR: /* start packet */ + goto START; + case ETX_CHAR: /* ^C waiting for packet */ + return (0); + default: + ; + } + } +START: + /* get length of packet */ + sum = 0; + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + length = untochar(new_char); + /* get sequence number */ + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + n = untochar(new_char); + --length; + + /* NEW CODE - check sequence numbers for retried packets */ + /* Note - this new code assumes that the sequence number is correctly + * received. Handling an invalid sequence number adds another layer + * of complexity that may not be needed - yet! At this time, I'm hoping + * that I don't need to buffer the incoming data packets and can write + * the data into memory in real time. + */ + if (n == last_n) { + /* same sequence number, restore the previous state */ + k_state = k_state_saved; + k_data_restore(); + } else { + /* new sequence number, checkpoint the download */ + last_n = n; + k_state_saved = k_state; + k_data_save(); + } + /* END NEW CODE */ + + /* get packet type */ + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + k_state = new_char; + --length; + /* check for extended length */ + if (length == -2) { + /* (length byte was 0, decremented twice) */ + /* get the two length bytes */ + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + len_hi = untochar(new_char); + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + len_lo = untochar(new_char); + length = len_hi * 95 + len_lo; + /* check header checksum */ + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + sum += new_char & 0xff; +/* --length; */ /* new length includes only data and block check to come */ + } + /* bring in rest of packet */ + while (length > 1) { + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + sum += new_char & 0xff; + --length; + if (k_state == DATA_TYPE) { + /* pass on the data if this is a data packet */ + k_data_char (new_char); + } else if (k_state == SEND_TYPE) { + /* save send pack in buffer as is */ + *send_ptr++ = new_char; + /* if too much data, back off the pointer */ + if (send_ptr >= &send_parms[SEND_DATA_SIZE]) + --send_ptr; + } + } + /* get and validate checksum character */ + new_char = getchar(); + if ((new_char & 0xE0) == 0) + goto packet_error; + if (new_char != tochar((sum + ((sum >> 6) & 0x03)) & 0x3f)) + goto packet_error; + /* get END_CHAR */ + new_char = getchar(); + if (new_char != END_CHAR) { + packet_error: + /* restore state machines */ + k_state = k_state_saved; + k_data_restore(); + /* send a negative acknowledge packet in */ + send_nack(n); + } else if (k_state == SEND_TYPE) { + /* crack the protocol parms, build an appropriate ack packet */ + handle_send_packet(n); + } else { + /* send simple acknowledge packet in */ + send_ack(n); + /* quit if end of transmission */ + if (k_state == BREAK_TYPE) + done = 1; + } + } + return ((ulong) os_data_addr - (ulong) bin_start_address); +} + +static int getcxmodem(void) { + if (tstc()) + return (getchar()); + return -1; +} +static ulong load_serial_ymodem(ulong offset, int mode) +{ + int size; + int err; + int res; + connection_info_t info; + char ymodemBuf[1024]; + ulong store_addr = ~0; + ulong addr = 0; + + size = 0; + info.mode = mode; + res = xyzModem_stream_open(&info, &err); + if (!res) { + + while ((res = + xyzModem_stream_read(ymodemBuf, 1024, &err)) > 0) { + store_addr = addr + offset; + size += res; + addr += res; +#ifdef CONFIG_MTD_NOR_FLASH + if (addr2info(store_addr)) { + int rc; + + rc = flash_write((char *) ymodemBuf, + store_addr, res); + if (rc != 0) { + flash_perror(rc); + return (~0); + } + } else +#endif + { + memcpy((char *)(store_addr), ymodemBuf, + res); + } + + } + if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) + efi_set_bootdev("Uart", "", "", + map_sysmem(offset, 0), size); + + } else { + printf("%s\n", xyzModem_error(err)); + } + + xyzModem_stream_close(&err); + xyzModem_stream_terminate(false, &getcxmodem); + + + flush_cache(offset, ALIGN(size, ARCH_DMA_MINALIGN)); + + printf("## Total Size = 0x%08x = %d Bytes\n", size, size); + env_set_hex("filesize", size); + + return offset; +} + +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(CONFIG_CMD_LOADS) + +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE +U_BOOT_CMD( + loads, 3, 0, do_load_serial, + "load S-Record file over serial line", + "[ off ] [ baud ]\n" + " - load S-Record file over serial line" + " with offset 'off' and baudrate 'baud'" +); + +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ +U_BOOT_CMD( + loads, 2, 0, do_load_serial, + "load S-Record file over serial line", + "[ off ]\n" + " - load S-Record file over serial line with offset 'off'" +); +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ + +/* + * SAVES always requires LOADS support, but not vice versa + */ + + +#if defined(CONFIG_CMD_SAVES) +#ifdef CONFIG_SYS_LOADS_BAUD_CHANGE +U_BOOT_CMD( + saves, 4, 0, do_save_serial, + "save S-Record file over serial line", + "[ off ] [size] [ baud ]\n" + " - save S-Record file over serial line" + " with offset 'off', size 'size' and baudrate 'baud'" +); +#else /* ! CONFIG_SYS_LOADS_BAUD_CHANGE */ +U_BOOT_CMD( + saves, 3, 0, do_save_serial, + "save S-Record file over serial line", + "[ off ] [size]\n" + " - save S-Record file over serial line with offset 'off' and size 'size'" +); +#endif /* CONFIG_SYS_LOADS_BAUD_CHANGE */ +#endif /* CONFIG_CMD_SAVES */ +#endif /* CONFIG_CMD_LOADS */ + + +#if defined(CONFIG_CMD_LOADB) +U_BOOT_CMD( + loadb, 3, 0, do_load_serial_bin, + "load binary file over serial line (kermit mode)", + "[ addr [ baud ] ]\n" + " - load binary file over serial line" + " at address 'addr' with baudrate 'baud'" +); + +U_BOOT_CMD( + loadx, 3, 0, do_load_serial_bin, + "load binary file over serial line (xmodem mode)", + "[ addr [ baud ] ]\n" + " - load binary file over serial line" + " at address 'addr' with baudrate 'baud'" +); + +U_BOOT_CMD( + loady, 3, 0, do_load_serial_bin, + "load binary file over serial line (ymodem mode)", + "[ addr [ baud ] ]\n" + " - load binary file over serial line" + " at address 'addr' with baudrate 'baud'" +); + +#endif /* CONFIG_CMD_LOADB */ diff --git a/roms/u-boot/cmd/log.c b/roms/u-boot/cmd/log.c new file mode 100644 index 000000000..72380c569 --- /dev/null +++ b/roms/u-boot/cmd/log.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <getopt.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> + +static char log_fmt_chars[LOGF_COUNT] = "clFLfm"; + +static enum log_level_t parse_log_level(char *const arg) +{ + enum log_level_t ret; + ulong level; + + if (!strict_strtoul(arg, 10, &level)) { + if (level > _LOG_MAX_LEVEL) { + printf("Only log levels <= %d are supported\n", + _LOG_MAX_LEVEL); + return LOGL_NONE; + } + return level; + } + + ret = log_get_level_by_name(arg); + if (ret == LOGL_NONE) + printf("Unknown log level \"%s\"\n", arg); + return ret; +} + +static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum log_level_t log_level; + + if (argc > 1) { + log_level = parse_log_level(argv[1]); + + if (log_level == LOGL_NONE) + return CMD_RET_FAILURE; + gd->default_log_level = log_level; + } else { + for (log_level = LOGL_FIRST; log_level <= _LOG_MAX_LEVEL; + log_level++) + printf("%s%s\n", log_get_level_name(log_level), + log_level == gd->default_log_level ? + " (default)" : ""); + } + + return CMD_RET_SUCCESS; +} + +static int do_log_categories(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum log_category_t cat; + const char *name; + + for (cat = LOGC_FIRST; cat < LOGC_COUNT; cat++) { + name = log_get_cat_name(cat); + /* + * Invalid category names (e.g. <invalid> or <missing>) begin + * with '<'. + */ + if (name[0] == '<') + continue; + printf("%s\n", name); + } + + return CMD_RET_SUCCESS; +} + +static int do_log_drivers(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct log_device *ldev; + + list_for_each_entry(ldev, &gd->log_head, sibling_node) + printf("%s\n", ldev->drv->name); + + return CMD_RET_SUCCESS; +} + +static int do_log_filter_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int opt; + const char *drv_name = "console"; + struct getopt_state gs; + struct log_filter *filt; + struct log_device *ldev; + + getopt_init_state(&gs); + while ((opt = getopt(&gs, argc, argv, "d:")) > 0) { + switch (opt) { + case 'd': + drv_name = gs.arg; + break; + default: + return CMD_RET_USAGE; + } + } + + if (gs.index != argc) + return CMD_RET_USAGE; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) { + printf("Could not find log device for \"%s\"\n", drv_name); + return CMD_RET_FAILURE; + } + + /* <3> < 6 > <2+1 + 7 > < 16 > < unbounded... */ + printf("num policy level categories files\n"); + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + printf("%3d %6.6s %s %-7.7s ", filt->filter_num, + filt->flags & LOGFF_DENY ? "deny" : "allow", + filt->flags & LOGFF_LEVEL_MIN ? ">=" : "<=", + log_get_level_name(filt->level)); + + if (filt->flags & LOGFF_HAS_CAT) { + int i; + + if (filt->cat_list[0] != LOGC_END) + printf("%16.16s %s\n", + log_get_cat_name(filt->cat_list[0]), + filt->file_list ? filt->file_list : ""); + + for (i = 1; i < LOGF_MAX_CATEGORIES && + filt->cat_list[i] != LOGC_END; i++) + printf("%21c %16.16s\n", ' ', + log_get_cat_name(filt->cat_list[i])); + } else { + printf("%16c %s\n", ' ', + filt->file_list ? filt->file_list : ""); + } + } + + return CMD_RET_SUCCESS; +} + +static int do_log_filter_add(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool level_set = false; + bool print_num = false; + bool type_set = false; + char *file_list = NULL; + const char *drv_name = "console"; + int opt, err; + int cat_count = 0; + int flags = 0; + enum log_category_t cat_list[LOGF_MAX_CATEGORIES + 1]; + enum log_level_t level = LOGL_MAX; + struct getopt_state gs; + + getopt_init_state(&gs); + while ((opt = getopt(&gs, argc, argv, "Ac:d:Df:l:L:p")) > 0) { + switch (opt) { + case 'A': +#define do_type() do { \ + if (type_set) { \ + printf("Allow or deny set twice\n"); \ + return CMD_RET_USAGE; \ + } \ + type_set = true; \ +} while (0) + do_type(); + break; + case 'c': { + enum log_category_t cat; + + if (cat_count >= LOGF_MAX_CATEGORIES) { + printf("Too many categories\n"); + return CMD_RET_FAILURE; + } + + cat = log_get_cat_by_name(gs.arg); + if (cat == LOGC_NONE) { + printf("Unknown category \"%s\"\n", gs.arg); + return CMD_RET_FAILURE; + } + + cat_list[cat_count++] = cat; + break; + } + case 'd': + drv_name = gs.arg; + break; + case 'D': + do_type(); + flags |= LOGFF_DENY; + break; + case 'f': + file_list = gs.arg; + break; + case 'l': +#define do_level() do { \ + if (level_set) { \ + printf("Log level set twice\n"); \ + return CMD_RET_USAGE; \ + } \ + level = parse_log_level(gs.arg); \ + if (level == LOGL_NONE) \ + return CMD_RET_FAILURE; \ + level_set = true; \ +} while (0) + do_level(); + break; + case 'L': + do_level(); + flags |= LOGFF_LEVEL_MIN; + break; + case 'p': + print_num = true; + break; + default: + return CMD_RET_USAGE; + } + } + + if (gs.index != argc) + return CMD_RET_USAGE; + + cat_list[cat_count] = LOGC_END; + err = log_add_filter_flags(drv_name, cat_count ? cat_list : NULL, level, + file_list, flags); + if (err < 0) { + printf("Could not add filter (err = %d)\n", err); + return CMD_RET_FAILURE; + } else if (print_num) { + printf("%d\n", err); + } + + return CMD_RET_SUCCESS; +} + +static int do_log_filter_remove(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool all = false; + int opt, err; + ulong filter_num; + const char *drv_name = "console"; + struct getopt_state gs; + + getopt_init_state(&gs); + while ((opt = getopt(&gs, argc, argv, "ad:")) > 0) { + switch (opt) { + case 'a': + all = true; + break; + case 'd': + drv_name = gs.arg; + break; + default: + return CMD_RET_USAGE; + } + } + + if (all) { + struct log_filter *filt, *tmp_filt; + struct log_device *ldev; + + if (gs.index != argc) + return CMD_RET_USAGE; + + ldev = log_device_find_by_name(drv_name); + if (!ldev) { + printf("Could not find log device for \"%s\"\n", + drv_name); + return CMD_RET_FAILURE; + } + + list_for_each_entry_safe(filt, tmp_filt, &ldev->filter_head, + sibling_node) { + list_del(&filt->sibling_node); + free(filt); + } + } else { + if (gs.index + 1 != argc) + return CMD_RET_USAGE; + + if (strict_strtoul(argv[gs.index], 10, &filter_num)) { + printf("Invalid filter number \"%s\"\n", argv[gs.index]); + return CMD_RET_FAILURE; + } + + err = log_remove_filter(drv_name, filter_num); + if (err) { + printf("Could not remove filter (err = %d)\n", err); + return CMD_RET_FAILURE; + } + } + + return CMD_RET_SUCCESS; +} + +static int do_log_format(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + + if (argc > 1) { + const char *str = argv[1]; + + if (!strcmp(str, "default")) { + gd->log_fmt = log_get_default_format(); + } else if (!strcmp(str, "all")) { + gd->log_fmt = LOGF_ALL; + } else { + gd->log_fmt = 0; + for (; *str; str++) { + char *ptr = strchr(log_fmt_chars, *str); + + if (!ptr) { + printf("Invalid log char '%c'\n", *str); + return CMD_RET_FAILURE; + } + gd->log_fmt |= 1 << (ptr - log_fmt_chars); + } + } + } else { + printf("Log format: "); + for (i = 0; i < LOGF_COUNT; i++) { + if (gd->log_fmt & (1 << i)) + printf("%c", log_fmt_chars[i]); + } + printf("\n"); + } + + return 0; +} + +static int do_log_rec(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum log_category_t cat; + enum log_level_t level; + const char *file; + uint line; + const char *func; + const char *msg; + char *end; + + if (argc < 7) + return CMD_RET_USAGE; + cat = log_get_cat_by_name(argv[1]); + level = simple_strtoul(argv[2], &end, 10); + if (end == argv[2]) { + level = log_get_level_by_name(argv[2]); + + if (level == LOGL_NONE) { + printf("Invalid log level '%s'\n", argv[2]); + return CMD_RET_USAGE; + } + } + if (level >= LOGL_MAX) { + printf("Invalid log level %u\n", level); + return CMD_RET_USAGE; + } + file = argv[3]; + line = simple_strtoul(argv[4], NULL, 10); + func = argv[5]; + msg = argv[6]; + if (_log(cat, level, file, line, func, "%s\n", msg)) + return CMD_RET_FAILURE; + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char log_help_text[] = + "level [<level>] - get/set log level\n" + "categories - list log categories\n" + "drivers - list log drivers\n" + "log filter-list [OPTIONS] - list all filters for a log driver\n" + "\t-d <driver> - Specify the log driver to list filters from; defaults\n" + "\t to console\n" + "log filter-add [OPTIONS] - add a new filter to a driver\n" + "\t-A - Allow messages matching this filter; mutually exclusive with -D\n" + "\t This is the default.\n" + "\t-c <category> - Category to match; may be specified multiple times\n" + "\t-d <driver> - Specify the log driver to add the filter to; defaults\n" + "\t to console\n" + "\t-D - Deny messages matching this filter; mutually exclusive with -A\n" + "\t-f <files_list> - A comma-separated list of files to match\n" + "\t-l <level> - Match log levels less than or equal to <level>;\n" + "\t mutually-exclusive with -L\n" + "\t-L <level> - Match log levels greather than or equal to <level>;\n" + "\t mutually-exclusive with -l\n" + "\t-p - Print the filter number on success\n" + "log filter-remove [OPTIONS] [<num>] - Remove filter number <num>\n" + "\t-a - Remove ALL filters\n" + "\t-d <driver> - Specify the log driver to remove the filter from;\n" + "\t defaults to console\n" + "log format <fmt> - set log output format. <fmt> is a string where\n" + "\teach letter indicates something that should be displayed:\n" + "\tc=category, l=level, F=file, L=line number, f=function, m=msg\n" + "\tor 'default', or 'all' for all\n" + "log rec <category> <level> <file> <line> <func> <message> - " + "output a log record" + ; +#endif + +U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text, + U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level), + U_BOOT_SUBCMD_MKENT(categories, 1, 1, do_log_categories), + U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_log_drivers), + U_BOOT_SUBCMD_MKENT(filter-list, 3, 1, do_log_filter_list), + U_BOOT_SUBCMD_MKENT(filter-add, CONFIG_SYS_MAXARGS, 1, + do_log_filter_add), + U_BOOT_SUBCMD_MKENT(filter-remove, 4, 1, do_log_filter_remove), + U_BOOT_SUBCMD_MKENT(format, 2, 1, do_log_format), + U_BOOT_SUBCMD_MKENT(rec, 7, 1, do_log_rec), +); diff --git a/roms/u-boot/cmd/lsblk.c b/roms/u-boot/cmd/lsblk.c new file mode 100644 index 000000000..6a1c8f5ef --- /dev/null +++ b/roms/u-boot/cmd/lsblk.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Niel Fourie, DENX Software Engineering, lusus@denx.de. + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <dm.h> + +static int do_lsblk(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + struct driver *d = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + struct udevice *udev; + struct uclass *uc; + struct blk_desc *desc; + int ret, i; + + ret = uclass_get(UCLASS_BLK, &uc); + if (ret) { + puts("Could not get BLK uclass.\n"); + return CMD_RET_FAILURE; + } + puts("Block Driver Devices\n"); + puts("-----------------------------\n"); + for (entry = d; entry < d + n_ents; entry++) { + if (entry->id != UCLASS_BLK) + continue; + i = 0; + printf("%-20.20s", entry->name); + uclass_foreach_dev(udev, uc) { + if (udev->driver != entry) + continue; + desc = dev_get_uclass_plat(udev); + printf("%c %s %u", i ? ',' : ':', + blk_get_if_type_name(desc->if_type), + desc->devnum); + i++; + } + if (!i) + puts(": <none>"); + puts("\n"); + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(lsblk, 1, 0, do_lsblk, "list block drivers and devices", + "- display list of block device drivers and attached block devices" +); diff --git a/roms/u-boot/cmd/lzmadec.c b/roms/u-boot/cmd/lzmadec.c new file mode 100644 index 000000000..3b8db0a85 --- /dev/null +++ b/roms/u-boot/cmd/lzmadec.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2013 Patrice Bouchand <pbfwdlist_gmail_com> + * lzma uncompress command in Uboot + * + * made from existing cmd_unzip.c file of Uboot + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <mapmem.h> +#include <asm/io.h> + +#include <lzma/LzmaTools.h> + +static int do_lzmadec(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long src, dst; + SizeT src_len = ~0UL, dst_len = ~0UL; + int ret; + + switch (argc) { + case 4: + dst_len = simple_strtoul(argv[3], NULL, 16); + /* fall through */ + case 3: + src = simple_strtoul(argv[1], NULL, 16); + dst = simple_strtoul(argv[2], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + ret = lzmaBuffToBuffDecompress(map_sysmem(dst, dst_len), &src_len, + map_sysmem(src, 0), dst_len); + + if (ret != SZ_OK) + return 1; + printf("Uncompressed size: %ld = %#lX\n", (ulong)src_len, + (ulong)src_len); + env_set_hex("filesize", src_len); + + return 0; +} + +U_BOOT_CMD( + lzmadec, 4, 1, do_lzmadec, + "lzma uncompress a memory region", + "srcaddr dstaddr [dstsize]" +); diff --git a/roms/u-boot/cmd/mac.c b/roms/u-boot/cmd/mac.c new file mode 100644 index 000000000..a39e1168a --- /dev/null +++ b/roms/u-boot/cmd/mac.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2006 Freescale Semiconductor + * York Sun (yorksun@freescale.com) + */ + +#include <common.h> +#include <command.h> + +extern int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); + +U_BOOT_CMD( + mac, 3, 1, do_mac, + "display and program the system ID and MAC addresses in EEPROM", + "[read|save|id|num|errata|date|ports|port_number]\n" + "mac read\n" + " - read EEPROM content into memory data structure\n" + "mac save\n" + " - save memory data structure to the EEPROM\n" + "mac id\n" + " - program system id per hard coded value\n" + "mac num string\n" + " - program system serial number to value string\n" + "mac errata string\n" + " - program errata data to value string\n" + "mac date YYMMDDhhmmss\n" + " - program date to string value YYMMDDhhmmss\n" + "mac ports N\n" + " - program the number of network ports to integer N\n" + "mac X string\n" + " - program MAC addr for port X [X=0,1..] to colon separated string" +); diff --git a/roms/u-boot/cmd/mbr.c b/roms/u-boot/cmd/mbr.c new file mode 100644 index 000000000..da2e3a472 --- /dev/null +++ b/roms/u-boot/cmd/mbr.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_mbr.c -- MBR (Master Boot Record) handling command + * + * Copyright (C) 2020 Samsung Electronics + * author: Marek Szyprowski <m.szyprowski@samsung.com> + * + * based on the gpt command. + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <malloc.h> +#include <part.h> + +/** + * extract_val() - Extract a value from the key=value pair list + * @str: pointer to string with key=values pairs + * @key: pointer to the key to search for + * + * The list of parameters is come separated, only a value for + * the given key is returend. + * + * Function allocates memory for the value, remember to free! + * + * Return: Pointer to allocated string with the value. + */ +static char *extract_val(const char *str, const char *key) +{ + char *v, *k; + char *s, *strcopy; + char *new = NULL; + + strcopy = strdup(str); + if (strcopy == NULL) + return NULL; + + s = strcopy; + while (s) { + v = strsep(&s, ","); + if (!v) + break; + k = strsep(&v, "="); + if (!k) + break; + if (strcmp(k, key) == 0) { + new = strdup(v); + break; + } + } + + free(strcopy); + + return new; +} + +/** + * found_key() - Search for a key without a value in the parameter list + * @str: pointer to string with key + * @key: pointer to the key to search for + * + * The list of parameters is come separated. + * + * Return: True if key has been found. + */ +static bool found_key(const char *str, const char *key) +{ + char *k; + char *s, *strcopy; + bool result = false; + + strcopy = strdup(str); + if (!strcopy) + return NULL; + + s = strcopy; + while (s) { + k = strsep(&s, ","); + if (!k) + break; + if (strcmp(k, key) == 0) { + result = true; + break; + } + } + + free(strcopy); + + return result; +} + +static int str_to_partitions(const char *str_part, int blksz, + unsigned long *disk_uuid, struct disk_partition **partitions, + int *parts_count) +{ + char *tok, *str, *s; + int i; + char *val, *p; + int p_count; + struct disk_partition *parts; + int errno = 0; + uint64_t size_ll, start_ll; + + if (str_part == NULL) + return -1; + + str = strdup(str_part); + if (str == NULL) + return -ENOMEM; + + /* extract disk guid */ + s = str; + val = extract_val(str, "uuid_disk"); + if (val) { + val = strsep(&val, ";"); + p = val; + *disk_uuid = ustrtoull(p, &p, 0); + free(val); + /* Move s to first partition */ + strsep(&s, ";"); + } + if (s == NULL) { + printf("Error: is the partitions string NULL-terminated?\n"); + return -EINVAL; + } + + /* remove the optional semicolon at the end of the string */ + i = strlen(s) - 1; + if (s[i] == ';') + s[i] = '\0'; + + /* calculate expected number of partitions */ + p_count = 1; + p = s; + while (*p) { + if (*p++ == ';') + p_count++; + } + + /* allocate memory for partitions */ + parts = calloc(sizeof(struct disk_partition), p_count); + if (parts == NULL) + return -ENOMEM; + + /* retrieve partitions data from string */ + for (i = 0; i < p_count; i++) { + tok = strsep(&s, ";"); + + if (tok == NULL) + break; + + /* size */ + val = extract_val(tok, "size"); + if (!val) { /* 'size' is mandatory */ + errno = -4; + goto err; + } + p = val; + if ((strcmp(p, "-") == 0)) { + /* auto extend the size */ + parts[i].size = 0; + } else { + size_ll = ustrtoull(p, &p, 0); + parts[i].size = size_ll / blksz; + } + free(val); + + /* start address */ + val = extract_val(tok, "start"); + if (val) { /* start address is optional */ + p = val; + start_ll = ustrtoull(p, &p, 0); + parts[i].start = start_ll / blksz; + free(val); + } + + /* system id */ + val = extract_val(tok, "id"); + if (!val) { /* '' is mandatory */ + errno = -4; + goto err; + } + p = val; + parts[i].sys_ind = ustrtoul(p, &p, 0); + free(val); + + /* bootable */ + if (found_key(tok, "bootable")) + parts[i].bootable = PART_BOOTABLE; + } + + *parts_count = p_count; + *partitions = parts; + free(str); + + return 0; +err: + free(str); + free(parts); + + return errno; +} + +static int do_write_mbr(struct blk_desc *dev, const char *str) +{ + unsigned long disk_uuid = 0; + struct disk_partition *partitions; + int blksz = dev->blksz; + int count; + + if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) { + printf("MBR: failed to setup partitions from \"%s\"\n", str); + return -1; + } + + if (layout_mbr_partitions(partitions, count, dev->lba)) { + printf("MBR: failed to layout partitions on the device\n"); + free(partitions); + return -1; + } + + if (write_mbr_partitions(dev, partitions, count, disk_uuid)) { + printf("MBR: failed to write partitions to the device\n"); + free(partitions); + return -1; + } + + return 0; +} + +static int do_verify_mbr(struct blk_desc *dev, const char *str) +{ + unsigned long disk_uuid = 0; + struct disk_partition *partitions; + int blksz = dev->blksz; + int count, i, ret = 1; + + if (str_to_partitions(str, blksz, &disk_uuid, &partitions, &count)) { + printf("MBR: failed to setup partitions from \"%s\"\n", str); + return -1; + } + + for (i = 0; i < count; i++) { + struct disk_partition p; + + if (part_get_info(dev, i+1, &p)) + goto fail; + + if ((partitions[i].size && p.size < partitions[i].size) || + (partitions[i].start && p.start < partitions[i].start) || + (p.sys_ind != partitions[i].sys_ind)) + goto fail; + } + ret = 0; +fail: + free(partitions); + return ret; +} + +static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *parts = NULL; + int ret = CMD_RET_SUCCESS; + int dev = 0; + char *ep; + struct blk_desc *blk_dev_desc = NULL; + + if (argc != 4 && argc != 5) + return CMD_RET_USAGE; + + dev = (int)simple_strtoul(argv[3], &ep, 10); + if (!ep || ep[0] != '\0') { + printf("'%s' is not a number\n", argv[3]); + return CMD_RET_USAGE; + } + blk_dev_desc = blk_get_dev(argv[2], dev); + if (!blk_dev_desc) { + printf("%s: %s dev %d NOT available\n", + __func__, argv[2], dev); + return CMD_RET_FAILURE; + } + + if ((strcmp(argv[1], "write") == 0)) { + parts = (argc == 5) ? argv[4] : env_get("mbr_parts"); + printf("MBR: write "); + ret = do_write_mbr(blk_dev_desc, parts); + } else if ((strcmp(argv[1], "verify") == 0)) { + printf("MBR: verify "); + parts = (argc == 5) ? argv[4] : env_get("mbr_parts"); + ret = do_verify_mbr(blk_dev_desc, parts); + } else { + return CMD_RET_USAGE; + } + + if (ret) { + printf("error!\n"); + return CMD_RET_FAILURE; + } + + printf("success!\n"); + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr, + "MBR (Master Boot Record)", + "<command> <interface> <dev> <partitions_list>\n" + " - MBR partition table restoration utility\n" + " Restore or check partition information on a device connected\n" + " to the given block interface\n" + " Example usage:\n" + " mbr write mmc 0 [\"${mbr_parts}\"]\n" + " mbr verify mmc 0 [\"${partitions}\"]\n" +); diff --git a/roms/u-boot/cmd/md5sum.c b/roms/u-boot/cmd/md5sum.c new file mode 100644 index 000000000..5ae3ddf85 --- /dev/null +++ b/roms/u-boot/cmd/md5sum.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <mapmem.h> +#include <u-boot/md5.h> +#include <asm/io.h> + +/* + * Store the resulting sum to an address or variable + */ +static void store_result(const u8 *sum, const char *dest) +{ + unsigned int i; + + if (*dest == '*') { + u8 *ptr; + + ptr = (u8 *)simple_strtoul(dest + 1, NULL, 16); + for (i = 0; i < 16; i++) + *ptr++ = sum[i]; + } else { + char str_output[33]; + char *str_ptr = str_output; + + for (i = 0; i < 16; i++) { + sprintf(str_ptr, "%02x", sum[i]); + str_ptr += 2; + } + env_set(dest, str_output); + } +} + +#ifdef CONFIG_MD5SUM_VERIFY +static int parse_verify_sum(char *verify_str, u8 *vsum) +{ + if (*verify_str == '*') { + u8 *ptr; + + ptr = (u8 *)simple_strtoul(verify_str + 1, NULL, 16); + memcpy(vsum, ptr, 16); + } else { + unsigned int i; + char *vsum_str; + + if (strlen(verify_str) == 32) + vsum_str = verify_str; + else { + vsum_str = env_get(verify_str); + if (vsum_str == NULL || strlen(vsum_str) != 32) + return 1; + } + + for (i = 0; i < 16; i++) { + char *nullp = vsum_str + (i + 1) * 2; + char end = *nullp; + + *nullp = '\0'; + *(u8 *)(vsum + i) = + simple_strtoul(vsum_str + (i * 2), NULL, 16); + *nullp = end; + } + } + return 0; +} + +int do_md5sum(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr, len; + unsigned int i; + u8 output[16]; + u8 vsum[16]; + int verify = 0; + int ac; + char * const *av; + void *buf; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; + if (strcmp(*av, "-v") == 0) { + verify = 1; + av++; + ac--; + if (ac < 3) + return CMD_RET_USAGE; + } + + addr = simple_strtoul(*av++, NULL, 16); + len = simple_strtoul(*av++, NULL, 16); + + buf = map_sysmem(addr, len); + md5_wd(buf, len, output, CHUNKSZ_MD5); + unmap_sysmem(buf); + + if (!verify) { + printf("md5 for %08lx ... %08lx ==> ", addr, addr + len - 1); + for (i = 0; i < 16; i++) + printf("%02x", output[i]); + printf("\n"); + + if (ac > 2) + store_result(output, *av); + } else { + char *verify_str = *av++; + + if (parse_verify_sum(verify_str, vsum)) { + printf("ERROR: %s does not contain a valid md5 sum\n", + verify_str); + return 1; + } + if (memcmp(output, vsum, 16) != 0) { + printf("md5 for %08lx ... %08lx ==> ", addr, + addr + len - 1); + for (i = 0; i < 16; i++) + printf("%02x", output[i]); + printf(" != "); + for (i = 0; i < 16; i++) + printf("%02x", vsum[i]); + printf(" ** ERROR **\n"); + return 1; + } + } + + return 0; +} +#else +static int do_md5sum(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long addr, len; + unsigned int i; + u8 output[16]; + void *buf; + + if (argc < 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + len = simple_strtoul(argv[2], NULL, 16); + + buf = map_sysmem(addr, len); + md5_wd(buf, len, output, CHUNKSZ_MD5); + unmap_sysmem(buf); + + printf("md5 for %08lx ... %08lx ==> ", addr, addr + len - 1); + for (i = 0; i < 16; i++) + printf("%02x", output[i]); + printf("\n"); + + if (argc > 3) + store_result(output, argv[3]); + + return 0; +} +#endif + +#ifdef CONFIG_MD5SUM_VERIFY +U_BOOT_CMD( + md5sum, 5, 1, do_md5sum, + "compute MD5 message digest", + "address count [[*]sum]\n" + " - compute MD5 message digest [save to sum]\n" + "md5sum -v address count [*]sum\n" + " - verify md5sum of memory area" +); +#else +U_BOOT_CMD( + md5sum, 4, 1, do_md5sum, + "compute MD5 message digest", + "address count [[*]sum]\n" + " - compute MD5 message digest [save to sum]" +); +#endif diff --git a/roms/u-boot/cmd/mdio.c b/roms/u-boot/cmd/mdio.c new file mode 100644 index 000000000..cfa45ad12 --- /dev/null +++ b/roms/u-boot/cmd/mdio.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 Freescale Semiconductor, Inc + * Andy Fleming + */ + +/* + * MDIO Commands + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <miiphy.h> +#include <phy.h> + +static char last_op[2]; +static uint last_data; +static uint last_addr_lo; +static uint last_addr_hi; +static uint last_devad_lo; +static uint last_devad_hi; +static uint last_reg_lo; +static uint last_reg_hi; + +static int extract_range(char *input, int *plo, int *phi) +{ + char *end; + *plo = simple_strtol(input, &end, 16); + if (end == input) + return -1; + + if ((*end == '-') && *(++end)) + *phi = simple_strtol(end, NULL, 16); + else if (*end == '\0') + *phi = *plo; + else + return -1; + + return 0; +} + +static int mdio_write_ranges(struct mii_dev *bus, + int addrlo, + int addrhi, int devadlo, int devadhi, + int reglo, int reghi, unsigned short data, + int extended) +{ + struct phy_device *phydev; + int addr, devad, reg; + int err = 0; + + for (addr = addrlo; addr <= addrhi; addr++) { + phydev = bus->phymap[addr]; + + for (devad = devadlo; devad <= devadhi; devad++) { + for (reg = reglo; reg <= reghi; reg++) { + if (!phydev) + err = bus->write(bus, addr, devad, + reg, data); + else if (!extended) + err = phy_write_mmd(phydev, devad, + reg, data); + else + err = phydev->drv->writeext(phydev, + addr, devad, reg, data); + + if (err) + goto err_out; + } + } + } + +err_out: + return err; +} + +static int mdio_read_ranges(struct mii_dev *bus, + int addrlo, + int addrhi, int devadlo, int devadhi, + int reglo, int reghi, int extended) +{ + int addr, devad, reg; + struct phy_device *phydev; + + printf("Reading from bus %s\n", bus->name); + for (addr = addrlo; addr <= addrhi; addr++) { + phydev = bus->phymap[addr]; + printf("PHY at address %x:\n", addr); + + for (devad = devadlo; devad <= devadhi; devad++) { + for (reg = reglo; reg <= reghi; reg++) { + int val; + + if (!phydev) + val = bus->read(bus, addr, devad, reg); + else if (!extended) + val = phy_read_mmd(phydev, devad, reg); + else + val = phydev->drv->readext(phydev, addr, + devad, reg); + + if (val < 0) { + printf("Error\n"); + + return val; + } + + if (devad >= 0) + printf("%d.", devad); + + printf("%d - 0x%x\n", reg, val & 0xffff); + } + } + } + + return 0; +} + +/* The register will be in the form [a[-b].]x[-y] */ +static int extract_reg_range(char *input, int *devadlo, int *devadhi, + int *reglo, int *reghi) +{ + char *regstr; + + /* use strrchr to find the last string after a '.' */ + regstr = strrchr(input, '.'); + + /* If it exists, extract the devad(s) */ + if (regstr) { + char devadstr[32]; + + strncpy(devadstr, input, regstr - input); + devadstr[regstr - input] = '\0'; + + if (extract_range(devadstr, devadlo, devadhi)) + return -1; + + regstr++; + } else { + /* Otherwise, we have no devad, and we just got regs */ + *devadlo = *devadhi = MDIO_DEVAD_NONE; + + regstr = input; + } + + return extract_range(regstr, reglo, reghi); +} + +static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, + struct phy_device **phydev, + int *addrlo, int *addrhi) +{ + struct phy_device *dev = *phydev; + + if ((argc < 1) || (argc > 2)) + return -1; + + /* If there are two arguments, it's busname addr */ + if (argc == 2) { + *bus = miiphy_get_dev_by_name(argv[0]); + + if (!*bus) + return -1; + + return extract_range(argv[1], addrlo, addrhi); + } + + /* It must be one argument, here */ + + /* + * This argument can be one of two things: + * 1) Ethernet device name + * 2) Just an address (use the previously-used bus) + * + * We check all buses for a PHY which is connected to an ethernet + * device by the given name. If none are found, we call + * extract_range() on the string, and see if it's an address range. + */ + dev = mdio_phydev_for_ethname(argv[0]); + + if (dev) { + *addrlo = *addrhi = dev->addr; + *bus = dev->bus; + + return 0; + } + + /* It's an address or nothing useful */ + return extract_range(argv[0], addrlo, addrhi); +} + +/* ---------------------------------------------------------------- */ +static int do_mdio(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char op[2]; + int addrlo, addrhi, reglo, reghi, devadlo, devadhi; + unsigned short data; + int pos = argc - 1; + struct mii_dev *bus; + struct phy_device *phydev = NULL; + int extended = 0; + + if (argc < 2) + return CMD_RET_USAGE; + +#ifdef CONFIG_DM_MDIO + /* probe DM MII device before any operation so they are all accesible */ + dm_mdio_probe_devices(); +#endif + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + op[0] = argv[1][0]; + addrlo = last_addr_lo; + addrhi = last_addr_hi; + devadlo = last_devad_lo; + devadhi = last_devad_hi; + reglo = last_reg_lo; + reghi = last_reg_hi; + data = last_data; + + bus = mdio_get_current_dev(); + + if (flag & CMD_FLAG_REPEAT) + op[0] = last_op[0]; + + if (strlen(argv[1]) > 1) { + op[1] = argv[1][1]; + if (op[1] == 'x') { + phydev = mdio_phydev_for_ethname(argv[2]); + + if (phydev) { + addrlo = phydev->addr; + addrhi = addrlo; + bus = phydev->bus; + extended = 1; + } else { + return CMD_RET_FAILURE; + } + + if (!phydev->drv || + (!phydev->drv->writeext && (op[0] == 'w')) || + (!phydev->drv->readext && (op[0] == 'r'))) { + puts("PHY does not have extended functions\n"); + return CMD_RET_FAILURE; + } + } + } + + switch (op[0]) { + case 'w': + if (pos > 1) + data = simple_strtoul(argv[pos--], NULL, 16); + /* Intentional fall-through - Get reg for read and write */ + case 'r': + if (pos > 1) + if (extract_reg_range(argv[pos--], &devadlo, &devadhi, + ®lo, ®hi)) + return CMD_RET_FAILURE; + /* Intentional fall-through - Get phy for all commands */ + default: + if (pos > 1) + if (extract_phy_range(&argv[2], pos - 1, &bus, + &phydev, &addrlo, &addrhi)) + return CMD_RET_FAILURE; + + break; + } + + if (!bus) { + puts("No MDIO bus found\n"); + return CMD_RET_FAILURE; + } + + if (op[0] == 'l') { + mdio_list_devices(); + + return 0; + } + + /* Save the chosen bus */ + miiphy_set_current_dev(bus->name); + + switch (op[0]) { + case 'w': + mdio_write_ranges(bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, data, extended); + break; + + case 'r': + mdio_read_ranges(bus, addrlo, addrhi, devadlo, devadhi, + reglo, reghi, extended); + break; + } + + /* + * Save the parameters for repeats. + */ + last_op[0] = op[0]; + last_addr_lo = addrlo; + last_addr_hi = addrhi; + last_devad_lo = devadlo; + last_devad_hi = devadhi; + last_reg_lo = reglo; + last_reg_hi = reghi; + last_data = data; + + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + mdio, 6, 1, do_mdio, + "MDIO utility commands", + "list - List MDIO buses\n" + "mdio read <phydev> [<devad>.]<reg> - " + "read PHY's register at <devad>.<reg>\n" + "mdio write <phydev> [<devad>.]<reg> <data> - " + "write PHY's register at <devad>.<reg>\n" + "mdio rx <phydev> [<devad>.]<reg> - " + "read PHY's extended register at <devad>.<reg>\n" + "mdio wx <phydev> [<devad>.]<reg> <data> - " + "write PHY's extended register at <devad>.<reg>\n" + "<phydev> may be:\n" + " <busname> <addr>\n" + " <addr>\n" + " <eth name>\n" + "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n" +); diff --git a/roms/u-boot/cmd/mem.c b/roms/u-boot/cmd/mem.c new file mode 100644 index 000000000..1eb83b757 --- /dev/null +++ b/roms/u-boot/cmd/mem.c @@ -0,0 +1,1456 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Memory Functions + * + * Copied from FADS ROM, Dan Malek (dmalek@jlc.net) + */ + +#include <common.h> +#include <console.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <flash.h> +#include <hash.h> +#include <log.h> +#include <mapmem.h> +#include <rand.h> +#include <watchdog.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/ctype.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Create a compile-time value */ +#ifdef MEM_SUPPORT_64BIT_DATA +#define SUPPORT_64BIT_DATA 1 +#define HELP_Q ", .q" +#else +#define SUPPORT_64BIT_DATA 0 +#define HELP_Q "" +#endif + +static int mod_mem(struct cmd_tbl *, int, int, int, char * const []); + +/* Display values from last command. + * Memory modify remembered values are different from display memory. + */ +static ulong dp_last_addr, dp_last_size; +static ulong dp_last_length = 0x40; +static ulong mm_last_addr, mm_last_size; + +static ulong base_address = 0; +#ifdef CONFIG_CMD_MEM_SEARCH +static ulong dp_last_ms_length; +static u8 search_buf[64]; +static uint search_len; +#endif + +/* Memory Display + * + * Syntax: + * md{.b, .w, .l, .q} {addr} {len} + */ +#define DISP_LINE_LEN 16 +static int do_mem_md(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, length, bytes; + const void *buf; + int size; + int rc = 0; + + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = dp_last_addr; + size = dp_last_size; + length = dp_last_length; + + if (argc < 2) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + /* If another parameter, it is the length to display. + * Length is the number of objects, not number of bytes. + */ + if (argc > 2) + length = simple_strtoul(argv[2], NULL, 16); + } + + bytes = size * length; + buf = map_sysmem(addr, bytes); + + /* Print the lines. */ + print_buffer(addr, buf, size, length, DISP_LINE_LEN / size); + addr += bytes; + unmap_sysmem(buf); + + dp_last_addr = addr; + dp_last_length = length; + dp_last_size = size; + return (rc); +} + +static int do_mem_mm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return mod_mem (cmdtp, 1, flag, argc, argv); +} + +static int do_mem_nm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return mod_mem (cmdtp, 0, flag, argc, argv); +} + +static int do_mem_mw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong writeval; /* 64-bit if SUPPORT_64BIT_DATA */ + ulong addr, count; + int size; + void *buf, *start; + ulong bytes; + + if ((argc < 3) || (argc > 4)) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 1) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + /* Get the value to write. + */ + if (SUPPORT_64BIT_DATA) + writeval = simple_strtoull(argv[2], NULL, 16); + else + writeval = simple_strtoul(argv[2], NULL, 16); + + /* Count ? */ + if (argc == 4) { + count = simple_strtoul(argv[3], NULL, 16); + } else { + count = 1; + } + + bytes = size * count; + start = map_sysmem(addr, bytes); + buf = start; + while (count-- > 0) { + if (size == 4) + *((u32 *)buf) = (u32)writeval; + else if (SUPPORT_64BIT_DATA && size == 8) + *((ulong *)buf) = writeval; + else if (size == 2) + *((u16 *)buf) = (u16)writeval; + else + *((u8 *)buf) = (u8)writeval; + buf += size; + } + unmap_sysmem(start); + return 0; +} + +#ifdef CONFIG_CMD_MX_CYCLIC +static int do_mem_mdc(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + ulong count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = simple_strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_md (NULL, 0, 3, argv); + + /* delay for <count> ms... */ + for (i=0; i<count; i++) + udelay(1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return 0; + } + } + + return 0; +} + +static int do_mem_mwc(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + ulong count; + + if (argc < 4) + return CMD_RET_USAGE; + + count = simple_strtoul(argv[3], NULL, 10); + + for (;;) { + do_mem_mw (NULL, 0, 3, argv); + + /* delay for <count> ms... */ + for (i=0; i<count; i++) + udelay(1000); + + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return 0; + } + } + + return 0; +} +#endif /* CONFIG_CMD_MX_CYCLIC */ + +static int do_mem_cmp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr1, addr2, count, ngood, bytes; + int size; + int rcode = 0; + const char *type; + const void *buf1, *buf2, *base; + ulong word1, word2; /* 64-bit if SUPPORT_64BIT_DATA */ + + if (argc != 4) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + type = size == 8 ? "double word" : + size == 4 ? "word" : + size == 2 ? "halfword" : "byte"; + + addr1 = simple_strtoul(argv[1], NULL, 16); + addr1 += base_address; + + addr2 = simple_strtoul(argv[2], NULL, 16); + addr2 += base_address; + + count = simple_strtoul(argv[3], NULL, 16); + + bytes = size * count; + base = buf1 = map_sysmem(addr1, bytes); + buf2 = map_sysmem(addr2, bytes); + for (ngood = 0; ngood < count; ++ngood) { + if (size == 4) { + word1 = *(u32 *)buf1; + word2 = *(u32 *)buf2; + } else if (SUPPORT_64BIT_DATA && size == 8) { + word1 = *(ulong *)buf1; + word2 = *(ulong *)buf2; + } else if (size == 2) { + word1 = *(u16 *)buf1; + word2 = *(u16 *)buf2; + } else { + word1 = *(u8 *)buf1; + word2 = *(u8 *)buf2; + } + if (word1 != word2) { + ulong offset = buf1 - base; + printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n", + type, (ulong)(addr1 + offset), size, word1, + type, (ulong)(addr2 + offset), size, word2); + rcode = 1; + break; + } + + buf1 += size; + buf2 += size; + + /* reset watchdog from time to time */ + if ((ngood % (64 << 10)) == 0) + WATCHDOG_RESET(); + } + unmap_sysmem(buf1); + unmap_sysmem(buf2); + + printf("Total of %ld %s(s) were the same\n", ngood, type); + return rcode; +} + +static int do_mem_cp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, dest, count; + void *src, *dst; + int size; + + if (argc != 4) + return CMD_RET_USAGE; + + /* Check for size specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + + dest = simple_strtoul(argv[2], NULL, 16); + dest += base_address; + + count = simple_strtoul(argv[3], NULL, 16); + + if (count == 0) { + puts ("Zero length ???\n"); + return 1; + } + + src = map_sysmem(addr, count * size); + dst = map_sysmem(dest, count * size); + +#ifdef CONFIG_MTD_NOR_FLASH + /* check if we are copying to Flash */ + if (addr2info((ulong)dst)) { + int rc; + + puts ("Copy to Flash... "); + + rc = flash_write((char *)src, (ulong)dst, count * size); + if (rc != 0) { + flash_perror(rc); + unmap_sysmem(src); + unmap_sysmem(dst); + return (1); + } + puts ("done\n"); + unmap_sysmem(src); + unmap_sysmem(dst); + return 0; + } +#endif + + memcpy(dst, src, count * size); + + unmap_sysmem(src); + unmap_sysmem(dst); + return 0; +} + +#ifdef CONFIG_CMD_MEM_SEARCH +static int do_mem_search(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, length, bytes, offset; + u8 *ptr, *end, *buf; + bool quiet = false; + ulong last_pos; /* Offset of last match in 'size' units*/ + ulong last_addr; /* Address of last displayed line */ + int limit = 10; + int used_len; + int count; + int size; + int i; + + /* We use the last specified parameters, unless new ones are entered */ + addr = dp_last_addr; + size = dp_last_size; + length = dp_last_ms_length; + + if (argc < 3) + return CMD_RET_USAGE; + + if (!(flag & CMD_FLAG_REPEAT)) { + /* + * Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + size = cmd_get_data_size(argv[0], 4); + if (size < 0 && size != CMD_DATA_SIZE_STR) + return 1; + + argc--; + argv++; + while (argc && *argv[0] == '-') { + int ch = argv[0][1]; + + if (ch == 'q') + quiet = true; + else if (ch == 'l' && isxdigit(argv[0][2])) + limit = simple_strtoul(argv[0] + 2, NULL, 16); + else + return CMD_RET_USAGE; + argc--; + argv++; + } + + /* Address is specified since argc > 1 */ + addr = simple_strtoul(argv[0], NULL, 16); + addr += base_address; + + /* Length is the number of objects, not number of bytes */ + length = simple_strtoul(argv[1], NULL, 16); + + /* Read the bytes to search for */ + end = search_buf + sizeof(search_buf); + for (i = 2, ptr = search_buf; i < argc && ptr < end; i++) { + if (MEM_SUPPORT_64BIT_DATA && size == 8) { + u64 val = simple_strtoull(argv[i], NULL, 16); + + *(u64 *)ptr = val; + } else if (size == -2) { /* string */ + int len = min(strlen(argv[i]), + (size_t)(end - ptr)); + + memcpy(ptr, argv[i], len); + ptr += len; + continue; + } else { + u32 val = simple_strtoul(argv[i], NULL, 16); + + switch (size) { + case 1: + *ptr = val; + break; + case 2: + *(u16 *)ptr = val; + break; + case 4: + *(u32 *)ptr = val; + break; + } + } + ptr += size; + } + search_len = ptr - search_buf; + } + + /* Do the search */ + if (size == -2) + size = 1; + bytes = size * length; + buf = map_sysmem(addr, bytes); + last_pos = 0; + last_addr = 0; + count = 0; + for (offset = 0; + offset < bytes && offset <= bytes - search_len && count < limit; + offset += size) { + void *ptr = buf + offset; + + if (!memcmp(ptr, search_buf, search_len)) { + uint align = (addr + offset) & 0xf; + ulong match = addr + offset; + + if (!count || (last_addr & ~0xf) != (match & ~0xf)) { + if (!quiet) { + if (count) + printf("--\n"); + print_buffer(match - align, ptr - align, + size, + ALIGN(search_len + align, + 16) / size, 0); + } + last_addr = match; + last_pos = offset / size; + } + count++; + } + } + if (!quiet) { + printf("%d match%s", count, count == 1 ? "" : "es"); + if (count == limit) + printf(" (repeat command to check for more)"); + printf("\n"); + } + env_set_hex("memmatches", count); + env_set_hex("memaddr", last_addr); + env_set_hex("mempos", last_pos); + + unmap_sysmem(buf); + + used_len = offset / size; + dp_last_addr = addr + used_len; + dp_last_size = size; + dp_last_ms_length = length < used_len ? 0 : length - used_len; + + return count ? 0 : CMD_RET_FAILURE; +} +#endif + +static int do_mem_base(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc > 1) { + /* Set new base address. + */ + base_address = simple_strtoul(argv[1], NULL, 16); + } + /* Print the current base address. + */ + printf("Base Address: 0x%08lx\n", base_address); + return 0; +} + +static int do_mem_loop(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, length, i, bytes; + int size; + volatile ulong *llp; /* 64-bit if SUPPORT_64BIT_DATA */ + volatile u32 *longp; + volatile u16 *shortp; + volatile u8 *cp; + const void *buf; + + if (argc < 3) + return CMD_RET_USAGE; + + /* + * Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is always specified. + */ + addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[2], NULL, 16); + + bytes = size * length; + buf = map_sysmem(addr, bytes); + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { + if (SUPPORT_64BIT_DATA && size == 8) { + llp = (ulong *)buf; + for (;;) + i = *llp; + } + if (size == 4) { + longp = (u32 *)buf; + for (;;) + i = *longp; + } + if (size == 2) { + shortp = (u16 *)buf; + for (;;) + i = *shortp; + } + cp = (u8 *)buf; + for (;;) + i = *cp; + } + + if (SUPPORT_64BIT_DATA && size == 8) { + for (;;) { + llp = (ulong *)buf; + i = length; + while (i-- > 0) + *llp++; + } + } + if (size == 4) { + for (;;) { + longp = (u32 *)buf; + i = length; + while (i-- > 0) + *longp++; + } + } + if (size == 2) { + for (;;) { + shortp = (u16 *)buf; + i = length; + while (i-- > 0) + *shortp++; + } + } + for (;;) { + cp = (u8 *)buf; + i = length; + while (i-- > 0) + *cp++; + } + unmap_sysmem(buf); + + return 0; +} + +#ifdef CONFIG_LOOPW +static int do_mem_loopw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, length, i, bytes; + int size; + volatile ulong *llp; /* 64-bit if SUPPORT_64BIT_DATA */ + ulong data; /* 64-bit if SUPPORT_64BIT_DATA */ + volatile u32 *longp; + volatile u16 *shortp; + volatile u8 *cp; + void *buf; + + if (argc < 4) + return CMD_RET_USAGE; + + /* + * Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is always specified. + */ + addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is the number of objects, not number of bytes. + */ + length = simple_strtoul(argv[2], NULL, 16); + + /* data to write */ + if (SUPPORT_64BIT_DATA) + data = simple_strtoull(argv[3], NULL, 16); + else + data = simple_strtoul(argv[3], NULL, 16); + + bytes = size * length; + buf = map_sysmem(addr, bytes); + + /* We want to optimize the loops to run as fast as possible. + * If we have only one object, just run infinite loops. + */ + if (length == 1) { + if (SUPPORT_64BIT_DATA && size == 8) { + llp = (ulong *)buf; + for (;;) + *llp = data; + } + if (size == 4) { + longp = (u32 *)buf; + for (;;) + *longp = data; + } + if (size == 2) { + shortp = (u16 *)buf; + for (;;) + *shortp = data; + } + cp = (u8 *)buf; + for (;;) + *cp = data; + } + + if (SUPPORT_64BIT_DATA && size == 8) { + for (;;) { + llp = (ulong *)buf; + i = length; + while (i-- > 0) + *llp++ = data; + } + } + if (size == 4) { + for (;;) { + longp = (u32 *)buf; + i = length; + while (i-- > 0) + *longp++ = data; + } + } + if (size == 2) { + for (;;) { + shortp = (u16 *)buf; + i = length; + while (i-- > 0) + *shortp++ = data; + } + } + for (;;) { + cp = (u8 *)buf; + i = length; + while (i-- > 0) + *cp++ = data; + } +} +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +static ulong mem_test_alt(vu_long *buf, ulong start_addr, ulong end_addr, + vu_long *dummy) +{ + vu_long *addr; + ulong errs = 0; + ulong val, readback; + int j; + vu_long offset; + vu_long test_offset; + vu_long pattern; + vu_long temp; + vu_long anti_pattern; + vu_long num_words; + static const ulong bitpattern[] = { + 0x00000001, /* single bit */ + 0x00000003, /* two adjacent bits */ + 0x00000007, /* three adjacent bits */ + 0x0000000F, /* four adjacent bits */ + 0x00000005, /* two non-adjacent bits */ + 0x00000015, /* three non-adjacent bits */ + 0x00000055, /* four non-adjacent bits */ + 0xaaaaaaaa, /* alternating 1/0 */ + }; + + num_words = (end_addr - start_addr) / sizeof(vu_long); + + /* + * Data line test: write a pattern to the first + * location, write the 1's complement to a 'parking' + * address (changes the state of the data bus so a + * floating bus doesn't give a false OK), and then + * read the value back. Note that we read it back + * into a variable because the next time we read it, + * it might be right (been there, tough to explain to + * the quality guys why it prints a failure when the + * "is" and "should be" are obviously the same in the + * error message). + * + * Rather than exhaustively testing, we test some + * patterns by shifting '1' bits through a field of + * '0's and '0' bits through a field of '1's (i.e. + * pattern and ~pattern). + */ + addr = buf; + for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) { + val = bitpattern[j]; + for (; val != 0; val <<= 1) { + *addr = val; + *dummy = ~val; /* clear the test data off the bus */ + readback = *addr; + if (readback != val) { + printf("FAILURE (data line): " + "expected %08lx, actual %08lx\n", + val, readback); + errs++; + if (ctrlc()) + return -1; + } + *addr = ~val; + *dummy = val; + readback = *addr; + if (readback != ~val) { + printf("FAILURE (data line): " + "Is %08lx, should be %08lx\n", + readback, ~val); + errs++; + if (ctrlc()) + return -1; + } + } + } + + /* + * Based on code whose Original Author and Copyright + * information follows: Copyright (c) 1998 by Michael + * Barr. This software is placed into the public + * domain and may be used for any purpose. However, + * this notice must not be changed or removed and no + * warranty is either expressed or implied by its + * publication or distribution. + */ + + /* + * Address line test + + * Description: Test the address bus wiring in a + * memory region by performing a walking + * 1's test on the relevant bits of the + * address and checking for aliasing. + * This test will find single-bit + * address failures such as stuck-high, + * stuck-low, and shorted pins. The base + * address and size of the region are + * selected by the caller. + + * Notes: For best results, the selected base + * address should have enough LSB 0's to + * guarantee single address bit changes. + * For example, to test a 64-Kbyte + * region, select a base address on a + * 64-Kbyte boundary. Also, select the + * region size as a power-of-two if at + * all possible. + * + * Returns: 0 if the test succeeds, 1 if the test fails. + */ + pattern = (vu_long) 0xaaaaaaaa; + anti_pattern = (vu_long) 0x55555555; + + debug("%s:%d: length = 0x%.8lx\n", __func__, __LINE__, num_words); + /* + * Write the default pattern at each of the + * power-of-two offsets. + */ + for (offset = 1; offset < num_words; offset <<= 1) + addr[offset] = pattern; + + /* + * Check for address bits stuck high. + */ + test_offset = 0; + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if (temp != pattern) { + printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + WATCHDOG_RESET(); + + /* + * Check for addr bits stuck low or shorted. + */ + for (test_offset = 1; test_offset < num_words; test_offset <<= 1) { + addr[test_offset] = anti_pattern; + + for (offset = 1; offset < num_words; offset <<= 1) { + temp = addr[offset]; + if ((temp != pattern) && (offset != test_offset)) { + printf("\nFAILURE: Address bit stuck low or" + " shorted @ 0x%.8lx: expected 0x%.8lx," + " actual 0x%.8lx\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + } + addr[test_offset] = pattern; + } + + /* + * Description: Test the integrity of a physical + * memory device by performing an + * increment/decrement test over the + * entire region. In the process every + * storage bit in the device is tested + * as a zero and a one. The base address + * and the size of the region are + * selected by the caller. + * + * Returns: 0 if the test succeeds, 1 if the test fails. + */ + num_words++; + + /* + * Fill memory with a known pattern. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + addr[offset] = pattern; + } + + /* + * Check each location and invert it for the second pass. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + temp = addr[offset]; + if (temp != pattern) { + printf("\nFAILURE (read/write) @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx)\n", + start_addr + offset*sizeof(vu_long), + pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + + anti_pattern = ~pattern; + addr[offset] = anti_pattern; + } + + /* + * Check each location for the inverted pattern and zero it. + */ + for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { + WATCHDOG_RESET(); + anti_pattern = ~pattern; + temp = addr[offset]; + if (temp != anti_pattern) { + printf("\nFAILURE (read/write): @ 0x%.8lx:" + " expected 0x%.8lx, actual 0x%.8lx)\n", + start_addr + offset*sizeof(vu_long), + anti_pattern, temp); + errs++; + if (ctrlc()) + return -1; + } + addr[offset] = 0; + } + + return errs; +} + +static int compare_regions(volatile unsigned long *bufa, + volatile unsigned long *bufb, size_t count) +{ + volatile unsigned long *p1 = bufa; + volatile unsigned long *p2 = bufb; + int errs = 0; + size_t i; + + for (i = 0; i < count; i++, p1++, p2++) { + if (*p1 != *p2) { + printf("FAILURE: 0x%08lx != 0x%08lx (delta=0x%08lx -> bit %ld) at offset 0x%08lx\n", + (unsigned long)*p1, (unsigned long)*p2, + *p1 ^ *p2, __ffs(*p1 ^ *p2), + (unsigned long)(i * sizeof(unsigned long))); + errs++; + } + } + + return errs; +} + +static ulong test_bitflip_comparison(volatile unsigned long *bufa, + volatile unsigned long *bufb, size_t count) +{ + volatile unsigned long *p1 = bufa; + volatile unsigned long *p2 = bufb; + unsigned int j, k; + unsigned long q; + size_t i; + int max; + int errs = 0; + + max = sizeof(unsigned long) * 8; + for (k = 0; k < max; k++) { + q = 0x00000001L << k; + for (j = 0; j < 8; j++) { + WATCHDOG_RESET(); + q = ~q; + p1 = (volatile unsigned long *)bufa; + p2 = (volatile unsigned long *)bufb; + for (i = 0; i < count; i++) + *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; + + errs += compare_regions(bufa, bufb, count); + } + + if (ctrlc()) + return -1UL; + } + + return errs; +} + +static ulong mem_test_bitflip(vu_long *buf, ulong start, ulong end) +{ + /* + * Split the specified range into two halves. + * Note that mtest range is inclusive of start,end. + * Bitflip test instead uses a count (of 32-bit words). + */ + ulong half_size = (end - start + 1) / 2 / sizeof(unsigned long); + + return test_bitflip_comparison(buf, buf + half_size, half_size); +} + +static ulong mem_test_quick(vu_long *buf, ulong start_addr, ulong end_addr, + vu_long pattern, int iteration) +{ + vu_long *end; + vu_long *addr; + ulong errs = 0; + ulong incr, length; + ulong val, readback; + + /* Alternate the pattern */ + incr = 1; + if (iteration & 1) { + incr = -incr; + /* + * Flip the pattern each time to make lots of zeros and + * then, the next time, lots of ones. We decrement + * the "negative" patterns and increment the "positive" + * patterns to preserve this feature. + */ + if (pattern & 0x80000000) + pattern = -pattern; /* complement & increment */ + else + pattern = ~pattern; + } + length = (end_addr - start_addr) / sizeof(ulong); + end = buf + length; + printf("\rPattern %08lX Writing..." + "%12s" + "\b\b\b\b\b\b\b\b\b\b", + pattern, ""); + + for (addr = buf, val = pattern; addr < end; addr++) { + WATCHDOG_RESET(); + *addr = val; + val += incr; + } + + puts("Reading..."); + + for (addr = buf, val = pattern; addr < end; addr++) { + WATCHDOG_RESET(); + readback = *addr; + if (readback != val) { + ulong offset = addr - buf; + + printf("\nMem error @ 0x%08X: " + "found %08lX, expected %08lX\n", + (uint)(uintptr_t)(start_addr + offset*sizeof(vu_long)), + readback, val); + errs++; + if (ctrlc()) + return -1; + } + val += incr; + } + + return errs; +} + +/* + * Perform a memory test. A more complete alternative test can be + * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until + * interrupted by ctrl-c or by a failure of one of the sub-tests. + */ +static int do_mem_mtest(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong start, end; + vu_long scratch_space; + vu_long *buf, *dummy = &scratch_space; + ulong iteration_limit = 0; + ulong count = 0; + ulong errs = 0; /* number of errors, or -1 if interrupted */ + ulong pattern = 0; + int iteration; + + start = CONFIG_SYS_MEMTEST_START; + end = CONFIG_SYS_MEMTEST_END; + + if (argc > 1) + if (strict_strtoul(argv[1], 16, &start) < 0) + return CMD_RET_USAGE; + + if (argc > 2) + if (strict_strtoul(argv[2], 16, &end) < 0) + return CMD_RET_USAGE; + + if (argc > 3) + if (strict_strtoul(argv[3], 16, &pattern) < 0) + return CMD_RET_USAGE; + + if (argc > 4) + if (strict_strtoul(argv[4], 16, &iteration_limit) < 0) + return CMD_RET_USAGE; + + if (end < start) { + printf("Refusing to do empty test\n"); + return -1; + } + + printf("Testing %08lx ... %08lx:\n", start, end); + debug("%s:%d: start %#08lx end %#08lx\n", __func__, __LINE__, + start, end); + + buf = map_sysmem(start, end - start); + for (iteration = 0; + !iteration_limit || iteration < iteration_limit; + iteration++) { + if (ctrlc()) { + errs = -1UL; + break; + } + + printf("Iteration: %6d\r", iteration + 1); + debug("\n"); + if (IS_ENABLED(CONFIG_SYS_ALT_MEMTEST)) { + errs = mem_test_alt(buf, start, end, dummy); + if (errs == -1UL) + break; + if (IS_ENABLED(CONFIG_SYS_ALT_MEMTEST_BITFLIP)) { + count += errs; + errs = mem_test_bitflip(buf, start, end); + } + } else { + errs = mem_test_quick(buf, start, end, pattern, + iteration); + } + if (errs == -1UL) + break; + count += errs; + } + + unmap_sysmem((void *)buf); + + if (errs == -1UL) { + /* Memory test was aborted - write a newline to finish off */ + putc('\n'); + } + printf("Tested %d iteration(s) with %lu errors.\n", iteration, count); + + return errs != 0; +} +#endif /* CONFIG_CMD_MEMTEST */ + +/* Modify memory. + * + * Syntax: + * mm{.b, .w, .l, .q} {addr} + */ +static int +mod_mem(struct cmd_tbl *cmdtp, int incrflag, int flag, int argc, + char *const argv[]) +{ + ulong addr; + ulong i; /* 64-bit if SUPPORT_64BIT_DATA */ + int nbytes, size; + void *ptr = NULL; + + if (argc != 2) + return CMD_RET_USAGE; + + bootretry_reset_cmd_timeout(); /* got a good command to get here */ + /* We use the last specified parameters, unless new ones are + * entered. + */ + addr = mm_last_addr; + size = mm_last_size; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + /* New command specified. Check for a size specification. + * Defaults to long if no or incorrect specification. + */ + if ((size = cmd_get_data_size(argv[0], 4)) < 0) + return 1; + + /* Address is specified since argc > 1 + */ + addr = simple_strtoul(argv[1], NULL, 16); + addr += base_address; + } + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + ptr = map_sysmem(addr, size); + printf("%08lx:", addr); + if (size == 4) + printf(" %08x", *((u32 *)ptr)); + else if (SUPPORT_64BIT_DATA && size == 8) + printf(" %0lx", *((ulong *)ptr)); + else if (size == 2) + printf(" %04x", *((u16 *)ptr)); + else + printf(" %02x", *((u8 *)ptr)); + + nbytes = cli_readline(" ? "); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -size : size; + nbytes = 1; + /* good enough to not time out */ + bootretry_reset_cmd_timeout(); + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) { + break; /* timed out, exit the command */ + } +#endif + else { + char *endp; + if (SUPPORT_64BIT_DATA) + i = simple_strtoull(console_buffer, &endp, 16); + else + i = simple_strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { + /* good enough to not time out + */ + bootretry_reset_cmd_timeout(); + if (size == 4) + *((u32 *)ptr) = i; + else if (SUPPORT_64BIT_DATA && size == 8) + *((ulong *)ptr) = i; + else if (size == 2) + *((u16 *)ptr) = i; + else + *((u8 *)ptr) = i; + if (incrflag) + addr += size; + } + } + } while (nbytes); + if (ptr) + unmap_sysmem(ptr); + + mm_last_addr = addr; + mm_last_size = size; + return 0; +} + +#ifdef CONFIG_CMD_CRC32 + +static int do_mem_crc(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int flags = 0; + int ac; + char * const *av; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; +#ifdef CONFIG_CRC32_VERIFY + if (strcmp(*av, "-v") == 0) { + flags |= HASH_FLAG_VERIFY | HASH_FLAG_ENV; + av++; + ac--; + } +#endif + + return hash_command("crc32", flags, cmdtp, flag, ac, av); +} + +#endif + +#ifdef CONFIG_CMD_RANDOM +static int do_random(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long addr, len; + unsigned long seed; // NOT INITIALIZED ON PURPOSE + unsigned int *buf, *start; + unsigned char *buf8; + unsigned int i; + + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + len = simple_strtoul(argv[2], NULL, 16); + addr = simple_strtoul(argv[1], NULL, 16); + + if (argc == 4) { + seed = simple_strtoul(argv[3], NULL, 16); + if (seed == 0) { + printf("The seed cannot be 0. Using 0xDEADBEEF.\n"); + seed = 0xDEADBEEF; + } + } else { + seed = get_timer(0) ^ rand(); + } + + srand(seed); + start = map_sysmem(addr, len); + buf = start; + for (i = 0; i < (len / 4); i++) + *buf++ = rand(); + + buf8 = (unsigned char *)buf; + for (i = 0; i < (len % 4); i++) + *buf8++ = rand() & 0xFF; + + unmap_sysmem(start); + printf("%lu bytes filled with random data\n", len); + + return CMD_RET_SUCCESS; +} +#endif + +/**************************************************/ +U_BOOT_CMD( + md, 3, 1, do_mem_md, + "memory display", + "[.b, .w, .l" HELP_Q "] address [# of objects]" +); + + +U_BOOT_CMD( + mm, 2, 1, do_mem_mm, + "memory modify (auto-incrementing address)", + "[.b, .w, .l" HELP_Q "] address" +); + + +U_BOOT_CMD( + nm, 2, 1, do_mem_nm, + "memory modify (constant address)", + "[.b, .w, .l" HELP_Q "] address" +); + +U_BOOT_CMD( + mw, 4, 1, do_mem_mw, + "memory write (fill)", + "[.b, .w, .l" HELP_Q "] address value [count]" +); + +U_BOOT_CMD( + cp, 4, 1, do_mem_cp, + "memory copy", + "[.b, .w, .l" HELP_Q "] source target count" +); + +U_BOOT_CMD( + cmp, 4, 1, do_mem_cmp, + "memory compare", + "[.b, .w, .l" HELP_Q "] addr1 addr2 count" +); + +#ifdef CONFIG_CMD_MEM_SEARCH +/**************************************************/ +U_BOOT_CMD( + ms, 255, 1, do_mem_search, + "memory search", + "[.b, .w, .l" HELP_Q ", .s] [-q | -<n>] address #-of-objects <value>..." + " -q = quiet, -l<val> = match limit" +); +#endif + +#ifdef CONFIG_CMD_CRC32 + +#ifndef CONFIG_CRC32_VERIFY + +U_BOOT_CMD( + crc32, 4, 1, do_mem_crc, + "checksum calculation", + "address count [addr]\n - compute CRC32 checksum [save at addr]" +); + +#else /* CONFIG_CRC32_VERIFY */ + +U_BOOT_CMD( + crc32, 5, 1, do_mem_crc, + "checksum calculation", + "address count [addr]\n - compute CRC32 checksum [save at addr]\n" + "-v address count crc\n - verify crc of memory area" +); + +#endif /* CONFIG_CRC32_VERIFY */ + +#endif + +#ifdef CONFIG_CMD_MEMINFO +static int do_mem_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + puts("DRAM: "); + print_size(gd->ram_size, "\n"); + + return 0; +} +#endif + +U_BOOT_CMD( + base, 2, 1, do_mem_base, + "print or set address offset", + "\n - print address offset for memory commands\n" + "base off\n - set address offset for memory commands to 'off'" +); + +U_BOOT_CMD( + loop, 3, 1, do_mem_loop, + "infinite loop on address range", + "[.b, .w, .l" HELP_Q "] address number_of_objects" +); + +#ifdef CONFIG_LOOPW +U_BOOT_CMD( + loopw, 4, 1, do_mem_loopw, + "infinite write loop on address range", + "[.b, .w, .l" HELP_Q "] address number_of_objects data_to_write" +); +#endif /* CONFIG_LOOPW */ + +#ifdef CONFIG_CMD_MEMTEST +U_BOOT_CMD( + mtest, 5, 1, do_mem_mtest, + "simple RAM read/write test", + "[start [end [pattern [iterations]]]]" +); +#endif /* CONFIG_CMD_MEMTEST */ + +#ifdef CONFIG_CMD_MX_CYCLIC +U_BOOT_CMD( + mdc, 4, 1, do_mem_mdc, + "memory display cyclic", + "[.b, .w, .l" HELP_Q "] address count delay(ms)" +); + +U_BOOT_CMD( + mwc, 4, 1, do_mem_mwc, + "memory write cyclic", + "[.b, .w, .l" HELP_Q "] address value delay(ms)" +); +#endif /* CONFIG_CMD_MX_CYCLIC */ + +#ifdef CONFIG_CMD_MEMINFO +U_BOOT_CMD( + meminfo, 3, 1, do_mem_info, + "display memory information", + "" +); +#endif + +#ifdef CONFIG_CMD_RANDOM +U_BOOT_CMD( + random, 4, 0, do_random, + "fill memory with random pattern", + "<addr> <len> [<seed>]\n" + " - Fill 'len' bytes of memory starting at 'addr' with random data\n" +); +#endif diff --git a/roms/u-boot/cmd/mfsl.c b/roms/u-boot/cmd/mfsl.c new file mode 100644 index 000000000..31f5b36ad --- /dev/null +++ b/roms/u-boot/cmd/mfsl.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 Michal Simek + * + * Michal SIMEK <monstr@monstr.eu> + */ + +/* + * Microblaze FSL support + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <asm/asm.h> + +int do_frd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned int fslnum; + unsigned int num; + unsigned int blocking; + + if (argc < 2) + return CMD_RET_USAGE; + + fslnum = (unsigned int)simple_strtoul (argv[1], NULL, 16); + blocking = (unsigned int)simple_strtoul (argv[2], NULL, 16); + if (fslnum < 0 || fslnum >= XILINX_FSL_NUMBER) { + puts ("Bad number of FSL\n"); + return CMD_RET_USAGE; + } + + switch (fslnum) { +#if (XILINX_FSL_NUMBER > 0) + case 0: + switch (blocking) { + case 0: NGET (num, 0); + break; + case 1: NCGET (num, 0); + break; + case 2: GET (num, 0); + break; + case 3: CGET (num, 0); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 1) + case 1: + switch (blocking) { + case 0: NGET (num, 1); + break; + case 1: NCGET (num, 1); + break; + case 2: GET (num, 1); + break; + case 3: CGET (num, 1); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 2) + case 2: + switch (blocking) { + case 0: NGET (num, 2); + break; + case 1: NCGET (num, 2); + break; + case 2: GET (num, 2); + break; + case 3: CGET (num, 2); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 3) + case 3: + switch (blocking) { + case 0: NGET (num, 3); + break; + case 1: NCGET (num, 3); + break; + case 2: GET (num, 3); + break; + case 3: CGET (num, 3); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 4) + case 4: + switch (blocking) { + case 0: NGET (num, 4); + break; + case 1: NCGET (num, 4); + break; + case 2: GET (num, 4); + break; + case 3: CGET (num, 4); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 5) + case 5: + switch (blocking) { + case 0: NGET (num, 5); + break; + case 1: NCGET (num, 5); + break; + case 2: GET (num, 5); + break; + case 3: CGET (num, 5); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 6) + case 6: + switch (blocking) { + case 0: NGET (num, 6); + break; + case 1: NCGET (num, 6); + break; + case 2: GET (num, 6); + break; + case 3: CGET (num, 6); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 7) + case 7: + switch (blocking) { + case 0: NGET (num, 7); + break; + case 1: NCGET (num, 7); + break; + case 2: GET (num, 7); + break; + case 3: CGET (num, 7); + break; + default: + return 2; + } + break; +#endif + default: + return 1; + } + + printf ("%01x: 0x%08x - %s %s read\n", fslnum, num, + blocking < 2 ? "non blocking" : "blocking", + ((blocking == 1) || (blocking == 3)) ? "control" : "data" ); + return 0; +} + +int do_fwr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned int fslnum; + unsigned int num; + unsigned int blocking; + + if (argc < 3) + return CMD_RET_USAGE; + + fslnum = (unsigned int)simple_strtoul (argv[1], NULL, 16); + num = (unsigned int)simple_strtoul (argv[2], NULL, 16); + blocking = (unsigned int)simple_strtoul (argv[3], NULL, 16); + if (fslnum < 0 || fslnum >= XILINX_FSL_NUMBER) + return CMD_RET_USAGE; + + switch (fslnum) { +#if (XILINX_FSL_NUMBER > 0) + case 0: + switch (blocking) { + case 0: NPUT (num, 0); + break; + case 1: NCPUT (num, 0); + break; + case 2: PUT (num, 0); + break; + case 3: CPUT (num, 0); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 1) + case 1: + switch (blocking) { + case 0: NPUT (num, 1); + break; + case 1: NCPUT (num, 1); + break; + case 2: PUT (num, 1); + break; + case 3: CPUT (num, 1); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 2) + case 2: + switch (blocking) { + case 0: NPUT (num, 2); + break; + case 1: NCPUT (num, 2); + break; + case 2: PUT (num, 2); + break; + case 3: CPUT (num, 2); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 3) + case 3: + switch (blocking) { + case 0: NPUT (num, 3); + break; + case 1: NCPUT (num, 3); + break; + case 2: PUT (num, 3); + break; + case 3: CPUT (num, 3); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 4) + case 4: + switch (blocking) { + case 0: NPUT (num, 4); + break; + case 1: NCPUT (num, 4); + break; + case 2: PUT (num, 4); + break; + case 3: CPUT (num, 4); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 5) + case 5: + switch (blocking) { + case 0: NPUT (num, 5); + break; + case 1: NCPUT (num, 5); + break; + case 2: PUT (num, 5); + break; + case 3: CPUT (num, 5); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 6) + case 6: + switch (blocking) { + case 0: NPUT (num, 6); + break; + case 1: NCPUT (num, 6); + break; + case 2: PUT (num, 6); + break; + case 3: CPUT (num, 6); + break; + default: + return 2; + } + break; +#endif +#if (XILINX_FSL_NUMBER > 7) + case 7: + switch (blocking) { + case 0: NPUT (num, 7); + break; + case 1: NCPUT (num, 7); + break; + case 2: PUT (num, 7); + break; + case 3: CPUT (num, 7); + break; + default: + return 2; + } + break; +#endif + default: + return 1; + } + + printf ("%01x: 0x%08x - %s %s write\n", fslnum, num, + blocking < 2 ? "non blocking" : "blocking", + ((blocking == 1) || (blocking == 3)) ? "control" : "data" ); + return 0; + +} + +int do_rspr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned int reg = 0; + unsigned int val = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + reg = (unsigned int)simple_strtoul (argv[1], NULL, 16); + val = (unsigned int)simple_strtoul (argv[2], NULL, 16); + switch (reg) { + case 0x1: + if (argc > 2) { + MTS (val, rmsr); + NOP; + MFS (val, rmsr); + } else { + MFS (val, rmsr); + } + puts ("MSR"); + break; + case 0x3: + MFS (val, rear); + puts ("EAR"); + break; + case 0x5: + MFS (val, resr); + puts ("ESR"); + break; + default: + puts ("Unsupported register\n"); + return 1; + } + printf (": 0x%08x\n", val); + return 0; +} + +/***************************************************/ + +U_BOOT_CMD (frd, 3, 1, do_frd, + "read data from FSL", + "- [fslnum [0|1|2|3]]\n" + " 0 - non blocking data read\n" + " 1 - non blocking control read\n" + " 2 - blocking data read\n" + " 3 - blocking control read"); + +U_BOOT_CMD (fwr, 4, 1, do_fwr, + "write data to FSL", + "- [fslnum [0|1|2|3]]\n" + " 0 - non blocking data write\n" + " 1 - non blocking control write\n" + " 2 - blocking data write\n" + " 3 - blocking control write"); + +U_BOOT_CMD (rspr, 3, 1, do_rspr, + "read/write special purpose register", + "- reg_num [write value] read/write special purpose register\n" + " 1 - MSR - Machine status register\n" + " 3 - EAR - Exception address register\n" + " 5 - ESR - Exception status register"); diff --git a/roms/u-boot/cmd/mii.c b/roms/u-boot/cmd/mii.c new file mode 100644 index 000000000..fe8602eb7 --- /dev/null +++ b/roms/u-boot/cmd/mii.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + */ + +/* + * MII Utilities + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <miiphy.h> + +typedef struct _MII_field_desc_t { + ushort hi; + ushort lo; + ushort mask; + const char *name; +} MII_field_desc_t; + +static const MII_field_desc_t reg_0_desc_tbl[] = { + { 15, 15, 0x01, "reset" }, + { 14, 14, 0x01, "loopback" }, + { 13, 6, 0x81, "speed selection" }, /* special */ + { 12, 12, 0x01, "A/N enable" }, + { 11, 11, 0x01, "power-down" }, + { 10, 10, 0x01, "isolate" }, + { 9, 9, 0x01, "restart A/N" }, + { 8, 8, 0x01, "duplex" }, /* special */ + { 7, 7, 0x01, "collision test enable" }, + { 5, 0, 0x3f, "(reserved)" } +}; + +static const MII_field_desc_t reg_1_desc_tbl[] = { + { 15, 15, 0x01, "100BASE-T4 able" }, + { 14, 14, 0x01, "100BASE-X full duplex able" }, + { 13, 13, 0x01, "100BASE-X half duplex able" }, + { 12, 12, 0x01, "10 Mbps full duplex able" }, + { 11, 11, 0x01, "10 Mbps half duplex able" }, + { 10, 10, 0x01, "100BASE-T2 full duplex able" }, + { 9, 9, 0x01, "100BASE-T2 half duplex able" }, + { 8, 8, 0x01, "extended status" }, + { 7, 7, 0x01, "(reserved)" }, + { 6, 6, 0x01, "MF preamble suppression" }, + { 5, 5, 0x01, "A/N complete" }, + { 4, 4, 0x01, "remote fault" }, + { 3, 3, 0x01, "A/N able" }, + { 2, 2, 0x01, "link status" }, + { 1, 1, 0x01, "jabber detect" }, + { 0, 0, 0x01, "extended capabilities" }, +}; + +static const MII_field_desc_t reg_2_desc_tbl[] = { + { 15, 0, 0xffff, "OUI portion" }, +}; + +static const MII_field_desc_t reg_3_desc_tbl[] = { + { 15, 10, 0x3f, "OUI portion" }, + { 9, 4, 0x3f, "manufacturer part number" }, + { 3, 0, 0x0f, "manufacturer rev. number" }, +}; + +static const MII_field_desc_t reg_4_desc_tbl[] = { + { 15, 15, 0x01, "next page able" }, + { 14, 14, 0x01, "(reserved)" }, + { 13, 13, 0x01, "remote fault" }, + { 12, 12, 0x01, "(reserved)" }, + { 11, 11, 0x01, "asymmetric pause" }, + { 10, 10, 0x01, "pause enable" }, + { 9, 9, 0x01, "100BASE-T4 able" }, + { 8, 8, 0x01, "100BASE-TX full duplex able" }, + { 7, 7, 0x01, "100BASE-TX able" }, + { 6, 6, 0x01, "10BASE-T full duplex able" }, + { 5, 5, 0x01, "10BASE-T able" }, + { 4, 0, 0x1f, "selector" }, +}; + +static const MII_field_desc_t reg_5_desc_tbl[] = { + { 15, 15, 0x01, "next page able" }, + { 14, 14, 0x01, "acknowledge" }, + { 13, 13, 0x01, "remote fault" }, + { 12, 12, 0x01, "(reserved)" }, + { 11, 11, 0x01, "asymmetric pause able" }, + { 10, 10, 0x01, "pause able" }, + { 9, 9, 0x01, "100BASE-T4 able" }, + { 8, 8, 0x01, "100BASE-X full duplex able" }, + { 7, 7, 0x01, "100BASE-TX able" }, + { 6, 6, 0x01, "10BASE-T full duplex able" }, + { 5, 5, 0x01, "10BASE-T able" }, + { 4, 0, 0x1f, "partner selector" }, +}; + +static const MII_field_desc_t reg_9_desc_tbl[] = { + { 15, 13, 0x07, "test mode" }, + { 12, 12, 0x01, "manual master/slave enable" }, + { 11, 11, 0x01, "manual master/slave value" }, + { 10, 10, 0x01, "multi/single port" }, + { 9, 9, 0x01, "1000BASE-T full duplex able" }, + { 8, 8, 0x01, "1000BASE-T half duplex able" }, + { 7, 7, 0x01, "automatic TDR on link down" }, + { 6, 6, 0x7f, "(reserved)" }, +}; + +static const MII_field_desc_t reg_10_desc_tbl[] = { + { 15, 15, 0x01, "master/slave config fault" }, + { 14, 14, 0x01, "master/slave config result" }, + { 13, 13, 0x01, "local receiver status OK" }, + { 12, 12, 0x01, "remote receiver status OK" }, + { 11, 11, 0x01, "1000BASE-T full duplex able" }, + { 10, 10, 0x01, "1000BASE-T half duplex able" }, + { 9, 8, 0x03, "(reserved)" }, + { 7, 0, 0xff, "1000BASE-T idle error counter"}, +}; + +typedef struct _MII_reg_desc_t { + ushort regno; + const MII_field_desc_t *pdesc; + ushort len; + const char *name; +} MII_reg_desc_t; + +static const MII_reg_desc_t mii_reg_desc_tbl[] = { + { MII_BMCR, reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl), + "PHY control register" }, + { MII_BMSR, reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl), + "PHY status register" }, + { MII_PHYSID1, reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl), + "PHY ID 1 register" }, + { MII_PHYSID2, reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl), + "PHY ID 2 register" }, + { MII_ADVERTISE, reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl), + "Autonegotiation advertisement register" }, + { MII_LPA, reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl), + "Autonegotiation partner abilities register" }, + { MII_CTRL1000, reg_9_desc_tbl, ARRAY_SIZE(reg_9_desc_tbl), + "1000BASE-T control register" }, + { MII_STAT1000, reg_10_desc_tbl, ARRAY_SIZE(reg_10_desc_tbl), + "1000BASE-T status register" }, +}; + +static void dump_reg( + ushort regval, + const MII_reg_desc_t *prd); + +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval); + +static void MII_dump(const ushort *regvals, uchar reglo, uchar reghi) +{ + ulong i; + + for (i = 0; i < ARRAY_SIZE(mii_reg_desc_tbl); i++) { + const uchar reg = mii_reg_desc_tbl[i].regno; + + if (reg >= reglo && reg <= reghi) + dump_reg(regvals[reg - reglo], &mii_reg_desc_tbl[i]); + } +} + +/* Print out field position, value, name */ +static void dump_field(const MII_field_desc_t *pdesc, ushort regval) +{ + if (pdesc->hi == pdesc->lo) + printf("%2u ", pdesc->lo); + else + printf("%2u-%2u", pdesc->hi, pdesc->lo); + + printf(" = %5u %s", (regval >> pdesc->lo) & pdesc->mask, + pdesc->name); +} + +static void dump_reg( + ushort regval, + const MII_reg_desc_t *prd) +{ + ulong i; + ushort mask_in_place; + const MII_field_desc_t *pdesc; + + printf("%u. (%04hx) -- %s --\n", + prd->regno, regval, prd->name); + + for (i = 0; i < prd->len; i++) { + pdesc = &prd->pdesc[i]; + + mask_in_place = pdesc->mask << pdesc->lo; + + printf(" (%04hx:%04x) %u.", + mask_in_place, + regval & mask_in_place, + prd->regno); + + if (!special_field(prd->regno, pdesc, regval)) + dump_field(pdesc, regval); + printf("\n"); + + } + printf("\n"); +} + +/* Special fields: +** 0.6,13 +** 0.8 +** 2.15-0 +** 3.15-0 +** 4.4-0 +** 5.4-0 +*/ + +static bool special_field(ushort regno, const MII_field_desc_t *pdesc, + ushort regval) +{ + const ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; + + if ((regno == MII_BMCR) && (pdesc->lo == 6)) { + ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100); + printf("%2u,%2u = b%u%u speed selection = %s Mbps", + 6, 13, + (regval >> 6) & 1, + (regval >> 13) & 1, + speed_bits == BMCR_SPEED1000 ? "1000" : + speed_bits == BMCR_SPEED100 ? "100" : + "10"); + return 1; + } + + else if ((regno == MII_BMCR) && (pdesc->lo == 8)) { + dump_field(pdesc, regval); + printf(" = %s", ((regval >> pdesc->lo) & 1) ? "full" : "half"); + return 1; + } + + else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) { + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); + return 1; + } + + else if ((regno == MII_LPA) && (pdesc->lo == 0)) { + dump_field(pdesc, regval); + printf(" = %s", + sel_bits == PHY_ANLPAR_PSB_802_3 ? "IEEE 802.3 CSMA/CD" : + sel_bits == PHY_ANLPAR_PSB_802_9 ? + "IEEE 802.9 ISLAN-16T" : "???"); + return 1; + } + + return 0; +} + +static char last_op[2]; +static uint last_data; +static uint last_addr_lo; +static uint last_addr_hi; +static uint last_reg_lo; +static uint last_reg_hi; +static uint last_mask; + +static void extract_range( + char * input, + unsigned char * plo, + unsigned char * phi) +{ + char * end; + *plo = simple_strtoul(input, &end, 16); + if (*end == '-') { + end++; + *phi = simple_strtoul(end, NULL, 16); + } + else { + *phi = *plo; + } +} + +/* ---------------------------------------------------------------- */ +static int do_mii(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char op[2]; + unsigned char addrlo, addrhi, reglo, reghi; + unsigned char addr, reg; + unsigned short data, mask; + int rcode = 0; + const char *devname; + + if (argc < 2) + return CMD_RET_USAGE; + +#if defined(CONFIG_MII_INIT) + mii_init (); +#endif + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + op[0] = last_op[0]; + op[1] = last_op[1]; + addrlo = last_addr_lo; + addrhi = last_addr_hi; + reglo = last_reg_lo; + reghi = last_reg_hi; + data = last_data; + mask = last_mask; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + op[0] = argv[1][0]; + if (strlen(argv[1]) > 1) + op[1] = argv[1][1]; + else + op[1] = '\0'; + + if (argc >= 3) + extract_range(argv[2], &addrlo, &addrhi); + if (argc >= 4) + extract_range(argv[3], ®lo, ®hi); + if (argc >= 5) + data = simple_strtoul(argv[4], NULL, 16); + if (argc >= 6) + mask = simple_strtoul(argv[5], NULL, 16); + } + + if (addrhi > 31 && strncmp(op, "de", 2)) { + printf("Incorrect PHY address. Range should be 0-31\n"); + return CMD_RET_USAGE; + } + + /* use current device */ + devname = miiphy_get_current_dev(); + + /* + * check info/read/write. + */ + if (op[0] == 'i') { + unsigned char j, start, end; + unsigned int oui; + unsigned char model; + unsigned char rev; + + /* + * Look for any and all PHYs. Valid addresses are 0..31. + */ + if (argc >= 3) { + start = addrlo; end = addrhi; + } else { + start = 0; end = 31; + } + + for (j = start; j <= end; j++) { + if (miiphy_info (devname, j, &oui, &model, &rev) == 0) { + printf("PHY 0x%02X: " + "OUI = 0x%04X, " + "Model = 0x%02X, " + "Rev = 0x%02X, " + "%3dbase%s, %s\n", + j, oui, model, rev, + miiphy_speed (devname, j), + miiphy_is_1000base_x (devname, j) + ? "X" : "T", + (miiphy_duplex (devname, j) == FULL) + ? "FDX" : "HDX"); + } + } + } else if (op[0] == 'r') { + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + data = 0xffff; + if (miiphy_read (devname, addr, reg, &data) != 0) { + printf( + "Error reading from the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } else { + if ((addrlo != addrhi) || (reglo != reghi)) + printf("addr=%02x reg=%02x data=", + (uint)addr, (uint)reg); + printf("%04X\n", data & 0x0000FFFF); + } + } + if ((addrlo != addrhi) && (reglo != reghi)) + printf("\n"); + } + } else if (op[0] == 'w') { + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + if (miiphy_write (devname, addr, reg, data) != 0) { + printf("Error writing to the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } + } + } + } else if (op[0] == 'm') { + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + unsigned short val = 0; + if (miiphy_read(devname, addr, + reg, &val)) { + printf("Error reading from the PHY"); + printf(" addr=%02x", addr); + printf(" reg=%02x\n", reg); + rcode = 1; + } else { + val = (val & ~mask) | (data & mask); + if (miiphy_write(devname, addr, + reg, val)) { + printf("Error writing to the PHY"); + printf(" addr=%02x", addr); + printf(" reg=%02x\n", reg); + rcode = 1; + } + } + } + } + } else if (strncmp(op, "du", 2) == 0) { + ushort regs[MII_STAT1000 + 1]; /* Last reg is 0x0a */ + int ok = 1; + if (reglo > MII_STAT1000 || reghi > MII_STAT1000) { + printf("The MII dump command only formats the standard MII registers, 0-5, 9-a.\n"); + return 1; + } + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + if (miiphy_read(devname, addr, reg, + ®s[reg - reglo]) != 0) { + ok = 0; + printf( + "Error reading from the PHY addr=%02x reg=%02x\n", + addr, reg); + rcode = 1; + } + } + if (ok) + MII_dump(regs, reglo, reghi); + printf("\n"); + } + } else if (strncmp(op, "de", 2) == 0) { + if (argc == 2) + miiphy_listdev (); + else + miiphy_set_current_dev (argv[2]); + } else { + return CMD_RET_USAGE; + } + + /* + * Save the parameters for repeats. + */ + last_op[0] = op[0]; + last_op[1] = op[1]; + last_addr_lo = addrlo; + last_addr_hi = addrhi; + last_reg_lo = reglo; + last_reg_hi = reghi; + last_data = data; + last_mask = mask; + + return rcode; +} + +/***************************************************/ + +U_BOOT_CMD( + mii, 6, 1, do_mii, + "MII utility commands", + "device - list available devices\n" + "mii device <devname> - set current device\n" + "mii info <addr> - display MII PHY info\n" + "mii read <addr> <reg> - read MII PHY <addr> register <reg>\n" + "mii write <addr> <reg> <data> - write MII PHY <addr> register <reg>\n" + "mii modify <addr> <reg> <data> <mask> - modify MII PHY <addr> register <reg>\n" + " updating bits identified in <mask>\n" + "mii dump <addr> <reg> - pretty-print <addr> <reg> (0-5 only)\n" + "Addr and/or reg may be ranges, e.g. 2-7." +); diff --git a/roms/u-boot/cmd/misc.c b/roms/u-boot/cmd/misc.c new file mode 100644 index 000000000..ef540e836 --- /dev/null +++ b/roms/u-boot/cmd/misc.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * A command interface to access misc devices with MISC uclass driver APIs. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <misc.h> + +enum misc_op { + MISC_OP_READ, + MISC_OP_WRITE +}; + +static char *misc_op_str[] = { + "read", + "write" +}; + +static int do_misc_list(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + + printf("Device Index Driver\n"); + printf("-------------------------------------\n"); + for (uclass_first_device(UCLASS_MISC, &dev); + dev; + uclass_next_device(&dev)) { + printf("%-20s %5d %10s\n", dev->name, dev_seq(dev), + dev->driver->name); + } + + return 0; +} + +static int do_misc_op(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[], enum misc_op op) +{ + int (*misc_op)(struct udevice *, int, void *, int); + struct udevice *dev; + int offset; + void *buf; + int size; + int ret; + + ret = uclass_get_device_by_name(UCLASS_MISC, argv[0], &dev); + if (ret) { + printf("Unable to find device %s\n", argv[0]); + return ret; + } + + offset = simple_strtoul(argv[1], NULL, 16); + buf = (void *)simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[3], NULL, 16); + + if (op == MISC_OP_READ) + misc_op = misc_read; + else + misc_op = misc_write; + + ret = misc_op(dev, offset, buf, size); + if (ret < 0) { + if (ret == -ENOSYS) { + printf("The device does not support %s\n", + misc_op_str[op]); + ret = 0; + } + } else { + if (ret == size) + ret = 0; + else + printf("Partially %s %d bytes\n", misc_op_str[op], ret); + } + + return ret; +} + +static int do_misc_read(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_READ); +} + +static int do_misc_write(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_WRITE); +} + +static struct cmd_tbl misc_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, do_misc_list, "", ""), + U_BOOT_CMD_MKENT(read, 4, 1, do_misc_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 1, do_misc_write, "", ""), +}; + +static int do_misc(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct cmd_tbl *misc_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + misc_cmd = find_cmd_tbl(argv[1], misc_commands, + ARRAY_SIZE(misc_commands)); + argc -= 2; + argv += 2; + if (!misc_cmd || argc != misc_cmd->maxargs) + return CMD_RET_USAGE; + + ret = misc_cmd->cmd(misc_cmd, flag, argc, argv); + + return cmd_process_error(misc_cmd, ret); +} + +U_BOOT_CMD( + misc, 6, 1, do_misc, + "Access miscellaneous devices with MISC uclass driver APIs", + "list - list all miscellaneous devices\n" + "misc read name offset addr len - read `len' bytes starting at\n" + " `offset' of device `name'\n" + " to memory at `addr'\n" + "misc write name offset addr len - write `len' bytes starting at\n" + " `offset' of device `name'\n" + " from memory at `addr'" +); diff --git a/roms/u-boot/cmd/mmc.c b/roms/u-boot/cmd/mmc.c new file mode 100644 index 000000000..b942576b5 --- /dev/null +++ b/roms/u-boot/cmd/mmc.c @@ -0,0 +1,1092 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2003 + * Kyle Harris, kharris@nexus-tech.net + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <console.h> +#include <memalign.h> +#include <mmc.h> +#include <part.h> +#include <sparse_format.h> +#include <image-sparse.h> + +static int curr_device = -1; + +static void print_mmcinfo(struct mmc *mmc) +{ + int i; + + printf("Device: %s\n", mmc->cfg->name); + printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); + printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + + printf("Bus Speed: %d\n", mmc->clock); +#if CONFIG_IS_ENABLED(MMC_VERBOSE) + printf("Mode: %s\n", mmc_mode_name(mmc->selected_mode)); + mmc_dump_capabilities("card capabilities", mmc->card_caps); + mmc_dump_capabilities("host capabilities", mmc->host_caps); +#endif + printf("Rd Block Len: %d\n", mmc->read_bl_len); + + printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", + EXTRACT_SDMMC_MAJOR_VERSION(mmc->version), + EXTRACT_SDMMC_MINOR_VERSION(mmc->version)); + if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0) + printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version)); + printf("\n"); + + printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); + puts("Capacity: "); + print_size(mmc->capacity, "\n"); + + printf("Bus Width: %d-bit%s\n", mmc->bus_width, + mmc->ddr_mode ? " DDR" : ""); + +#if CONFIG_IS_ENABLED(MMC_WRITE) + puts("Erase Group Size: "); + print_size(((u64)mmc->erase_grp_size) << 9, "\n"); +#endif + + if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) { + bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; + bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR); + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + u8 wp; + int ret; + +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) + puts("HC WP Group Size: "); + print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n"); +#endif + + puts("User Capacity: "); + print_size(mmc->capacity_user, usr_enh ? " ENH" : ""); + if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR) + puts(" WRREL\n"); + else + putc('\n'); + if (usr_enh) { + puts("User Enhanced Start: "); + print_size(mmc->enh_user_start, "\n"); + puts("User Enhanced Size: "); + print_size(mmc->enh_user_size, "\n"); + } + puts("Boot Capacity: "); + print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n"); + puts("RPMB Capacity: "); + print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n"); + + for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) { + bool is_enh = has_enh && + (mmc->part_attr & EXT_CSD_ENH_GP(i)); + if (mmc->capacity_gp[i]) { + printf("GP%i Capacity: ", i+1); + print_size(mmc->capacity_gp[i], + is_enh ? " ENH" : ""); + if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i)) + puts(" WRREL\n"); + else + putc('\n'); + } + } + ret = mmc_send_ext_csd(mmc, ext_csd); + if (ret) + return; + wp = ext_csd[EXT_CSD_BOOT_WP_STATUS]; + for (i = 0; i < 2; ++i) { + printf("Boot area %d is ", i); + switch (wp & 3) { + case 0: + printf("not write protected\n"); + break; + case 1: + printf("power on protected\n"); + break; + case 2: + printf("permanently protected\n"); + break; + default: + printf("in reserved protection state\n"); + break; + } + wp >>= 2; + } + } +} +static struct mmc *init_mmc_device(int dev, bool force_init) +{ + struct mmc *mmc; + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return NULL; + } + + if (!mmc_getcd(mmc)) + force_init = true; + + if (force_init) + mmc->has_init = 0; + if (mmc_init(mmc)) + return NULL; + +#ifdef CONFIG_BLOCK_CACHE + struct blk_desc *bd = mmc_get_blk_desc(mmc); + blkcache_invalidate(bd->if_type, bd->devnum); +#endif + + return mmc; +} + +static int do_mmcinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mmc *mmc; + + if (curr_device < 0) { + if (get_mmc_num() > 0) + curr_device = 0; + else { + puts("No MMC device available\n"); + return 1; + } + } + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + print_mmcinfo(mmc); + return CMD_RET_SUCCESS; +} + +#if CONFIG_IS_ENABLED(CMD_MMC_RPMB) +static int confirm_key_prog(void) +{ + puts("Warning: Programming authentication key can be done only once !\n" + " Use this command only if you are sure of what you are doing,\n" + "Really perform the key programming? <y/N> "); + if (confirm_yesno()) + return 1; + + puts("Authentication key programming aborted\n"); + return 0; +} + +static int do_mmcrpmb_key(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + void *key_addr; + struct mmc *mmc = find_mmc_device(curr_device); + + if (argc != 2) + return CMD_RET_USAGE; + + key_addr = (void *)simple_strtoul(argv[1], NULL, 16); + if (!confirm_key_prog()) + return CMD_RET_FAILURE; + if (mmc_rpmb_set_key(mmc, key_addr)) { + printf("ERROR - Key already programmed ?\n"); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int do_mmcrpmb_read(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u16 blk, cnt; + void *addr; + int n; + void *key_addr = NULL; + struct mmc *mmc = find_mmc_device(curr_device); + + if (argc < 4) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + + if (argc == 5) + key_addr = (void *)simple_strtoul(argv[4], NULL, 16); + + printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr); + + printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + if (n != cnt) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} + +static int do_mmcrpmb_write(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u16 blk, cnt; + void *addr; + int n; + void *key_addr; + struct mmc *mmc = find_mmc_device(curr_device); + + if (argc != 5) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + key_addr = (void *)simple_strtoul(argv[4], NULL, 16); + + printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr); + + printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + if (n != cnt) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} + +static int do_mmcrpmb_counter(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + unsigned long counter; + struct mmc *mmc = find_mmc_device(curr_device); + + if (mmc_rpmb_get_counter(mmc, &counter)) + return CMD_RET_FAILURE; + printf("RPMB Write counter= %lx\n", counter); + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_rpmb[] = { + U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""), + U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""), + U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""), + U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""), +}; + +static int do_mmcrpmb(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + struct mmc *mmc; + char original_part; + int ret; + + cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb)); + + /* Drop the rpmb subcommand */ + argc--; + argv++; + + if (cp == NULL || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) + return CMD_RET_SUCCESS; + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (!(mmc->version & MMC_VERSION_MMC)) { + printf("It is not an eMMC device\n"); + return CMD_RET_FAILURE; + } + if (mmc->version < MMC_VERSION_4_41) { + printf("RPMB not supported before version 4.41\n"); + return CMD_RET_FAILURE; + } + /* Switch to the RPMB partition */ +#ifndef CONFIG_BLK + original_part = mmc->block_dev.hwpart; +#else + original_part = mmc_get_blk_desc(mmc)->hwpart; +#endif + if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, MMC_PART_RPMB) != + 0) + return CMD_RET_FAILURE; + ret = cp->cmd(cmdtp, flag, argc, argv); + + /* Return to original partition */ + if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, original_part) != + 0) + return CMD_RET_FAILURE; + return ret; +} +#endif + +static int do_mmc_read(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; + + if (argc != 4) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + printf("\nMMC read: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + + n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr); + printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) +static lbaint_t mmc_sparse_write(struct sparse_storage *info, lbaint_t blk, + lbaint_t blkcnt, const void *buffer) +{ + struct blk_desc *dev_desc = info->priv; + + return blk_dwrite(dev_desc, blk, blkcnt, buffer); +} + +static lbaint_t mmc_sparse_reserve(struct sparse_storage *info, + lbaint_t blk, lbaint_t blkcnt) +{ + return blkcnt; +} + +static int do_mmc_sparse_write(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct sparse_storage sparse; + struct blk_desc *dev_desc; + struct mmc *mmc; + char dest[11]; + void *addr; + u32 blk; + + if (argc != 3) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + + if (!is_sparse_image(addr)) { + printf("Not a sparse image\n"); + return CMD_RET_FAILURE; + } + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + printf("\nMMC Sparse write: dev # %d, block # %d ... ", + curr_device, blk); + + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } + + dev_desc = mmc_get_blk_desc(mmc); + sparse.priv = dev_desc; + sparse.blksz = 512; + sparse.start = blk; + sparse.size = dev_desc->lba - blk; + sparse.write = mmc_sparse_write; + sparse.reserve = mmc_sparse_reserve; + sparse.mssg = NULL; + sprintf(dest, "0x" LBAF, sparse.start * sparse.blksz); + + if (write_sparse_image(&sparse, dest, addr, NULL)) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; +} +#endif + +#if CONFIG_IS_ENABLED(MMC_WRITE) +static int do_mmc_write(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; + + if (argc != 4) + return CMD_RET_USAGE; + + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + printf("\nMMC write: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } + n = blk_dwrite(mmc_get_blk_desc(mmc), blk, cnt, addr); + printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +static int do_mmc_erase(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; + + if (argc != 3) + return CMD_RET_USAGE; + + blk = simple_strtoul(argv[1], NULL, 16); + cnt = simple_strtoul(argv[2], NULL, 16); + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + printf("\nMMC erase: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } + n = blk_derase(mmc_get_blk_desc(mmc), blk, cnt); + printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} +#endif + +static int do_mmc_rescan(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + + mmc = init_mmc_device(curr_device, true); + if (!mmc) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int do_mmc_part(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct blk_desc *mmc_dev; + struct mmc *mmc; + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + mmc_dev = blk_get_devnum_by_type(IF_TYPE_MMC, curr_device); + if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) { + part_print(mmc_dev); + return CMD_RET_SUCCESS; + } + + puts("get mmc type error!\n"); + return CMD_RET_FAILURE; +} + +static int do_mmc_dev(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev, part = 0, ret; + struct mmc *mmc; + + if (argc == 1) { + dev = curr_device; + } else if (argc == 2) { + dev = simple_strtoul(argv[1], NULL, 10); + } else if (argc == 3) { + dev = (int)simple_strtoul(argv[1], NULL, 10); + part = (int)simple_strtoul(argv[2], NULL, 10); + if (part > PART_ACCESS_MASK) { + printf("#part_num shouldn't be larger than %d\n", + PART_ACCESS_MASK); + return CMD_RET_FAILURE; + } + } else { + return CMD_RET_USAGE; + } + + mmc = init_mmc_device(dev, true); + if (!mmc) + return CMD_RET_FAILURE; + + ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part); + printf("switch to partitions #%d, %s\n", + part, (!ret) ? "OK" : "ERROR"); + if (ret) + return 1; + + curr_device = dev; + if (mmc->part_config == MMCPART_NOAVAILABLE) + printf("mmc%d is current device\n", curr_device); + else + printf("mmc%d(part %d) is current device\n", + curr_device, mmc_get_blk_desc(mmc)->hwpart); + + return CMD_RET_SUCCESS; +} + +static int do_mmc_list(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + print_mmc_devices('\n'); + return CMD_RET_SUCCESS; +} + +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) +static int parse_hwpart_user(struct mmc_hwpart_conf *pconf, + int argc, char *const argv[]) +{ + int i = 0; + + memset(&pconf->user, 0, sizeof(pconf->user)); + + while (i < argc) { + if (!strcmp(argv[i], "enh")) { + if (i + 2 >= argc) + return -1; + pconf->user.enh_start = + simple_strtoul(argv[i+1], NULL, 10); + pconf->user.enh_size = + simple_strtoul(argv[i+2], NULL, 10); + i += 3; + } else if (!strcmp(argv[i], "wrrel")) { + if (i + 1 >= argc) + return -1; + pconf->user.wr_rel_change = 1; + if (!strcmp(argv[i+1], "on")) + pconf->user.wr_rel_set = 1; + else if (!strcmp(argv[i+1], "off")) + pconf->user.wr_rel_set = 0; + else + return -1; + i += 2; + } else { + break; + } + } + return i; +} + +static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx, + int argc, char *const argv[]) +{ + int i; + + memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx])); + + if (1 >= argc) + return -1; + pconf->gp_part[pidx].size = simple_strtoul(argv[0], NULL, 10); + + i = 1; + while (i < argc) { + if (!strcmp(argv[i], "enh")) { + pconf->gp_part[pidx].enhanced = 1; + i += 1; + } else if (!strcmp(argv[i], "wrrel")) { + if (i + 1 >= argc) + return -1; + pconf->gp_part[pidx].wr_rel_change = 1; + if (!strcmp(argv[i+1], "on")) + pconf->gp_part[pidx].wr_rel_set = 1; + else if (!strcmp(argv[i+1], "off")) + pconf->gp_part[pidx].wr_rel_set = 0; + else + return -1; + i += 2; + } else { + break; + } + } + return i; +} + +static int do_mmc_hwpartition(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + struct mmc_hwpart_conf pconf = { }; + enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK; + int i, r, pidx; + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (argc < 1) + return CMD_RET_USAGE; + i = 1; + while (i < argc) { + if (!strcmp(argv[i], "user")) { + i++; + r = parse_hwpart_user(&pconf, argc-i, &argv[i]); + if (r < 0) + return CMD_RET_USAGE; + i += r; + } else if (!strncmp(argv[i], "gp", 2) && + strlen(argv[i]) == 3 && + argv[i][2] >= '1' && argv[i][2] <= '4') { + pidx = argv[i][2] - '1'; + i++; + r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]); + if (r < 0) + return CMD_RET_USAGE; + i += r; + } else if (!strcmp(argv[i], "check")) { + mode = MMC_HWPART_CONF_CHECK; + i++; + } else if (!strcmp(argv[i], "set")) { + mode = MMC_HWPART_CONF_SET; + i++; + } else if (!strcmp(argv[i], "complete")) { + mode = MMC_HWPART_CONF_COMPLETE; + i++; + } else { + return CMD_RET_USAGE; + } + } + + puts("Partition configuration:\n"); + if (pconf.user.enh_size) { + puts("\tUser Enhanced Start: "); + print_size(((u64)pconf.user.enh_start) << 9, "\n"); + puts("\tUser Enhanced Size: "); + print_size(((u64)pconf.user.enh_size) << 9, "\n"); + } else { + puts("\tNo enhanced user data area\n"); + } + if (pconf.user.wr_rel_change) + printf("\tUser partition write reliability: %s\n", + pconf.user.wr_rel_set ? "on" : "off"); + for (pidx = 0; pidx < 4; pidx++) { + if (pconf.gp_part[pidx].size) { + printf("\tGP%i Capacity: ", pidx+1); + print_size(((u64)pconf.gp_part[pidx].size) << 9, + pconf.gp_part[pidx].enhanced ? + " ENH\n" : "\n"); + } else { + printf("\tNo GP%i partition\n", pidx+1); + } + if (pconf.gp_part[pidx].wr_rel_change) + printf("\tGP%i write reliability: %s\n", pidx+1, + pconf.gp_part[pidx].wr_rel_set ? "on" : "off"); + } + + if (!mmc_hwpart_config(mmc, &pconf, mode)) { + if (mode == MMC_HWPART_CONF_COMPLETE) + puts("Partitioning successful, " + "power-cycle to make effective\n"); + return CMD_RET_SUCCESS; + } else { + puts("Failed!\n"); + return CMD_RET_FAILURE; + } +} +#endif + +#ifdef CONFIG_SUPPORT_EMMC_BOOT +static int do_mmc_bootbus(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev; + struct mmc *mmc; + u8 width, reset, mode; + + if (argc != 5) + return CMD_RET_USAGE; + dev = simple_strtoul(argv[1], NULL, 10); + width = simple_strtoul(argv[2], NULL, 10); + reset = simple_strtoul(argv[3], NULL, 10); + mode = simple_strtoul(argv[4], NULL, 10); + + mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("BOOT_BUS_WIDTH only exists on eMMC\n"); + return CMD_RET_FAILURE; + } + + /* + * BOOT_BUS_CONDITIONS[177] + * BOOT_MODE[4:3] + * 0x0 : Use SDR + Backward compatible timing in boot operation + * 0x1 : Use SDR + High Speed Timing in boot operation mode + * 0x2 : Use DDR in boot operation + * RESET_BOOT_BUS_CONDITIONS + * 0x0 : Reset bus width to x1, SDR, Backward compatible + * 0x1 : Retain BOOT_BUS_WIDTH and BOOT_MODE + * BOOT_BUS_WIDTH + * 0x0 : x1(sdr) or x4 (ddr) buswidth + * 0x1 : x4(sdr/ddr) buswith + * 0x2 : x8(sdr/ddr) buswith + * + */ + if (width >= 0x3) { + printf("boot_bus_width %d is invalid\n", width); + return CMD_RET_FAILURE; + } + + if (reset >= 0x2) { + printf("reset_boot_bus_width %d is invalid\n", reset); + return CMD_RET_FAILURE; + } + + if (mode >= 0x3) { + printf("reset_boot_bus_width %d is invalid\n", mode); + return CMD_RET_FAILURE; + } + + /* acknowledge to be sent during boot operation */ + if (mmc_set_boot_bus_width(mmc, width, reset, mode)) { + puts("BOOT_BUS_WIDTH is failed to change.\n"); + return CMD_RET_FAILURE; + } + + printf("Set to BOOT_BUS_WIDTH = 0x%x, RESET = 0x%x, BOOT_MODE = 0x%x\n", + width, reset, mode); + return CMD_RET_SUCCESS; +} + +static int do_mmc_boot_resize(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev; + struct mmc *mmc; + u32 bootsize, rpmbsize; + + if (argc != 4) + return CMD_RET_USAGE; + dev = simple_strtoul(argv[1], NULL, 10); + bootsize = simple_strtoul(argv[2], NULL, 10); + rpmbsize = simple_strtoul(argv[3], NULL, 10); + + mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + printf("It is not an eMMC device\n"); + return CMD_RET_FAILURE; + } + + if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) { + printf("EMMC boot partition Size change Failed.\n"); + return CMD_RET_FAILURE; + } + + printf("EMMC boot partition Size %d MB\n", bootsize); + printf("EMMC RPMB partition Size %d MB\n", rpmbsize); + return CMD_RET_SUCCESS; +} + +static int mmc_partconf_print(struct mmc *mmc, const char *varname) +{ + u8 ack, access, part; + + if (mmc->part_config == MMCPART_NOAVAILABLE) { + printf("No part_config info for ver. 0x%x\n", mmc->version); + return CMD_RET_FAILURE; + } + + access = EXT_CSD_EXTRACT_PARTITION_ACCESS(mmc->part_config); + ack = EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config); + part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config); + + if(varname) + env_set_hex(varname, part); + + printf("EXT_CSD[179], PARTITION_CONFIG:\n" + "BOOT_ACK: 0x%x\n" + "BOOT_PARTITION_ENABLE: 0x%x\n" + "PARTITION_ACCESS: 0x%x\n", ack, part, access); + + return CMD_RET_SUCCESS; +} + +static int do_mmc_partconf(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev; + struct mmc *mmc; + u8 ack, part_num, access; + + if (argc != 2 && argc != 3 && argc != 5) + return CMD_RET_USAGE; + + dev = simple_strtoul(argv[1], NULL, 10); + + mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("PARTITION_CONFIG only exists on eMMC\n"); + return CMD_RET_FAILURE; + } + + if (argc == 2 || argc == 3) + return mmc_partconf_print(mmc, argc == 3 ? argv[2] : NULL); + + ack = simple_strtoul(argv[2], NULL, 10); + part_num = simple_strtoul(argv[3], NULL, 10); + access = simple_strtoul(argv[4], NULL, 10); + + /* acknowledge to be sent during boot operation */ + return mmc_set_part_conf(mmc, ack, part_num, access); +} + +static int do_mmc_rst_func(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev; + struct mmc *mmc; + u8 enable; + + /* + * Set the RST_n_ENABLE bit of RST_n_FUNCTION + * The only valid values are 0x0, 0x1 and 0x2 and writing + * a value of 0x1 or 0x2 sets the value permanently. + */ + if (argc != 3) + return CMD_RET_USAGE; + + dev = simple_strtoul(argv[1], NULL, 10); + enable = simple_strtoul(argv[2], NULL, 10); + + if (enable > 2) { + puts("Invalid RST_n_ENABLE value\n"); + return CMD_RET_USAGE; + } + + mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("RST_n_FUNCTION only exists on eMMC\n"); + return CMD_RET_FAILURE; + } + + return mmc_set_rst_n_function(mmc, enable); +} +#endif +static int do_mmc_setdsr(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct mmc *mmc; + u32 val; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + val = simple_strtoul(argv[1], NULL, 16); + + mmc = find_mmc_device(curr_device); + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return CMD_RET_FAILURE; + } + ret = mmc_set_dsr(mmc, val); + printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); + if (!ret) { + mmc->has_init = 0; + if (mmc_init(mmc)) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; + } + return ret; +} + +#ifdef CONFIG_CMD_BKOPS_ENABLE +static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int dev; + struct mmc *mmc; + + if (argc != 2) + return CMD_RET_USAGE; + + dev = simple_strtoul(argv[1], NULL, 10); + + mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("BKOPS_EN only exists on eMMC\n"); + return CMD_RET_FAILURE; + } + + return mmc_set_bkops_enable(mmc); +} +#endif + +static int do_mmc_boot_wp(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + int err; + struct mmc *mmc; + + mmc = init_mmc_device(curr_device, false); + if (!mmc) + return CMD_RET_FAILURE; + if (IS_SD(mmc)) { + printf("It is not an eMMC device\n"); + return CMD_RET_FAILURE; + } + err = mmc_boot_wp(mmc); + if (err) + return CMD_RET_FAILURE; + printf("boot areas protected\n"); + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_mmc[] = { + U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), + U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), + U_BOOT_CMD_MKENT(wp, 1, 0, do_mmc_boot_wp, "", ""), +#if CONFIG_IS_ENABLED(MMC_WRITE) + U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), + U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), +#endif +#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) + U_BOOT_CMD_MKENT(swrite, 3, 0, do_mmc_sparse_write, "", ""), +#endif + U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""), + U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""), + U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""), + U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""), +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) + U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""), +#endif +#ifdef CONFIG_SUPPORT_EMMC_BOOT + U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""), + U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""), + U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""), + U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""), +#endif +#if CONFIG_IS_ENABLED(CMD_MMC_RPMB) + U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""), +#endif + U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""), +#ifdef CONFIG_CMD_BKOPS_ENABLE + U_BOOT_CMD_MKENT(bkops-enable, 2, 0, do_mmc_bkops_enable, "", ""), +#endif +}; + +static int do_mmcops(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cp; + + cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc)); + + /* Drop the mmc command */ + argc--; + argv++; + + if (cp == NULL || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) + return CMD_RET_SUCCESS; + + if (curr_device < 0) { + if (get_mmc_num() > 0) { + curr_device = 0; + } else { + puts("No MMC device available\n"); + return CMD_RET_FAILURE; + } + } + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + mmc, 29, 1, do_mmcops, + "MMC sub system", + "info - display info of the current MMC device\n" + "mmc read addr blk# cnt\n" + "mmc write addr blk# cnt\n" +#if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) + "mmc swrite addr blk#\n" +#endif + "mmc erase blk# cnt\n" + "mmc rescan\n" + "mmc part - lists available partition on current mmc device\n" + "mmc dev [dev] [part] - show or set current mmc device [partition]\n" + "mmc list - lists available devices\n" + "mmc wp - power on write protect boot partitions\n" +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) + "mmc hwpartition <USER> <GP> <MODE> - does hardware partitioning\n" + " arguments (sizes in 512-byte blocks):\n" + " USER - <user> <enh> <start> <cnt> <wrrel> <{on|off}>\n" + " : sets user data area attributes\n" + " GP - <{gp1|gp2|gp3|gp4}> <cnt> <enh> <wrrel> <{on|off}>\n" + " : general purpose partition\n" + " MODE - <{check|set|complete}>\n" + " : mode, complete set partitioning completed\n" + " WARNING: Partitioning is a write-once setting once it is set to complete.\n" + " Power cycling is required to initialize partitions after set to complete.\n" +#endif +#ifdef CONFIG_SUPPORT_EMMC_BOOT + "mmc bootbus <dev> <boot_bus_width> <reset_boot_bus_width> <boot_mode>\n" + " - Set the BOOT_BUS_WIDTH field of the specified device\n" + "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n" + " - Change sizes of boot and RPMB partitions of specified device\n" + "mmc partconf <dev> [[varname] | [<boot_ack> <boot_partition> <partition_access>]]\n" + " - Show or change the bits of the PARTITION_CONFIG field of the specified device\n" + " If showing the bits, optionally store the boot_partition field into varname\n" + "mmc rst-function <dev> <value>\n" + " - Change the RST_n_FUNCTION field of the specified device\n" + " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n" +#endif +#if CONFIG_IS_ENABLED(CMD_MMC_RPMB) + "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n" + "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n" + "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n" + "mmc rpmb counter - read the value of the write counter\n" +#endif + "mmc setdsr <value> - set DSR register value\n" +#ifdef CONFIG_CMD_BKOPS_ENABLE + "mmc bkops-enable <dev> - enable background operations handshake on device\n" + " WARNING: This is a write-once setting.\n" +#endif + ); + +/* Old command kept for compatibility. Same as 'mmc info' */ +U_BOOT_CMD( + mmcinfo, 1, 0, do_mmcinfo, + "display MMC info", + "- display info of the current MMC device" +); diff --git a/roms/u-boot/cmd/mp.c b/roms/u-boot/cmd/mp.c new file mode 100644 index 000000000..c2b5235a3 --- /dev/null +++ b/roms/u-boot/cmd/mp.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <command.h> +#include <cpu_func.h> + +static int cpu_status_all(void) +{ + unsigned long cpuid; + + for (cpuid = 0; ; cpuid++) { + if (!is_core_valid(cpuid)) { + if (cpuid == 0) { + printf("Core num: %lu is not valid\n", cpuid); + return 1; + } + break; + } + cpu_status(cpuid); + } + + return 0; +} + +static int +cpu_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long cpuid; + + if (argc == 2 && strncmp(argv[1], "status", 6) == 0) + return cpu_status_all(); + + if (argc < 3) + return CMD_RET_USAGE; + + cpuid = simple_strtoul(argv[1], NULL, 10); + if (!is_core_valid(cpuid)) { + printf ("Core num: %lu is not valid\n", cpuid); + return 1; + } + + + if (argc == 3) { + if (strncmp(argv[2], "reset", 5) == 0) + cpu_reset(cpuid); + else if (strncmp(argv[2], "status", 6) == 0) + cpu_status(cpuid); + else if (strncmp(argv[2], "disable", 7) == 0) + return cpu_disable(cpuid); + else + return CMD_RET_USAGE; + + return 0; + } + + /* 4 or greater, make sure its release */ + if (strncmp(argv[2], "release", 7) != 0) + return CMD_RET_USAGE; + + if (cpu_release(cpuid, argc - 3, argv + 3)) + return CMD_RET_USAGE; + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char cpu_help_text[] = + "<num> reset - Reset cpu <num>\n" + "cpu status - Status of all cpus\n" + "cpu <num> status - Status of cpu <num>\n" + "cpu <num> disable - Disable cpu <num>\n" + "cpu <num> release <addr> [args] - Release cpu <num> at <addr> with [args]" +#ifdef CONFIG_PPC + "\n" + " [args] : <pir> <r3> <r6>\n" \ + " pir - processor id (if writeable)\n" \ + " r3 - value for gpr 3\n" \ + " r6 - value for gpr 6\n" \ + "\n" \ + " Use '-' for any arg if you want the default value.\n" \ + " Default for r3 is <num> and r6 is 0\n" \ + "\n" \ + " When cpu <num> is released r4 and r5 = 0.\n" \ + " r7 will contain the size of the initial mapped area" +#endif + ""; +#endif + +U_BOOT_CMD( + cpu, CONFIG_SYS_MAXARGS, 1, cpu_cmd, + "Multiprocessor CPU boot manipulation and release", cpu_help_text +); diff --git a/roms/u-boot/cmd/mtd.c b/roms/u-boot/cmd/mtd.c new file mode 100644 index 000000000..485a963bd --- /dev/null +++ b/roms/u-boot/cmd/mtd.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * mtd.c + * + * Generic command to handle basic operations on any memory device. + * + * Copyright: Bootlin, 2018 + * Author: Miquèl Raynal <miquel.raynal@bootlin.com> + */ + +#include <command.h> +#include <common.h> +#include <console.h> +#include <malloc.h> +#include <mapmem.h> +#include <mtd.h> +#include <dm/devres.h> +#include <linux/err.h> + +#include <linux/ctype.h> + +static struct mtd_info *get_mtd_by_name(const char *name) +{ + struct mtd_info *mtd; + + mtd_probe_devices(); + + mtd = get_mtd_device_nm(name); + if (IS_ERR_OR_NULL(mtd)) + printf("MTD device %s not found, ret %ld\n", name, + PTR_ERR(mtd)); + + return mtd; +} + +static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len) +{ + do_div(len, mtd->writesize); + + return len; +} + +static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->writesize); +} + +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->erasesize); +} + +static void mtd_dump_buf(const u8 *buf, uint len, uint offset) +{ + int i, j; + + for (i = 0; i < len; ) { + printf("0x%08x:\t", offset + i); + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf(" "); + i += 8; + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf("\n"); + i += 8; + } +} + +static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off, + const u8 *buf, u64 len, bool woob) +{ + bool has_pages = mtd->type == MTD_NANDFLASH || + mtd->type == MTD_MLCNANDFLASH; + int npages = mtd_len_to_pages(mtd, len); + uint page; + + if (has_pages) { + for (page = 0; page < npages; page++) { + u64 data_off = page * mtd->writesize; + + printf("\nDump %d data bytes from 0x%08llx:\n", + mtd->writesize, start_off + data_off); + mtd_dump_buf(&buf[data_off], + mtd->writesize, start_off + data_off); + + if (woob) { + u64 oob_off = page * mtd->oobsize; + + printf("Dump %d OOB bytes from page at 0x%08llx:\n", + mtd->oobsize, start_off + data_off); + mtd_dump_buf(&buf[len + oob_off], + mtd->oobsize, 0); + } + } + } else { + printf("\nDump %lld data bytes from 0x%llx:\n", + len, start_off); + mtd_dump_buf(buf, len, start_off); + } +} + +static void mtd_show_parts(struct mtd_info *mtd, int level) +{ + struct mtd_info *part; + int i; + + list_for_each_entry(part, &mtd->partitions, node) { + for (i = 0; i < level; i++) + printf("\t"); + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", + part->offset, part->offset + part->size, part->name); + + mtd_show_parts(part, level + 1); + } +} + +static void mtd_show_device(struct mtd_info *mtd) +{ + /* Device */ + printf("* %s\n", mtd->name); +#if defined(CONFIG_DM) + if (mtd->dev) { + printf(" - device: %s\n", mtd->dev->name); + printf(" - parent: %s\n", mtd->dev->parent->name); + printf(" - driver: %s\n", mtd->dev->driver->name); + } +#endif + + /* MTD device information */ + printf(" - type: "); + switch (mtd->type) { + case MTD_RAM: + printf("RAM\n"); + break; + case MTD_ROM: + printf("ROM\n"); + break; + case MTD_NORFLASH: + printf("NOR flash\n"); + break; + case MTD_NANDFLASH: + printf("NAND flash\n"); + break; + case MTD_DATAFLASH: + printf("Data flash\n"); + break; + case MTD_UBIVOLUME: + printf("UBI volume\n"); + break; + case MTD_MLCNANDFLASH: + printf("MLC NAND flash\n"); + break; + case MTD_ABSENT: + default: + printf("Unknown\n"); + break; + } + + printf(" - block size: 0x%x bytes\n", mtd->erasesize); + printf(" - min I/O: 0x%x bytes\n", mtd->writesize); + + if (mtd->oobsize) { + printf(" - OOB size: %u bytes\n", mtd->oobsize); + printf(" - OOB available: %u bytes\n", mtd->oobavail); + } + + if (mtd->ecc_strength) { + printf(" - ECC strength: %u bits\n", mtd->ecc_strength); + printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size); + printf(" - bitflip threshold: %u bits\n", + mtd->bitflip_threshold); + } + + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", + mtd->offset, mtd->offset + mtd->size, mtd->name); + + /* MTD partitions, if any */ + mtd_show_parts(mtd, 1); +} + +/* Logic taken from fs/ubifs/recovery.c:is_empty() */ +static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) +{ + int i; + + for (i = 0; i < op->len; i++) + if (op->datbuf[i] != 0xff) + return false; + + for (i = 0; i < op->ooblen; i++) + if (op->oobbuf[i] != 0xff) + return false; + + return true; +} + +static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mtd_info *mtd; + int dev_nb = 0; + + /* Ensure all devices (and their partitions) are probed */ + mtd_probe_devices(); + + printf("List of MTD devices:\n"); + mtd_for_each_device(mtd) { + if (!mtd_is_partition(mtd)) + mtd_show_device(mtd); + + dev_nb++; + } + + if (!dev_nb) { + printf("No MTD device found\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int mtd_special_write_oob(struct mtd_info *mtd, u64 off, + struct mtd_oob_ops *io_op, + bool write_empty_pages, bool woob) +{ + int ret = 0; + + /* + * By default, do not write an empty page. + * Skip it by simulating a successful write. + */ + if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) { + io_op->retlen = mtd->writesize; + io_op->oobretlen = woob ? mtd->oobsize : 0; + } else { + ret = mtd_write_oob(mtd, off, io_op); + } + + return ret; +} + +static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool dump, read, raw, woob, write_empty_pages, has_pages = false; + u64 start_off, off, len, remaining, default_len; + struct mtd_oob_ops io_op = {}; + uint user_addr = 0, npages; + const char *cmd = argv[0]; + struct mtd_info *mtd; + u32 oob_len; + u8 *buf; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + mtd = get_mtd_by_name(argv[1]); + if (IS_ERR_OR_NULL(mtd)) + return CMD_RET_FAILURE; + + if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH) + has_pages = true; + + dump = !strncmp(cmd, "dump", 4); + read = dump || !strncmp(cmd, "read", 4); + raw = strstr(cmd, ".raw"); + woob = strstr(cmd, ".oob"); + write_empty_pages = !has_pages || strstr(cmd, ".dontskipff"); + + argc -= 2; + argv += 2; + + if (!dump) { + if (!argc) { + ret = CMD_RET_USAGE; + goto out_put_mtd; + } + + user_addr = simple_strtoul(argv[0], NULL, 16); + argc--; + argv++; + } + + start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) { + printf("Offset not aligned with a page (0x%x)\n", + mtd->writesize); + ret = CMD_RET_FAILURE; + goto out_put_mtd; + } + + default_len = dump ? mtd->writesize : mtd->size; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : default_len; + if (!mtd_is_aligned_with_min_io_size(mtd, len)) { + len = round_up(len, mtd->writesize); + printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n", + mtd->writesize, len); + } + + remaining = len; + npages = mtd_len_to_pages(mtd, len); + oob_len = woob ? npages * mtd->oobsize : 0; + + if (dump) + buf = kmalloc(len + oob_len, GFP_KERNEL); + else + buf = map_sysmem(user_addr, 0); + + if (!buf) { + printf("Could not map/allocate the user buffer\n"); + ret = CMD_RET_FAILURE; + goto out_put_mtd; + } + + if (has_pages) + printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n", + read ? "Reading" : "Writing", len, npages, start_off, + raw ? " [raw]" : "", woob ? " [oob]" : "", + !read && write_empty_pages ? " [dontskipff]" : ""); + else + printf("%s %lld byte(s) at offset 0x%08llx\n", + read ? "Reading" : "Writing", len, start_off); + + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB; + io_op.len = has_pages ? mtd->writesize : len; + io_op.ooblen = woob ? mtd->oobsize : 0; + io_op.datbuf = buf; + io_op.oobbuf = woob ? &buf[len] : NULL; + + /* Search for the first good block after the given offset */ + off = start_off; + while (mtd_block_isbad(mtd, off)) + off += mtd->erasesize; + + /* Loop over the pages to do the actual read/write */ + while (remaining) { + /* Skip the block if it is bad */ + if (mtd_is_aligned_with_block_size(mtd, off) && + mtd_block_isbad(mtd, off)) { + off += mtd->erasesize; + continue; + } + + if (read) + ret = mtd_read_oob(mtd, off, &io_op); + else + ret = mtd_special_write_oob(mtd, off, &io_op, + write_empty_pages, woob); + + if (ret) { + printf("Failure while %s at offset 0x%llx\n", + read ? "reading" : "writing", off); + break; + } + + off += io_op.retlen; + remaining -= io_op.retlen; + io_op.datbuf += io_op.retlen; + io_op.oobbuf += io_op.oobretlen; + } + + if (!ret && dump) + mtd_dump_device_buf(mtd, start_off, buf, len, woob); + + if (dump) + kfree(buf); + else + unmap_sysmem(buf); + + if (ret) { + printf("%s on %s failed with error %d\n", + read ? "Read" : "Write", mtd->name, ret); + ret = CMD_RET_FAILURE; + } else { + ret = CMD_RET_SUCCESS; + } + +out_put_mtd: + put_mtd_device(mtd); + + return ret; +} + +static int do_mtd_erase(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct erase_info erase_op = {}; + struct mtd_info *mtd; + u64 off, len; + bool scrub; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + mtd = get_mtd_by_name(argv[1]); + if (IS_ERR_OR_NULL(mtd)) + return CMD_RET_FAILURE; + + scrub = strstr(argv[0], ".dontskipbad"); + + argc -= 2; + argv += 2; + + off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size; + + if (!mtd_is_aligned_with_block_size(mtd, off)) { + printf("Offset not aligned with a block (0x%x)\n", + mtd->erasesize); + ret = CMD_RET_FAILURE; + goto out_put_mtd; + } + + if (!mtd_is_aligned_with_block_size(mtd, len)) { + printf("Size not a multiple of a block (0x%x)\n", + mtd->erasesize); + ret = CMD_RET_FAILURE; + goto out_put_mtd; + } + + printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n", + off, off + len - 1, mtd_div_by_eb(len, mtd)); + + erase_op.mtd = mtd; + erase_op.addr = off; + erase_op.len = mtd->erasesize; + erase_op.scrub = scrub; + + while (len) { + ret = mtd_erase(mtd, &erase_op); + + if (ret) { + /* Abort if its not a bad block error */ + if (ret != -EIO) + break; + printf("Skipping bad block at 0x%08llx\n", + erase_op.addr); + } + + len -= mtd->erasesize; + erase_op.addr += mtd->erasesize; + } + + if (ret && ret != -EIO) + ret = CMD_RET_FAILURE; + else + ret = CMD_RET_SUCCESS; + +out_put_mtd: + put_mtd_device(mtd); + + return ret; +} + +static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mtd_info *mtd; + loff_t off; + + if (argc < 2) + return CMD_RET_USAGE; + + mtd = get_mtd_by_name(argv[1]); + if (IS_ERR_OR_NULL(mtd)) + return CMD_RET_FAILURE; + + if (!mtd_can_have_bb(mtd)) { + printf("Only NAND-based devices can have bad blocks\n"); + goto out_put_mtd; + } + + printf("MTD device %s bad blocks list:\n", mtd->name); + for (off = 0; off < mtd->size; off += mtd->erasesize) { + if (mtd_block_isbad(mtd, off)) + printf("\t0x%08llx\n", off); + } + +out_put_mtd: + put_mtd_device(mtd); + + return CMD_RET_SUCCESS; +} + +#ifdef CONFIG_AUTO_COMPLETE +static int mtd_name_complete(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]) +{ + int len = 0, n_found = 0; + struct mtd_info *mtd; + + argc--; + argv++; + + if (argc > 1 || + (argc == 1 && (last_char == '\0' || isblank(last_char)))) + return 0; + + if (argc) + len = strlen(argv[0]); + + mtd_for_each_device(mtd) { + if (argc && + (len > strlen(mtd->name) || + strncmp(argv[0], mtd->name, len))) + continue; + + if (n_found >= maxv - 2) { + cmdv[n_found++] = "..."; + break; + } + + cmdv[n_found++] = mtd->name; + } + + cmdv[n_found] = NULL; + + return n_found; +} +#endif /* CONFIG_AUTO_COMPLETE */ + +#ifdef CONFIG_SYS_LONGHELP +static char mtd_help_text[] = + "- generic operations on memory technology devices\n\n" + "mtd list\n" + "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n" + "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n" + "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n" + "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n" + "\n" + "Specific functions:\n" + "mtd bad <name>\n" + "\n" + "With:\n" + "\t<name>: NAND partition/chip name\n" + "\t<addr>: user address from/to which data will be retrieved/stored\n" + "\t<off>: offset in <name> in bytes (default: start of the part)\n" + "\t\t* must be block-aligned for erase\n" + "\t\t* must be page-aligned otherwise\n" + "\t<size>: length of the operation in bytes (default: the entire device)\n" + "\t\t* must be a multiple of a block for erase\n" + "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n" + "\n" + "The .dontskipff option forces writing empty pages, don't use it if unsure.\n"; +#endif + +U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list), + U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io, + mtd_name_complete), + U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io, + mtd_name_complete), + U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io, + mtd_name_complete), + U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase, + mtd_name_complete), + U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad, + mtd_name_complete)); diff --git a/roms/u-boot/cmd/mtdparts.c b/roms/u-boot/cmd/mtdparts.c new file mode 100644 index 000000000..ed373a8c4 --- /dev/null +++ b/roms/u-boot/cmd/mtdparts.c @@ -0,0 +1,2135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + * + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Added support for reading flash partition table from environment. + * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 + * kernel tree. + * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org> + * + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ + * Copyright 2002 SYSGO Real-Time Solutions GmbH + */ + +/* + * Three environment variables are used by the parsing routines: + * + * 'partition' - keeps current partition identifier + * + * partition := <part-id> + * <part-id> := <dev-id>,part_num + * + * + * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping + * + * mtdids=<idmap>[,<idmap>,...] + * + * <idmap> := <dev-id>=<mtd-id> + * <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num> + * <dev-num> := mtd device number, 0... + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * + * + * 'mtdparts' - partition list + * + * mtdparts=[mtdparts=]<mtd-def>[;<mtd-def>...] + * + * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...] + * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name) + * <part-def> := <size>[@<offset>][<name>][<ro-flag>] + * <size> := standard linux memsize OR '-' to denote all remaining space + * <offset> := partition start offset within the device + * <name> := '(' NAME ')' + * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel) + * + * Notes: + * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping + * - if the above variables are not set defaults for a given target are used + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * mtdids=nor0=edb7312-nor + * mtdparts=[mtdparts=]edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * mtdids=nor0=edb7312-nor,nand0=edb7312-nand + * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <jffs2/load_kernel.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> + +#if defined(CONFIG_CMD_NAND) +#include <linux/mtd/rawnand.h> +#include <nand.h> +#endif + +#if defined(CONFIG_CMD_ONENAND) +#include <linux/mtd/onenand.h> +#include <onenand_uboot.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING (~0llu) + +/* special offset value, it is used when not provided by user + * + * this value is used temporarily during parsing, later such offests + * are recalculated */ +#define OFFSET_NOT_SPECIFIED (~0llu) + +/* minimum partition size */ +#define MIN_PART_SIZE 4096 + +/* this flag needs to be set in part_info struct mask_flags + * field for read-only partitions */ +#define MTD_WRITEABLE_CMD 1 + +/* default values for mtdids and mtdparts variables */ +#if !defined(MTDIDS_DEFAULT) +#ifdef CONFIG_MTDIDS_DEFAULT +#define MTDIDS_DEFAULT CONFIG_MTDIDS_DEFAULT +#else +#define MTDIDS_DEFAULT NULL +#endif +#endif +#if !defined(MTDPARTS_DEFAULT) +#ifdef CONFIG_MTDPARTS_DEFAULT +#define MTDPARTS_DEFAULT CONFIG_MTDPARTS_DEFAULT +#else +#define MTDPARTS_DEFAULT NULL +#endif +#endif +#if defined(CONFIG_SYS_MTDPARTS_RUNTIME) +extern void board_mtdparts_default(const char **mtdids, const char **mtdparts); +#endif +static const char *mtdids_default = MTDIDS_DEFAULT; +static const char *mtdparts_default = MTDPARTS_DEFAULT; + +/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */ +#define MTDIDS_MAXLEN 128 +#define MTDPARTS_MAXLEN 512 +#define PARTITION_MAXLEN 16 +static char last_ids[MTDIDS_MAXLEN + 1]; +static char last_parts[MTDPARTS_MAXLEN + 1]; +static char last_partition[PARTITION_MAXLEN + 1]; + +/* low level jffs2 cache cleaning routine */ +extern void jffs2_free_cache(struct part_info *part); + +/* mtdids mapping list, filled by parse_ids() */ +static struct list_head mtdids; + +/* device/partition list, parse_cmdline() parses into here */ +static struct list_head devices; + +/* current active device and partition number */ +struct mtd_device *current_mtd_dev = NULL; +u8 current_mtd_partnum = 0; + +u8 use_defaults; + +static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num); + +/* command line only routines */ +static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len); +static int device_del(struct mtd_device *dev); + +/** + * Parses a string into a number. The number stored at ptr is + * potentially suffixed with K (for kilobytes, or 1024 bytes), + * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or + * 1073741824). If the number is suffixed with K, M, or G, then + * the return value is the number multiplied by one kilobyte, one + * megabyte, or one gigabyte, respectively. + * + * @param ptr where parse begins + * @param retptr output pointer to next char after parse completes (output) + * @return resulting unsigned int + */ +static u64 memsize_parse (const char *const ptr, const char **retptr) +{ + u64 ret = simple_strtoull(ptr, (char **)retptr, 0); + + switch (**retptr) { + case 'G': + case 'g': + ret <<= 10; + /* Fallthrough */ + case 'M': + case 'm': + ret <<= 10; + /* Fallthrough */ + case 'K': + case 'k': + ret <<= 10; + (*retptr)++; + /* Fallthrough */ + default: + break; + } + + return ret; +} + +/** + * Format string describing supplied size. This routine does the opposite job + * to memsize_parse(). Size in bytes is converted to string and if possible + * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix. + * + * Note, that this routine does not check for buffer overflow, it's the caller + * who must assure enough space. + * + * @param buf output buffer + * @param size size to be converted to string + */ +static void memsize_format(char *buf, u64 size) +{ +#define SIZE_GB ((u32)1024*1024*1024) +#define SIZE_MB ((u32)1024*1024) +#define SIZE_KB ((u32)1024) + + if ((size % SIZE_GB) == 0) + sprintf(buf, "%llug", size/SIZE_GB); + else if ((size % SIZE_MB) == 0) + sprintf(buf, "%llum", size/SIZE_MB); + else if (size % SIZE_KB == 0) + sprintf(buf, "%lluk", size/SIZE_KB); + else + sprintf(buf, "%llu", size); +} + +/** + * This routine does global indexing of all partitions. Resulting index for + * current partition is saved in 'mtddevnum'. Current partition name in + * 'mtddevname'. + */ +static void index_partitions(void) +{ + u16 mtddevnum; + struct part_info *part; + struct list_head *dentry; + struct mtd_device *dev; + + debug("--- index partitions ---\n"); + + if (current_mtd_dev) { + mtddevnum = 0; + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + if (dev == current_mtd_dev) { + mtddevnum += current_mtd_partnum; + env_set_ulong("mtddevnum", mtddevnum); + debug("=> mtddevnum %d,\n", mtddevnum); + break; + } + mtddevnum += dev->num_parts; + } + + part = mtd_part_info(current_mtd_dev, current_mtd_partnum); + if (part) { + env_set("mtddevname", part->name); + + debug("=> mtddevname %s\n", part->name); + } else { + env_set("mtddevname", NULL); + + debug("=> mtddevname NULL\n"); + } + } else { + env_set("mtddevnum", NULL); + env_set("mtddevname", NULL); + + debug("=> mtddevnum NULL\n=> mtddevname NULL\n"); + } +} + +/** + * Save current device and partition in environment variable 'partition'. + */ +static void current_save(void) +{ + char buf[16]; + + debug("--- current_save ---\n"); + + if (current_mtd_dev) { + sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum); + + env_set("partition", buf); + strncpy(last_partition, buf, 16); + + debug("=> partition %s\n", buf); + } else { + env_set("partition", NULL); + last_partition[0] = '\0'; + + debug("=> partition NULL\n"); + } + index_partitions(); +} + + +/** + * Produce a mtd_info given a type and num. + * + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + put_mtd_device(*mtd); + + return 0; +} + +/** + * Performs sanity check for supplied flash partition. + * Table of existing MTD flash devices is searched and partition device + * is located. Alignment with the granularity of nand erasesize is verified. + * + * @param id of the parent device + * @param part partition to validate + * @return 0 if partition is valid, 1 otherwise + */ +static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) +{ + struct mtd_info *mtd = NULL; + int i, j; + ulong start; + u64 offset, size; + + if (get_mtd_info(id->type, id->num, &mtd)) + return 1; + + part->sector_size = mtd->erasesize; + + if (!mtd->numeraseregions) { + /* + * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR), + * checking for alignment is easy here + */ + offset = part->offset; + if (do_div(offset, mtd->erasesize)) { + printf("%s%d: partition (%s) start offset" + "alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + + size = part->size; + if (do_div(size, mtd->erasesize)) { + printf("%s%d: partition (%s) size alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + } else { + /* + * Multiple eraseregions (non-uniform NOR), + * checking for alignment is more complex here + */ + + /* Check start alignment */ + for (i = 0; i < mtd->numeraseregions; i++) { + start = mtd->eraseregions[i].offset; + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if (part->offset == start) + goto start_ok; + start += mtd->eraseregions[i].erasesize; + } + } + + printf("%s%d: partition (%s) start offset alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + + start_ok: + + /* Check end/size alignment */ + for (i = 0; i < mtd->numeraseregions; i++) { + start = mtd->eraseregions[i].offset; + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if ((part->offset + part->size) == start) + goto end_ok; + start += mtd->eraseregions[i].erasesize; + } + } + /* Check last sector alignment */ + if ((part->offset + part->size) == start) + goto end_ok; + + printf("%s%d: partition (%s) size alignment incorrect\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + + end_ok: + return 0; + } + + return 0; +} + + +/** + * Performs sanity check for supplied partition. Offset and size are + * verified to be within valid range. Partition type is checked and + * part_validate_eraseblock() is called with the argument of part. + * + * @param id of the parent device + * @param part partition to validate + * @return 0 if partition is valid, 1 otherwise + */ +static int part_validate(struct mtdids *id, struct part_info *part) +{ + if (part->size == SIZE_REMAINING) + part->size = id->size - part->offset; + + if (part->offset > id->size) { + printf("%s: offset %08llx beyond flash size %08llx\n", + id->mtd_id, part->offset, id->size); + return 1; + } + + if ((part->offset + part->size) <= part->offset) { + printf("%s%d: partition (%s) size too big\n", + MTD_DEV_TYPE(id->type), id->num, part->name); + return 1; + } + + if (part->offset + part->size > id->size) { + printf("%s: partitioning exceeds flash size\n", id->mtd_id); + return 1; + } + + /* + * Now we need to check if the partition starts and ends on + * sector (eraseblock) regions + */ + return part_validate_eraseblock(id, part); +} + +/** + * Delete selected partition from the partition list of the specified device. + * + * @param dev device to delete partition from + * @param part partition to delete + * @return 0 on success, 1 otherwise + */ +static int part_del(struct mtd_device *dev, struct part_info *part) +{ + u8 current_save_needed = 0; + + /* if there is only one partition, remove whole device */ + if (dev->num_parts == 1) + return device_del(dev); + + /* otherwise just delete this partition */ + + if (dev == current_mtd_dev) { + /* we are modyfing partitions for the current device, + * update current */ + struct part_info *curr_pi; + curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum); + + if (curr_pi) { + if (curr_pi == part) { + printf("current partition deleted, resetting current to 0\n"); + current_mtd_partnum = 0; + } else if (part->offset <= curr_pi->offset) { + current_mtd_partnum--; + } + current_save_needed = 1; + } + } + + list_del(&part->link); + free(part); + dev->num_parts--; + + if (current_save_needed > 0) + current_save(); + else + index_partitions(); + + return 0; +} + +/** + * Delete all partitions from parts head list, free memory. + * + * @param head list of partitions to delete + */ +static void part_delall(struct list_head *head) +{ + struct list_head *entry, *n; + struct part_info *part_tmp; + + /* clean tmp_list and free allocated memory */ + list_for_each_safe(entry, n, head) { + part_tmp = list_entry(entry, struct part_info, link); + + list_del(entry); + free(part_tmp); + } +} + +/** + * Add new partition to the supplied partition list. Make sure partitions are + * sorted by offset in ascending order. + * + * @param head list this partition is to be added to + * @param new partition to be added + */ +static int part_sort_add(struct mtd_device *dev, struct part_info *part) +{ + struct list_head *entry; + struct part_info *new_pi, *curr_pi; + + /* link partition to parrent dev */ + part->dev = dev; + + if (list_empty(&dev->parts)) { + debug("part_sort_add: list empty\n"); + list_add(&part->link, &dev->parts); + dev->num_parts++; + index_partitions(); + return 0; + } + + new_pi = list_entry(&part->link, struct part_info, link); + + /* get current partition info if we are updating current device */ + curr_pi = NULL; + if (dev == current_mtd_dev) + curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum); + + list_for_each(entry, &dev->parts) { + struct part_info *pi; + + pi = list_entry(entry, struct part_info, link); + + /* be compliant with kernel cmdline, allow only one partition at offset zero */ + if ((new_pi->offset == pi->offset) && (pi->offset == 0)) { + printf("cannot add second partition at offset 0\n"); + return 1; + } + + if (new_pi->offset <= pi->offset) { + list_add_tail(&part->link, entry); + dev->num_parts++; + + if (curr_pi && (pi->offset <= curr_pi->offset)) { + /* we are modyfing partitions for the current + * device, update current */ + current_mtd_partnum++; + current_save(); + } else { + index_partitions(); + } + return 0; + } + } + + list_add_tail(&part->link, &dev->parts); + dev->num_parts++; + index_partitions(); + return 0; +} + +/** + * Add provided partition to the partition list of a given device. + * + * @param dev device to which partition is added + * @param part partition to be added + * @return 0 on success, 1 otherwise + */ +static int part_add(struct mtd_device *dev, struct part_info *part) +{ + /* verify alignment and size */ + if (part_validate(dev->id, part) != 0) + return 1; + + /* partition is ok, add it to the list */ + if (part_sort_add(dev, part) != 0) + return 1; + + return 0; +} + +/** + * Parse one partition definition, allocate memory and return pointer to this + * location in retpart. + * + * @param partdef pointer to the partition definition string i.e. <part-def> + * @param ret output pointer to next char after parse completes (output) + * @param retpart pointer to the allocated partition (output) + * @return 0 on success, 1 otherwise + */ +static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart) +{ + struct part_info *part; + u64 size; + u64 offset; + const char *name; + int name_len; + unsigned int mask_flags; + const char *p; + + p = partdef; + *retpart = NULL; + *ret = NULL; + + /* fetch the partition size */ + if (*p == '-') { + /* assign all remaining space to this partition */ + debug("'-': remaining size assigned\n"); + size = SIZE_REMAINING; + p++; + } else { + size = memsize_parse(p, &p); + if (size < MIN_PART_SIZE) { + printf("partition size too small (%llx)\n", size); + return 1; + } + } + + /* check for offset */ + offset = OFFSET_NOT_SPECIFIED; + if (*p == '@') { + p++; + offset = memsize_parse(p, &p); + } + + /* now look for the name */ + if (*p == '(') { + name = ++p; + if ((p = strchr(name, ')')) == NULL) { + printf("no closing ) found in partition name\n"); + return 1; + } + name_len = p - name + 1; + if ((name_len - 1) == 0) { + printf("empty partition name\n"); + return 1; + } + p++; + } else { + /* 0x00000000@0x00000000 */ + name_len = 22; + name = NULL; + } + + /* test for options */ + mask_flags = 0; + if (strncmp(p, "ro", 2) == 0) { + mask_flags |= MTD_WRITEABLE_CMD; + p += 2; + } + + /* check for next partition definition */ + if (*p == ',') { + if (size == SIZE_REMAINING) { + *ret = NULL; + printf("no partitions allowed after a fill-up partition\n"); + return 1; + } + *ret = ++p; + } else if ((*p == ';') || (*p == '\0')) { + *ret = p; + } else { + printf("unexpected character '%c' at the end of partition\n", *p); + *ret = NULL; + return 1; + } + + /* allocate memory */ + part = (struct part_info *)malloc(sizeof(struct part_info) + name_len); + if (!part) { + printf("out of memory\n"); + return 1; + } + memset(part, 0, sizeof(struct part_info) + name_len); + part->size = size; + part->offset = offset; + part->mask_flags = mask_flags; + part->name = (char *)(part + 1); + + if (name) { + /* copy user provided name */ + strncpy(part->name, name, name_len - 1); + part->auto_name = 0; + } else { + /* auto generated name in form of size@offset */ + snprintf(part->name, name_len, "0x%08llx@0x%08llx", size, offset); + part->auto_name = 1; + } + + part->name[name_len - 1] = '\0'; + INIT_LIST_HEAD(&part->link); + + debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n", + part->name, part->size, + part->offset, part->mask_flags); + + *retpart = part; + return 0; +} + +/** + * Check device number to be within valid range for given device type. + * + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) + * @return 0 if device is valid, 1 otherwise + */ +static int mtd_device_validate(u8 type, u8 num, u64 *size) +{ + struct mtd_info *mtd = NULL; + + if (get_mtd_info(type, num, &mtd)) + return 1; + + *size = mtd->size; + + return 0; +} + +/** + * Delete all mtd devices from a supplied devices list, free memory allocated for + * each device and delete all device partitions. + * + * @return 0 on success, 1 otherwise + */ +static int device_delall(struct list_head *head) +{ + struct list_head *entry, *n; + struct mtd_device *dev_tmp; + + /* clean devices list */ + list_for_each_safe(entry, n, head) { + dev_tmp = list_entry(entry, struct mtd_device, link); + list_del(entry); + part_delall(&dev_tmp->parts); + free(dev_tmp); + } + INIT_LIST_HEAD(&devices); + + return 0; +} + +/** + * If provided device exists it's partitions are deleted, device is removed + * from device list and device memory is freed. + * + * @param dev device to be deleted + * @return 0 on success, 1 otherwise + */ +static int device_del(struct mtd_device *dev) +{ + part_delall(&dev->parts); + list_del(&dev->link); + free(dev); + + if (dev == current_mtd_dev) { + /* we just deleted current device */ + if (list_empty(&devices)) { + current_mtd_dev = NULL; + } else { + /* reset first partition from first dev from the + * devices list as current */ + current_mtd_dev = list_entry(devices.next, struct mtd_device, link); + current_mtd_partnum = 0; + } + current_save(); + return 0; + } + + index_partitions(); + return 0; +} + +/** + * Search global device list and return pointer to the device of type and num + * specified. + * + * @param type device type + * @param num device number + * @return NULL if requested device does not exist + */ +struct mtd_device *device_find(u8 type, u8 num) +{ + struct list_head *entry; + struct mtd_device *dev_tmp; + + list_for_each(entry, &devices) { + dev_tmp = list_entry(entry, struct mtd_device, link); + + if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num)) + return dev_tmp; + } + + return NULL; +} + +/** + * Add specified device to the global device list. + * + * @param dev device to be added + */ +static void device_add(struct mtd_device *dev) +{ + u8 current_save_needed = 0; + + if (list_empty(&devices)) { + current_mtd_dev = dev; + current_mtd_partnum = 0; + current_save_needed = 1; + } + + list_add_tail(&dev->link, &devices); + + if (current_save_needed > 0) + current_save(); + else + index_partitions(); +} + +/** + * Parse device type, name and mtd-id. If syntax is ok allocate memory and + * return pointer to the device structure. + * + * @param mtd_dev pointer to the device definition string i.e. <mtd-dev> + * @param ret output pointer to next char after parse completes (output) + * @param retdev pointer to the allocated device (output) + * @return 0 on success, 1 otherwise + */ +static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev) +{ + struct mtd_device *dev; + struct part_info *part; + struct mtdids *id; + const char *mtd_id; + unsigned int mtd_id_len; + const char *p; + const char *pend; + LIST_HEAD(tmp_list); + struct list_head *entry, *n; + u16 num_parts; + u64 offset; + int err = 1; + + debug("===device_parse===\n"); + + assert(retdev); + *retdev = NULL; + + if (ret) + *ret = NULL; + + /* fetch <mtd-id> */ + mtd_id = p = mtd_dev; + if (!(p = strchr(mtd_id, ':'))) { + printf("no <mtd-id> identifier\n"); + return 1; + } + mtd_id_len = p - mtd_id + 1; + p++; + + /* verify if we have a valid device specified */ + if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) { + printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id); + return 1; + } + + pend = strchr(p, ';'); + debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n", + id->type, MTD_DEV_TYPE(id->type), + id->num, id->mtd_id); + debug("parsing partitions %.*s\n", (int)(pend ? pend - p : strlen(p)), p); + + /* parse partitions */ + num_parts = 0; + + offset = 0; + if ((dev = device_find(id->type, id->num)) != NULL) { + /* if device already exists start at the end of the last partition */ + part = list_entry(dev->parts.prev, struct part_info, link); + offset = part->offset + part->size; + } + + while (p && (*p != '\0') && (*p != ';')) { + err = 1; + if ((part_parse(p, &p, &part) != 0) || (!part)) + break; + + /* calculate offset when not specified */ + if (part->offset == OFFSET_NOT_SPECIFIED) + part->offset = offset; + else + offset = part->offset; + + /* verify alignment and size */ + if (part_validate(id, part) != 0) + break; + + offset += part->size; + + /* partition is ok, add it to the list */ + list_add_tail(&part->link, &tmp_list); + num_parts++; + err = 0; + } + if (err == 1) { + part_delall(&tmp_list); + return 1; + } + + debug("\ntotal partitions: %d\n", num_parts); + + /* check for next device presence */ + if (p) { + if (*p == ';') { + if (ret) + *ret = ++p; + } else if (*p == '\0') { + if (ret) + *ret = p; + } else { + printf("unexpected character '%c' at the end of device\n", *p); + if (ret) + *ret = NULL; + return 1; + } + } + + /* allocate memory for mtd_device structure */ + if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) { + printf("out of memory\n"); + return 1; + } + memset(dev, 0, sizeof(struct mtd_device)); + dev->id = id; + dev->num_parts = 0; /* part_sort_add increments num_parts */ + INIT_LIST_HEAD(&dev->parts); + INIT_LIST_HEAD(&dev->link); + + /* move partitions from tmp_list to dev->parts */ + list_for_each_safe(entry, n, &tmp_list) { + part = list_entry(entry, struct part_info, link); + list_del(entry); + if (part_sort_add(dev, part) != 0) { + device_del(dev); + return 1; + } + } + + *retdev = dev; + + debug("===\n\n"); + return 0; +} + +/** + * Initialize global device list. + * + * @return 0 on success, 1 otherwise + */ +static int mtd_devices_init(void) +{ + last_parts[0] = '\0'; + current_mtd_dev = NULL; + current_save(); + + return device_delall(&devices); +} + +/* + * Search global mtdids list and find id of requested type and number. + * + * @return pointer to the id if it exists, NULL otherwise + */ +static struct mtdids* id_find(u8 type, u8 num) +{ + struct list_head *entry; + struct mtdids *id; + + list_for_each(entry, &mtdids) { + id = list_entry(entry, struct mtdids, link); + + if ((id->type == type) && (id->num == num)) + return id; + } + + return NULL; +} + +/** + * Search global mtdids list and find id of a requested mtd_id. + * + * Note: first argument is not null terminated. + * + * @param mtd_id string containing requested mtd_id + * @param mtd_id_len length of supplied mtd_id + * @return pointer to the id if it exists, NULL otherwise + */ +static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len) +{ + struct list_head *entry; + struct mtdids *id; + + debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n", + mtd_id_len, mtd_id, mtd_id_len); + + list_for_each(entry, &mtdids) { + id = list_entry(entry, struct mtdids, link); + + debug("entry: '%s' (len = %zu)\n", + id->mtd_id, strlen(id->mtd_id)); + + if (mtd_id_len != strlen(id->mtd_id)) + continue; + if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0) + return id; + } + + return NULL; +} + +/** + * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>, + * return device type and number. + * + * @param id string describing device id + * @param ret_id output pointer to next char after parse completes (output) + * @param dev_type parsed device type (output) + * @param dev_num parsed device number (output) + * @return 0 on success, 1 otherwise + */ +int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, + u8 *dev_num) +{ + const char *p = id; + + *dev_type = 0; + if (strncmp(p, "nand", 4) == 0) { + *dev_type = MTD_DEV_TYPE_NAND; + p += 4; + } else if (strncmp(p, "nor", 3) == 0) { + *dev_type = MTD_DEV_TYPE_NOR; + p += 3; + } else if (strncmp(p, "onenand", 7) == 0) { + *dev_type = MTD_DEV_TYPE_ONENAND; + p += 7; + } else if (strncmp(p, "spi-nand", 8) == 0) { + *dev_type = MTD_DEV_TYPE_SPINAND; + p += 8; + } else { + printf("incorrect device type in %s\n", id); + return 1; + } + + if (!isdigit(*p)) { + printf("incorrect device number in %s\n", id); + return 1; + } + + *dev_num = simple_strtoul(p, (char **)&p, 0); + if (ret_id) + *ret_id = p; + return 0; +} + +/** + * Process all devices and generate corresponding mtdparts string describing + * all partitions on all devices. + * + * @param buf output buffer holding generated mtdparts string (output) + * @param buflen buffer size + * @return 0 on success, 1 otherwise + */ +static int generate_mtdparts(char *buf, u32 buflen) +{ + struct list_head *pentry, *dentry; + struct mtd_device *dev; + struct part_info *part, *prev_part; + char *p = buf; + char tmpbuf[32]; + u64 size, offset; + u32 len, part_cnt; + u32 maxlen = buflen - 1; + + debug("--- generate_mtdparts ---\n"); + + if (list_empty(&devices)) { + buf[0] = '\0'; + return 0; + } + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + /* copy mtd_id */ + len = strlen(dev->id->mtd_id) + 1; + if (len > maxlen) + goto cleanup; + memcpy(p, dev->id->mtd_id, len - 1); + p += len - 1; + *(p++) = ':'; + maxlen -= len; + + /* format partitions */ + prev_part = NULL; + part_cnt = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + size = part->size; + offset = part->offset; + part_cnt++; + + /* partition size */ + memsize_format(tmpbuf, size); + len = strlen(tmpbuf); + if (len > maxlen) + goto cleanup; + memcpy(p, tmpbuf, len); + p += len; + maxlen -= len; + + + /* add offset only when there is a gap between + * partitions */ + if ((!prev_part && (offset != 0)) || + (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) { + + memsize_format(tmpbuf, offset); + len = strlen(tmpbuf) + 1; + if (len > maxlen) + goto cleanup; + *(p++) = '@'; + memcpy(p, tmpbuf, len - 1); + p += len - 1; + maxlen -= len; + } + + /* copy name only if user supplied */ + if(!part->auto_name) { + len = strlen(part->name) + 2; + if (len > maxlen) + goto cleanup; + + *(p++) = '('; + memcpy(p, part->name, len - 2); + p += len - 2; + *(p++) = ')'; + maxlen -= len; + } + + /* ro mask flag */ + if (part->mask_flags && MTD_WRITEABLE_CMD) { + len = 2; + if (len > maxlen) + goto cleanup; + *(p++) = 'r'; + *(p++) = 'o'; + maxlen -= 2; + } + + /* print ',' separator if there are other partitions + * following */ + if (dev->num_parts > part_cnt) { + if (1 > maxlen) + goto cleanup; + *(p++) = ','; + maxlen--; + } + prev_part = part; + } + /* print ';' separator if there are other devices following */ + if (dentry->next != &devices) { + if (1 > maxlen) + goto cleanup; + *(p++) = ';'; + maxlen--; + } + } + + /* we still have at least one char left, as we decremented maxlen at + * the begining */ + *p = '\0'; + + return 0; + +cleanup: + last_parts[0] = '\0'; + return 1; +} + +/** + * Call generate_mtdparts to process all devices and generate corresponding + * mtdparts string, save it in mtdparts environment variable. + * + * @param buf output buffer holding generated mtdparts string (output) + * @param buflen buffer size + * @return 0 on success, 1 otherwise + */ +static int generate_mtdparts_save(char *buf, u32 buflen) +{ + int ret; + + ret = generate_mtdparts(buf, buflen); + + if ((buf[0] != '\0') && (ret == 0)) + env_set("mtdparts", buf); + else + env_set("mtdparts", NULL); + + return ret; +} + +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) +/** + * Get the net size (w/o bad blocks) of the given partition. + * + * @param mtd the mtd info + * @param part the partition + * @return the calculated net size of this partition + */ +static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part) +{ + uint64_t i, net_size = 0; + + if (!mtd->_block_isbad) + return part->size; + + for (i = 0; i < part->size; i += mtd->erasesize) { + if (!mtd->_block_isbad(mtd, part->offset + i)) + net_size += mtd->erasesize; + } + + return net_size; +} +#endif + +static void print_partition_table(void) +{ + struct list_head *dentry, *pentry; + struct part_info *part; + struct mtd_device *dev; + int part_num; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + /* list partitions for given device */ + part_num = 0; +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + struct mtd_info *mtd; + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return; + + printf("\ndevice %s%d <%s>, # parts = %d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + dev->id->mtd_id, dev->num_parts); + printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n"); + + list_for_each(pentry, &dev->parts) { + u32 net_size; + char *size_note; + + part = list_entry(pentry, struct part_info, link); + net_size = net_part_size(mtd, part); + size_note = part->size == net_size ? " " : " (!)"; + printf("%2d: %-20s0x%08llx\t0x%08x%s\t0x%08llx\t%d\n", + part_num, part->name, part->size, + net_size, size_note, part->offset, + part->mask_flags); +#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ + printf("\ndevice %s%d <%s>, # parts = %d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + dev->id->mtd_id, dev->num_parts); + printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n"); + + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", + part_num, part->name, part->size, + part->offset, part->mask_flags); +#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ + part_num++; + } + } + + if (list_empty(&devices)) + printf("no partitions defined\n"); +} + +/** + * Format and print out a partition list for each device from global device + * list. + */ +static void list_partitions(void) +{ + struct part_info *part; + + debug("\n---list_partitions---\n"); + print_partition_table(); + + /* current_mtd_dev is not NULL only when we have non empty device list */ + if (current_mtd_dev) { + part = mtd_part_info(current_mtd_dev, current_mtd_partnum); + if (part) { + printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n", + MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum, + part->name, part->size, part->offset); + } else { + printf("could not get current partition info\n\n"); + } + } + + printf("\ndefaults:\n"); + printf("mtdids : %s\n", + mtdids_default ? mtdids_default : "none"); + /* + * Using printf() here results in printbuffer overflow + * if default mtdparts string is greater than console + * printbuffer. Use puts() to prevent system crashes. + */ + puts("mtdparts: "); + puts(mtdparts_default ? mtdparts_default : "none"); + puts("\n"); +} + +/** + * Given partition identifier in form of <dev_type><dev_num>,<part_num> find + * corresponding device and verify partition number. + * + * @param id string describing device and partition or partition name + * @param dev pointer to the requested device (output) + * @param part_num verified partition number (output) + * @param part pointer to requested partition (output) + * @return 0 on success, 1 otherwise + */ +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part) +{ + struct list_head *dentry, *pentry; + u8 type, dnum, pnum; + const char *p; + + debug("--- find_dev_and_part ---\nid = %s\n", id); + + list_for_each(dentry, &devices) { + *part_num = 0; + *dev = list_entry(dentry, struct mtd_device, link); + list_for_each(pentry, &(*dev)->parts) { + *part = list_entry(pentry, struct part_info, link); + if (strcmp((*part)->name, id) == 0) + return 0; + (*part_num)++; + } + } + + p = id; + *dev = NULL; + *part = NULL; + *part_num = 0; + + if (mtd_id_parse(p, &p, &type, &dnum) != 0) + return 1; + + if ((*p++ != ',') || (*p == '\0')) { + printf("no partition number specified\n"); + return 1; + } + pnum = simple_strtoul(p, (char **)&p, 0); + if (*p != '\0') { + printf("unexpected trailing character '%c'\n", *p); + return 1; + } + + if ((*dev = device_find(type, dnum)) == NULL) { + printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); + return 1; + } + + if ((*part = mtd_part_info(*dev, pnum)) == NULL) { + printf("no such partition\n"); + *dev = NULL; + return 1; + } + + *part_num = pnum; + + return 0; +} + +/** + * Find and delete partition. For partition id format see find_dev_and_part(). + * + * @param id string describing device and partition + * @return 0 on success, 1 otherwise + */ +static int delete_partition(const char *id) +{ + u8 pnum; + struct mtd_device *dev; + struct part_info *part; + + if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { + + debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, + part->name, part->size, part->offset); + + if (part_del(dev, part) != 0) + return 1; + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + return 0; + } + + printf("partition %s not found\n", id); + return 1; +} + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next partition would start on a + * good block if it were adjacent to this partition. + * + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + uint64_t *next_offset) +{ + uint64_t net_size, padding_size = 0; + int truncated; + + mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size, + &truncated); + + /* + * Absorb bad blocks immediately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + if (!truncated) { + mtd_get_len_incl_bad(mtd, part->offset + net_size, + mtd->erasesize, &padding_size, &truncated); + if (truncated) + padding_size = 0; + else + padding_size -= mtd->erasesize; + } + + if (truncated) { + printf("truncated partition %s to %lld bytes\n", part->name, + (uint64_t) net_size + padding_size); + } + + part->size = net_size + padding_size; + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + uint64_t cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08llx@0x%08llx\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + +/** + * The mtdparts variable tends to be long. If we need to access it + * before the env is relocated, then we need to use our own stack + * buffer. gd->env_buf will be too small. + * + * @param buf temporary buffer pointer MTDPARTS_MAXLEN long + * @return mtdparts variable string, NULL if not found + */ +static const char *env_get_mtdparts(char *buf) +{ + if (gd->flags & GD_FLG_ENV_READY) + return env_get("mtdparts"); + if (env_get_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1) + return buf; + return NULL; +} + +/** + * Accept character string describing mtd partitions and call device_parse() + * for each entry. Add created devices to the global devices list. + * + * @param mtdparts string specifing mtd partitions + * @return 0 on success, 1 otherwise + */ +static int parse_mtdparts(const char *const mtdparts) +{ + const char *p; + struct mtd_device *dev; + int err = 1; + char tmp_parts[MTDPARTS_MAXLEN]; + + debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", mtdparts); + + /* delete all devices and partitions */ + if (mtd_devices_init() != 0) { + printf("could not initialise device list\n"); + return err; + } + + /* re-read 'mtdparts' variable, mtd_devices_init may be updating env */ + p = env_get_mtdparts(tmp_parts); + if (!p) + p = mtdparts; + + /* Skip the useless prefix, if any */ + if (strncmp(p, "mtdparts=", 9) == 0) + p += 9; + + while (*p != '\0') { + err = 1; + if ((device_parse(p, &p, &dev) != 0) || (!dev)) + break; + + debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + /* check if parsed device is already on the list */ + if (device_find(dev->id->type, dev->id->num) != NULL) { + printf("device %s%d redefined, please correct mtdparts variable\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num); + break; + } + + list_add_tail(&dev->link, &devices); + err = 0; + } + if (err == 1) { + free(dev); + device_delall(&devices); + } + + return err; +} + +/** + * Parse provided string describing mtdids mapping (see file header for mtdids + * variable format). Allocate memory for each entry and add all found entries + * to the global mtdids list. + * + * @param ids mapping string + * @return 0 on success, 1 otherwise + */ +static int parse_mtdids(const char *const ids) +{ + const char *p = ids; + const char *mtd_id; + int mtd_id_len; + struct mtdids *id; + struct list_head *entry, *n; + struct mtdids *id_tmp; + u8 type, num; + u64 size; + int ret = 1; + + debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids); + + /* clean global mtdids list */ + list_for_each_safe(entry, n, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num); + list_del(entry); + free(id_tmp); + } + last_ids[0] = '\0'; + INIT_LIST_HEAD(&mtdids); + + while(p && (*p != '\0')) { + + ret = 1; + /* parse 'nor'|'nand'|'onenand'|'spi-nand'<dev-num> */ + if (mtd_id_parse(p, &p, &type, &num) != 0) + break; + + if (*p != '=') { + printf("mtdids: incorrect <dev-num>\n"); + break; + } + p++; + + /* check if requested device exists */ + if (mtd_device_validate(type, num, &size) != 0) + return 1; + + /* locate <mtd-id> */ + mtd_id = p; + if ((p = strchr(mtd_id, ',')) != NULL) { + mtd_id_len = p - mtd_id + 1; + p++; + } else { + mtd_id_len = strlen(mtd_id) + 1; + } + if (mtd_id_len == 0) { + printf("mtdids: no <mtd-id> identifier\n"); + break; + } + + /* check if this id is already on the list */ + int double_entry = 0; + list_for_each(entry, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + if ((id_tmp->type == type) && (id_tmp->num == num)) { + double_entry = 1; + break; + } + } + if (double_entry) { + printf("device id %s%d redefined, please correct mtdids variable\n", + MTD_DEV_TYPE(type), num); + break; + } + + /* allocate mtdids structure */ + if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) { + printf("out of memory\n"); + break; + } + memset(id, 0, sizeof(struct mtdids) + mtd_id_len); + id->num = num; + id->type = type; + id->size = size; + id->mtd_id = (char *)(id + 1); + strncpy(id->mtd_id, mtd_id, mtd_id_len - 1); + id->mtd_id[mtd_id_len - 1] = '\0'; + INIT_LIST_HEAD(&id->link); + + debug("+ id %s%d\t%16lld bytes\t%s\n", + MTD_DEV_TYPE(id->type), id->num, + id->size, id->mtd_id); + + list_add_tail(&id->link, &mtdids); + ret = 0; + } + if (ret == 1) { + /* clean mtdids list and free allocated memory */ + list_for_each_safe(entry, n, &mtdids) { + id_tmp = list_entry(entry, struct mtdids, link); + list_del(entry); + free(id_tmp); + } + return 1; + } + + return 0; +} + + +/** + * Parse and initialize global mtdids mapping and create global + * device/partition list. + * + * @return 0 on success, 1 otherwise + */ +int mtdparts_init(void) +{ + static int initialized = 0; + const char *ids, *parts; + const char *current_partition; + int ids_changed; + char tmp_ep[PARTITION_MAXLEN + 1]; + char tmp_parts[MTDPARTS_MAXLEN]; + + debug("\n---mtdparts_init---\n"); + if (!initialized) { + INIT_LIST_HEAD(&mtdids); + INIT_LIST_HEAD(&devices); + memset(last_ids, 0, sizeof(last_ids)); + memset(last_parts, 0, sizeof(last_parts)); + memset(last_partition, 0, sizeof(last_partition)); +#if defined(CONFIG_SYS_MTDPARTS_RUNTIME) + board_mtdparts_default(&mtdids_default, &mtdparts_default); +#endif + use_defaults = 1; + initialized = 1; + } + + /* get variables */ + ids = env_get("mtdids"); + parts = env_get_mtdparts(tmp_parts); + current_partition = env_get("partition"); + + /* save it for later parsing, cannot rely on current partition pointer + * as 'partition' variable may be updated during init */ + memset(tmp_parts, 0, sizeof(tmp_parts)); + memset(tmp_ep, 0, sizeof(tmp_ep)); + if (current_partition) + strncpy(tmp_ep, current_partition, PARTITION_MAXLEN); + + debug("last_ids : %s\n", last_ids); + debug("env_ids : %s\n", ids); + debug("last_parts: %s\n", last_parts); + debug("env_parts : %s\n\n", parts); + + debug("last_partition : %s\n", last_partition); + debug("env_partition : %s\n", current_partition); + + /* if mtdids variable is empty try to use defaults */ + if (!ids) { + if (mtdids_default) { + debug("mtdids variable not defined, using default\n"); + ids = mtdids_default; + env_set("mtdids", (char *)ids); + } else { + printf("mtdids not defined, no default present\n"); + return 1; + } + } + if (strlen(ids) > MTDIDS_MAXLEN - 1) { + printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN); + return 1; + } + + /* use defaults when mtdparts variable is not defined + * once mtdparts is saved environment, drop use_defaults flag */ + if (!parts) { + if (mtdparts_default && use_defaults) { + parts = mtdparts_default; + if (env_set("mtdparts", (char *)parts) == 0) + use_defaults = 0; + } else + printf("mtdparts variable not set, see 'help mtdparts'\n"); + } + + if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) { + printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN); + return 1; + } + + /* check if we have already parsed those mtdids */ + if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) { + ids_changed = 0; + } else { + ids_changed = 1; + + if (parse_mtdids(ids) != 0) { + mtd_devices_init(); + return 1; + } + + /* ok it's good, save new ids */ + strncpy(last_ids, ids, MTDIDS_MAXLEN); + } + + /* parse partitions if either mtdparts or mtdids were updated */ + if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) { + if (parse_mtdparts(parts) != 0) + return 1; + + if (list_empty(&devices)) { + printf("mtdparts_init: no valid partitions\n"); + return 1; + } + + /* ok it's good, save new parts */ + strncpy(last_parts, parts, MTDPARTS_MAXLEN); + + /* reset first partition from first dev from the list as current */ + current_mtd_dev = list_entry(devices.next, struct mtd_device, link); + current_mtd_partnum = 0; + current_save(); + + debug("mtdparts_init: current_mtd_dev = %s%d, current_mtd_partnum = %d\n", + MTD_DEV_TYPE(current_mtd_dev->id->type), + current_mtd_dev->id->num, current_mtd_partnum); + } + + /* mtdparts variable was reset to NULL, delete all devices/partitions */ + if (!parts && (last_parts[0] != '\0')) + return mtd_devices_init(); + + /* do not process current partition if mtdparts variable is null */ + if (!parts) + return 0; + + /* is current partition set in environment? if so, use it */ + if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) { + struct part_info *p; + struct mtd_device *cdev; + u8 pnum; + + debug("--- getting current partition: %s\n", tmp_ep); + + if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) { + current_mtd_dev = cdev; + current_mtd_partnum = pnum; + current_save(); + } + } else if (env_get("partition") == NULL) { + debug("no partition variable set, setting...\n"); + current_save(); + } + + return 0; +} + +/** + * Return pointer to the partition of a requested number from a requested + * device. + * + * @param dev device that is to be searched for a partition + * @param part_num requested partition number + * @return pointer to the part_info, NULL otherwise + */ +static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num) +{ + struct list_head *entry; + struct part_info *part; + int num; + + if (!dev) + return NULL; + + debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + if (part_num >= dev->num_parts) { + printf("invalid partition number %d for device %s%d (%s)\n", + part_num, MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + return NULL; + } + + /* locate partition number, return it */ + num = 0; + list_for_each(entry, &dev->parts) { + part = list_entry(entry, struct part_info, link); + + if (part_num == num++) { + return part; + } + } + + return NULL; +} + +/***************************************************/ +/* U-Boot commands */ +/***************************************************/ +/* command line only */ +/** + * Routine implementing u-boot chpart command. Sets new current partition based + * on the user supplied partition id. For partition id format see find_dev_and_part(). + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +static int do_chpart(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +/* command line only */ + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (mtdparts_init() !=0) + return 1; + + if (argc < 2) { + printf("no partition id specified\n"); + return 1; + } + + if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0) + return 1; + + current_mtd_dev = dev; + current_mtd_partnum = pnum; + current_save(); + + printf("partition changed to %s%d,%d\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum); + + return 0; +} + +/** + * Routine implementing u-boot mtdparts command. Initialize/update default global + * partition list and process user partition request (list, add, del). + * + * @param cmdtp command internal data + * @param flag command flag + * @param argc number of arguments supplied to the command + * @param argv arguments list + * @return 0 on success, 1 otherwise + */ +static int do_mtdparts(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc == 2) { + if (strcmp(argv[1], "default") == 0) { + env_set("mtdids", NULL); + env_set("mtdparts", NULL); + env_set("partition", NULL); + use_defaults = 1; + + mtdparts_init(); + return 0; + } else if (strcmp(argv[1], "delall") == 0) { + /* this may be the first run, initialize lists if needed */ + mtdparts_init(); + + env_set("mtdparts", NULL); + + /* mtd_devices_init() calls current_save() */ + return mtd_devices_init(); + } + } + + /* make sure we are in sync with env variables */ + if (mtdparts_init() != 0) + return 1; + + if (argc == 1) { + list_partitions(); + return 0; + } + + /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { +#define PART_ADD_DESC_MAXLEN 64 + char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + struct mtd_info *mtd; + uint64_t next_offset; +#endif + u8 type, num, len; + struct mtd_device *dev; + struct mtd_device *dev_tmp; + struct mtdids *id; + struct part_info *p; + + if (mtd_id_parse(argv[2], NULL, &type, &num) != 0) + return 1; + + if ((id = id_find(type, num)) == NULL) { + printf("no such device %s defined in mtdids variable\n", argv[2]); + return 1; + } + + len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */ + len += strlen(argv[3]); /* size@offset */ + len += strlen(argv[4]) + 2; /* '(' name ')' */ + if (argv[5] && (strlen(argv[5]) == 2)) + len += 2; /* 'ro' */ + + if (len >= PART_ADD_DESC_MAXLEN) { + printf("too long partition description\n"); + return 1; + } + sprintf(tmpbuf, "%s:%s(%s)%s", + id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : ""); + debug("add tmpbuf: %s\n", tmpbuf); + + if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev)) + return 1; + + debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), + dev->id->num, dev->id->mtd_id); + + p = list_entry(dev->parts.next, struct part_info, link); + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + if (!strcmp(&argv[1][3], ".spread")) { + spread_partition(mtd, p, &next_offset); + debug("increased %s to %llu bytes\n", p->name, p->size); + } +#endif + + dev_tmp = device_find(dev->id->type, dev->id->num); + if (dev_tmp == NULL) { + device_add(dev); + } else if (part_add(dev_tmp, p) != 0) { + /* merge new partition with existing ones*/ + device_del(dev); + return 1; + } + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, resetting to null\n"); + return 1; + } + + return 0; + } + + /* mtdparts del part-id */ + if ((argc == 3) && (strcmp(argv[1], "del") == 0)) { + debug("del: part-id = %s\n", argv[2]); + + return delete_partition(argv[2]); + } + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + + return CMD_RET_USAGE; +} + +/***************************************************/ +U_BOOT_CMD( + chpart, 2, 0, do_chpart, + "change active partition", + "part-id\n" + " - change active partition (e.g. part-id = nand0,1)" +); + +#ifdef CONFIG_SYS_LONGHELP +static char mtdparts_help_text[] = + "\n" + " - list partition table\n" + "mtdparts delall\n" + " - delete all partitions\n" + "mtdparts del part-id\n" + " - delete partition (e.g. part-id = nand0,1)\n" + "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif + "mtdparts default\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + "-----\n\n" + "this command uses three environment variables:\n\n" + "'partition' - keeps current partition identifier\n\n" + "partition := <part-id>\n" + "<part-id> := <dev-id>,part_num\n\n" + "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" + "mtdids=<idmap>[,<idmap>,...]\n\n" + "<idmap> := <dev-id>=<mtd-id>\n" + "<dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>\n" + "<dev-num> := mtd device number, 0...\n" + "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" + "'mtdparts' - partition list\n\n" + "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n" + "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n" + "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n" + "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n" + "<size> := standard linux memsize OR '-' to denote all remaining space\n" + "<offset> := partition start offset within the device\n" + "<name> := '(' NAME ')'\n" + "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)"; +#endif + +U_BOOT_CMD( + mtdparts, 6, 0, do_mtdparts, + "define flash/nand partitions", mtdparts_help_text +); +/***************************************************/ diff --git a/roms/u-boot/cmd/mux.c b/roms/u-boot/cmd/mux.c new file mode 100644 index 000000000..833266f08 --- /dev/null +++ b/roms/u-boot/cmd/mux.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * List, select, and deselect mux controllers on the fly. + * + * Copyright (c) 2020 Texas Instruments Inc. + * Author: Pratyush Yadav <p.yadav@ti.com> + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <mux.h> +#include <mux-internal.h> +#include <linux/err.h> +#include <dt-bindings/mux/mux.h> + +#define COLUMN_SIZE 16 + +/* + * Print a member of a column. The total size of the text printed, including + * trailing whitespace, will always be COLUMN_SIZE. + */ +#define PRINT_COLUMN(fmt, args...) do { \ + char buf[COLUMN_SIZE + 1]; \ + snprintf(buf, COLUMN_SIZE + 1, fmt, ##args); \ + printf("%-*s", COLUMN_SIZE, buf); \ +} while (0) + +/* + * Find a mux based on its device name in argv[1] and index in the chip in + * argv[2]. + */ +static struct mux_control *cmd_mux_find(char *const argv[]) +{ + struct udevice *dev; + struct mux_chip *chip; + int ret; + unsigned long id; + + ret = strict_strtoul(argv[2], 10, &id); + if (ret) + return ERR_PTR(ret); + + ret = uclass_get_device_by_name(UCLASS_MUX, argv[1], &dev); + if (ret) + return ERR_PTR(ret); + + chip = dev_get_uclass_priv(dev); + if (!chip) + return ERR_PTR(ret); + + if (id >= chip->controllers) + return ERR_PTR(-EINVAL); + + return &chip->mux[id]; +} + +/* + * Print the details of a mux. The columns printed correspond to: "Selected", + * "Current State", "Idle State", and "Num States". + */ +static void print_mux(struct mux_control *mux) +{ + PRINT_COLUMN("%s", mux->in_use ? "yes" : "no"); + + if (mux->cached_state == MUX_IDLE_AS_IS) + PRINT_COLUMN("%s", "unknown"); + else + PRINT_COLUMN("0x%x", mux->cached_state); + + if (mux->idle_state == MUX_IDLE_AS_IS) + PRINT_COLUMN("%s", "as-is"); + else if (mux->idle_state == MUX_IDLE_DISCONNECT) + PRINT_COLUMN("%s", "disconnect"); + else + PRINT_COLUMN("0x%x", mux->idle_state); + + PRINT_COLUMN("0x%x", mux->states); + + printf("\n"); +} + +static int do_mux_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct mux_chip *chip; + int j; + + for (uclass_first_device(UCLASS_MUX, &dev); + dev; + uclass_next_device(&dev)) { + chip = dev_get_uclass_priv(dev); + if (!chip) { + dev_err(dev, "can't find mux chip\n"); + continue; + } + + printf("%s:\n", dev->name); + + printf(" "); + PRINT_COLUMN("ID"); + PRINT_COLUMN("Selected"); + PRINT_COLUMN("Current State"); + PRINT_COLUMN("Idle State"); + PRINT_COLUMN("Num States"); + printf("\n"); + for (j = 0; j < chip->controllers; j++) { + printf(" "); + PRINT_COLUMN("%d", j); + print_mux(&chip->mux[j]); + } + printf("\n"); + } + + return 0; +} + +static int do_mux_select(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mux_control *mux; + int ret; + unsigned long state; + + if (argc != 4) + return CMD_RET_USAGE; + + mux = cmd_mux_find(argv); + if (IS_ERR_OR_NULL(mux)) { + printf("Failed to find the specified mux\n"); + return CMD_RET_FAILURE; + } + + ret = strict_strtoul(argv[3], 16, &state); + if (ret) { + printf("Invalid state\n"); + return CMD_RET_FAILURE; + } + + ret = mux_control_select(mux, state); + if (ret) { + printf("Failed to select requested state\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_mux_deselect(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct mux_control *mux; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + mux = cmd_mux_find(argv); + if (IS_ERR_OR_NULL(mux)) { + printf("Failed to find the specified mux\n"); + return CMD_RET_FAILURE; + } + + ret = mux_control_deselect(mux); + if (ret) { + printf("Failed to deselect mux\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static char mux_help_text[] = + "list - List all Muxes and their states\n" + "select <chip> <id> <state> - Select the given mux state\n" + "deselect <chip> <id> - Deselect the given mux and reset it to its idle state"; + +U_BOOT_CMD_WITH_SUBCMDS(mux, "List, select, and deselect muxes", mux_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mux_list), + U_BOOT_SUBCMD_MKENT(select, 4, 0, do_mux_select), + U_BOOT_SUBCMD_MKENT(deselect, 3, 0, do_mux_deselect)); diff --git a/roms/u-boot/cmd/mvebu/Kconfig b/roms/u-boot/cmd/mvebu/Kconfig new file mode 100644 index 000000000..7c42c75af --- /dev/null +++ b/roms/u-boot/cmd/mvebu/Kconfig @@ -0,0 +1,58 @@ +menu "MVEBU commands" +depends on ARCH_MVEBU + +config CMD_MVEBU_BUBT + bool "bubt" + default n + help + bubt - Burn a u-boot image to flash + For details about bubt command please see the documentation + in doc/mvebu/cmd/bubt.txt + +choice + prompt "Flash for image" + default MVEBU_SPI_BOOT + +config MVEBU_NAND_BOOT + bool "NAND flash boot" + depends on NAND_PXA3XX + help + Enable boot from NAND flash. + Allow usage of NAND flash as a target for "bubt" command + For details about bubt command please see the documentation + in doc/mvebu/cmd/bubt.txt + +config MVEBU_SPI_BOOT + bool "SPI flash boot" + depends on SPI_FLASH + help + Enable boot from SPI flash. + Allow usage of SPI flash as a target for "bubt" command + For details about bubt command please see the documentation + in doc/mvebu/cmd/bubt.txt + +config MVEBU_MMC_BOOT + bool "eMMC flash boot" + depends on MVEBU_MMC || MMC_SDHCI_XENON + help + Enable boot from eMMC boot partition + Allow usage of eMMC/SD device as a target for "bubt" command + For details about bubt command please see the documentation + in doc/mvebu/cmd/bubt.txt + +endchoice + +config MVEBU_UBOOT_DFLT_NAME + string "Default image name for bubt command" + default "flash-image.bin" + help + This option should contain a default file name to be used with + MVEBU "bubt" command if the source file name is omitted + +config CMD_MVEBU_COMPHY_RX_TRAINING + bool "mvebu_comphy_rx_training" + depends on ARMADA_8K + help + Perform COMPHY RX training sequence + +endmenu diff --git a/roms/u-boot/cmd/mvebu/Makefile b/roms/u-boot/cmd/mvebu/Makefile new file mode 100644 index 000000000..ca96ad01d --- /dev/null +++ b/roms/u-boot/cmd/mvebu/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2016 Marvell International Ltd. +# +# https://spdx.org/licenses + +obj-$(CONFIG_CMD_MVEBU_BUBT) += bubt.o +obj-$(CONFIG_CMD_MVEBU_COMPHY_RX_TRAINING) += comphy_rx_training.o diff --git a/roms/u-boot/cmd/mvebu/bubt.c b/roms/u-boot/cmd/mvebu/bubt.c new file mode 100644 index 000000000..5cd520e46 --- /dev/null +++ b/roms/u-boot/cmd/mvebu/bubt.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Marvell International Ltd. + * https://spdx.org/licenses + */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <env.h> +#include <flash.h> +#include <image.h> +#include <net.h> +#include <vsprintf.h> +#include <errno.h> +#include <dm.h> + +#include <spi_flash.h> +#include <spi.h> +#include <nand.h> +#include <usb.h> +#include <fs.h> +#include <mmc.h> +#ifdef CONFIG_BLK +#include <blk.h> +#endif +#include <u-boot/sha1.h> +#include <u-boot/sha256.h> + +#if defined(CONFIG_ARMADA_8K) +#define MAIN_HDR_MAGIC 0xB105B002 + +struct mvebu_image_header { + u32 magic; /* 0-3 */ + u32 prolog_size; /* 4-7 */ + u32 prolog_checksum; /* 8-11 */ + u32 boot_image_size; /* 12-15 */ + u32 boot_image_checksum; /* 16-19 */ + u32 rsrvd0; /* 20-23 */ + u32 load_addr; /* 24-27 */ + u32 exec_addr; /* 28-31 */ + u8 uart_cfg; /* 32 */ + u8 baudrate; /* 33 */ + u8 ext_count; /* 34 */ + u8 aux_flags; /* 35 */ + u32 io_arg_0; /* 36-39 */ + u32 io_arg_1; /* 40-43 */ + u32 io_arg_2; /* 43-47 */ + u32 io_arg_3; /* 48-51 */ + u32 rsrvd1; /* 52-55 */ + u32 rsrvd2; /* 56-59 */ + u32 rsrvd3; /* 60-63 */ +}; +#elif defined(CONFIG_ARMADA_3700) /* A3700 */ +#define HASH_SUM_LEN 16 +#define IMAGE_VERSION_3_6_0 0x030600 +#define IMAGE_VERSION_3_5_0 0x030500 + +struct common_tim_data { + u32 version; + u32 identifier; + u32 trusted; + u32 issue_date; + u32 oem_unique_id; + u32 reserved[5]; /* Reserve 20 bytes */ + u32 boot_flash_sign; + u32 num_images; + u32 num_keys; + u32 size_of_reserved; +}; + +struct mvebu_image_info { + u32 image_id; + u32 next_image_id; + u32 flash_entry_addr; + u32 load_addr; + u32 image_size; + u32 image_size_to_hash; + u32 hash_algorithm_id; + u32 hash[HASH_SUM_LEN]; /* Reserve 512 bits for the hash */ + u32 partition_number; + u32 enc_algorithm_id; + u32 encrypt_start_offset; + u32 encrypt_size; +}; +#endif + +/* Structure of the main header, version 1 (Armada 370/38x/XP) */ +struct a38x_main_hdr_v1 { + u8 blockid; /* 0x0 */ + u8 flags; /* 0x1 */ + u16 reserved2; /* 0x2-0x3 */ + u32 blocksize; /* 0x4-0x7 */ + u8 version; /* 0x8 */ + u8 headersz_msb; /* 0x9 */ + u16 headersz_lsb; /* 0xA-0xB */ + u32 srcaddr; /* 0xC-0xF */ + u32 destaddr; /* 0x10-0x13 */ + u32 execaddr; /* 0x14-0x17 */ + u8 options; /* 0x18 */ + u8 nandblocksize; /* 0x19 */ + u8 nandbadblklocation; /* 0x1A */ + u8 reserved4; /* 0x1B */ + u16 reserved5; /* 0x1C-0x1D */ + u8 ext; /* 0x1E */ + u8 checksum; /* 0x1F */ +}; + +struct a38x_boot_mode { + unsigned int id; + const char *name; +}; + +/* The blockid header field values used to indicate boot device of image */ +struct a38x_boot_mode a38x_boot_modes[] = { + { 0x4D, "i2c" }, + { 0x5A, "spi" }, + { 0x69, "uart" }, + { 0x78, "sata" }, + { 0x8B, "nand" }, + { 0x9C, "pex" }, + { 0xAE, "mmc" }, + {}, +}; + +struct bubt_dev { + char name[8]; + size_t (*read)(const char *file_name); + int (*write)(size_t image_size); + int (*active)(void); +}; + +static ulong get_load_addr(void) +{ + const char *addr_str; + unsigned long addr; + + addr_str = env_get("loadaddr"); + if (addr_str) + addr = simple_strtoul(addr_str, NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + + return addr; +} + +/******************************************************************** + * eMMC services + ********************************************************************/ +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE) +static int mmc_burn_image(size_t image_size) +{ + struct mmc *mmc; + lbaint_t start_lba; + lbaint_t blk_count; + ulong blk_written; + int err; + const u8 mmc_dev_num = CONFIG_SYS_MMC_ENV_DEV; +#ifdef CONFIG_BLK + struct blk_desc *blk_desc; +#endif + mmc = find_mmc_device(mmc_dev_num); + if (!mmc) { + printf("No SD/MMC/eMMC card found\n"); + return -ENOMEDIUM; + } + + err = mmc_init(mmc); + if (err) { + printf("%s(%d) init failed\n", IS_SD(mmc) ? "SD" : "MMC", + mmc_dev_num); + return err; + } + + /* SD reserves LBA-0 for MBR and boots from LBA-1, + * MMC/eMMC boots from LBA-0 + */ + start_lba = IS_SD(mmc) ? 1 : 0; +#ifdef CONFIG_BLK + blk_count = image_size / mmc->write_bl_len; + if (image_size % mmc->write_bl_len) + blk_count += 1; + + blk_desc = mmc_get_blk_desc(mmc); + if (!blk_desc) { + printf("Error - failed to obtain block descriptor\n"); + return -ENODEV; + } + blk_written = blk_dwrite(blk_desc, start_lba, blk_count, + (void *)get_load_addr()); +#else + blk_count = image_size / mmc->block_dev.blksz; + if (image_size % mmc->block_dev.blksz) + blk_count += 1; + + blk_written = mmc->block_dev.block_write(mmc_dev_num, + start_lba, blk_count, + (void *)get_load_addr()); +#endif /* CONFIG_BLK */ + if (blk_written != blk_count) { + printf("Error - written %#lx blocks\n", blk_written); + return -ENOSPC; + } + printf("Done!\n"); + + return 0; +} + +static size_t mmc_read_file(const char *file_name) +{ + loff_t act_read = 0; + int rc; + struct mmc *mmc; + const u8 mmc_dev_num = CONFIG_SYS_MMC_ENV_DEV; + + mmc = find_mmc_device(mmc_dev_num); + if (!mmc) { + printf("No SD/MMC/eMMC card found\n"); + return 0; + } + + if (mmc_init(mmc)) { + printf("%s(%d) init failed\n", IS_SD(mmc) ? "SD" : "MMC", + mmc_dev_num); + return 0; + } + + /* Load from data partition (0) */ + if (fs_set_blk_dev("mmc", "0", FS_TYPE_ANY)) { + printf("Error: MMC 0 not found\n"); + return 0; + } + + /* Perfrom file read */ + rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read); + if (rc) + return 0; + + return act_read; +} + +static int is_mmc_active(void) +{ + return 1; +} +#else /* CONFIG_DM_MMC */ +static int mmc_burn_image(size_t image_size) +{ + return -ENODEV; +} + +static size_t mmc_read_file(const char *file_name) +{ + return 0; +} + +static int is_mmc_active(void) +{ + return 0; +} +#endif /* CONFIG_DM_MMC */ + +/******************************************************************** + * SPI services + ********************************************************************/ +#ifdef CONFIG_SPI_FLASH +static int spi_burn_image(size_t image_size) +{ + int ret; + struct spi_flash *flash; + u32 erase_bytes; + + /* Probe the SPI bus to get the flash device */ + flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, + CONFIG_ENV_SPI_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!flash) { + printf("Failed to probe SPI Flash\n"); + return -ENOMEDIUM; + } + + erase_bytes = image_size + + (flash->erase_size - image_size % flash->erase_size); + printf("Erasing %d bytes (%d blocks) at offset 0 ...", + erase_bytes, erase_bytes / flash->erase_size); + ret = spi_flash_erase(flash, 0, erase_bytes); + if (ret) + printf("Error!\n"); + else + printf("Done!\n"); + + printf("Writing %d bytes from 0x%lx to offset 0 ...", + (int)image_size, get_load_addr()); + ret = spi_flash_write(flash, 0, image_size, (void *)get_load_addr()); + if (ret) + printf("Error!\n"); + else + printf("Done!\n"); + + return ret; +} + +static int is_spi_active(void) +{ + return 1; +} + +#else /* CONFIG_SPI_FLASH */ +static int spi_burn_image(size_t image_size) +{ + return -ENODEV; +} + +static int is_spi_active(void) +{ + return 0; +} +#endif /* CONFIG_SPI_FLASH */ + +/******************************************************************** + * NAND services + ********************************************************************/ +#ifdef CONFIG_CMD_NAND +static int nand_burn_image(size_t image_size) +{ + int ret; + uint32_t block_size; + struct mtd_info *mtd; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) { + puts("\nno devices available\n"); + return -ENOMEDIUM; + } + block_size = mtd->erasesize; + + /* Align U-Boot size to currently used blocksize */ + image_size = ((image_size + (block_size - 1)) & (~(block_size - 1))); + + /* Erase the U-Boot image space */ + printf("Erasing 0x%x - 0x%x:...", 0, (int)image_size); + ret = nand_erase(mtd, 0, image_size); + if (ret) { + printf("Error!\n"); + goto error; + } + printf("Done!\n"); + + /* Write the image to flash */ + printf("Writing %d bytes from 0x%lx to offset 0 ... ", + (int)image_size, get_load_addr()); + ret = nand_write(mtd, 0, &image_size, (void *)get_load_addr()); + if (ret) + printf("Error!\n"); + else + printf("Done!\n"); + +error: + return ret; +} + +static int is_nand_active(void) +{ + return 1; +} + +#else /* CONFIG_CMD_NAND */ +static int nand_burn_image(size_t image_size) +{ + return -ENODEV; +} + +static int is_nand_active(void) +{ + return 0; +} +#endif /* CONFIG_CMD_NAND */ + +/******************************************************************** + * USB services + ********************************************************************/ +#if defined(CONFIG_USB_STORAGE) && defined(CONFIG_BLK) +static size_t usb_read_file(const char *file_name) +{ + loff_t act_read = 0; + struct udevice *dev; + int rc; + + usb_stop(); + + if (usb_init() < 0) { + printf("Error: usb_init failed\n"); + return 0; + } + + /* Try to recognize storage devices immediately */ + blk_first_device(IF_TYPE_USB, &dev); + if (!dev) { + printf("Error: USB storage device not found\n"); + return 0; + } + + /* Always load from usb 0 */ + if (fs_set_blk_dev("usb", "0", FS_TYPE_ANY)) { + printf("Error: USB 0 not found\n"); + return 0; + } + + /* Perfrom file read */ + rc = fs_read(file_name, get_load_addr(), 0, 0, &act_read); + if (rc) + return 0; + + return act_read; +} + +static int is_usb_active(void) +{ + return 1; +} + +#else /* defined(CONFIG_USB_STORAGE) && defined (CONFIG_BLK) */ +static size_t usb_read_file(const char *file_name) +{ + return 0; +} + +static int is_usb_active(void) +{ + return 0; +} +#endif /* defined(CONFIG_USB_STORAGE) && defined (CONFIG_BLK) */ + +/******************************************************************** + * Network services + ********************************************************************/ +#ifdef CONFIG_CMD_NET +static size_t tftp_read_file(const char *file_name) +{ + /* + * update global variable image_load_addr before tftp file from network + */ + image_load_addr = get_load_addr(); + return net_loop(TFTPGET); +} + +static int is_tftp_active(void) +{ + return 1; +} + +#else +static size_t tftp_read_file(const char *file_name) +{ + return 0; +} + +static int is_tftp_active(void) +{ + return 0; +} +#endif /* CONFIG_CMD_NET */ + +enum bubt_devices { + BUBT_DEV_NET = 0, + BUBT_DEV_USB, + BUBT_DEV_MMC, + BUBT_DEV_SPI, + BUBT_DEV_NAND, + + BUBT_MAX_DEV +}; + +struct bubt_dev bubt_devs[BUBT_MAX_DEV] = { + {"tftp", tftp_read_file, NULL, is_tftp_active}, + {"usb", usb_read_file, NULL, is_usb_active}, + {"mmc", mmc_read_file, mmc_burn_image, is_mmc_active}, + {"spi", NULL, spi_burn_image, is_spi_active}, + {"nand", NULL, nand_burn_image, is_nand_active}, +}; + +static int bubt_write_file(struct bubt_dev *dst, size_t image_size) +{ + if (!dst->write) { + printf("Error: Write not supported on device %s\n", dst->name); + return -ENOTSUPP; + } + + return dst->write(image_size); +} + +#if defined(CONFIG_ARMADA_8K) +u32 do_checksum32(u32 *start, int32_t len) +{ + u32 sum = 0; + u32 *startp = start; + + do { + sum += *startp; + startp++; + len -= 4; + } while (len > 0); + + return sum; +} + +static int check_image_header(void) +{ + struct mvebu_image_header *hdr = + (struct mvebu_image_header *)get_load_addr(); + u32 header_len = hdr->prolog_size; + u32 checksum; + u32 checksum_ref = hdr->prolog_checksum; + + /* + * For now compare checksum, and magic. Later we can + * verify more stuff on the header like interface type, etc + */ + if (hdr->magic != MAIN_HDR_MAGIC) { + printf("ERROR: Bad MAGIC 0x%08x != 0x%08x\n", + hdr->magic, MAIN_HDR_MAGIC); + return -ENOEXEC; + } + + /* The checksum value is discarded from checksum calculation */ + hdr->prolog_checksum = 0; + + checksum = do_checksum32((u32 *)hdr, header_len); + if (checksum != checksum_ref) { + printf("Error: Bad Image checksum. 0x%x != 0x%x\n", + checksum, checksum_ref); + return -ENOEXEC; + } + + /* Restore the checksum before writing */ + hdr->prolog_checksum = checksum_ref; + printf("Image checksum...OK!\n"); + + return 0; +} +#elif defined(CONFIG_ARMADA_3700) /* Armada 3700 */ +static int check_image_header(void) +{ + struct common_tim_data *hdr = (struct common_tim_data *)get_load_addr(); + int image_num; + u8 hash_160_output[SHA1_SUM_LEN]; + u8 hash_256_output[SHA256_SUM_LEN]; + sha1_context hash1_text; + sha256_context hash256_text; + u8 *hash_output; + u32 hash_algorithm_id; + u32 image_size_to_hash; + u32 flash_entry_addr; + u32 *hash_value; + u32 internal_hash[HASH_SUM_LEN]; + const u8 *buff; + u32 num_of_image = hdr->num_images; + u32 version = hdr->version; + u32 trusted = hdr->trusted; + + /* bubt checksum validation only supports nontrusted images */ + if (trusted == 1) { + printf("bypass image validation, "); + printf("only untrusted image is supported now\n"); + return 0; + } + /* only supports image version 3.5 and 3.6 */ + if (version != IMAGE_VERSION_3_5_0 && version != IMAGE_VERSION_3_6_0) { + printf("Error: Unsupported Image version = 0x%08x\n", version); + return -ENOEXEC; + } + /* validate images hash value */ + for (image_num = 0; image_num < num_of_image; image_num++) { + struct mvebu_image_info *info = + (struct mvebu_image_info *)(get_load_addr() + + sizeof(struct common_tim_data) + + image_num * sizeof(struct mvebu_image_info)); + hash_algorithm_id = info->hash_algorithm_id; + image_size_to_hash = info->image_size_to_hash; + flash_entry_addr = info->flash_entry_addr; + hash_value = info->hash; + buff = (const u8 *)(get_load_addr() + flash_entry_addr); + + if (image_num == 0) { + /* + * The first image includes hash values in its content. + * For hash calculation, we need to save the original + * hash values to a local variable that will be + * copied back for comparsion and set all zeros to + * the orignal hash values for calculating new value. + * First image original format : + * x...x (datum1) x...x(orig. hash values) x...x(datum2) + * Replaced first image format : + * x...x (datum1) 0...0(hash values) x...x(datum2) + */ + memcpy(internal_hash, hash_value, + sizeof(internal_hash)); + memset(hash_value, 0, sizeof(internal_hash)); + } + if (image_size_to_hash == 0) { + printf("Warning: Image_%d hash checksum is disabled, ", + image_num); + printf("skip the image validation.\n"); + continue; + } + switch (hash_algorithm_id) { + case SHA1_SUM_LEN: + sha1_starts(&hash1_text); + sha1_update(&hash1_text, buff, image_size_to_hash); + sha1_finish(&hash1_text, hash_160_output); + hash_output = hash_160_output; + break; + case SHA256_SUM_LEN: + sha256_starts(&hash256_text); + sha256_update(&hash256_text, buff, image_size_to_hash); + sha256_finish(&hash256_text, hash_256_output); + hash_output = hash_256_output; + break; + default: + printf("Error: Unsupported hash_algorithm_id = %d\n", + hash_algorithm_id); + return -ENOEXEC; + } + if (image_num == 0) + memcpy(hash_value, internal_hash, + sizeof(internal_hash)); + if (memcmp(hash_value, hash_output, hash_algorithm_id) != 0) { + printf("Error: Image_%d checksum is not correct\n", + image_num); + return -ENOEXEC; + } + } + printf("Image checksum...OK!\n"); + + return 0; +} +#elif defined(CONFIG_ARMADA_38X) +static size_t a38x_header_size(const struct a38x_main_hdr_v1 *h) +{ + if (h->version == 1) + return (h->headersz_msb << 16) | le16_to_cpu(h->headersz_lsb); + + printf("Error: Invalid A38x image (header version 0x%x unknown)!\n", + h->version); + return 0; +} + +static uint8_t image_checksum8(const void *start, size_t len) +{ + u8 csum = 0; + const u8 *p = start; + + while (len) { + csum += *p; + ++p; + --len; + } + + return csum; +} + +static int check_image_header(void) +{ + u8 checksum; + const struct a38x_main_hdr_v1 *hdr = + (struct a38x_main_hdr_v1 *)get_load_addr(); + const size_t image_size = a38x_header_size(hdr); + + if (!image_size) + return -ENOEXEC; + + checksum = image_checksum8(hdr, image_size); + checksum -= hdr->checksum; + if (checksum != hdr->checksum) { + printf("Error: Bad A38x image checksum. 0x%x != 0x%x\n", + checksum, hdr->checksum); + return -ENOEXEC; + } + + printf("Image checksum...OK!\n"); + return 0; +} +#else /* Not ARMADA? */ +static int check_image_header(void) +{ + printf("bubt cmd does not support this SoC device or family!\n"); + return -ENOEXEC; +} +#endif + +static int bubt_check_boot_mode(const struct bubt_dev *dst) +{ + if (IS_ENABLED(CONFIG_ARMADA_38X)) { + int mode; + const struct a38x_main_hdr_v1 *hdr = + (struct a38x_main_hdr_v1 *)get_load_addr(); + + for (mode = 0; mode < ARRAY_SIZE(a38x_boot_modes); mode++) { + if (strcmp(a38x_boot_modes[mode].name, dst->name) == 0) + break; + } + + if (a38x_boot_modes[mode].id == hdr->blockid) + return 0; + + for (int i = 0; i < ARRAY_SIZE(a38x_boot_modes); i++) { + if (a38x_boot_modes[i].id == hdr->blockid) { + printf("Error: A38x image meant to be booted from " + "\"%s\", not \"%s\"!\n", + a38x_boot_modes[i].name, dst->name); + return -ENOEXEC; + } + } + + printf("Error: unknown boot device in A38x image header: " + "0x%x\n", hdr->blockid); + return -ENOEXEC; + } else { + return 0; + } +} + +static int bubt_verify(const struct bubt_dev *dst) +{ + int err; + + /* Check a correct image header exists */ + err = check_image_header(); + if (err) { + printf("Error: Image header verification failed\n"); + return err; + } + + err = bubt_check_boot_mode(dst); + if (err) { + printf("Error: Image boot mode verification failed\n"); + return err; + } + + return 0; +} + +static int bubt_read_file(struct bubt_dev *src) +{ + size_t image_size; + + if (!src->read) { + printf("Error: Read not supported on device \"%s\"\n", + src->name); + return 0; + } + + image_size = src->read(net_boot_file_name); + if (image_size <= 0) { + printf("Error: Failed to read file %s from %s\n", + net_boot_file_name, src->name); + return 0; + } + + return image_size; +} + +static int bubt_is_dev_active(struct bubt_dev *dev) +{ + if (!dev->active) { + printf("Device \"%s\" not supported by U-Boot image\n", + dev->name); + return 0; + } + + if (!dev->active()) { + printf("Device \"%s\" is inactive\n", dev->name); + return 0; + } + + return 1; +} + +struct bubt_dev *find_bubt_dev(char *dev_name) +{ + int dev; + + for (dev = 0; dev < BUBT_MAX_DEV; dev++) { + if (strcmp(bubt_devs[dev].name, dev_name) == 0) + return &bubt_devs[dev]; + } + + return 0; +} + +#define DEFAULT_BUBT_SRC "tftp" + +#ifndef DEFAULT_BUBT_DST +#ifdef CONFIG_MVEBU_SPI_BOOT +#define DEFAULT_BUBT_DST "spi" +#elif defined(CONFIG_MVEBU_NAND_BOOT) +#define DEFAULT_BUBT_DST "nand" +#elif defined(CONFIG_MVEBU_MMC_BOOT) +#define DEFAULT_BUBT_DST "mmc" +#else +#define DEFAULT_BUBT_DST "error" +#endif +#endif /* DEFAULT_BUBT_DST */ + +int do_bubt_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct bubt_dev *src, *dst; + size_t image_size; + char src_dev_name[8]; + char dst_dev_name[8]; + char *name; + int err; + + if (argc < 2) + copy_filename(net_boot_file_name, + CONFIG_MVEBU_UBOOT_DFLT_NAME, + sizeof(net_boot_file_name)); + else + copy_filename(net_boot_file_name, argv[1], + sizeof(net_boot_file_name)); + + if (argc >= 3) { + strncpy(dst_dev_name, argv[2], 8); + } else { + name = DEFAULT_BUBT_DST; + strncpy(dst_dev_name, name, 8); + } + + if (argc >= 4) + strncpy(src_dev_name, argv[3], 8); + else + strncpy(src_dev_name, DEFAULT_BUBT_SRC, 8); + + /* Figure out the destination device */ + dst = find_bubt_dev(dst_dev_name); + if (!dst) { + printf("Error: Unknown destination \"%s\"\n", dst_dev_name); + return -EINVAL; + } + + if (!bubt_is_dev_active(dst)) + return -ENODEV; + + /* Figure out the source device */ + src = find_bubt_dev(src_dev_name); + if (!src) { + printf("Error: Unknown source \"%s\"\n", src_dev_name); + return 1; + } + + if (!bubt_is_dev_active(src)) + return -ENODEV; + + printf("Burning U-Boot image \"%s\" from \"%s\" to \"%s\"\n", + net_boot_file_name, src->name, dst->name); + + image_size = bubt_read_file(src); + if (!image_size) + return -EIO; + + err = bubt_verify(dst); + if (err) + return err; + + err = bubt_write_file(dst, image_size); + if (err) + return err; + + return 0; +} + +U_BOOT_CMD( + bubt, 4, 0, do_bubt_cmd, + "Burn a u-boot image to flash", + "[file-name] [destination [source]]\n" + "\t-file-name The image file name to burn. Default = " CONFIG_MVEBU_UBOOT_DFLT_NAME "\n" + "\t-destination Flash to burn to [spi, nand, mmc]. Default = " DEFAULT_BUBT_DST "\n" + "\t-source The source to load image from [tftp, usb, mmc]. Default = " DEFAULT_BUBT_SRC "\n" + "Examples:\n" + "\tbubt - Burn flash-image.bin from tftp to active boot device\n" + "\tbubt flash-image-new.bin nand - Burn flash-image-new.bin from tftp to NAND flash\n" + "\tbubt backup-flash-image.bin mmc usb - Burn backup-flash-image.bin from usb to MMC\n" + +); diff --git a/roms/u-boot/cmd/mvebu/comphy_rx_training.c b/roms/u-boot/cmd/mvebu/comphy_rx_training.c new file mode 100644 index 000000000..0798decfa --- /dev/null +++ b/roms/u-boot/cmd/mvebu/comphy_rx_training.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Marvell International Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <fdtdec.h> +#include <dm/device-internal.h> +#include <mvebu/comphy.h> + +int mvebu_comphy_rx_training_cmd(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct udevice *dev; + struct uclass *uc; + int ret, cp_index, comphy_index, i = 0; + + if (argc != 3) { + printf("missing arguments\n"); + return -1; + } + + cp_index = simple_strtoul(argv[1], NULL, 16); + comphy_index = simple_strtoul(argv[2], NULL, 16); + + ret = uclass_get(UCLASS_MISC, &uc); + if (ret) { + printf("Couldn't find UCLASS_MISC\n"); + return ret; + } + + uclass_foreach_dev(dev, uc) { + if (!(memcmp(dev->name, "comphy", 5))) { + if (i == cp_index) { + comphy_rx_training(dev, comphy_index); + return 0; + } + + i++; + } + } + + printf("Coudn't find comphy %d\n", cp_index); + + return 0; +} + +U_BOOT_CMD( + mvebu_comphy_rx_training, 3, 0, mvebu_comphy_rx_training_cmd, + "mvebu_comphy_rx_training <cp id> <comphy id>\n", + "\n\tRun COMPHY RX training sequence, the user must state CP index (0/1) and comphy ID (0/5)" +); diff --git a/roms/u-boot/cmd/nand.c b/roms/u-boot/cmd/nand.c new file mode 100644 index 000000000..97e117a97 --- /dev/null +++ b/roms/u-boot/cmd/nand.c @@ -0,0 +1,1022 @@ +/* + * Driver for NAND support, Rick Bronson + * borrowed heavily from: + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * + * Ported 'dynenv' to 'nand env.oob' command + * (C) 2010 Nanometrics, Inc. + * 'dynenv' -- Dynamic environment offset in NAND OOB + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Added 16-bit nand support + * (C) 2004 Texas Instruments + * + * Copyright 2010, 2012 Freescale Semiconductor + * The portions of this file whose copyright is held by Freescale and which + * are not considered a derived work of GPL v2-only code may be distributed + * and/or modified 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. + */ + +#include <common.h> +#include <bootstage.h> +#include <image.h> +#include <asm/cache.h> +#include <linux/mtd/mtd.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <watchdog.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <jffs2/jffs2.h> +#include <nand.h> + +#include "legacy-mtd-utils.h" + +#if defined(CONFIG_CMD_MTDPARTS) + +/* partition handling routines */ +int mtdparts_init(void); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); +#endif + +static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob, + int repeat) +{ + int i; + u_char *datbuf, *oobbuf, *p; + static loff_t last; + int ret = 0; + + if (repeat) + off = last + mtd->writesize; + + last = off; + + datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize); + if (!datbuf) { + puts("No memory for page buffer\n"); + return 1; + } + + oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize); + if (!oobbuf) { + puts("No memory for page buffer\n"); + ret = 1; + goto free_dat; + } + off &= ~(mtd->writesize - 1); + loff_t addr = (loff_t) off; + struct mtd_oob_ops ops; + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.mode = MTD_OPS_RAW; + i = mtd_read_oob(mtd, addr, &ops); + if (i < 0) { + printf("Error (%d) reading page %08lx\n", i, off); + ret = 1; + goto free_all; + } + printf("Page %08lx dump:\n", off); + + if (!only_oob) { + i = mtd->writesize >> 4; + p = datbuf; + + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], + p[15]); + p += 16; + } + } + + puts("OOB:\n"); + i = mtd->oobsize >> 3; + p = oobbuf; + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + +free_all: + free(oobbuf); +free_dat: + free(datbuf); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int set_dev(int dev) +{ + struct mtd_info *mtd = get_nand_dev_by_index(dev); + + if (!mtd) + return -ENODEV; + + if (nand_curr_device == dev) + return 0; + + printf("Device %d: %s", dev, mtd->name); + puts("... is now current device\n"); + nand_curr_device = dev; + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE + board_nand_select_device(mtd_to_nand(mtd), dev); +#endif + + return 0; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK +static void print_status(ulong start, ulong end, ulong erasesize, int status) +{ + /* + * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is + * not the same as others. Instead of bit 1 being lock, it is + * #lock_tight. To make the driver support either format, ignore bit 1 + * and use only bit 0 and bit 2. + */ + printf("%08lx - %08lx: %08lx blocks %s%s%s\n", + start, + end - 1, + (end - start) / erasesize, + ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), + (!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""), + ((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); +} + +static void do_nand_status(struct mtd_info *mtd) +{ + ulong block_start = 0; + ulong off; + int last_status = -1; + + struct nand_chip *nand_chip = mtd_to_nand(mtd); + /* check the WP bit */ + nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + printf("device is %swrite protected\n", + (nand_chip->read_byte(mtd) & 0x80 ? + "NOT " : "")); + + for (off = 0; off < mtd->size; off += mtd->erasesize) { + int s = nand_get_lock_status(mtd, off); + + /* print message only if status has changed */ + if (s != last_status && off != 0) { + print_status(block_start, off, mtd->erasesize, + last_status); + block_start = off; + } + last_status = s; + } + /* Print the last block info */ + print_status(block_start, off, mtd->erasesize, last_status); +} +#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(struct cmd_tbl *cmdtp, int argc, char *const argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + struct mtd_info *mtd = get_nand_dev_by_index(0); + char *cmd = argv[1]; + + if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !mtd) { + puts("no devices available\n"); + return 1; + } + + set_dev(0); + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(mtd, &nand_env_oob_offset); + if (ret) + return 1; + + printf("0x%08lx\n", nand_env_oob_offset); + } else if (!strcmp(cmd, "set")) { + loff_t addr; + loff_t maxsize; + struct mtd_oob_ops ops; + int idx = 0; + + if (argc < 3) + goto usage; + + mtd = get_nand_dev_by_index(idx); + /* We don't care about size, or maxsize. */ + if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize, + MTD_DEV_TYPE_NAND, mtd->size)) { + puts("Offset or partition name expected\n"); + return 1; + } + if (set_dev(idx)) { + puts("Offset or partition name expected\n"); + return 1; + } + + if (idx != 0) { + puts("Partition not on first NAND device\n"); + return 1; + } + + if (mtd->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes:\n" + "%d OOB bytes available but %d required for " + "env.oob support\n", + mtd->oobavail, ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (mtd->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / mtd->erasesize; + + ret = mtd->write_oob(mtd, ENV_OFFSET_SIZE, &ops); + if (ret) { + printf("Error writing OOB block 0\n"); + return ret; + } + + ret = get_nand_env_oob(mtd, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB failed: " + "0x%08llx expected but got 0x%08lx\n", + (unsigned long long)addr, nand_env_oob_offset); + return 1; + } + } else { + goto usage; + } + + return ret; + +usage: + return CMD_RET_USAGE; +} + +#endif + +static void nand_print_and_set_info(int idx) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + + mtd = get_nand_dev_by_index(idx); + if (!mtd) + return; + + chip = mtd_to_nand(mtd); + printf("Device %d: ", idx); + if (chip->numchips > 1) + printf("%dx ", chip->numchips); + printf("%s, sector size %u KiB\n", + mtd->name, mtd->erasesize >> 10); + printf(" Page size %8d b\n", mtd->writesize); + printf(" OOB size %8d b\n", mtd->oobsize); + printf(" Erase size %8d b\n", mtd->erasesize); + printf(" subpagesize %8d b\n", chip->subpagesize); + printf(" options 0x%08x\n", chip->options); + printf(" bbt options 0x%08x\n", chip->bbt_options); + + /* Set geometry info */ + env_set_hex("nand_writesize", mtd->writesize); + env_set_hex("nand_oobsize", mtd->oobsize); + env_set_hex("nand_erasesize", mtd->erasesize); +} + +static int raw_access(struct mtd_info *mtd, ulong addr, loff_t off, + ulong count, int read, int no_verify) +{ + int ret = 0; + + while (count--) { + /* Raw access */ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)addr, + .oobbuf = ((u8 *)addr) + mtd->writesize, + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_RAW + }; + + if (read) { + ret = mtd_read_oob(mtd, off, &ops); + } else { + ret = mtd_write_oob(mtd, off, &ops); + if (!ret && !no_verify) + ret = nand_verify_page_oob(mtd, &ops, off); + } + + if (ret) { + printf("%s: error at offset %llx, ret %d\n", + __func__, (long long)off, ret); + break; + } + + addr += mtd->writesize + mtd->oobsize; + off += mtd->writesize; + } + + return ret; +} + +/* Adjust a chip/partition size down for bad blocks so we don't + * read/write past the end of a chip/partition by accident. + */ +static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) +{ + /* We grab the nand info object here fresh because this is usually + * called after arg_off_size() which can change the value of dev. + */ + struct mtd_info *mtd = get_nand_dev_by_index(dev); + loff_t maxoffset = offset + *size; + int badblocks = 0; + + /* count badblocks in NAND from offset to offset + size */ + for (; offset < maxoffset; offset += mtd->erasesize) { + if (nand_block_isbad(mtd, offset)) + badblocks++; + } + /* adjust size if any bad blocks found */ + if (badblocks) { + *size -= badblocks * mtd->erasesize; + printf("size adjusted to 0x%llx (%d bad blocks)\n", + (unsigned long long)*size, badblocks); + } +} + +static int do_nand(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i, ret = 0; + ulong addr; + loff_t off, size, maxsize; + char *cmd, *s; + struct mtd_info *mtd; +#ifdef CONFIG_SYS_NAND_QUIET + int quiet = CONFIG_SYS_NAND_QUIET; +#else + int quiet = 0; +#endif + const char *quiet_str = env_get("quiet"); + int dev = nand_curr_device; + int repeat = flag & CMD_FLAG_REPEAT; + + /* at least two arguments please */ + if (argc < 2) + goto usage; + + if (quiet_str) + quiet = simple_strtoul(quiet_str, NULL, 0) != 0; + + cmd = argv[1]; + + /* Only "dump" is repeatable. */ + if (repeat && strcmp(cmd, "dump")) + return 0; + + if (strcmp(cmd, "info") == 0) { + + putc('\n'); + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + nand_print_and_set_info(i); + return 0; + } + + if (strcmp(cmd, "device") == 0) { + if (argc < 3) { + putc('\n'); + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) + puts("no devices available\n"); + else + nand_print_and_set_info(dev); + return 0; + } + + dev = (int)simple_strtoul(argv[2], NULL, 10); + set_dev(dev); + + return 0; + } + +#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd, "env.oob") == 0) + return do_nand_env_oob(cmdtp, argc - 1, argv + 1); +#endif + + /* The following commands operate on the current device, unless + * overridden by a partition specifier. Note that if somehow the + * current device is invalid, it will have to be changed to a valid + * one before these commands can run, even if a partition specifier + * for another device is to be used. + */ + mtd = get_nand_dev_by_index(dev); + if (!mtd) { + puts("\nno devices available\n"); + return 1; + } + + if (strcmp(cmd, "bad") == 0) { + printf("\nDevice %d bad blocks:\n", dev); + for (off = 0; off < mtd->size; off += mtd->erasesize) + if (nand_block_isbad(mtd, off)) + printf(" %08llx\n", (unsigned long long)off); + return 0; + } + + /* + * Syntax is: + * 0 1 2 3 4 + * nand erase [clean] [off size] + */ + if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { + nand_erase_options_t opts; + /* "clean" at index 2 means request to write cleanmarker */ + int clean = argc > 2 && !strcmp("clean", argv[2]); + int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); + int o = (clean || scrub_yes) ? 3 : 2; + int scrub = !strncmp(cmd, "scrub", 5); + int spread = 0; + int args = 2; + const char *scrub_warn = + "Warning: " + "scrub option will erase all factory set bad blocks!\n" + " " + "There is no reliable way to recover them.\n" + " " + "Use this command only for testing purposes if you\n" + " " + "are sure of what you are doing!\n" + "\nReally scrub this NAND flash? <y/N>\n"; + + if (cmd[5] != 0) { + if (!strcmp(&cmd[5], ".spread")) { + spread = 1; + } else if (!strcmp(&cmd[5], ".part")) { + args = 1; + } else if (!strcmp(&cmd[5], ".chip")) { + args = 0; + } else { + goto usage; + } + } + + /* + * Don't allow missing arguments to cause full chip/partition + * erases -- easy to do accidentally, e.g. with a misspelled + * variable name. + */ + if (argc != o + args) + goto usage; + + printf("\nNAND %s: ", cmd); + /* skip first two or three arguments, look for offset and size */ + if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, + &maxsize, MTD_DEV_TYPE_NAND, + mtd->size) != 0) + return 1; + + if (set_dev(dev)) + return 1; + + mtd = get_nand_dev_by_index(dev); + + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet; + opts.spread = spread; + + if (scrub) { + if (scrub_yes) { + opts.scrub = 1; + } else { + puts(scrub_warn); + if (confirm_yesno()) { + opts.scrub = 1; + } else { + puts("scrub aborted\n"); + return 1; + } + } + } + ret = nand_erase_opts(mtd, &opts); + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; + } + + if (strncmp(cmd, "dump", 4) == 0) { + if (argc < 3) + goto usage; + + off = (int)simple_strtoul(argv[2], NULL, 16); + ret = nand_dump(mtd, off, !strcmp(&cmd[4], ".oob"), repeat); + + return ret == 0 ? 1 : 0; + } + + if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { + size_t rwsize; + ulong pagecount = 1; + int read; + int raw = 0; + int no_verify = 0; + + if (argc < 4) + goto usage; + + addr = (ulong)simple_strtoul(argv[2], NULL, 16); + + read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ + printf("\nNAND %s: ", read ? "read" : "write"); + + s = strchr(cmd, '.'); + + if (s && !strncmp(s, ".raw", 4)) { + raw = 1; + + if (!strcmp(s, ".raw.noverify")) + no_verify = 1; + + if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, + MTD_DEV_TYPE_NAND, + mtd->size)) + return 1; + + if (set_dev(dev)) + return 1; + + mtd = get_nand_dev_by_index(dev); + + if (argc > 4 && !str2long(argv[4], &pagecount)) { + printf("'%s' is not a number\n", argv[4]); + return 1; + } + + if (pagecount * mtd->writesize > size) { + puts("Size exceeds partition or device limit\n"); + return -1; + } + + rwsize = pagecount * (mtd->writesize + mtd->oobsize); + } else { + if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, + &size, &maxsize, + MTD_DEV_TYPE_NAND, + mtd->size) != 0) + return 1; + + if (set_dev(dev)) + return 1; + + /* size is unspecified */ + if (argc < 5) + adjust_size_for_badblocks(&size, off, dev); + rwsize = size; + } + + mtd = get_nand_dev_by_index(dev); + + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + if (read) + ret = nand_read_skip_bad(mtd, off, &rwsize, + NULL, maxsize, + (u_char *)addr); + else + ret = nand_write_skip_bad(mtd, off, &rwsize, + NULL, maxsize, + (u_char *)addr, + WITH_WR_VERIFY); +#ifdef CONFIG_CMD_NAND_TRIMFFS + } else if (!strcmp(s, ".trimffs")) { + if (read) { + printf("Unknown nand command suffix '%s'\n", s); + return 1; + } + ret = nand_write_skip_bad(mtd, off, &rwsize, NULL, + maxsize, (u_char *)addr, + WITH_DROP_FFS | WITH_WR_VERIFY); +#endif + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = rwsize, + .mode = MTD_OPS_RAW + }; + + if (read) + ret = mtd_read_oob(mtd, off, &ops); + else + ret = mtd_write_oob(mtd, off, &ops); + } else if (raw) { + ret = raw_access(mtd, addr, off, pagecount, read, + no_verify); + } else { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; + } + + printf(" %zu bytes %s: %s\n", rwsize, + read ? "read" : "written", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; + } + +#ifdef CONFIG_CMD_NAND_TORTURE + if (strcmp(cmd, "torture") == 0) { + loff_t endoff; + unsigned int failed = 0, passed = 0; + + if (argc < 3) + goto usage; + + if (!str2off(argv[2], &off)) { + puts("Offset is not a valid number\n"); + return 1; + } + + size = mtd->erasesize; + if (argc > 3) { + if (!str2off(argv[3], &size)) { + puts("Size is not a valid number\n"); + return 1; + } + } + + endoff = off + size; + if (endoff > mtd->size) { + puts("Arguments beyond end of NAND\n"); + return 1; + } + + off = round_down(off, mtd->erasesize); + endoff = round_up(endoff, mtd->erasesize); + size = endoff - off; + printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n", + dev, off, size, mtd->erasesize); + while (off < endoff) { + ret = nand_torture(mtd, off); + if (ret) { + failed++; + printf(" block at 0x%llx failed\n", off); + } else { + passed++; + } + off += mtd->erasesize; + } + printf(" Passed: %u, failed: %u\n", passed, failed); + return failed != 0; + } +#endif + + if (strcmp(cmd, "markbad") == 0) { + argc -= 2; + argv += 2; + + if (argc <= 0) + goto usage; + + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (mtd_block_markbad(mtd, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; + } else { + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); + } + --argc; + ++argv; + } + return ret; + } + + if (strcmp(cmd, "biterr") == 0) { + /* todo */ + return 1; + } + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + if (strcmp(cmd, "lock") == 0) { + int tight = 0; + int status = 0; + if (argc == 3) { + if (!strcmp("tight", argv[2])) + tight = 1; + if (!strcmp("status", argv[2])) + status = 1; + } + if (status) { + do_nand_status(mtd); + } else { + if (!nand_lock(mtd, tight)) { + puts("NAND flash successfully locked\n"); + } else { + puts("Error locking NAND flash\n"); + return 1; + } + } + return 0; + } + + if (strncmp(cmd, "unlock", 5) == 0) { + int allexcept = 0; + + s = strchr(cmd, '.'); + + if (s && !strcmp(s, ".allexcept")) + allexcept = 1; + + if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, + &maxsize, MTD_DEV_TYPE_NAND, + mtd->size) < 0) + return 1; + + if (set_dev(dev)) + return 1; + + mtd = get_nand_dev_by_index(dev); + + if (!nand_unlock(mtd, off, size, allexcept)) { + puts("NAND flash successfully unlocked\n"); + } else { + puts("Error unlocking NAND flash, " + "write and erase will probably fail\n"); + return 1; + } + return 0; + } +#endif + +usage: + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char nand_help_text[] = + "info - show available NAND devices\n" + "nand device [dev] - show or set current device\n" + "nand read - addr off|partition size\n" + "nand write - addr off|partition size\n" + " read/write 'size' bytes starting at offset 'off'\n" + " to/from memory address 'addr', skipping bad blocks.\n" + "nand read.raw - addr off|partition [count]\n" + "nand write.raw[.noverify] - addr off|partition [count]\n" + " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" +#ifdef CONFIG_CMD_NAND_TRIMFFS + "nand write.trimffs - addr off|partition size\n" + " write 'size' bytes starting at offset 'off' from memory address\n" + " 'addr', skipping bad blocks and dropping any pages at the end\n" + " of eraseblocks that contain only 0xFF\n" +#endif + "nand erase[.spread] [clean] off size - erase 'size' bytes " + "from offset 'off'\n" + " With '.spread', erase enough for given file size, otherwise,\n" + " 'size' includes skipped bad blocks.\n" + "nand erase.part [clean] partition - erase entire mtd partition'\n" + "nand erase.chip [clean] - erase entire chip'\n" + "nand bad - show bad blocks\n" + "nand dump[.oob] off - dump page\n" +#ifdef CONFIG_CMD_NAND_TORTURE + "nand torture off - torture one block at offset\n" + "nand torture off [size] - torture blocks from off to off+size\n" +#endif + "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" + " really clean NAND erasing bad blocks (UNSAFE)\n" + "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" + "nand biterr off - make a bit error at offset (UNSAFE)" +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + "\n" + "nand lock [tight] [status]\n" + " bring nand to lock state or display locked pages\n" + "nand unlock[.allexcept] [offset] [size] - unlock section" +#endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif + ""; +#endif + +U_BOOT_CMD( + nand, CONFIG_SYS_MAXARGS, 1, do_nand, + "NAND sub-system", nand_help_text +); + +static int nand_load_image(struct cmd_tbl *cmdtp, struct mtd_info *mtd, + ulong offset, ulong addr, char *cmd) +{ + int r; + char *s; + size_t cnt; +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + image_header_t *hdr; +#endif +#if defined(CONFIG_FIT) + const void *fit_hdr = NULL; +#endif + + s = strchr(cmd, '.'); + if (s != NULL && + (strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { + printf("Unknown nand load suffix '%s'\n", s); + bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); + return 1; + } + + printf("\nLoading from %s, offset 0x%lx\n", mtd->name, offset); + + cnt = mtd->writesize; + r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, + (u_char *)addr); + if (r) { + puts("** Read error\n"); + bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); + + switch (genimg_get_format ((void *)addr)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + hdr = (image_header_t *)addr; + + bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); + image_print_contents (hdr); + + cnt = image_get_image_size (hdr); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = (const void *)addr; + puts ("Fit image detected...\n"); + + cnt = fit_get_size (fit_hdr); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_NAND_TYPE); + puts ("** Unknown image type\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); + + r = nand_read_skip_bad(mtd, offset, &cnt, NULL, mtd->size, + (u_char *)addr); + if (r) { + puts("** Read error\n"); + bootstage_error(BOOTSTAGE_ID_NAND_READ); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_READ); + +#if defined(CONFIG_FIT) + /* This cannot be done earlier, we need complete FIT image in RAM first */ + if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { + if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) { + bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); + puts ("** Bad FIT image format\n"); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); + fit_print_contents (fit_hdr); + } +#endif + + /* Loading ok, update default load address */ + + image_load_addr = addr; + + return bootm_maybe_autostart(cmdtp, cmd); +} + +static int do_nandboot(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *boot_device = NULL; + int idx; + ulong addr, offset = 0; + struct mtd_info *mtd; +#if defined(CONFIG_CMD_MTDPARTS) + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (argc >= 2) { + char *p = (argc == 2) ? argv[1] : argv[2]; + if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && + (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { + if (dev->id->type != MTD_DEV_TYPE_NAND) { + puts("Not a NAND device\n"); + return 1; + } + if (argc > 3) + goto usage; + if (argc == 3) + addr = simple_strtoul(argv[1], NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + + mtd = get_nand_dev_by_index(dev->id->num); + return nand_load_image(cmdtp, mtd, part->offset, + addr, argv[0]); + } + } +#endif + + bootstage_mark(BOOTSTAGE_ID_NAND_PART); + switch (argc) { + case 1: + addr = CONFIG_SYS_LOAD_ADDR; + boot_device = env_get("bootdevice"); + break; + case 2: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = env_get("bootdevice"); + break; + case 3: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + break; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + offset = simple_strtoul(argv[3], NULL, 16); + break; + default: +#if defined(CONFIG_CMD_MTDPARTS) +usage: +#endif + bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); + + if (!boot_device) { + puts("\n** No boot device **\n"); + bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); + + idx = simple_strtoul(boot_device, NULL, 16); + + mtd = get_nand_dev_by_index(idx); + if (!mtd) { + printf("\n** Device %d not available\n", idx); + bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); + return 1; + } + bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); + + return nand_load_image(cmdtp, mtd, offset, addr, argv[0]); +} + +U_BOOT_CMD(nboot, 4, 1, do_nandboot, + "boot from NAND device", + "[partition] | [[[loadAddr] dev] offset]" +); diff --git a/roms/u-boot/cmd/net.c b/roms/u-boot/cmd/net.c new file mode 100644 index 000000000..beb2877df --- /dev/null +++ b/roms/u-boot/cmd/net.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +/* + * Boot support + */ +#include <common.h> +#include <bootstage.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <net.h> +#include <net/udp.h> +#include <net/sntp.h> + +static int netboot_common(enum proto_t, struct cmd_tbl *, int, char * const []); + +#ifdef CONFIG_CMD_BOOTP +static int do_bootp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(BOOTP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + bootp, 3, 1, do_bootp, + "boot image via network using BOOTP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#ifdef CONFIG_CMD_TFTPBOOT +int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret; + + bootstage_mark_name(BOOTSTAGE_KERNELREAD_START, "tftp_start"); + ret = netboot_common(TFTPGET, cmdtp, argc, argv); + bootstage_mark_name(BOOTSTAGE_KERNELREAD_STOP, "tftp_done"); + return ret; +} + +U_BOOT_CMD( + tftpboot, 3, 1, do_tftpb, + "boot image via network using TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#ifdef CONFIG_CMD_TFTPPUT +static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(TFTPPUT, cmdtp, argc, argv); +} + +U_BOOT_CMD( + tftpput, 4, 1, do_tftpput, + "TFTP put command, for uploading files to a server", + "Address Size [[hostIPaddr:]filename]" +); +#endif + +#ifdef CONFIG_CMD_TFTPSRV +static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(TFTPSRV, cmdtp, argc, argv); +} + +U_BOOT_CMD( + tftpsrv, 2, 1, do_tftpsrv, + "act as a TFTP server and boot the first received file", + "[loadAddress]\n" + "Listen for an incoming TFTP transfer, receive a file and boot it.\n" + "The transfer is aborted if a transfer has not been started after\n" + "about 50 seconds or if Ctrl-C is pressed." +); +#endif + + +#ifdef CONFIG_CMD_RARP +int do_rarpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return netboot_common(RARP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + rarpboot, 3, 1, do_rarpb, + "boot image via network using RARP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#if defined(CONFIG_CMD_DHCP) +static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(DHCP, cmdtp, argc, argv); +} + +U_BOOT_CMD( + dhcp, 3, 1, do_dhcp, + "boot image via network using DHCP/TFTP protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +#if defined(CONFIG_CMD_NFS) +static int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(NFS, cmdtp, argc, argv); +} + +U_BOOT_CMD( + nfs, 3, 1, do_nfs, + "boot image via network using NFS protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" +); +#endif + +static void netboot_update_env(void) +{ + char tmp[22]; + + if (net_gateway.s_addr) { + ip_to_string(net_gateway, tmp); + env_set("gatewayip", tmp); + } + + if (net_netmask.s_addr) { + ip_to_string(net_netmask, tmp); + env_set("netmask", tmp); + } + +#ifdef CONFIG_CMD_BOOTP + if (net_hostname[0]) + env_set("hostname", net_hostname); +#endif + +#ifdef CONFIG_CMD_BOOTP + if (net_root_path[0]) + env_set("rootpath", net_root_path); +#endif + + if (net_ip.s_addr) { + ip_to_string(net_ip, tmp); + env_set("ipaddr", tmp); + } +#if !defined(CONFIG_BOOTP_SERVERIP) + /* + * Only attempt to change serverip if net/bootp.c:store_net_params() + * could have set it + */ + if (net_server_ip.s_addr) { + ip_to_string(net_server_ip, tmp); + env_set("serverip", tmp); + } +#endif + if (net_dns_server.s_addr) { + ip_to_string(net_dns_server, tmp); + env_set("dnsip", tmp); + } +#if defined(CONFIG_BOOTP_DNS2) + if (net_dns_server2.s_addr) { + ip_to_string(net_dns_server2, tmp); + env_set("dnsip2", tmp); + } +#endif +#ifdef CONFIG_CMD_BOOTP + if (net_nis_domain[0]) + env_set("domain", net_nis_domain); +#endif + +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET) + if (net_ntp_time_offset) { + sprintf(tmp, "%d", net_ntp_time_offset); + env_set("timeoffset", tmp); + } +#endif +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) + if (net_ntp_server.s_addr) { + ip_to_string(net_ntp_server, tmp); + env_set("ntpserverip", tmp); + } +#endif +} + +static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc, + char *const argv[]) +{ + char *s; + char *end; + int rcode = 0; + int size; + ulong addr; + + net_boot_file_name_explicit = false; + + /* pre-set image_load_addr */ + s = env_get("loadaddr"); + if (s != NULL) + image_load_addr = simple_strtoul(s, NULL, 16); + + switch (argc) { + case 1: + /* refresh bootfile name from env */ + copy_filename(net_boot_file_name, env_get("bootfile"), + sizeof(net_boot_file_name)); + break; + + case 2: /* + * Only one arg - accept two forms: + * Just load address, or just boot file name. The latter + * form must be written in a format which can not be + * mis-interpreted as a valid number. + */ + addr = simple_strtoul(argv[1], &end, 16); + if (end == (argv[1] + strlen(argv[1]))) { + image_load_addr = addr; + /* refresh bootfile name from env */ + copy_filename(net_boot_file_name, env_get("bootfile"), + sizeof(net_boot_file_name)); + } else { + net_boot_file_name_explicit = true; + copy_filename(net_boot_file_name, argv[1], + sizeof(net_boot_file_name)); + } + break; + + case 3: + image_load_addr = simple_strtoul(argv[1], NULL, 16); + net_boot_file_name_explicit = true; + copy_filename(net_boot_file_name, argv[2], + sizeof(net_boot_file_name)); + + break; + +#ifdef CONFIG_CMD_TFTPPUT + case 4: + if (strict_strtoul(argv[1], 16, &image_save_addr) < 0 || + strict_strtoul(argv[2], 16, &image_save_size) < 0) { + printf("Invalid address/size\n"); + return CMD_RET_USAGE; + } + net_boot_file_name_explicit = true; + copy_filename(net_boot_file_name, argv[3], + sizeof(net_boot_file_name)); + break; +#endif + default: + bootstage_error(BOOTSTAGE_ID_NET_START); + return CMD_RET_USAGE; + } + bootstage_mark(BOOTSTAGE_ID_NET_START); + + size = net_loop(proto); + if (size < 0) { + bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK); + return CMD_RET_FAILURE; + } + bootstage_mark(BOOTSTAGE_ID_NET_NETLOOP_OK); + + /* net_loop ok, update environment */ + netboot_update_env(); + + /* done if no file was loaded (no errors though) */ + if (size == 0) { + bootstage_error(BOOTSTAGE_ID_NET_LOADED); + return CMD_RET_SUCCESS; + } + + bootstage_mark(BOOTSTAGE_ID_NET_LOADED); + + rcode = bootm_maybe_autostart(cmdtp, argv[0]); + + if (rcode == CMD_RET_SUCCESS) + bootstage_mark(BOOTSTAGE_ID_NET_DONE); + else + bootstage_error(BOOTSTAGE_ID_NET_DONE_ERR); + return rcode; +} + +#if defined(CONFIG_CMD_PING) +static int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + net_ping_ip = string_to_ip(argv[1]); + if (net_ping_ip.s_addr == 0) + return CMD_RET_USAGE; + + if (net_loop(PING) < 0) { + printf("ping failed; host %s is not alive\n", argv[1]); + return CMD_RET_FAILURE; + } + + printf("host %s is alive\n", argv[1]); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + ping, 2, 1, do_ping, + "send ICMP ECHO_REQUEST to network host", + "pingAddress" +); +#endif + +#if defined(CONFIG_CMD_CDP) + +static void cdp_update_env(void) +{ + char tmp[16]; + + if (cdp_appliance_vlan != htons(-1)) { + printf("CDP offered appliance VLAN %d\n", + ntohs(cdp_appliance_vlan)); + vlan_to_string(cdp_appliance_vlan, tmp); + env_set("vlan", tmp); + net_our_vlan = cdp_appliance_vlan; + } + + if (cdp_native_vlan != htons(-1)) { + printf("CDP offered native VLAN %d\n", ntohs(cdp_native_vlan)); + vlan_to_string(cdp_native_vlan, tmp); + env_set("nvlan", tmp); + net_native_vlan = cdp_native_vlan; + } +} + +int do_cdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int r; + + r = net_loop(CDP); + if (r < 0) { + printf("cdp failed; perhaps not a CISCO switch?\n"); + return CMD_RET_FAILURE; + } + + cdp_update_env(); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + cdp, 1, 1, do_cdp, + "Perform CDP network configuration", + "\n" +); +#endif + +#if defined(CONFIG_CMD_SNTP) +static struct udp_ops sntp_ops = { + .prereq = sntp_prereq, + .start = sntp_start, + .data = NULL, +}; + +int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *toff; + + if (argc < 2) { + net_ntp_server = env_get_ip("ntpserverip"); + if (net_ntp_server.s_addr == 0) { + printf("ntpserverip not set\n"); + return CMD_RET_FAILURE; + } + } else { + net_ntp_server = string_to_ip(argv[1]); + if (net_ntp_server.s_addr == 0) { + printf("Bad NTP server IP address\n"); + return CMD_RET_FAILURE; + } + } + + toff = env_get("timeoffset"); + if (toff == NULL) + net_ntp_time_offset = 0; + else + net_ntp_time_offset = simple_strtol(toff, NULL, 10); + + if (udp_loop(&sntp_ops) < 0) { + printf("SNTP failed: host %pI4 not responding\n", + &net_ntp_server); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + sntp, 2, 1, do_sntp, + "synchronize RTC via network", + "[NTP server IP]\n" +); +#endif + +#if defined(CONFIG_CMD_DNS) +int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc == 1) + return CMD_RET_USAGE; + + /* + * We should check for a valid hostname: + * - Each label must be between 1 and 63 characters long + * - the entire hostname has a maximum of 255 characters + * - only the ASCII letters 'a' through 'z' (case-insensitive), + * the digits '0' through '9', and the hyphen + * - cannot begin or end with a hyphen + * - no other symbols, punctuation characters, or blank spaces are + * permitted + * but hey - this is a minimalist implmentation, so only check length + * and let the name server deal with things. + */ + if (strlen(argv[1]) >= 255) { + printf("dns error: hostname too long\n"); + return CMD_RET_FAILURE; + } + + net_dns_resolve = argv[1]; + + if (argc == 3) + net_dns_env_var = argv[2]; + else + net_dns_env_var = NULL; + + if (net_loop(DNS) < 0) { + printf("dns lookup of %s failed, check setup\n", argv[1]); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + dns, 3, 1, do_dns, + "lookup the IP of a hostname", + "hostname [envvar]" +); + +#endif /* CONFIG_CMD_DNS */ + +#if defined(CONFIG_CMD_LINK_LOCAL) +static int do_link_local(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char tmp[22]; + + if (net_loop(LINKLOCAL) < 0) + return CMD_RET_FAILURE; + + net_gateway.s_addr = 0; + ip_to_string(net_gateway, tmp); + env_set("gatewayip", tmp); + + ip_to_string(net_netmask, tmp); + env_set("netmask", tmp); + + ip_to_string(net_ip, tmp); + env_set("ipaddr", tmp); + env_set("llipaddr", tmp); /* store this for next time */ + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + linklocal, 1, 1, do_link_local, + "acquire a network IP address using the link-local protocol", + "" +); + +#endif /* CONFIG_CMD_LINK_LOCAL */ diff --git a/roms/u-boot/cmd/nvedit.c b/roms/u-boot/cmd/nvedit.c new file mode 100644 index 000000000..d14ba10ce --- /dev/null +++ b/roms/u-boot/cmd/nvedit.c @@ -0,0 +1,1585 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2013 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * Copyright 2011 Freescale Semiconductor, Inc. + */ + +/* + * Support for persistent environment data + * + * The "environment" is stored on external storage as a list of '\0' + * terminated "name=value" strings. The end of the list is marked by + * a double '\0'. The environment is preceded by a 32 bit CRC over + * the data part and, in case of redundant environment, a byte of + * flags. + * + * This linearized representation will also be used before + * relocation, i. e. as long as we don't have a full C runtime + * environment. After that, we use a hash table. + */ + +#include <common.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <env_internal.h> +#include <log.h> +#include <net.h> +#include <search.h> +#include <errno.h> +#include <malloc.h> +#include <mapmem.h> +#include <asm/global_data.h> +#include <linux/bitops.h> +#include <u-boot/crc.h> +#include <watchdog.h> +#include <linux/stddef.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_ENV_IS_IN_EEPROM) || \ + defined(CONFIG_ENV_IS_IN_FLASH) || \ + defined(CONFIG_ENV_IS_IN_MMC) || \ + defined(CONFIG_ENV_IS_IN_FAT) || \ + defined(CONFIG_ENV_IS_IN_EXT4) || \ + defined(CONFIG_ENV_IS_IN_NAND) || \ + defined(CONFIG_ENV_IS_IN_NVRAM) || \ + defined(CONFIG_ENV_IS_IN_ONENAND) || \ + defined(CONFIG_ENV_IS_IN_SATA) || \ + defined(CONFIG_ENV_IS_IN_SPI_FLASH) || \ + defined(CONFIG_ENV_IS_IN_REMOTE) || \ + defined(CONFIG_ENV_IS_IN_UBI) + +#define ENV_IS_IN_DEVICE + +#endif + +#if !defined(ENV_IS_IN_DEVICE) && \ + !defined(CONFIG_ENV_IS_NOWHERE) +# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\ +NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE +#endif + +/* + * Maximum expected input data size for import command + */ +#define MAX_ENV_SIZE (1 << 20) /* 1 MiB */ + +/* + * This variable is incremented on each do_env_set(), so it can + * be used via env_get_id() as an indication, if the environment + * has changed or not. So it is possible to reread an environment + * variable only if the environment was changed ... done so for + * example in NetInitLoop() + */ +static int env_id = 1; + +int env_get_id(void) +{ + return env_id; +} + +#ifndef CONFIG_SPL_BUILD +/* + * Command interface: print one or all environment variables + * + * Returns 0 in case of error, or length of printed string + */ +static int env_print(char *name, int flag) +{ + char *res = NULL; + ssize_t len; + + if (name) { /* print a single name */ + struct env_entry e, *ep; + + e.key = name; + e.data = NULL; + hsearch_r(e, ENV_FIND, &ep, &env_htab, flag); + if (ep == NULL) + return 0; + len = printf("%s=%s\n", ep->key, ep->data); + return len; + } + + /* print whole list */ + len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL); + + if (len > 0) { + puts(res); + free(res); + return len; + } + + /* should never happen */ + printf("## Error: cannot export environment\n"); + return 0; +} + +static int do_env_print(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + int rcode = 0; + int env_flag = H_HIDE_DOT; + +#if defined(CONFIG_CMD_NVEDIT_EFI) + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e') + return do_env_print_efi(cmdtp, flag, --argc, ++argv); +#endif + + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') { + argc--; + argv++; + env_flag &= ~H_HIDE_DOT; + } + + if (argc == 1) { + /* print all env vars */ + rcode = env_print(NULL, env_flag); + if (!rcode) + return 1; + printf("\nEnvironment size: %d/%ld bytes\n", + rcode, (ulong)ENV_SIZE); + return 0; + } + + /* print selected env vars */ + env_flag &= ~H_HIDE_DOT; + for (i = 1; i < argc; ++i) { + int rc = env_print(argv[i], env_flag); + if (!rc) { + printf("## Error: \"%s\" not defined\n", argv[i]); + ++rcode; + } + } + + return rcode; +} + +#ifdef CONFIG_CMD_GREPENV +static int do_env_grep(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + char *res = NULL; + int len, grep_how, grep_what; + + if (argc < 2) + return CMD_RET_USAGE; + + grep_how = H_MATCH_SUBSTR; /* default: substring search */ + grep_what = H_MATCH_BOTH; /* default: grep names and values */ + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { +#ifdef CONFIG_REGEX + case 'e': /* use regex matching */ + grep_how = H_MATCH_REGEX; + break; +#endif + case 'n': /* grep for name */ + grep_what = H_MATCH_KEY; + break; + case 'v': /* grep for value */ + grep_what = H_MATCH_DATA; + break; + case 'b': /* grep for both */ + grep_what = H_MATCH_BOTH; + break; + case '-': + goto DONE; + default: + return CMD_RET_USAGE; + } + } + } + +DONE: + len = hexport_r(&env_htab, '\n', + flag | grep_what | grep_how, + &res, 0, argc, argv); + + if (len > 0) { + puts(res); + free(res); + } + + if (len < 2) + return 1; + + return 0; +} +#endif +#endif /* CONFIG_SPL_BUILD */ + +/* + * Set a new environment variable, + * or replace or delete an existing one. + */ +static int _do_env_set(int flag, int argc, char *const argv[], int env_flag) +{ + int i, len; + char *name, *value, *s; + struct env_entry e, *ep; + + debug("Initial value for argc=%d\n", argc); + +#if CONFIG_IS_ENABLED(CMD_NVEDIT_EFI) + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e') + return do_env_set_efi(NULL, flag, --argc, ++argv); +#endif + + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'f': /* force */ + env_flag |= H_FORCE; + break; + default: + return CMD_RET_USAGE; + } + } + } + debug("Final value for argc=%d\n", argc); + name = argv[1]; + + if (strchr(name, '=')) { + printf("## Error: illegal character '='" + "in variable name \"%s\"\n", name); + return 1; + } + + env_id++; + + /* Delete only ? */ + if (argc < 3 || argv[2] == NULL) { + int rc = hdelete_r(name, &env_htab, env_flag); + + /* If the variable didn't exist, don't report an error */ + return rc && rc != -ENOENT ? 1 : 0; + } + + /* + * Insert / replace new value + */ + for (i = 2, len = 0; i < argc; ++i) + len += strlen(argv[i]) + 1; + + value = malloc(len); + if (value == NULL) { + printf("## Can't malloc %d bytes\n", len); + return 1; + } + for (i = 2, s = value; i < argc; ++i) { + char *v = argv[i]; + + while ((*s++ = *v++) != '\0') + ; + *(s - 1) = ' '; + } + if (s != value) + *--s = '\0'; + + e.key = name; + e.data = value; + hsearch_r(e, ENV_ENTER, &ep, &env_htab, env_flag); + free(value); + if (!ep) { + printf("## Error inserting \"%s\" variable, errno=%d\n", + name, errno); + return 1; + } + + return 0; +} + +int env_set(const char *varname, const char *varvalue) +{ + const char * const argv[4] = { "setenv", varname, varvalue, NULL }; + + /* before import into hashtable */ + if (!(gd->flags & GD_FLG_ENV_READY)) + return 1; + + if (varvalue == NULL || varvalue[0] == '\0') + return _do_env_set(0, 2, (char * const *)argv, H_PROGRAMMATIC); + else + return _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC); +} + +/** + * Set an environment variable to an integer value + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int env_set_ulong(const char *varname, ulong value) +{ + /* TODO: this should be unsigned */ + char *str = simple_itoa(value); + + return env_set(varname, str); +} + +/** + * Set an environment variable to an value in hex + * + * @param varname Environment variable to set + * @param value Value to set it to + * @return 0 if ok, 1 on error + */ +int env_set_hex(const char *varname, ulong value) +{ + char str[17]; + + sprintf(str, "%lx", value); + return env_set(varname, str); +} + +ulong env_get_hex(const char *varname, ulong default_val) +{ + const char *s; + ulong value; + char *endp; + + s = env_get(varname); + if (s) + value = simple_strtoul(s, &endp, 16); + if (!s || endp == s) + return default_val; + + return value; +} + +int eth_env_get_enetaddr(const char *name, uint8_t *enetaddr) +{ + string_to_enetaddr(env_get(name), enetaddr); + return is_valid_ethaddr(enetaddr); +} + +int eth_env_set_enetaddr(const char *name, const uint8_t *enetaddr) +{ + char buf[ARP_HLEN_ASCII + 1]; + + if (eth_env_get_enetaddr(name, (uint8_t *)buf)) + return -EEXIST; + + sprintf(buf, "%pM", enetaddr); + + return env_set(name, buf); +} + +#ifndef CONFIG_SPL_BUILD +static int do_env_set(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + return _do_env_set(flag, argc, argv, H_INTERACTIVE); +} + +/* + * Prompt for environment variable + */ +#if defined(CONFIG_CMD_ASKENV) +int do_env_ask(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char message[CONFIG_SYS_CBSIZE]; + int i, len, pos, size; + char *local_args[4]; + char *endptr; + + local_args[0] = argv[0]; + local_args[1] = argv[1]; + local_args[2] = NULL; + local_args[3] = NULL; + + /* + * Check the syntax: + * + * env_ask envname [message1 ...] [size] + */ + if (argc == 1) + return CMD_RET_USAGE; + + /* + * We test the last argument if it can be converted + * into a decimal number. If yes, we assume it's + * the size. Otherwise we echo it as part of the + * message. + */ + i = simple_strtoul(argv[argc - 1], &endptr, 10); + if (*endptr != '\0') { /* no size */ + size = CONFIG_SYS_CBSIZE - 1; + } else { /* size given */ + size = i; + --argc; + } + + if (argc <= 2) { + sprintf(message, "Please enter '%s': ", argv[1]); + } else { + /* env_ask envname message1 ... messagen [size] */ + for (i = 2, pos = 0; i < argc && pos+1 < sizeof(message); i++) { + if (pos) + message[pos++] = ' '; + + strncpy(message + pos, argv[i], sizeof(message) - pos); + pos += strlen(argv[i]); + } + if (pos < sizeof(message) - 1) { + message[pos++] = ' '; + message[pos] = '\0'; + } else + message[CONFIG_SYS_CBSIZE - 1] = '\0'; + } + + if (size >= CONFIG_SYS_CBSIZE) + size = CONFIG_SYS_CBSIZE - 1; + + if (size <= 0) + return 1; + + /* prompt for input */ + len = cli_readline(message); + + if (size < len) + console_buffer[size] = '\0'; + + len = 2; + if (console_buffer[0] != '\0') { + local_args[2] = console_buffer; + len = 3; + } + + /* Continue calling setenv code */ + return _do_env_set(flag, len, local_args, H_INTERACTIVE); +} +#endif + +#if defined(CONFIG_CMD_ENV_CALLBACK) +static int print_static_binding(const char *var_name, const char *callback_name, + void *priv) +{ + printf("\t%-20s %-20s\n", var_name, callback_name); + + return 0; +} + +static int print_active_callback(struct env_entry *entry) +{ + struct env_clbk_tbl *clbkp; + int i; + int num_callbacks; + + if (entry->callback == NULL) + return 0; + + /* look up the callback in the linker-list */ + num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); + i < num_callbacks; + i++, clbkp++) { +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + if (entry->callback == clbkp->callback + gd->reloc_off) +#else + if (entry->callback == clbkp->callback) +#endif + break; + } + + if (i == num_callbacks) + /* this should probably never happen, but just in case... */ + printf("\t%-20s %p\n", entry->key, entry->callback); + else + printf("\t%-20s %-20s\n", entry->key, clbkp->name); + + return 0; +} + +/* + * Print the callbacks available and what they are bound to + */ +int do_env_callback(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct env_clbk_tbl *clbkp; + int i; + int num_callbacks; + + /* Print the available callbacks */ + puts("Available callbacks:\n"); + puts("\tCallback Name\n"); + puts("\t-------------\n"); + num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); + i < num_callbacks; + i++, clbkp++) + printf("\t%s\n", clbkp->name); + puts("\n"); + + /* Print the static bindings that may exist */ + puts("Static callback bindings:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Callback Name"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + env_attr_walk(ENV_CALLBACK_LIST_STATIC, print_static_binding, NULL); + puts("\n"); + + /* walk through each variable and print the callback if it has one */ + puts("Active callback bindings:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Callback Name"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + hwalk_r(&env_htab, print_active_callback); + return 0; +} +#endif + +#if defined(CONFIG_CMD_ENV_FLAGS) +static int print_static_flags(const char *var_name, const char *flags, + void *priv) +{ + enum env_flags_vartype type = env_flags_parse_vartype(flags); + enum env_flags_varaccess access = env_flags_parse_varaccess(flags); + + printf("\t%-20s %-20s %-20s\n", var_name, + env_flags_get_vartype_name(type), + env_flags_get_varaccess_name(access)); + + return 0; +} + +static int print_active_flags(struct env_entry *entry) +{ + enum env_flags_vartype type; + enum env_flags_varaccess access; + + if (entry->flags == 0) + return 0; + + type = (enum env_flags_vartype) + (entry->flags & ENV_FLAGS_VARTYPE_BIN_MASK); + access = env_flags_parse_varaccess_from_binflags(entry->flags); + printf("\t%-20s %-20s %-20s\n", entry->key, + env_flags_get_vartype_name(type), + env_flags_get_varaccess_name(access)); + + return 0; +} + +/* + * Print the flags available and what variables have flags + */ +int do_env_flags(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + /* Print the available variable types */ + printf("Available variable type flags (position %d):\n", + ENV_FLAGS_VARTYPE_LOC); + puts("\tFlag\tVariable Type Name\n"); + puts("\t----\t------------------\n"); + env_flags_print_vartypes(); + puts("\n"); + + /* Print the available variable access types */ + printf("Available variable access flags (position %d):\n", + ENV_FLAGS_VARACCESS_LOC); + puts("\tFlag\tVariable Access Name\n"); + puts("\t----\t--------------------\n"); + env_flags_print_varaccess(); + puts("\n"); + + /* Print the static flags that may exist */ + puts("Static flags:\n"); + printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type", + "Variable Access"); + printf("\t%-20s %-20s %-20s\n", "-------------", "-------------", + "---------------"); + env_attr_walk(ENV_FLAGS_LIST_STATIC, print_static_flags, NULL); + puts("\n"); + + /* walk through each variable and print the flags if non-default */ + puts("Active flags:\n"); + printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type", + "Variable Access"); + printf("\t%-20s %-20s %-20s\n", "-------------", "-------------", + "---------------"); + hwalk_r(&env_htab, print_active_flags); + return 0; +} +#endif + +/* + * Interactively edit an environment variable + */ +#if defined(CONFIG_CMD_EDITENV) +static int do_env_edit(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char buffer[CONFIG_SYS_CBSIZE]; + char *init_val; + + if (argc < 2) + return CMD_RET_USAGE; + + /* before import into hashtable */ + if (!(gd->flags & GD_FLG_ENV_READY)) + return 1; + + /* Set read buffer to initial value or empty sting */ + init_val = env_get(argv[1]); + if (init_val) + snprintf(buffer, CONFIG_SYS_CBSIZE, "%s", init_val); + else + buffer[0] = '\0'; + + if (cli_readline_into_buffer("edit: ", buffer, 0) < 0) + return 1; + + if (buffer[0] == '\0') { + const char * const _argv[3] = { "setenv", argv[1], NULL }; + + return _do_env_set(0, 2, (char * const *)_argv, H_INTERACTIVE); + } else { + const char * const _argv[4] = { "setenv", argv[1], buffer, + NULL }; + + return _do_env_set(0, 3, (char * const *)_argv, H_INTERACTIVE); + } +} +#endif /* CONFIG_CMD_EDITENV */ +#endif /* CONFIG_SPL_BUILD */ + +/* + * Look up variable from environment, + * return address of storage for that variable, + * or NULL if not found + */ +char *env_get(const char *name) +{ + if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */ + struct env_entry e, *ep; + + WATCHDOG_RESET(); + + e.key = name; + e.data = NULL; + hsearch_r(e, ENV_FIND, &ep, &env_htab, 0); + + return ep ? ep->data : NULL; + } + + /* restricted capabilities before import */ + if (env_get_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0) + return (char *)(gd->env_buf); + + return NULL; +} + +/* + * Like env_get, but prints an error if envvar isn't defined in the + * environment. It always returns what env_get does, so it can be used in + * place of env_get without changing error handling otherwise. + */ +char *from_env(const char *envvar) +{ + char *ret; + + ret = env_get(envvar); + + if (!ret) + printf("missing environment variable: %s\n", envvar); + + return ret; +} + +/* + * Look up variable from environment for restricted C runtime env. + */ +int env_get_f(const char *name, char *buf, unsigned len) +{ + int i, nxt, c; + + for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { + int val, n; + + for (nxt = i; (c = env_get_char(nxt)) != '\0'; ++nxt) { + if (c < 0) + return c; + if (nxt >= CONFIG_ENV_SIZE) + return -1; + } + + val = env_match((uchar *)name, i); + if (val < 0) + continue; + + /* found; copy out */ + for (n = 0; n < len; ++n, ++buf) { + c = env_get_char(val++); + if (c < 0) + return c; + *buf = c; + if (*buf == '\0') + return n; + } + + if (n) + *--buf = '\0'; + + printf("env_buf [%u bytes] too small for value of \"%s\"\n", + len, name); + + return n; + } + + return -1; +} + +/** + * Decode the integer value of an environment variable and return it. + * + * @param name Name of environment variable + * @param base Number base to use (normally 10, or 16 for hex) + * @param default_val Default value to return if the variable is not + * found + * @return the decoded value, or default_val if not found + */ +ulong env_get_ulong(const char *name, int base, ulong default_val) +{ + /* + * We can use env_get() here, even before relocation, since the + * environment variable value is an integer and thus short. + */ + const char *str = env_get(name); + + return str ? simple_strtoul(str, NULL, base) : default_val; +} + +#ifndef CONFIG_SPL_BUILD +#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE) +static int do_env_save(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return env_save() ? 1 : 0; +} + +U_BOOT_CMD( + saveenv, 1, 0, do_env_save, + "save environment variables to persistent storage", + "" +); + +#if defined(CONFIG_CMD_ERASEENV) +static int do_env_erase(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return env_erase() ? 1 : 0; +} + +U_BOOT_CMD( + eraseenv, 1, 0, do_env_erase, + "erase environment variables from persistent storage", + "" +); +#endif +#endif + +#if defined(CONFIG_CMD_NVEDIT_LOAD) +static int do_env_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return env_reload() ? 1 : 0; +} +#endif + +#if defined(CONFIG_CMD_NVEDIT_SELECT) +static int do_env_select(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return env_select(argv[1]) ? 1 : 0; +} +#endif + +#endif /* CONFIG_SPL_BUILD */ + +int env_match(uchar *s1, int i2) +{ + if (s1 == NULL) + return -1; + + while (*s1 == env_get_char(i2++)) + if (*s1++ == '=') + return i2; + + if (*s1 == '\0' && env_get_char(i2-1) == '=') + return i2; + + return -1; +} + +#ifndef CONFIG_SPL_BUILD +static int do_env_default(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int all = 0, env_flag = H_INTERACTIVE; + + debug("Initial value for argc=%d\n", argc); + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + + while (*++arg) { + switch (*arg) { + case 'a': /* default all */ + all = 1; + break; + case 'f': /* force */ + env_flag |= H_FORCE; + break; + default: + return cmd_usage(cmdtp); + } + } + } + debug("Final value for argc=%d\n", argc); + if (all && (argc == 0)) { + /* Reset the whole environment */ + env_set_default("## Resetting to default environment\n", + env_flag); + return 0; + } + if (!all && (argc > 0)) { + /* Reset individual variables */ + env_set_default_vars(argc, argv, env_flag); + return 0; + } + + return cmd_usage(cmdtp); +} + +static int do_env_delete(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int env_flag = H_INTERACTIVE; + int ret = 0; + + debug("Initial value for argc=%d\n", argc); + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'f': /* force */ + env_flag |= H_FORCE; + break; + default: + return CMD_RET_USAGE; + } + } + } + debug("Final value for argc=%d\n", argc); + + env_id++; + + while (--argc > 0) { + char *name = *++argv; + + if (hdelete_r(name, &env_htab, env_flag)) + ret = 1; + } + + return ret; +} + +#ifdef CONFIG_CMD_EXPORTENV +/* + * env export [-t | -b | -c] [-s size] addr [var ...] + * -t: export as text format; if size is given, data will be + * padded with '\0' bytes; if not, one terminating '\0' + * will be added (which is included in the "filesize" + * setting so you can for exmple copy this to flash and + * keep the termination). + * -b: export as binary format (name=value pairs separated by + * '\0', list end marked by double "\0\0") + * -c: export as checksum protected environment format as + * used for example by "saveenv" command + * -s size: + * size of output buffer + * addr: memory address where environment gets stored + * var... List of variable names that get included into the + * export. Without arguments, the whole environment gets + * exported. + * + * With "-c" and size is NOT given, then the export command will + * format the data as currently used for the persistent storage, + * i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and + * prepend a valid CRC32 checksum and, in case of redundant + * environment, a "current" redundancy flag. If size is given, this + * value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 + * checksum and redundancy flag will be inserted. + * + * With "-b" and "-t", always only the real data (including a + * terminating '\0' byte) will be written; here the optional size + * argument will be used to make sure not to overflow the user + * provided buffer; the command will abort if the size is not + * sufficient. Any remaining space will be '\0' padded. + * + * On successful return, the variable "filesize" will be set. + * Note that filesize includes the trailing/terminating '\0' byte(s). + * + * Usage scenario: create a text snapshot/backup of the current settings: + * + * => env export -t 100000 + * => era ${backup_addr} +${filesize} + * => cp.b 100000 ${backup_addr} ${filesize} + * + * Re-import this snapshot, deleting all other settings: + * + * => env import -d -t ${backup_addr} + */ +static int do_env_export(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + char buf[32]; + ulong addr; + char *ptr, *cmd, *res; + size_t size = 0; + ssize_t len; + env_t *envp; + char sep = '\n'; + int chk = 0; + int fmt = 0; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 's': /* size given */ + if (--argc <= 0) + return cmd_usage(cmdtp); + size = simple_strtoul(*++argv, NULL, 16); + goto NXTARG; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + default: + return CMD_RET_USAGE; + } + } +NXTARG: ; + } + + if (argc < 1) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[0], NULL, 16); + ptr = map_sysmem(addr, size); + + if (size) + memset(ptr, '\0', size); + + argc--; + argv++; + + if (sep) { /* export as text file */ + len = hexport_r(&env_htab, sep, + H_MATCH_KEY | H_MATCH_IDENT, + &ptr, size, argc, argv); + if (len < 0) { + pr_err("## Error: Cannot export environment: errno = %d\n", + errno); + return 1; + } + sprintf(buf, "%zX", (size_t)len); + env_set("filesize", buf); + + return 0; + } + + envp = (env_t *)ptr; + + if (chk) /* export as checksum protected block */ + res = (char *)envp->data; + else /* export as raw binary data */ + res = ptr; + + len = hexport_r(&env_htab, '\0', + H_MATCH_KEY | H_MATCH_IDENT, + &res, ENV_SIZE, argc, argv); + if (len < 0) { + pr_err("## Error: Cannot export environment: errno = %d\n", + errno); + return 1; + } + + if (chk) { + envp->crc = crc32(0, envp->data, + size ? size - offsetof(env_t, data) : ENV_SIZE); +#ifdef CONFIG_ENV_ADDR_REDUND + envp->flags = ENV_REDUND_ACTIVE; +#endif + } + env_set_hex("filesize", len + offsetof(env_t, data)); + + return 0; + +sep_err: + printf("## Error: %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n", + cmd); + return 1; +} +#endif + +#ifdef CONFIG_CMD_IMPORTENV +/* + * env import [-d] [-t [-r] | -b | -c] addr [size] [var ...] + * -d: delete existing environment before importing if no var is + * passed; if vars are passed, if one var is in the current + * environment but not in the environment at addr, delete var from + * current environment; + * otherwise overwrite / append to existing definitions + * -t: assume text format; either "size" must be given or the + * text data must be '\0' terminated + * -r: handle CRLF like LF, that means exported variables with + * a content which ends with \r won't get imported. Used + * to import text files created with editors which are using CRLF + * for line endings. Only effective in addition to -t. + * -b: assume binary format ('\0' separated, "\0\0" terminated) + * -c: assume checksum protected environment format + * addr: memory address to read from + * size: length of input data; if missing, proper '\0' + * termination is mandatory + * if var is set and size should be missing (i.e. '\0' + * termination), set size to '-' + * var... List of the names of the only variables that get imported from + * the environment at address 'addr'. Without arguments, the whole + * environment gets imported. + */ +static int do_env_import(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + ulong addr; + char *cmd, *ptr; + char sep = '\n'; + int chk = 0; + int fmt = 0; + int del = 0; + int crlf_is_lf = 0; + int wl = 0; + size_t size; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + case 'r': /* handle CRLF like LF */ + crlf_is_lf = 1; + break; + case 'd': + del = 1; + break; + default: + return CMD_RET_USAGE; + } + } + } + + if (argc < 1) + return CMD_RET_USAGE; + + if (!fmt) + printf("## Warning: defaulting to text format\n"); + + if (sep != '\n' && crlf_is_lf ) + crlf_is_lf = 0; + + addr = simple_strtoul(argv[0], NULL, 16); + ptr = map_sysmem(addr, 0); + + if (argc >= 2 && strcmp(argv[1], "-")) { + size = simple_strtoul(argv[1], NULL, 16); + } else if (chk) { + puts("## Error: external checksum format must pass size\n"); + return CMD_RET_FAILURE; + } else { + char *s = ptr; + + size = 0; + + while (size < MAX_ENV_SIZE) { + if ((*s == sep) && (*(s+1) == '\0')) + break; + ++s; + ++size; + } + if (size == MAX_ENV_SIZE) { + printf("## Warning: Input data exceeds %d bytes" + " - truncated\n", MAX_ENV_SIZE); + } + size += 2; + printf("## Info: input data size = %zu = 0x%zX\n", size, size); + } + + if (argc > 2) + wl = 1; + + if (chk) { + uint32_t crc; + env_t *ep = (env_t *)ptr; + + if (size <= offsetof(env_t, data)) { + printf("## Error: Invalid size 0x%zX\n", size); + return 1; + } + + size -= offsetof(env_t, data); + memcpy(&crc, &ep->crc, sizeof(crc)); + + if (crc32(0, ep->data, size) != crc) { + puts("## Error: bad CRC, import failed\n"); + return 1; + } + ptr = (char *)ep->data; + } + + if (!himport_r(&env_htab, ptr, size, sep, del ? 0 : H_NOCLEAR, + crlf_is_lf, wl ? argc - 2 : 0, wl ? &argv[2] : NULL)) { + pr_err("## Error: Environment import failed: errno = %d\n", + errno); + return 1; + } + gd->flags |= GD_FLG_ENV_READY; + + return 0; + +sep_err: + printf("## %s: only one of \"-b\", \"-c\" or \"-t\" allowed\n", + cmd); + return 1; +} +#endif + +#if defined(CONFIG_CMD_NVEDIT_INFO) +/* + * print_env_info - print environment information + */ +static int print_env_info(void) +{ + const char *value; + + /* print environment validity value */ + switch (gd->env_valid) { + case ENV_INVALID: + value = "invalid"; + break; + case ENV_VALID: + value = "valid"; + break; + case ENV_REDUND: + value = "redundant"; + break; + default: + value = "unknown"; + break; + } + printf("env_valid = %s\n", value); + + /* print environment ready flag */ + value = gd->flags & GD_FLG_ENV_READY ? "true" : "false"; + printf("env_ready = %s\n", value); + + /* print environment using default flag */ + value = gd->flags & GD_FLG_ENV_DEFAULT ? "true" : "false"; + printf("env_use_default = %s\n", value); + + return CMD_RET_SUCCESS; +} + +#define ENV_INFO_IS_DEFAULT BIT(0) /* default environment bit mask */ +#define ENV_INFO_IS_PERSISTED BIT(1) /* environment persistence bit mask */ + +/* + * env info - display environment information + * env info [-d] - evaluate whether default environment is used + * env info [-p] - evaluate whether environment can be persisted + * Add [-q] - quiet mode, use only for command result, for test by example: + * test env info -p -d -q + */ +static int do_env_info(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int eval_flags = 0; + int eval_results = 0; + bool quiet = false; +#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE) + enum env_location loc; +#endif + + /* display environment information */ + if (argc <= 1) + return print_env_info(); + + /* process options */ + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + + while (*++arg) { + switch (*arg) { + case 'd': + eval_flags |= ENV_INFO_IS_DEFAULT; + break; + case 'p': + eval_flags |= ENV_INFO_IS_PERSISTED; + break; + case 'q': + quiet = true; + break; + default: + return CMD_RET_USAGE; + } + } + } + + /* evaluate whether default environment is used */ + if (eval_flags & ENV_INFO_IS_DEFAULT) { + if (gd->flags & GD_FLG_ENV_DEFAULT) { + if (!quiet) + printf("Default environment is used\n"); + eval_results |= ENV_INFO_IS_DEFAULT; + } else { + if (!quiet) + printf("Environment was loaded from persistent storage\n"); + } + } + + /* evaluate whether environment can be persisted */ + if (eval_flags & ENV_INFO_IS_PERSISTED) { +#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE) + loc = env_get_location(ENVOP_SAVE, gd->env_load_prio); + if (ENVL_NOWHERE != loc && ENVL_UNKNOWN != loc) { + if (!quiet) + printf("Environment can be persisted\n"); + eval_results |= ENV_INFO_IS_PERSISTED; + } else { + if (!quiet) + printf("Environment cannot be persisted\n"); + } +#else + if (!quiet) + printf("Environment cannot be persisted\n"); +#endif + } + + /* The result of evaluations is combined with AND */ + if (eval_flags != eval_results) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} +#endif + +#if defined(CONFIG_CMD_ENV_EXISTS) +static int do_env_exists(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct env_entry e, *ep; + + if (argc < 2) + return CMD_RET_USAGE; + + e.key = argv[1]; + e.data = NULL; + hsearch_r(e, ENV_FIND, &ep, &env_htab, 0); + + return (ep == NULL) ? 1 : 0; +} +#endif + +/* + * New command line interface: "env" command with subcommands + */ +static struct cmd_tbl cmd_env_sub[] = { +#if defined(CONFIG_CMD_ASKENV) + U_BOOT_CMD_MKENT(ask, CONFIG_SYS_MAXARGS, 1, do_env_ask, "", ""), +#endif + U_BOOT_CMD_MKENT(default, 1, 0, do_env_default, "", ""), + U_BOOT_CMD_MKENT(delete, CONFIG_SYS_MAXARGS, 0, do_env_delete, "", ""), +#if defined(CONFIG_CMD_EDITENV) + U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""), +#endif +#if defined(CONFIG_CMD_ENV_CALLBACK) + U_BOOT_CMD_MKENT(callbacks, 1, 0, do_env_callback, "", ""), +#endif +#if defined(CONFIG_CMD_ENV_FLAGS) + U_BOOT_CMD_MKENT(flags, 1, 0, do_env_flags, "", ""), +#endif +#if defined(CONFIG_CMD_EXPORTENV) + U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""), +#endif +#if defined(CONFIG_CMD_GREPENV) + U_BOOT_CMD_MKENT(grep, CONFIG_SYS_MAXARGS, 1, do_env_grep, "", ""), +#endif +#if defined(CONFIG_CMD_IMPORTENV) + U_BOOT_CMD_MKENT(import, 5, 0, do_env_import, "", ""), +#endif +#if defined(CONFIG_CMD_NVEDIT_INFO) + U_BOOT_CMD_MKENT(info, 3, 0, do_env_info, "", ""), +#endif +#if defined(CONFIG_CMD_NVEDIT_LOAD) + U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""), +#endif + U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""), +#if defined(CONFIG_CMD_RUN) + U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""), +#endif +#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE) + U_BOOT_CMD_MKENT(save, 1, 0, do_env_save, "", ""), +#if defined(CONFIG_CMD_ERASEENV) + U_BOOT_CMD_MKENT(erase, 1, 0, do_env_erase, "", ""), +#endif +#endif +#if defined(CONFIG_CMD_NVEDIT_SELECT) + U_BOOT_CMD_MKENT(select, 2, 0, do_env_select, "", ""), +#endif + U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 0, do_env_set, "", ""), +#if defined(CONFIG_CMD_ENV_EXISTS) + U_BOOT_CMD_MKENT(exists, 2, 0, do_env_exists, "", ""), +#endif +}; + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +void env_reloc(void) +{ + fixup_cmdtable(cmd_env_sub, ARRAY_SIZE(cmd_env_sub)); +} +#endif + +static int do_env(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + /* drop initial "env" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_env_sub, ARRAY_SIZE(cmd_env_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char env_help_text[] = +#if defined(CONFIG_CMD_ASKENV) + "ask name [message] [size] - ask for environment variable\nenv " +#endif +#if defined(CONFIG_CMD_ENV_CALLBACK) + "callbacks - print callbacks and their associated variables\nenv " +#endif + "default [-f] -a - [forcibly] reset default environment\n" + "env default [-f] var [...] - [forcibly] reset variable(s) to their default values\n" + "env delete [-f] var [...] - [forcibly] delete variable(s)\n" +#if defined(CONFIG_CMD_EDITENV) + "env edit name - edit environment variable\n" +#endif +#if defined(CONFIG_CMD_ENV_EXISTS) + "env exists name - tests for existence of variable\n" +#endif +#if defined(CONFIG_CMD_EXPORTENV) + "env export [-t | -b | -c] [-s size] addr [var ...] - export environment\n" +#endif +#if defined(CONFIG_CMD_ENV_FLAGS) + "env flags - print variables that have non-default flags\n" +#endif +#if defined(CONFIG_CMD_GREPENV) +#ifdef CONFIG_REGEX + "env grep [-e] [-n | -v | -b] string [...] - search environment\n" +#else + "env grep [-n | -v | -b] string [...] - search environment\n" +#endif +#endif +#if defined(CONFIG_CMD_IMPORTENV) + "env import [-d] [-t [-r] | -b | -c] addr [size] [var ...] - import environment\n" +#endif +#if defined(CONFIG_CMD_NVEDIT_INFO) + "env info - display environment information\n" + "env info [-d] [-p] [-q] - evaluate environment information\n" + " \"-d\": default environment is used\n" + " \"-p\": environment can be persisted\n" + " \"-q\": quiet output\n" +#endif + "env print [-a | name ...] - print environment\n" +#if defined(CONFIG_CMD_NVEDIT_EFI) + "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n" +#endif +#if defined(CONFIG_CMD_RUN) + "env run var [...] - run commands in an environment variable\n" +#endif +#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE) + "env save - save environment\n" +#if defined(CONFIG_CMD_ERASEENV) + "env erase - erase environment\n" +#endif +#endif +#if defined(CONFIG_CMD_NVEDIT_LOAD) + "env load - load environment\n" +#endif +#if defined(CONFIG_CMD_NVEDIT_SELECT) + "env select [target] - select environment target\n" +#endif +#if defined(CONFIG_CMD_NVEDIT_EFI) + "env set -e [-nv][-bs][-rt][-at][-a][-i addr:size][-v] name [arg ...]\n" + " - set UEFI variable; unset if '-i' or 'arg' not specified\n" +#endif + "env set [-f] name [arg ...]\n"; +#endif + +U_BOOT_CMD( + env, CONFIG_SYS_MAXARGS, 1, do_env, + "environment handling commands", env_help_text +); + +/* + * Old command line interface, kept for compatibility + */ + +#if defined(CONFIG_CMD_EDITENV) +U_BOOT_CMD_COMPLETE( + editenv, 2, 0, do_env_edit, + "edit environment variable", + "name\n" + " - edit environment variable 'name'", + var_complete +); +#endif + +U_BOOT_CMD_COMPLETE( + printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, + "print environment variables", + "[-a]\n - print [all] values of all environment variables\n" +#if defined(CONFIG_CMD_NVEDIT_EFI) + "printenv -e [-guid guid][-n] [name ...]\n" + " - print UEFI variable 'name' or all the variables\n" + " \"-guid\": GUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n" + " \"-n\": suppress dumping variable's value\n" +#endif + "printenv name ...\n" + " - print value of environment variable 'name'", + var_complete +); + +#ifdef CONFIG_CMD_GREPENV +U_BOOT_CMD_COMPLETE( + grepenv, CONFIG_SYS_MAXARGS, 0, do_env_grep, + "search environment variables", +#ifdef CONFIG_REGEX + "[-e] [-n | -v | -b] string ...\n" +#else + "[-n | -v | -b] string ...\n" +#endif + " - list environment name=value pairs matching 'string'\n" +#ifdef CONFIG_REGEX + " \"-e\": enable regular expressions;\n" +#endif + " \"-n\": search variable names; \"-v\": search values;\n" + " \"-b\": search both names and values (default)", + var_complete +); +#endif + +U_BOOT_CMD_COMPLETE( + setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, + "set environment variables", +#if defined(CONFIG_CMD_NVEDIT_EFI) + "-e [-guid guid][-nv][-bs][-rt][-at][-a][-v]\n" + " [-i addr:size name], or [name [value ...]]\n" + " - set UEFI variable 'name' to 'value' ...'\n" + " \"-guid\": GUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n" + " \"-nv\": set non-volatile attribute\n" + " \"-bs\": set boot-service attribute\n" + " \"-rt\": set runtime attribute\n" + " \"-at\": set time-based authentication attribute\n" + " \"-a\": append-write\n" + " \"-i addr,size\": use <addr,size> as variable's value\n" + " \"-v\": verbose message\n" + " - delete UEFI variable 'name' if 'value' not specified\n" +#endif + "setenv [-f] name value ...\n" + " - [forcibly] set environment variable 'name' to 'value ...'\n" + "setenv [-f] name\n" + " - [forcibly] delete environment variable 'name'", + var_complete +); + +#if defined(CONFIG_CMD_ASKENV) + +U_BOOT_CMD( + askenv, CONFIG_SYS_MAXARGS, 1, do_env_ask, + "get environment variables from stdin", + "name [message] [size]\n" + " - get environment variable 'name' from stdin (max 'size' chars)" +); +#endif + +#if defined(CONFIG_CMD_RUN) +U_BOOT_CMD_COMPLETE( + run, CONFIG_SYS_MAXARGS, 1, do_run, + "run commands in an environment variable", + "var [...]\n" + " - run the commands in the environment variable(s) 'var'", + var_complete +); +#endif +#endif /* CONFIG_SPL_BUILD */ diff --git a/roms/u-boot/cmd/nvedit_efi.c b/roms/u-boot/cmd/nvedit_efi.c new file mode 100644 index 000000000..094c0e809 --- /dev/null +++ b/roms/u-boot/cmd/nvedit_efi.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Integrate UEFI variables to u-boot env interface + * + * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited + */ + +#include <charset.h> +#include <common.h> +#include <command.h> +#include <efi_loader.h> +#include <efi_variable.h> +#include <env.h> +#include <exports.h> +#include <hexdump.h> +#include <malloc.h> +#include <mapmem.h> +#include <rtc.h> +#include <uuid.h> +#include <linux/kernel.h> + +/* + * From efi_variable.c, + * + * Mapping between UEFI variables and u-boot variables: + * + * efi_$guid_$varname = {attributes}(type)value + */ + +static const struct { + u32 mask; + char *text; +} efi_var_attrs[] = { + {EFI_VARIABLE_NON_VOLATILE, "NV"}, + {EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"}, + {EFI_VARIABLE_RUNTIME_ACCESS, "RT"}, + {EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"}, + {EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"}, + {EFI_VARIABLE_READ_ONLY, "RO"}, +}; + +static const struct { + efi_guid_t guid; + char *text; +} efi_guid_text[] = { + /* signature database */ + {EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"}, + {EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"}, + /* certificate type */ + {EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"}, + {EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"}, + {EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"}, +}; + +static const char unknown_guid[] = ""; + +/** + * efi_guid_to_str() - convert guid to readable name + * + * @guid: GUID + * Return: string for GUID + * + * convert guid to readable name + */ +static const char *efi_guid_to_str(const efi_guid_t *guid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(efi_guid_text); i++) + if (!guidcmp(guid, &efi_guid_text[i].guid)) + return efi_guid_text[i].text; + + return unknown_guid; +} + +/** + * efi_dump_single_var() - show information about a UEFI variable + * + * @name: Name of the variable + * @guid: Vendor GUID + * @verbose: if true, dump data + * + * Show information encoded in one UEFI variable + */ +static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose) +{ + u32 attributes; + u8 *data; + u64 time; + struct rtc_time tm; + efi_uintn_t size; + int count, i; + efi_status_t ret; + + data = NULL; + size = 0; + ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time); + if (ret == EFI_BUFFER_TOO_SMALL) { + data = malloc(size); + if (!data) + goto out; + + ret = efi_get_variable_int(name, guid, &attributes, &size, + data, &time); + } + if (ret == EFI_NOT_FOUND) { + printf("Error: \"%ls\" not defined\n", name); + goto out; + } + if (ret != EFI_SUCCESS) + goto out; + + rtc_to_tm(time, &tm); + printf("%ls:\n %pUl %s\n", name, guid, efi_guid_to_str(guid)); + if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) + printf(" %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year, + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + printf(" "); + for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++) + if (attributes & efi_var_attrs[i].mask) { + if (count) + putc('|'); + count++; + puts(efi_var_attrs[i].text); + } + printf(", DataSize = 0x%zx\n", size); + if (verbose) + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + data, size, true); + +out: + free(data); +} + +static bool match_name(int argc, char *const argv[], u16 *var_name16) +{ + char *buf, *p; + size_t buflen; + int i; + bool result = false; + + buflen = utf16_utf8_strlen(var_name16) + 1; + buf = calloc(1, buflen); + if (!buf) + return result; + + p = buf; + utf16_utf8_strcpy(&p, var_name16); + + for (i = 0; i < argc; argc--, argv++) { + if (!strcmp(buf, argv[i])) { + result = true; + goto out; + } + } + +out: + free(buf); + + return result; +} + +/** + * efi_dump_var_all() - show information about all the UEFI variables + * + * @argc: Number of arguments (variables) + * @argv: Argument (variable name) array + * @verbose: if true, dump data + * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE + * + * Show information encoded in all the UEFI variables + */ +static int efi_dump_var_all(int argc, char *const argv[], + const efi_guid_t *guid_p, bool verbose) +{ + u16 *var_name16, *p; + efi_uintn_t buf_size, size; + efi_guid_t guid; + efi_status_t ret; + bool match = false; + + buf_size = 128; + var_name16 = malloc(buf_size); + if (!var_name16) + return CMD_RET_FAILURE; + + var_name16[0] = 0; + for (;;) { + size = buf_size; + ret = EFI_CALL(efi_get_next_variable_name(&size, var_name16, + &guid)); + if (ret == EFI_NOT_FOUND) + break; + if (ret == EFI_BUFFER_TOO_SMALL) { + buf_size = size; + p = realloc(var_name16, buf_size); + if (!p) { + free(var_name16); + return CMD_RET_FAILURE; + } + var_name16 = p; + ret = EFI_CALL(efi_get_next_variable_name(&size, + var_name16, + &guid)); + } + if (ret != EFI_SUCCESS) { + free(var_name16); + return CMD_RET_FAILURE; + } + + if (guid_p && guidcmp(guid_p, &guid)) + continue; + if (!argc || match_name(argc, argv, var_name16)) { + match = true; + efi_dump_single_var(var_name16, &guid, verbose); + } + } + free(var_name16); + + if (!match && argc == 1) + printf("Error: \"%s\" not defined\n", argv[0]); + + return CMD_RET_SUCCESS; +} + +/** + * do_env_print_efi() - show information about UEFI variables + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE + * + * This function is for "env print -e" or "printenv -e" command: + * => env print -e [-n] [-guid <guid> | -all] [var [...]] + * If one or more variable names are specified, show information + * named UEFI variables, otherwise show all the UEFI variables. + */ +int do_env_print_efi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const efi_guid_t *guid_p = NULL; + bool verbose = true; + efi_status_t ret; + + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-guid")) { + efi_guid_t guid; + + if (argc == 1) + return CMD_RET_USAGE; + argc--; + argv++; + if (uuid_str_to_bin(argv[0], guid.b, + UUID_STR_FORMAT_GUID)) + return CMD_RET_USAGE; + guid_p = (const efi_guid_t *)guid.b; + } else if (!strcmp(argv[0], "-n")) { + verbose = false; + } else { + return CMD_RET_USAGE; + } + } + + /* enumerate and show all UEFI variables */ + return efi_dump_var_all(argc, argv, guid_p, verbose); +} + +/** + * append_value() - encode UEFI variable's value + * @bufp: Buffer of encoded UEFI variable's value + * @sizep: Size of buffer + * @data: data to be encoded into the value + * Return: 0 on success, -1 otherwise + * + * Interpret a given data string and append it to buffer. + * Buffer will be realloc'ed if necessary. + * + * Currently supported formats are: + * =0x0123...: Hexadecimal number + * =H0123...: Hexadecimal-byte array + * ="...", =S"..." or <string>: + * String + */ +static int append_value(char **bufp, size_t *sizep, char *data) +{ + char *tmp_buf = NULL, *new_buf = NULL, *value; + unsigned long len = 0; + + if (!strncmp(data, "=0x", 2)) { /* hexadecimal number */ + union { + u8 u8; + u16 u16; + u32 u32; + u64 u64; + } tmp_data; + unsigned long hex_value; + void *hex_ptr; + + data += 3; + len = strlen(data); + if ((len & 0x1)) /* not multiple of two */ + return -1; + + len /= 2; + if (len > 8) + return -1; + else if (len > 4) + len = 8; + else if (len > 2) + len = 4; + + /* convert hex hexadecimal number */ + if (strict_strtoul(data, 16, &hex_value) < 0) + return -1; + + tmp_buf = malloc(len); + if (!tmp_buf) + return -1; + + if (len == 1) { + tmp_data.u8 = hex_value; + hex_ptr = &tmp_data.u8; + } else if (len == 2) { + tmp_data.u16 = hex_value; + hex_ptr = &tmp_data.u16; + } else if (len == 4) { + tmp_data.u32 = hex_value; + hex_ptr = &tmp_data.u32; + } else { + tmp_data.u64 = hex_value; + hex_ptr = &tmp_data.u64; + } + memcpy(tmp_buf, hex_ptr, len); + value = tmp_buf; + + } else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */ + data += 2; + len = strlen(data); + if (len & 0x1) /* not multiple of two */ + return -1; + + len /= 2; + tmp_buf = malloc(len); + if (!tmp_buf) + return -1; + + if (hex2bin((u8 *)tmp_buf, data, len) < 0) { + printf("Error: illegal hexadecimal string\n"); + free(tmp_buf); + return -1; + } + + value = tmp_buf; + } else { /* string */ + if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) { + if (data[1] == '"') + data += 2; + else + data += 3; + value = data; + len = strlen(data) - 1; + if (data[len] != '"') + return -1; + } else { + value = data; + len = strlen(data); + } + } + + new_buf = realloc(*bufp, *sizep + len); + if (!new_buf) + goto out; + + memcpy(new_buf + *sizep, value, len); + *bufp = new_buf; + *sizep += len; + +out: + free(tmp_buf); + + return 0; +} + +/** + * do_env_set_efi() - set UEFI variable + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE + * + * This function is for "env set -e" or "setenv -e" command: + * => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v] + * [-i address,size] var, or + * var [value ...] + * Encode values specified and set given UEFI variable. + * If no value is specified, delete the variable. + */ +int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *var_name, *value, *ep; + ulong addr; + efi_uintn_t size; + efi_guid_t guid; + u32 attributes; + bool default_guid, verbose, value_on_memory; + u16 *var_name16 = NULL, *p; + size_t len; + efi_status_t ret; + + if (argc == 1) + return CMD_RET_USAGE; + + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + /* + * attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | + * EFI_VARIABLE_RUNTIME_ACCESS; + */ + value = NULL; + size = 0; + attributes = 0; + guid = efi_global_variable_guid; + default_guid = true; + verbose = false; + value_on_memory = false; + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-guid")) { + if (argc == 1) + return CMD_RET_USAGE; + + argc--; + argv++; + if (uuid_str_to_bin(argv[0], guid.b, + UUID_STR_FORMAT_GUID)) { + return CMD_RET_USAGE; + } + default_guid = false; + } else if (!strcmp(argv[0], "-bs")) { + attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS; + } else if (!strcmp(argv[0], "-rt")) { + attributes |= EFI_VARIABLE_RUNTIME_ACCESS; + } else if (!strcmp(argv[0], "-nv")) { + attributes |= EFI_VARIABLE_NON_VOLATILE; + } else if (!strcmp(argv[0], "-at")) { + attributes |= + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + } else if (!strcmp(argv[0], "-a")) { + attributes |= EFI_VARIABLE_APPEND_WRITE; + } else if (!strcmp(argv[0], "-i")) { + /* data comes from memory */ + if (argc == 1) + return CMD_RET_USAGE; + + argc--; + argv++; + addr = simple_strtoul(argv[0], &ep, 16); + if (*ep != ':') + return CMD_RET_USAGE; + + /* 0 should be allowed for delete */ + size = simple_strtoul(++ep, NULL, 16); + + value_on_memory = true; + } else if (!strcmp(argv[0], "-v")) { + verbose = true; + } else { + return CMD_RET_USAGE; + } + } + if (!argc) + return CMD_RET_USAGE; + + var_name = argv[0]; + if (default_guid) { + if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") || + !strcmp(var_name, "dbt")) + guid = efi_guid_image_security_database; + else + guid = efi_global_variable_guid; + } + + if (verbose) { + printf("GUID: %pUl %s\n", &guid, + efi_guid_to_str((const efi_guid_t *)&guid)); + printf("Attributes: 0x%x\n", attributes); + } + + /* for value */ + if (value_on_memory) + value = map_sysmem(addr, 0); + else if (argc > 1) + for (argc--, argv++; argc > 0; argc--, argv++) + if (append_value(&value, &size, argv[0]) < 0) { + printf("## Failed to process an argument, %s\n", + argv[0]); + ret = CMD_RET_FAILURE; + goto out; + } + + if (size && verbose) { + printf("Value:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, + 16, 1, value, size, true); + } + + len = utf8_utf16_strnlen(var_name, strlen(var_name)); + var_name16 = malloc((len + 1) * 2); + if (!var_name16) { + printf("## Out of memory\n"); + ret = CMD_RET_FAILURE; + goto out; + } + p = var_name16; + utf8_utf16_strncpy(&p, var_name, len + 1); + + ret = efi_set_variable_int(var_name16, &guid, attributes, size, value, + true); + unmap_sysmem(value); + if (ret == EFI_SUCCESS) { + ret = CMD_RET_SUCCESS; + } else { + const char *msg; + + switch (ret) { + case EFI_NOT_FOUND: + msg = " (not found)"; + break; + case EFI_WRITE_PROTECTED: + msg = " (read only)"; + break; + case EFI_INVALID_PARAMETER: + msg = " (invalid parameter)"; + break; + case EFI_SECURITY_VIOLATION: + msg = " (validation failed)"; + break; + case EFI_OUT_OF_RESOURCES: + msg = " (out of memory)"; + break; + default: + msg = ""; + break; + } + printf("## Failed to set EFI variable%s\n", msg); + ret = CMD_RET_FAILURE; + } +out: + if (value_on_memory) + unmap_sysmem(value); + else + free(value); + free(var_name16); + + return ret; +} diff --git a/roms/u-boot/cmd/nvme.c b/roms/u-boot/cmd/nvme.c new file mode 100644 index 000000000..e715c570a --- /dev/null +++ b/roms/u-boot/cmd/nvme.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP Semiconductors + * Copyright (C) 2017 Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <dm.h> +#include <nvme.h> + +static int nvme_curr_dev; + +static int do_nvme(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc == 2) { + if (strncmp(argv[1], "scan", 4) == 0) { + ret = nvme_scan_namespace(); + if (ret) + return CMD_RET_FAILURE; + + return ret; + } + if (strncmp(argv[1], "deta", 4) == 0) { + struct udevice *udev; + + ret = blk_get_device(IF_TYPE_NVME, nvme_curr_dev, + &udev); + if (ret < 0) + return CMD_RET_FAILURE; + + nvme_print_info(udev); + + return ret; + } + } + + return blk_common_cmd(argc, argv, IF_TYPE_NVME, &nvme_curr_dev); +} + +U_BOOT_CMD( + nvme, 8, 1, do_nvme, + "NVM Express sub-system", + "scan - scan NVMe devices\n" + "nvme detail - show details of current NVMe device\n" + "nvme info - show all available NVMe devices\n" + "nvme device [dev] - show or set current NVMe device\n" + "nvme part [dev] - print partition table of one or all NVMe devices\n" + "nvme read addr blk# cnt - read `cnt' blocks starting at block\n" + " `blk#' to memory address `addr'\n" + "nvme write addr blk# cnt - write `cnt' blocks starting at block\n" + " `blk#' from memory address `addr'" +); diff --git a/roms/u-boot/cmd/onenand.c b/roms/u-boot/cmd/onenand.c new file mode 100644 index 000000000..216458bb4 --- /dev/null +++ b/roms/u-boot/cmd/onenand.c @@ -0,0 +1,610 @@ +/* + * U-Boot command for OneNAND support + * + * Copyright (C) 2005-2008 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> + +#include <linux/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> + +#include <asm/io.h> + +static struct mtd_info *mtd; + +static loff_t next_ofs; +static loff_t skip_ofs; + +static int arg_off_size_onenand(int argc, char *const argv[], ulong *off, + size_t *size) +{ + if (argc >= 1) { + if (!(str2long(argv[0], off))) { + printf("'%s' is not a number\n", argv[0]); + return -1; + } + } else { + *off = 0; + } + + if (argc >= 2) { + if (!(str2long(argv[1], (ulong *)size))) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + } else { + *size = mtd->size - *off; + } + + if ((*off + *size) > mtd->size) { + printf("total chip size (0x%llx) exceeded!\n", mtd->size); + return -1; + } + + if (*size == mtd->size) + puts("whole chip\n"); + else + printf("offset 0x%lx, size 0x%x\n", *off, *size); + + return 0; +} + +static int onenand_block_read(loff_t from, size_t len, + size_t *retlen, u_char *buf, int oob) +{ + struct onenand_chip *this = mtd->priv; + int blocks = (int) len >> this->erase_shift; + int blocksize = (1 << this->erase_shift); + loff_t ofs = from; + struct mtd_oob_ops ops = { + .retlen = 0, + }; + int ret; + + if (oob) + ops.ooblen = blocksize; + else + ops.len = blocksize; + + while (blocks) { + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printk("Bad blocks %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + ofs += blocksize; + continue; + } + + if (oob) + ops.oobbuf = buf; + else + ops.datbuf = buf; + + ops.retlen = 0; + ret = mtd_read_oob(mtd, ofs, &ops); + if (ret) { + printk("Read failed 0x%x, %d\n", (u32)ofs, ret); + ofs += blocksize; + continue; + } + ofs += blocksize; + buf += blocksize; + blocks--; + *retlen += ops.retlen; + } + + return 0; +} + +static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf, + size_t *retlen) +{ + struct mtd_oob_ops ops = { + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_AUTO_OOB, + }; + int page, ret = 0; + for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) { + ops.datbuf = (u_char *)buf; + buf += mtd->writesize; + ops.oobbuf = (u_char *)buf; + buf += mtd->oobsize; + ret = mtd_write_oob(mtd, to, &ops); + if (ret) + break; + to += mtd->writesize; + } + + *retlen = (ret) ? 0 : mtd->erasesize; + return ret; +} + +static int onenand_block_write(loff_t to, size_t len, + size_t *retlen, const u_char * buf, int withoob) +{ + struct onenand_chip *this = mtd->priv; + int blocks = len >> this->erase_shift; + int blocksize = (1 << this->erase_shift); + loff_t ofs; + size_t _retlen = 0; + int ret; + + if ((to & (mtd->writesize - 1)) != 0) { + printf("Attempt to write non block-aligned data\n"); + *retlen = 0; + return 1; + } + + if (to == next_ofs) { + next_ofs = to + len; + to += skip_ofs; + } else { + next_ofs = to + len; + skip_ofs = 0; + } + ofs = to; + + while (blocks) { + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printk("Bad blocks %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + skip_ofs += blocksize; + goto next; + } + + if (!withoob) + ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf); + else + ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen); + if (ret) { + printk("Write failed 0x%x, %d", (u32)ofs, ret); + skip_ofs += blocksize; + goto next; + } + + buf += blocksize; + blocks--; + *retlen += _retlen; +next: + ofs += blocksize; + } + + return 0; +} + +static int onenand_block_erase(u32 start, u32 size, int force) +{ + struct onenand_chip *this = mtd->priv; + struct erase_info instr = { + .callback = NULL, + }; + loff_t ofs; + int ret; + int blocksize = 1 << this->erase_shift; + + for (ofs = start; ofs < (start + size); ofs += blocksize) { + ret = mtd_block_isbad(mtd, ofs); + if (ret && !force) { + printf("Skip erase bad block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + continue; + } + + instr.addr = ofs; + instr.len = blocksize; + instr.priv = force; + instr.mtd = mtd; + ret = mtd_erase(mtd, &instr); + if (ret) { + printf("erase failed block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + continue; + } + } + + return 0; +} + +static int onenand_block_test(u32 start, u32 size) +{ + struct onenand_chip *this = mtd->priv; + struct erase_info instr = { + .callback = NULL, + .priv = 0, + }; + + int blocks; + loff_t ofs; + int blocksize = 1 << this->erase_shift; + int start_block, end_block; + size_t retlen; + u_char *buf; + u_char *verify_buf; + int ret; + + buf = malloc(blocksize); + if (!buf) { + printf("Not enough malloc space available!\n"); + return -1; + } + + verify_buf = malloc(blocksize); + if (!verify_buf) { + printf("Not enough malloc space available!\n"); + return -1; + } + + start_block = start >> this->erase_shift; + end_block = (start + size) >> this->erase_shift; + + /* Protect boot-loader from badblock testing */ + if (start_block < 2) + start_block = 2; + + if (end_block > (mtd->size >> this->erase_shift)) + end_block = mtd->size >> this->erase_shift; + + blocks = start_block; + ofs = start; + while (blocks < end_block) { + printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs); + + ret = mtd_block_isbad(mtd, ofs); + if (ret) { + printf("Skip erase bad block %d at 0x%x\n", + (u32)(ofs >> this->erase_shift), (u32)ofs); + goto next; + } + + instr.addr = ofs; + instr.len = blocksize; + ret = mtd_erase(mtd, &instr); + if (ret) { + printk("Erase failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + ret = mtd_write(mtd, ofs, blocksize, &retlen, buf); + if (ret) { + printk("Write failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf); + if (ret) { + printk("Read failed 0x%x, %d\n", (u32)ofs, ret); + goto next; + } + + if (memcmp(buf, verify_buf, blocksize)) + printk("\nRead/Write test failed at 0x%x\n", (u32)ofs); + +next: + ofs += blocksize; + blocks++; + } + printf("...Done\n"); + + free(buf); + free(verify_buf); + + return 0; +} + +static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob) +{ + int i; + u_char *datbuf, *oobbuf, *p; + struct mtd_oob_ops ops; + loff_t addr; + + datbuf = malloc(mtd->writesize + mtd->oobsize); + oobbuf = malloc(mtd->oobsize); + if (!datbuf || !oobbuf) { + puts("No memory for page buffer\n"); + return 1; + } + off &= ~(mtd->writesize - 1); + addr = (loff_t) off; + memset(&ops, 0, sizeof(ops)); + ops.datbuf = datbuf; + ops.oobbuf = oobbuf; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.retlen = 0; + i = mtd_read_oob(mtd, addr, &ops); + if (i < 0) { + printf("Error (%d) reading page %08lx\n", i, off); + free(datbuf); + free(oobbuf); + return 1; + } + printf("Page %08lx dump:\n", off); + i = mtd->writesize >> 4; + p = datbuf; + + while (i--) { + if (!only_oob) + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], + p[15]); + p += 16; + } + puts("OOB:\n"); + i = mtd->oobsize >> 3; + p = oobbuf; + + while (i--) { + printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + free(datbuf); + free(oobbuf); + + return 0; +} + +static int do_onenand_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + printf("%s\n", mtd->name); + return 0; +} + +static int do_onenand_bad(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong ofs; + + mtd = &onenand_mtd; + /* Currently only one OneNAND device is supported */ + printf("\nDevice %d bad blocks:\n", 0); + for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) { + if (mtd_block_isbad(mtd, ofs)) + printf(" %08x\n", (u32)ofs); + } + + return 0; +} + +static int do_onenand_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *s; + int oob = 0; + ulong addr, ofs; + size_t len; + int ret = 0; + size_t retlen = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + s = strchr(argv[0], '.'); + if ((s != NULL) && (!strcmp(s, ".oob"))) + oob = 1; + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nOneNAND read: "); + if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0) + return 1; + + ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob); + + printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, ofs; + size_t len; + int ret = 0, withoob = 0; + size_t retlen = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + if (strncmp(argv[0] + 6, "yaffs", 5) == 0) + withoob = 1; + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nOneNAND write: "); + if (arg_off_size_onenand(argc - 2, argv + 2, &ofs, &len) != 0) + return 1; + + ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob); + + printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_erase(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong ofs; + int ret = 0; + size_t len; + int force; + + /* + * Syntax is: + * 0 1 2 3 4 + * onenand erase [force] [off size] + */ + argc--; + argv++; + if (argc) + { + if (!strcmp("force", argv[0])) + { + force = 1; + argc--; + argv++; + } + } + printf("\nOneNAND erase: "); + + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size_onenand(argc, argv, &ofs, &len) != 0) + return 1; + + ret = onenand_block_erase(ofs, len, force); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_test(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong ofs; + int ret = 0; + size_t len; + + /* + * Syntax is: + * 0 1 2 3 4 + * onenand test [force] [off size] + */ + + printf("\nOneNAND test: "); + + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size_onenand(argc - 1, argv + 1, &ofs, &len) != 0) + return 1; + + ret = onenand_block_test(ofs, len); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_onenand_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong ofs; + int ret = 0; + char *s; + + if (argc < 2) + return CMD_RET_USAGE; + + s = strchr(argv[0], '.'); + ofs = (int)simple_strtoul(argv[1], NULL, 16); + + if (s != NULL && strcmp(s, ".oob") == 0) + ret = onenand_dump(mtd, ofs, 1); + else + ret = onenand_dump(mtd, ofs, 0); + + return ret == 0 ? 1 : 0; +} + +static int do_onenand_markbad(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret = 0; + ulong addr; + + argc -= 2; + argv += 2; + + if (argc <= 0) + return CMD_RET_USAGE; + + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (mtd_block_markbad(mtd, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; + } else { + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); + } + --argc; + ++argv; + } + return ret; +} + +static struct cmd_tbl cmd_onenand_sub[] = { + U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""), + U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""), + U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""), + U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""), + U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""), + U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""), + U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""), +}; + +#ifdef CONFIG_NEEDS_MANUAL_RELOC +void onenand_reloc(void) { + fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub)); +} +#endif + +static int do_onenand(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + mtd = &onenand_mtd; + + /* Strip off leading 'onenand' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + onenand, CONFIG_SYS_MAXARGS, 1, do_onenand, + "OneNAND sub-system", + "info - show available OneNAND devices\n" + "onenand bad - show bad blocks\n" + "onenand read[.oob] addr off size\n" + "onenand write[.yaffs] addr off size\n" + " read/write 'size' bytes starting at offset 'off'\n" + " to/from memory address 'addr', skipping bad blocks.\n" + "onenand erase [force] [off size] - erase 'size' bytes from\n" + "onenand test [off size] - test 'size' bytes from\n" + " offset 'off' (entire device if not specified)\n" + "onenand dump[.oob] off - dump page\n" + "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)" +); diff --git a/roms/u-boot/cmd/optee_rpmb.c b/roms/u-boot/cmd/optee_rpmb.c new file mode 100644 index 000000000..0d6b1cb1d --- /dev/null +++ b/roms/u-boot/cmd/optee_rpmb.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + */ + +#include <command.h> +#include <common.h> +#include <env.h> +#include <errno.h> +#include <image.h> +#include <malloc.h> +#include <mmc.h> +#include <tee.h> +#include <tee/optee_ta_avb.h> + +static struct udevice *tee; +static u32 session; + +static int avb_ta_open_session(void) +{ + 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) + session = arg.session; + + return 0; +} + +static int invoke_func(u32 func, ulong num_param, struct tee_param *param) +{ + struct tee_invoke_arg arg; + + if (!tee) + if (avb_ta_open_session()) + return -ENODEV; + + memset(&arg, 0, sizeof(arg)); + arg.func = func; + arg.session = session; + + if (tee_invoke_func(tee, &arg, num_param, param)) + return -EFAULT; + switch (arg.ret) { + case TEE_SUCCESS: + return 0; + case TEE_ERROR_OUT_OF_MEMORY: + case TEE_ERROR_STORAGE_NO_SPACE: + return -ENOSPC; + case TEE_ERROR_ITEM_NOT_FOUND: + return -EIO; + case TEE_ERROR_TARGET_DEAD: + /* + * The TA has paniced, close the session to reload the TA + * for the next request. + */ + tee_close_session(tee, session); + tee = NULL; + return -EIO; + default: + return -EIO; + } +} + +static int read_persistent_value(const char *name, + size_t buffer_size, + u8 *out_buffer, + size_t *out_num_bytes_read) +{ + int rc = 0; + struct tee_shm *shm_name; + struct tee_shm *shm_buf; + struct tee_param param[2]; + size_t name_size = strlen(name) + 1; + + if (!tee) + if (avb_ta_open_session()) + return -ENODEV; + + rc = tee_shm_alloc(tee, name_size, + TEE_SHM_ALLOC, &shm_name); + if (rc) + return -ENOMEM; + + rc = tee_shm_alloc(tee, buffer_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) { + rc = -ENOMEM; + 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(TA_AVB_CMD_READ_PERSIST_VALUE, + 2, param); + if (rc) + goto out; + + if (param[1].u.memref.size > buffer_size) { + rc = -EINVAL; + 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 int write_persistent_value(const char *name, + size_t value_size, + const u8 *value) +{ + int rc = 0; + struct tee_shm *shm_name; + struct tee_shm *shm_buf; + struct tee_param param[2]; + size_t name_size = strlen(name) + 1; + + if (!tee) { + if (avb_ta_open_session()) + return -ENODEV; + } + if (!value_size) + return -EINVAL; + + rc = tee_shm_alloc(tee, name_size, + TEE_SHM_ALLOC, &shm_name); + if (rc) + return -ENOMEM; + + rc = tee_shm_alloc(tee, value_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) { + rc = -ENOMEM; + 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(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; +} + +int do_optee_rpmb_read(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + const char *name; + size_t bytes; + size_t bytes_read; + void *buffer; + char *endp; + + if (argc != 3) + return CMD_RET_USAGE; + + name = argv[1]; + bytes = simple_strtoul(argv[2], &endp, 10); + if (*endp && *endp != '\n') + return CMD_RET_USAGE; + + buffer = malloc(bytes); + if (!buffer) + return CMD_RET_FAILURE; + + if (read_persistent_value(name, bytes, buffer, &bytes_read) == 0) { + printf("Read %zu bytes, value = %s\n", bytes_read, + (char *)buffer); + free(buffer); + return CMD_RET_SUCCESS; + } + + printf("Failed to read persistent value\n"); + + free(buffer); + + return CMD_RET_FAILURE; +} + +int do_optee_rpmb_write(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + const char *name; + const char *value; + + if (argc != 3) + return CMD_RET_USAGE; + + name = argv[1]; + value = argv[2]; + + if (write_persistent_value(name, strlen(value) + 1, + (const uint8_t *)value) == 0) { + printf("Wrote %zu bytes\n", strlen(value) + 1); + return CMD_RET_SUCCESS; + } + + printf("Failed to write persistent value\n"); + + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_optee_rpmb[] = { + U_BOOT_CMD_MKENT(read_pvalue, 3, 0, do_optee_rpmb_read, "", ""), + U_BOOT_CMD_MKENT(write_pvalue, 3, 0, do_optee_rpmb_write, "", ""), +}; + +static int do_optee_rpmb(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct cmd_tbl *cp; + + cp = find_cmd_tbl(argv[1], cmd_optee_rpmb, ARRAY_SIZE(cmd_optee_rpmb)); + + argc--; + argv++; + + if (!cp || argc > cp->maxargs) + return CMD_RET_USAGE; + + if (flag == CMD_FLAG_REPEAT) + return CMD_RET_FAILURE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD ( + optee_rpmb, 29, 0, do_optee_rpmb, + "Provides commands for testing secure storage on RPMB on OPTEE", + "read_pvalue <name> <bytes> - read a persistent value <name>\n" + "optee_rpmb write_pvalue <name> <value> - write a persistent value <name>\n" + ); diff --git a/roms/u-boot/cmd/osd.c b/roms/u-boot/cmd/osd.c new file mode 100644 index 000000000..703d640b0 --- /dev/null +++ b/roms/u-boot/cmd/osd.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * based on the gdsys osd driver, which is + * + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <hexdump.h> +#include <video_osd.h> +#include <malloc.h> + +/* Container for selected OSD device */ +static struct udevice *osd_cur; + +/** + * cmd_osd_set_osd_num() - Set the OSD selected for operation + * + * Set the OSD device, which will be used by all subsequent OSD commands. + * + * Devices are identified by their uclass sequence number (as listed by 'osd + * show'). + * + * @osdnum: The OSD device to be selected, identified by its sequence number. + * Return: 0 if OK, -ve on error + */ +static int cmd_osd_set_osd_num(unsigned int osdnum) +{ + struct udevice *osd; + int res; + + res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, osdnum, &osd); + if (res) { + printf("%s: No OSD %u (err = %d)\n", __func__, osdnum, res); + return res; + } + osd_cur = osd; + + return 0; +} + +/** + * osd_get_osd_cur() - Get the selected OSD device + * + * Get the OSD device that is used by all OSD commands. + * + * @osdp: Pointer to structure that will receive the currently selected OSD + * device. + * Return: 0 if OK, -ve on error + */ +static int osd_get_osd_cur(struct udevice **osdp) +{ + if (!osd_cur) { + puts("No osd selected\n"); + return -ENODEV; + } + *osdp = osd_cur; + + return 0; +} + +/** + * show_osd() - Display information about a OSD device + * + * Display a device's ID (sequence number), and whether it is active (i.e. + * probed) or not. + * + * @osd: OSD device to print information for + */ +static void show_osd(struct udevice *osd) +{ + printf("OSD %d:\t%s", dev_seq(osd), osd->name); + if (device_active(osd)) + printf(" (active)"); + printf("\n"); +} + +static int do_osd_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint x, y; + uint count; + char *hexstr; + u8 *buffer; + size_t buflen; + int res; + + if (argc < 4 || (strlen(argv[3]) % 2)) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + hexstr = argv[3]; + count = (argc > 4) ? simple_strtoul(argv[4], NULL, 16) : 1; + + buflen = strlen(hexstr) / 2; + + buffer = malloc(buflen); + if (!buffer) { + puts("Memory allocation failure\n"); + return CMD_RET_FAILURE; + } + + res = hex2bin(buffer, hexstr, buflen); + if (res) { + free(buffer); + puts("Hexadecimal input contained invalid characters\n"); + return CMD_RET_FAILURE; + } + + res = video_osd_set_mem(osd_cur, x, y, buffer, buflen, count); + if (res) { + free(buffer); + printf("%s: Could not write to video mem\n", + osd_cur->name); + return CMD_RET_FAILURE; + } + + free(buffer); + + return CMD_RET_SUCCESS; +} + +static int do_osd_print(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint x, y; + u8 color; + char *text; + int res; + + if (argc < 5) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + color = simple_strtoul(argv[3], NULL, 16); + text = argv[4]; + + res = video_osd_print(osd_cur, x, y, color, text); + if (res) { + printf("Could not print string to osd %s\n", osd_cur->name); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_osd_size(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + uint x, y; + int res; + + if (argc < 3) + return CMD_RET_USAGE; + + if (!osd_cur) { + puts("No osd selected\n"); + return CMD_RET_FAILURE; + } + + x = simple_strtoul(argv[1], NULL, 16); + y = simple_strtoul(argv[2], NULL, 16); + + res = video_osd_set_size(osd_cur, x, y); + if (res) { + printf("Could not set size on osd %s\n", osd_cur->name); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_show_osd(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *osd; + + if (argc == 1) { + /* show all OSDs */ + struct uclass *uc; + int res; + + res = uclass_get(UCLASS_VIDEO_OSD, &uc); + if (res) { + printf("Error while getting OSD uclass (err=%d)\n", + res); + return CMD_RET_FAILURE; + } + + uclass_foreach_dev(osd, uc) + show_osd(osd); + } else { + int i, res; + + /* show specific OSD */ + i = simple_strtoul(argv[1], NULL, 10); + + res = uclass_get_device_by_seq(UCLASS_VIDEO_OSD, i, &osd); + if (res) { + printf("Invalid osd %d: err=%d\n", i, res); + return CMD_RET_FAILURE; + } + show_osd(osd); + } + + return CMD_RET_SUCCESS; +} + +static int do_osd_num(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int osd_no; + int res = 0; + + if (argc == 1) { + /* querying current setting */ + struct udevice *osd; + + if (!osd_get_osd_cur(&osd)) + osd_no = dev_seq(osd); + else + osd_no = -1; + printf("Current osd is %d\n", osd_no); + } else { + osd_no = simple_strtoul(argv[1], NULL, 10); + printf("Setting osd to %d\n", osd_no); + + res = cmd_osd_set_osd_num(osd_no); + if (res) + printf("Failure changing osd number (err = %d)\n", res); + } + + return res ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_osd_sub[] = { + U_BOOT_CMD_MKENT(show, 1, 1, do_show_osd, "", ""), + U_BOOT_CMD_MKENT(dev, 1, 1, do_osd_num, "", ""), + U_BOOT_CMD_MKENT(write, 4, 1, do_osd_write, "", ""), + U_BOOT_CMD_MKENT(print, 4, 1, do_osd_print, "", ""), + U_BOOT_CMD_MKENT(size, 2, 1, do_osd_size, "", ""), +}; + +static int do_osd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading 'osd' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_osd_sub[0], ARRAY_SIZE(cmd_osd_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +static char osd_help_text[] = + "show - show OSD info\n" + "osd dev [dev] - show or set current OSD\n" + "write [pos_x] [pos_y] [buffer] [count] - write 8-bit hex encoded buffer to osd memory at a given position\n" + "print [pos_x] [pos_y] [color] [text] - write ASCII buffer (given by text data and driver-specific color information) to osd memory\n" + "size [size_x] [size_y] - set OSD XY size in characters\n"; + +U_BOOT_CMD( + osd, 6, 1, do_osd, + "OSD sub-system", + osd_help_text +); diff --git a/roms/u-boot/cmd/panic.c b/roms/u-boot/cmd/panic.c new file mode 100644 index 000000000..f13b3f094 --- /dev/null +++ b/roms/u-boot/cmd/panic.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020 Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#include <command.h> + +static int do_panic(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + char *text = (argc < 2) ? "" : argv[1]; + + panic(text); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + panic, 2, 1, do_panic, + "Panic with optional message", + "[message]" +); diff --git a/roms/u-boot/cmd/part.c b/roms/u-boot/cmd/part.c new file mode 100644 index 000000000..3395c17b8 --- /dev/null +++ b/roms/u-boot/cmd/part.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * made from cmd_ext2, which was: + * + * (C) Copyright 2004 + * esd gmbh <www.esd-electronics.com> + * Reinhard Arlt <reinhard.arlt@esd-electronics.com> + * + * made from cmd_reiserfs by + * + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <env.h> +#include <part.h> +#include <vsprintf.h> + +enum cmd_part_info { + CMD_PART_INFO_START = 0, + CMD_PART_INFO_SIZE, + CMD_PART_INFO_NUMBER +}; + +static int do_part_uuid(int argc, char *const argv[]) +{ + int part; + struct blk_desc *dev_desc; + struct disk_partition info; + + if (argc < 2) + return CMD_RET_USAGE; + if (argc > 3) + return CMD_RET_USAGE; + + part = blk_get_device_part_str(argv[0], argv[1], &dev_desc, &info, 0); + if (part < 0) + return 1; + + if (argc > 2) + env_set(argv[2], info.uuid); + else + printf("%s\n", info.uuid); + + return 0; +} + +static int do_part_list(int argc, char *const argv[]) +{ + int ret; + struct blk_desc *desc; + char *var = NULL; + bool bootable = false; + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + if (argc > 2) { + for (i = 2; i < argc ; i++) { + if (argv[i][0] == '-') { + if (!strcmp(argv[i], "-bootable")) { + bootable = true; + } else { + printf("Unknown option %s\n", argv[i]); + return CMD_RET_USAGE; + } + } else { + var = argv[i]; + break; + } + } + + /* Loops should have been exited at the last argument, which + * as it contained the variable */ + if (argc != i + 1) + return CMD_RET_USAGE; + } + + ret = blk_get_device_by_str(argv[0], argv[1], &desc); + if (ret < 0) + return 1; + + if (var != NULL) { + int p; + char str[512] = { '\0', }; + struct disk_partition info; + + for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { + char t[5]; + int r = part_get_info(desc, p, &info); + + if (r != 0) + continue; + + if (bootable && !info.bootable) + continue; + + sprintf(t, "%s%x", str[0] ? " " : "", p); + strcat(str, t); + } + env_set(var, str); + return 0; + } + + part_print(desc); + + return 0; +} + +static int do_part_info(int argc, char *const argv[], enum cmd_part_info param) +{ + struct blk_desc *desc; + struct disk_partition info; + char buf[512] = { 0 }; + char *endp; + int part; + int err; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + if (argc > 4) + return CMD_RET_USAGE; + + ret = blk_get_device_by_str(argv[0], argv[1], &desc); + if (ret < 0) + return 1; + + part = simple_strtoul(argv[2], &endp, 0); + if (*endp == '\0') { + err = part_get_info(desc, part, &info); + if (err) + return 1; + } else { + part = part_get_info_by_name(desc, argv[2], &info); + if (part == -1) + return 1; + } + + switch (param) { + case CMD_PART_INFO_START: + snprintf(buf, sizeof(buf), LBAF, info.start); + break; + case CMD_PART_INFO_SIZE: + snprintf(buf, sizeof(buf), LBAF, info.size); + break; + case CMD_PART_INFO_NUMBER: + snprintf(buf, sizeof(buf), "0x%x", part); + break; + default: + printf("** Unknown cmd_part_info value: %d\n", param); + return 1; + } + + if (argc > 3) + env_set(argv[3], buf); + else + printf("%s\n", buf); + + return 0; +} + +static int do_part_start(int argc, char *const argv[]) +{ + return do_part_info(argc, argv, CMD_PART_INFO_START); +} + +static int do_part_size(int argc, char *const argv[]) +{ + return do_part_info(argc, argv, CMD_PART_INFO_SIZE); +} + +static int do_part_number(int argc, char *const argv[]) +{ + return do_part_info(argc, argv, CMD_PART_INFO_NUMBER); +} + +static int do_part_types(int argc, char * const argv[]) +{ + struct part_driver *drv = ll_entry_start(struct part_driver, + part_driver); + const int n_ents = ll_entry_count(struct part_driver, part_driver); + struct part_driver *entry; + int i = 0; + + puts("Supported partition tables"); + + for (entry = drv; entry != drv + n_ents; entry++) { + printf("%c %s", i ? ',' : ':', entry->name); + i++; + } + if (!i) + puts(": <none>"); + puts("\n"); + return CMD_RET_SUCCESS; +} + +static int do_part(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "uuid")) + return do_part_uuid(argc - 2, argv + 2); + else if (!strcmp(argv[1], "list")) + return do_part_list(argc - 2, argv + 2); + else if (!strcmp(argv[1], "start")) + return do_part_start(argc - 2, argv + 2); + else if (!strcmp(argv[1], "size")) + return do_part_size(argc - 2, argv + 2); + else if (!strcmp(argv[1], "number")) + return do_part_number(argc - 2, argv + 2); + else if (!strcmp(argv[1], "types")) + return do_part_types(argc - 2, argv + 2); + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + part, CONFIG_SYS_MAXARGS, 1, do_part, + "disk partition related commands", + "uuid <interface> <dev>:<part>\n" + " - print partition UUID\n" + "part uuid <interface> <dev>:<part> <varname>\n" + " - set environment variable to partition UUID\n" + "part list <interface> <dev>\n" + " - print a device's partition table\n" + "part list <interface> <dev> [flags] <varname>\n" + " - set environment variable to the list of partitions\n" + " flags can be -bootable (list only bootable partitions)\n" + "part start <interface> <dev> <part> <varname>\n" + " - set environment variable to the start of the partition (in blocks)\n" + " part can be either partition number or partition name\n" + "part size <interface> <dev> <part> <varname>\n" + " - set environment variable to the size of the partition (in blocks)\n" + " part can be either partition number or partition name\n" + "part number <interface> <dev> <part> <varname>\n" + " - set environment variable to the partition number using the partition name\n" + " part must be specified as partition name\n" + "part types\n" + " - list supported partition table types" +); diff --git a/roms/u-boot/cmd/pcap.c b/roms/u-boot/cmd/pcap.c new file mode 100644 index 000000000..d0172f3ff --- /dev/null +++ b/roms/u-boot/cmd/pcap.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Ramon Fried <rfried.dev@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <net/pcap.h> + +static int do_pcap_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + phys_addr_t addr; + unsigned int size; + + if (argc != 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], NULL, 16); + size = simple_strtoul(argv[2], NULL, 10); + + return pcap_init(addr, size) ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static int do_pcap_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return pcap_start_stop(true) ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static int do_pcap_stop(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return pcap_start_stop(false) ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static int do_pcap_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return pcap_print_status() ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static int do_pcap_clear(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return pcap_clear() ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +static char pcap_help_text[] = + "- network packet capture\n\n" + "pcap\n" + "pcap init\t\t\t<addr> <max_size>\n" + "pcap start\t\t\tstart capture\n" + "pcap stop\t\t\tstop capture\n" + "pcap status\t\t\tprint status\n" + "pcap clear\t\t\tclear capture buffer\n" + "\n" + "With:\n" + "\t<addr>: user address to which pcap will be stored (hexedcimal)\n" + "\t<max_size>: Maximum size of pcap file (decimal)\n" + "\n"; + +U_BOOT_CMD_WITH_SUBCMDS(pcap, "pcap", pcap_help_text, + U_BOOT_SUBCMD_MKENT(init, 3, 0, do_pcap_init), + U_BOOT_SUBCMD_MKENT(start, 1, 0, do_pcap_start), + U_BOOT_SUBCMD_MKENT(stop, 1, 0, do_pcap_stop), + U_BOOT_SUBCMD_MKENT(status, 1, 0, do_pcap_status), + U_BOOT_SUBCMD_MKENT(clear, 1, 0, do_pcap_clear), +); diff --git a/roms/u-boot/cmd/pci.c b/roms/u-boot/cmd/pci.c new file mode 100644 index 000000000..e53b7c858 --- /dev/null +++ b/roms/u-boot/cmd/pci.c @@ -0,0 +1,811 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Wolfgang Grandegger, DENX Software Engineering, wg@denx.de. + */ + +/* + * PCI routines + */ + +#include <common.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <init.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +struct pci_reg_info { + const char *name; + enum pci_size_t size; + u8 offset; +}; + +static int pci_byte_size(enum pci_size_t size) +{ + switch (size) { + case PCI_SIZE_8: + return 1; + case PCI_SIZE_16: + return 2; + case PCI_SIZE_32: + default: + return 4; + } +} + +static int pci_field_width(enum pci_size_t size) +{ + return pci_byte_size(size) * 2; +} + +#ifdef CONFIG_DM_PCI +static void pci_show_regs(struct udevice *dev, struct pci_reg_info *regs) +{ + for (; regs->name; regs++) { + unsigned long val; + + dm_pci_read_config(dev, regs->offset, &val, regs->size); + printf(" %s =%*s%#.*lx\n", regs->name, + (int)(28 - strlen(regs->name)), "", + pci_field_width(regs->size), val); + } +} +#else +static unsigned long pci_read_config(pci_dev_t dev, int offset, + enum pci_size_t size) +{ + u32 val32; + u16 val16; + u8 val8; + + switch (size) { + case PCI_SIZE_8: + pci_read_config_byte(dev, offset, &val8); + return val8; + case PCI_SIZE_16: + pci_read_config_word(dev, offset, &val16); + return val16; + case PCI_SIZE_32: + default: + pci_read_config_dword(dev, offset, &val32); + return val32; + } +} + +static void pci_show_regs(pci_dev_t dev, struct pci_reg_info *regs) +{ + for (; regs->name; regs++) { + printf(" %s =%*s%#.*lx\n", regs->name, + (int)(28 - strlen(regs->name)), "", + pci_field_width(regs->size), + pci_read_config(dev, regs->offset, regs->size)); + } +} +#endif + +#ifdef CONFIG_DM_PCI +int pci_bar_show(struct udevice *dev) +{ + u8 header_type; + int bar_cnt, bar_id, mem_type; + bool is_64, is_io; + u32 base_low, base_high; + u32 size_low, size_high; + u64 base, size; + u32 reg_addr; + int prefetchable; + + dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type); + + if (header_type == PCI_HEADER_TYPE_CARDBUS) { + printf("CardBus doesn't support BARs\n"); + return -ENOSYS; + } + + bar_cnt = (header_type == PCI_HEADER_TYPE_NORMAL) ? 6 : 2; + + printf("ID Base Size Width Type\n"); + printf("----------------------------------------------------------\n"); + + bar_id = 0; + reg_addr = PCI_BASE_ADDRESS_0; + while (bar_cnt) { + dm_pci_read_config32(dev, reg_addr, &base_low); + dm_pci_write_config32(dev, reg_addr, 0xffffffff); + dm_pci_read_config32(dev, reg_addr, &size_low); + dm_pci_write_config32(dev, reg_addr, base_low); + reg_addr += 4; + + base = base_low & ~0xf; + size = size_low & ~0xf; + base_high = 0x0; + size_high = 0xffffffff; + is_64 = 0; + prefetchable = base_low & PCI_BASE_ADDRESS_MEM_PREFETCH; + is_io = base_low & PCI_BASE_ADDRESS_SPACE_IO; + mem_type = base_low & PCI_BASE_ADDRESS_MEM_TYPE_MASK; + + if (mem_type == PCI_BASE_ADDRESS_MEM_TYPE_64) { + dm_pci_read_config32(dev, reg_addr, &base_high); + dm_pci_write_config32(dev, reg_addr, 0xffffffff); + dm_pci_read_config32(dev, reg_addr, &size_high); + dm_pci_write_config32(dev, reg_addr, base_high); + bar_cnt--; + reg_addr += 4; + is_64 = 1; + } + + base = base | ((u64)base_high << 32); + size = size | ((u64)size_high << 32); + + if ((!is_64 && size_low) || (is_64 && size)) { + size = ~size + 1; + printf(" %d %#018llx %#018llx %d %s %s\n", + bar_id, (unsigned long long)base, + (unsigned long long)size, is_64 ? 64 : 32, + is_io ? "I/O" : "MEM", + prefetchable ? "Prefetchable" : ""); + } + + bar_id++; + bar_cnt--; + } + + return 0; +} +#endif + +static struct pci_reg_info regs_start[] = { + { "vendor ID", PCI_SIZE_16, PCI_VENDOR_ID }, + { "device ID", PCI_SIZE_16, PCI_DEVICE_ID }, + { "command register ID", PCI_SIZE_16, PCI_COMMAND }, + { "status register", PCI_SIZE_16, PCI_STATUS }, + { "revision ID", PCI_SIZE_8, PCI_REVISION_ID }, + {}, +}; + +static struct pci_reg_info regs_rest[] = { + { "sub class code", PCI_SIZE_8, PCI_CLASS_SUB_CODE }, + { "programming interface", PCI_SIZE_8, PCI_CLASS_PROG }, + { "cache line", PCI_SIZE_8, PCI_CACHE_LINE_SIZE }, + { "latency time", PCI_SIZE_8, PCI_LATENCY_TIMER }, + { "header type", PCI_SIZE_8, PCI_HEADER_TYPE }, + { "BIST", PCI_SIZE_8, PCI_BIST }, + { "base address 0", PCI_SIZE_32, PCI_BASE_ADDRESS_0 }, + {}, +}; + +static struct pci_reg_info regs_normal[] = { + { "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 }, + { "base address 2", PCI_SIZE_32, PCI_BASE_ADDRESS_2 }, + { "base address 3", PCI_SIZE_32, PCI_BASE_ADDRESS_3 }, + { "base address 4", PCI_SIZE_32, PCI_BASE_ADDRESS_4 }, + { "base address 5", PCI_SIZE_32, PCI_BASE_ADDRESS_5 }, + { "cardBus CIS pointer", PCI_SIZE_32, PCI_CARDBUS_CIS }, + { "sub system vendor ID", PCI_SIZE_16, PCI_SUBSYSTEM_VENDOR_ID }, + { "sub system ID", PCI_SIZE_16, PCI_SUBSYSTEM_ID }, + { "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS }, + { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE }, + { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN }, + { "min Grant", PCI_SIZE_8, PCI_MIN_GNT }, + { "max Latency", PCI_SIZE_8, PCI_MAX_LAT }, + {}, +}; + +static struct pci_reg_info regs_bridge[] = { + { "base address 1", PCI_SIZE_32, PCI_BASE_ADDRESS_1 }, + { "primary bus number", PCI_SIZE_8, PCI_PRIMARY_BUS }, + { "secondary bus number", PCI_SIZE_8, PCI_SECONDARY_BUS }, + { "subordinate bus number", PCI_SIZE_8, PCI_SUBORDINATE_BUS }, + { "secondary latency timer", PCI_SIZE_8, PCI_SEC_LATENCY_TIMER }, + { "IO base", PCI_SIZE_8, PCI_IO_BASE }, + { "IO limit", PCI_SIZE_8, PCI_IO_LIMIT }, + { "secondary status", PCI_SIZE_16, PCI_SEC_STATUS }, + { "memory base", PCI_SIZE_16, PCI_MEMORY_BASE }, + { "memory limit", PCI_SIZE_16, PCI_MEMORY_LIMIT }, + { "prefetch memory base", PCI_SIZE_16, PCI_PREF_MEMORY_BASE }, + { "prefetch memory limit", PCI_SIZE_16, PCI_PREF_MEMORY_LIMIT }, + { "prefetch memory base upper", PCI_SIZE_32, PCI_PREF_BASE_UPPER32 }, + { "prefetch memory limit upper", PCI_SIZE_32, PCI_PREF_LIMIT_UPPER32 }, + { "IO base upper 16 bits", PCI_SIZE_16, PCI_IO_BASE_UPPER16 }, + { "IO limit upper 16 bits", PCI_SIZE_16, PCI_IO_LIMIT_UPPER16 }, + { "expansion ROM base address", PCI_SIZE_32, PCI_ROM_ADDRESS1 }, + { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE }, + { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN }, + { "bridge control", PCI_SIZE_16, PCI_BRIDGE_CONTROL }, + {}, +}; + +static struct pci_reg_info regs_cardbus[] = { + { "capabilities", PCI_SIZE_8, PCI_CB_CAPABILITY_LIST }, + { "secondary status", PCI_SIZE_16, PCI_CB_SEC_STATUS }, + { "primary bus number", PCI_SIZE_8, PCI_CB_PRIMARY_BUS }, + { "CardBus number", PCI_SIZE_8, PCI_CB_CARD_BUS }, + { "subordinate bus number", PCI_SIZE_8, PCI_CB_SUBORDINATE_BUS }, + { "CardBus latency timer", PCI_SIZE_8, PCI_CB_LATENCY_TIMER }, + { "CardBus memory base 0", PCI_SIZE_32, PCI_CB_MEMORY_BASE_0 }, + { "CardBus memory limit 0", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_0 }, + { "CardBus memory base 1", PCI_SIZE_32, PCI_CB_MEMORY_BASE_1 }, + { "CardBus memory limit 1", PCI_SIZE_32, PCI_CB_MEMORY_LIMIT_1 }, + { "CardBus IO base 0", PCI_SIZE_16, PCI_CB_IO_BASE_0 }, + { "CardBus IO base high 0", PCI_SIZE_16, PCI_CB_IO_BASE_0_HI }, + { "CardBus IO limit 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0 }, + { "CardBus IO limit high 0", PCI_SIZE_16, PCI_CB_IO_LIMIT_0_HI }, + { "CardBus IO base 1", PCI_SIZE_16, PCI_CB_IO_BASE_1 }, + { "CardBus IO base high 1", PCI_SIZE_16, PCI_CB_IO_BASE_1_HI }, + { "CardBus IO limit 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1 }, + { "CardBus IO limit high 1", PCI_SIZE_16, PCI_CB_IO_LIMIT_1_HI }, + { "interrupt line", PCI_SIZE_8, PCI_INTERRUPT_LINE }, + { "interrupt pin", PCI_SIZE_8, PCI_INTERRUPT_PIN }, + { "bridge control", PCI_SIZE_16, PCI_CB_BRIDGE_CONTROL }, + { "subvendor ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_VENDOR_ID }, + { "subdevice ID", PCI_SIZE_16, PCI_CB_SUBSYSTEM_ID }, + { "PC Card 16bit base address", PCI_SIZE_32, PCI_CB_LEGACY_MODE_BASE }, + {}, +}; + +/** + * pci_header_show() - Show the header of the specified PCI device. + * + * @dev: Bus+Device+Function number + */ +#ifdef CONFIG_DM_PCI +void pci_header_show(struct udevice *dev) +#else +void pci_header_show(pci_dev_t dev) +#endif +{ +#ifdef CONFIG_DM_PCI + unsigned long class, header_type; + + dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8); + dm_pci_read_config(dev, PCI_HEADER_TYPE, &header_type, PCI_SIZE_8); +#else + u8 class, header_type; + + pci_read_config_byte(dev, PCI_CLASS_CODE, &class); + pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); +#endif + pci_show_regs(dev, regs_start); + printf(" class code = 0x%.2x (%s)\n", (int)class, + pci_class_str(class)); + pci_show_regs(dev, regs_rest); + + switch (header_type & 0x03) { + case PCI_HEADER_TYPE_NORMAL: /* "normal" PCI device */ + pci_show_regs(dev, regs_normal); + break; + case PCI_HEADER_TYPE_BRIDGE: /* PCI-to-PCI bridge */ + pci_show_regs(dev, regs_bridge); + break; + case PCI_HEADER_TYPE_CARDBUS: /* PCI-to-CardBus bridge */ + pci_show_regs(dev, regs_cardbus); + break; + + default: + printf("unknown header\n"); + break; + } +} + +void pciinfo_header(int busnum, bool short_listing) +{ + printf("Scanning PCI devices on bus %d\n", busnum); + + if (short_listing) { + printf("BusDevFun VendorId DeviceId Device Class Sub-Class\n"); + printf("_____________________________________________________________\n"); + } +} + +#ifdef CONFIG_DM_PCI +/** + * pci_header_show_brief() - Show the short-form PCI device header + * + * Reads and prints the header of the specified PCI device in short form. + * + * @dev: PCI device to show + */ +static void pci_header_show_brief(struct udevice *dev) +{ + ulong vendor, device; + ulong class, subclass; + + dm_pci_read_config(dev, PCI_VENDOR_ID, &vendor, PCI_SIZE_16); + dm_pci_read_config(dev, PCI_DEVICE_ID, &device, PCI_SIZE_16); + dm_pci_read_config(dev, PCI_CLASS_CODE, &class, PCI_SIZE_8); + dm_pci_read_config(dev, PCI_CLASS_SUB_CODE, &subclass, PCI_SIZE_8); + + printf("0x%.4lx 0x%.4lx %-23s 0x%.2lx\n", + vendor, device, + pci_class_str(class), subclass); +} + +static void pciinfo(struct udevice *bus, bool short_listing) +{ + struct udevice *dev; + + pciinfo_header(dev_seq(bus), short_listing); + + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + struct pci_child_plat *pplat; + + pplat = dev_get_parent_plat(dev); + if (short_listing) { + printf("%02x.%02x.%02x ", dev_seq(bus), + PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn)); + pci_header_show_brief(dev); + } else { + printf("\nFound PCI device %02x.%02x.%02x:\n", + dev_seq(bus), + PCI_DEV(pplat->devfn), PCI_FUNC(pplat->devfn)); + pci_header_show(dev); + } + } +} + +#else + +/** + * pci_header_show_brief() - Show the short-form PCI device header + * + * Reads and prints the header of the specified PCI device in short form. + * + * @dev: Bus+Device+Function number + */ +void pci_header_show_brief(pci_dev_t dev) +{ + u16 vendor, device; + u8 class, subclass; + + pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &device); + pci_read_config_byte(dev, PCI_CLASS_CODE, &class); + pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &subclass); + + printf("0x%.4x 0x%.4x %-23s 0x%.2x\n", + vendor, device, + pci_class_str(class), subclass); +} + +/** + * pciinfo() - Show a list of devices on the PCI bus + * + * Show information about devices on PCI bus. Depending on @short_pci_listing + * the output will be more or less exhaustive. + * + * @bus_num: The number of the bus to be scanned + * @short_pci_listing: true to use short form, showing only a brief header + * for each device + */ +void pciinfo(int bus_num, int short_pci_listing) +{ + struct pci_controller *hose = pci_bus_to_hose(bus_num); + int device; + int function; + unsigned char header_type; + unsigned short vendor_id; + pci_dev_t dev; + int ret; + + if (!hose) + return; + + pciinfo_header(bus_num, short_pci_listing); + + for (device = 0; device < PCI_MAX_PCI_DEVICES; device++) { + header_type = 0; + vendor_id = 0; + for (function = 0; function < PCI_MAX_PCI_FUNCTIONS; + function++) { + /* + * If this is not a multi-function device, we skip + * the rest. + */ + if (function && !(header_type & 0x80)) + break; + + dev = PCI_BDF(bus_num, device, function); + + if (pci_skip_dev(hose, dev)) + continue; + + ret = pci_read_config_word(dev, PCI_VENDOR_ID, + &vendor_id); + if (ret) + goto error; + if ((vendor_id == 0xFFFF) || (vendor_id == 0x0000)) + continue; + + if (!function) { + pci_read_config_byte(dev, PCI_HEADER_TYPE, + &header_type); + } + + if (short_pci_listing) { + printf("%02x.%02x.%02x ", bus_num, device, + function); + pci_header_show_brief(dev); + } else { + printf("\nFound PCI device %02x.%02x.%02x:\n", + bus_num, device, function); + pci_header_show(dev); + } + } + } + + return; +error: + printf("Cannot read bus configuration: %d\n", ret); +} +#endif + +/** + * get_pci_dev() - Convert the "bus.device.function" identifier into a number + * + * @name: Device string in the form "bus.device.function" where each is in hex + * @return encoded pci_dev_t or -1 if the string was invalid + */ +static pci_dev_t get_pci_dev(char *name) +{ + char cnum[12]; + int len, i, iold, n; + int bdfs[3] = {0,0,0}; + + len = strlen(name); + if (len > 8) + return -1; + for (i = 0, iold = 0, n = 0; i < len; i++) { + if (name[i] == '.') { + memcpy(cnum, &name[iold], i - iold); + cnum[i - iold] = '\0'; + bdfs[n++] = simple_strtoul(cnum, NULL, 16); + iold = i + 1; + } + } + strcpy(cnum, &name[iold]); + if (n == 0) + n = 1; + bdfs[n] = simple_strtoul(cnum, NULL, 16); + + return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]); +} + +#ifdef CONFIG_DM_PCI +static int pci_cfg_display(struct udevice *dev, ulong addr, + enum pci_size_t size, ulong length) +#else +static int pci_cfg_display(pci_dev_t bdf, ulong addr, enum pci_size_t size, + ulong length) +#endif +{ +#define DISP_LINE_LEN 16 + ulong i, nbytes, linebytes; + int byte_size; + int rc = 0; + + byte_size = pci_byte_size(size); + if (length == 0) + length = 0x40 / byte_size; /* Standard PCI config space */ + + /* Print the lines. + * once, and all accesses are with the specified bus width. + */ + nbytes = length * byte_size; + do { + printf("%08lx:", addr); + linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes; + for (i = 0; i < linebytes; i += byte_size) { + unsigned long val; + +#ifdef CONFIG_DM_PCI + dm_pci_read_config(dev, addr, &val, size); +#else + val = pci_read_config(bdf, addr, size); +#endif + printf(" %0*lx", pci_field_width(size), val); + addr += byte_size; + } + printf("\n"); + nbytes -= linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + + return (rc); +} + +#ifndef CONFIG_DM_PCI +static int pci_cfg_write (pci_dev_t bdf, ulong addr, ulong size, ulong value) +{ + if (size == 4) { + pci_write_config_dword(bdf, addr, value); + } + else if (size == 2) { + ushort val = value & 0xffff; + pci_write_config_word(bdf, addr, val); + } + else { + u_char val = value & 0xff; + pci_write_config_byte(bdf, addr, val); + } + return 0; +} +#endif + +#ifdef CONFIG_DM_PCI +static int pci_cfg_modify(struct udevice *dev, ulong addr, ulong size, + ulong value, int incrflag) +#else +static int pci_cfg_modify(pci_dev_t bdf, ulong addr, ulong size, ulong value, + int incrflag) +#endif +{ + ulong i; + int nbytes; + ulong val; + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + printf("%08lx:", addr); +#ifdef CONFIG_DM_PCI + dm_pci_read_config(dev, addr, &val, size); +#else + val = pci_read_config(bdf, addr, size); +#endif + printf(" %0*lx", pci_field_width(size), val); + + nbytes = cli_readline(" ? "); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -size : size; + nbytes = 1; + /* good enough to not time out */ + bootretry_reset_cmd_timeout(); + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) { + break; /* timed out, exit the command */ + } +#endif + else { + char *endp; + i = simple_strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { + /* good enough to not time out + */ + bootretry_reset_cmd_timeout(); +#ifdef CONFIG_DM_PCI + dm_pci_write_config(dev, addr, i, size); +#else + pci_cfg_write(bdf, addr, size, i); +#endif + if (incrflag) + addr += size; + } + } + } while (nbytes); + + return 0; +} + +#ifdef CONFIG_DM_PCI +static const struct pci_flag_info { + uint flag; + const char *name; +} pci_flag_info[] = { + { PCI_REGION_IO, "io" }, + { PCI_REGION_PREFETCH, "prefetch" }, + { PCI_REGION_SYS_MEMORY, "sysmem" }, + { PCI_REGION_RO, "readonly" }, + { PCI_REGION_IO, "io" }, +}; + +static void pci_show_regions(struct udevice *bus) +{ + struct pci_controller *hose = dev_get_uclass_priv(bus); + const struct pci_region *reg; + int i, j; + + if (!hose) { + printf("Bus '%s' is not a PCI controller\n", bus->name); + return; + } + + printf("# %-18s %-18s %-18s %s\n", "Bus start", "Phys start", "Size", + "Flags"); + for (i = 0, reg = hose->regions; i < hose->region_count; i++, reg++) { + printf("%d %#018llx %#018llx %#018llx ", i, + (unsigned long long)reg->bus_start, + (unsigned long long)reg->phys_start, + (unsigned long long)reg->size); + if (!(reg->flags & PCI_REGION_TYPE)) + printf("mem "); + for (j = 0; j < ARRAY_SIZE(pci_flag_info); j++) { + if (reg->flags & pci_flag_info[j].flag) + printf("%s ", pci_flag_info[j].name); + } + printf("\n"); + } +} +#endif + +/* PCI Configuration Space access commands + * + * Syntax: + * pci display[.b, .w, .l] bus.device.function} [addr] [len] + * pci next[.b, .w, .l] bus.device.function [addr] + * pci modify[.b, .w, .l] bus.device.function [addr] + * pci write[.b, .w, .l] bus.device.function addr value + */ +static int do_pci(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr = 0, value = 0, cmd_size = 0; + enum pci_size_t size = PCI_SIZE_32; +#ifdef CONFIG_DM_PCI + struct udevice *dev, *bus; +#else + pci_dev_t dev; +#endif + int busnum = 0; + pci_dev_t bdf = 0; + char cmd = 's'; + int ret = 0; + + if (argc > 1) + cmd = argv[1][0]; + + switch (cmd) { + case 'd': /* display */ + case 'n': /* next */ + case 'm': /* modify */ + case 'w': /* write */ + /* Check for a size specification. */ + cmd_size = cmd_get_data_size(argv[1], 4); + size = (cmd_size == 4) ? PCI_SIZE_32 : cmd_size - 1; + if (argc > 3) + addr = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + value = simple_strtoul(argv[4], NULL, 16); + case 'h': /* header */ +#ifdef CONFIG_DM_PCI + case 'b': /* bars */ +#endif + if (argc < 3) + goto usage; + if ((bdf = get_pci_dev(argv[2])) == -1) + return 1; + break; +#if defined(CONFIG_DM_PCI) + case 'e': + pci_init(); + return 0; +#endif + case 'r': /* no break */ + default: /* scan bus */ + value = 1; /* short listing */ + if (argc > 1) { + if (cmd != 'r' && argv[argc-1][0] == 'l') { + value = 0; + argc--; + } + if (argc > 1) + busnum = simple_strtoul(argv[1], NULL, 16); + } +#ifdef CONFIG_DM_PCI + ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus); + if (ret) { + printf("No such bus\n"); + return CMD_RET_FAILURE; + } + if (cmd == 'r') + pci_show_regions(bus); + else + pciinfo(bus, value); +#else + pciinfo(busnum, value); +#endif + return 0; + } + +#ifdef CONFIG_DM_PCI + ret = dm_pci_bus_find_bdf(bdf, &dev); + if (ret) { + printf("No such device\n"); + return CMD_RET_FAILURE; + } +#else + dev = bdf; +#endif + + switch (argv[1][0]) { + case 'h': /* header */ + pci_header_show(dev); + break; + case 'd': /* display */ + return pci_cfg_display(dev, addr, size, value); + case 'n': /* next */ + if (argc < 4) + goto usage; + ret = pci_cfg_modify(dev, addr, size, value, 0); + break; + case 'm': /* modify */ + if (argc < 4) + goto usage; + ret = pci_cfg_modify(dev, addr, size, value, 1); + break; + case 'w': /* write */ + if (argc < 5) + goto usage; +#ifdef CONFIG_DM_PCI + ret = dm_pci_write_config(dev, addr, value, size); +#else + ret = pci_cfg_write(dev, addr, size, value); +#endif + break; +#ifdef CONFIG_DM_PCI + + case 'b': /* bars */ + return pci_bar_show(dev); +#endif + default: + ret = CMD_RET_USAGE; + break; + } + + return ret; + usage: + return CMD_RET_USAGE; +} + +/***************************************************/ + +#ifdef CONFIG_SYS_LONGHELP +static char pci_help_text[] = + "[bus] [long]\n" + " - short or long list of PCI devices on bus 'bus'\n" +#if defined(CONFIG_DM_PCI) + "pci enum\n" + " - Enumerate PCI buses\n" +#endif + "pci header b.d.f\n" + " - show header of PCI device 'bus.device.function'\n" +#ifdef CONFIG_DM_PCI + "pci bar b.d.f\n" + " - show BARs base and size for device b.d.f'\n" + "pci regions\n" + " - show PCI regions\n" +#endif + "pci display[.b, .w, .l] b.d.f [address] [# of objects]\n" + " - display PCI configuration space (CFG)\n" + "pci next[.b, .w, .l] b.d.f address\n" + " - modify, read and keep CFG address\n" + "pci modify[.b, .w, .l] b.d.f address\n" + " - modify, auto increment CFG address\n" + "pci write[.b, .w, .l] b.d.f address value\n" + " - write to CFG address"; +#endif + +U_BOOT_CMD( + pci, 5, 1, do_pci, + "list and access PCI Configuration Space", pci_help_text +); diff --git a/roms/u-boot/cmd/pinmux.c b/roms/u-boot/cmd/pinmux.c new file mode 100644 index 000000000..9942b1541 --- /dev/null +++ b/roms/u-boot/cmd/pinmux.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <dm/pinctrl.h> +#include <dm/uclass-internal.h> + +#define LIMIT_DEVNAME 30 + +static struct udevice *currdev; + +static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *name; + int ret; + + switch (argc) { + case 2: + name = argv[1]; + ret = uclass_get_device_by_name(UCLASS_PINCTRL, name, &currdev); + if (ret) { + printf("Can't get the pin-controller: %s!\n", name); + return CMD_RET_FAILURE; + } + /* fall through */ + case 1: + if (!currdev) { + printf("Pin-controller device is not set!\n"); + return CMD_RET_USAGE; + } + + printf("dev: %s\n", currdev->name); + } + + return CMD_RET_SUCCESS; +} + +static int show_pinmux(struct udevice *dev) +{ + char pin_name[PINNAME_SIZE]; + char pin_mux[PINMUX_SIZE]; + int pins_count; + int i; + int ret; + + pins_count = pinctrl_get_pins_count(dev); + + if (pins_count == -ENOSYS) { + printf("Ops get_pins_count not supported\n"); + return pins_count; + } + + for (i = 0; i < pins_count; i++) { + ret = pinctrl_get_pin_name(dev, i, pin_name, PINNAME_SIZE); + if (ret == -ENOSYS) { + printf("Ops get_pin_name not supported\n"); + return ret; + } + + ret = pinctrl_get_pin_muxing(dev, i, pin_mux, PINMUX_SIZE); + if (ret) { + printf("Ops get_pin_muxing error (%d)\n", ret); + return ret; + } + + printf("%-*s: %-*s\n", PINNAME_SIZE, pin_name, + PINMUX_SIZE, pin_mux); + } + + return 0; +} + +static int do_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret = CMD_RET_USAGE; + + if (currdev && (argc < 2 || strcmp(argv[1], "-a"))) + return show_pinmux(currdev); + + if (argc < 2 || strcmp(argv[1], "-a")) + return ret; + + uclass_foreach_dev_probe(UCLASS_PINCTRL, dev) { + /* insert a separator between each pin-controller display */ + printf("--------------------------\n"); + printf("%s:\n", dev->name); + ret = show_pinmux(dev); + if (ret < 0) + printf("Can't display pin muxing for %s\n", + dev->name); + } + + return ret; +} + +static int do_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + + printf("| %-*.*s| %-*.*s| %s\n", + LIMIT_DEVNAME, LIMIT_DEVNAME, "Device", + LIMIT_DEVNAME, LIMIT_DEVNAME, "Driver", + "Parent"); + + uclass_foreach_dev_probe(UCLASS_PINCTRL, dev) { + printf("| %-*.*s| %-*.*s| %s\n", + LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name, + LIMIT_DEVNAME, LIMIT_DEVNAME, dev->driver->name, + dev->parent->name); + } + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl pinmux_subcmd[] = { + U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""), + U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""), + U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""), +}; + +static int do_pinmux(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cmd; + + argc--; + argv++; + + cmd = find_cmd_tbl(argv[0], pinmux_subcmd, ARRAY_SIZE(pinmux_subcmd)); + if (!cmd || argc > cmd->maxargs) + return CMD_RET_USAGE; + + return cmd->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD(pinmux, CONFIG_SYS_MAXARGS, 1, do_pinmux, + "show pin-controller muxing", + "list - list UCLASS_PINCTRL devices\n" + "pinmux dev [pincontroller-name] - select pin-controller device\n" + "pinmux status [-a] - print pin-controller muxing [for all]\n" +) diff --git a/roms/u-boot/cmd/pmc.c b/roms/u-boot/cmd/pmc.c new file mode 100644 index 000000000..9a3ba2bff --- /dev/null +++ b/roms/u-boot/cmd/pmc.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel PMC command + * + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <power/acpi_pmc.h> + +static int get_pmc_dev(struct udevice **devp) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev); + if (ret) { + printf("Could not find device (err=%d)\n", ret); + return ret; + } + ret = pmc_init(dev); + if (ret) { + printf("Could not init device (err=%d)\n", ret); + return ret; + } + *devp = dev; + + return 0; +} + +static int do_pmc_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + + ret = get_pmc_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + + return 0; +} + +static int do_pmc_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + + ret = get_pmc_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + pmc_dump_info(dev); + + return 0; +} + +static struct cmd_tbl cmd_pmc_sub[] = { + U_BOOT_CMD_MKENT(init, 0, 1, do_pmc_init, "", ""), + U_BOOT_CMD_MKENT(info, 0, 1, do_pmc_info, "", ""), +}; + +static int do_pmc(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct cmd_tbl *cp; + + if (argc < 2) /* no subcommand */ + return cmd_usage(cmdtp); + + cp = find_cmd_tbl(argv[1], &cmd_pmc_sub[0], ARRAY_SIZE(cmd_pmc_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD( + pmc, 2, 1, do_pmc, "Power-management controller info", + "info - read state and show info about the PMC\n" + "pmc init - read state from the PMC\n" + ); diff --git a/roms/u-boot/cmd/pmic.c b/roms/u-boot/cmd/pmic.c new file mode 100644 index 000000000..0cb44d074 --- /dev/null +++ b/roms/u-boot/cmd/pmic.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ +#include <common.h> +#include <command.h> +#include <errno.h> +#include <dm.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> + +#define LIMIT_DEV 32 +#define LIMIT_PARENT 20 + +static struct udevice *currdev; + +static int failure(int ret) +{ + printf("Error: %d (%s)\n", ret, errno_str(ret)); + + return CMD_RET_FAILURE; +} + +static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *name; + int ret = -ENODEV; + + switch (argc) { + case 2: + name = argv[1]; + ret = pmic_get(name, &currdev); + if (ret) { + printf("Can't get PMIC: %s!\n", name); + return failure(ret); + } + case 1: + if (!currdev) { + printf("PMIC device is not set!\n\n"); + return CMD_RET_USAGE; + } + + printf("dev: %d @ %s\n", dev_seq(currdev), currdev->name); + } + + return CMD_RET_SUCCESS; +} + +static int do_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + + printf("| %-*.*s| %-*.*s| %s @ %s\n", + LIMIT_DEV, LIMIT_DEV, "Name", + LIMIT_PARENT, LIMIT_PARENT, "Parent name", + "Parent uclass", "seq"); + + for (ret = uclass_first_device(UCLASS_PMIC, &dev); dev; + ret = uclass_next_device(&dev)) { + if (ret) + continue; + + printf("| %-*.*s| %-*.*s| %s @ %d\n", + LIMIT_DEV, LIMIT_DEV, dev->name, + LIMIT_PARENT, LIMIT_PARENT, dev->parent->name, + dev_get_uclass_name(dev->parent), dev_seq(dev->parent)); + } + + if (ret) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int do_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct uc_pmic_priv *priv; + struct udevice *dev; + char fmt[16]; + uint reg; + int ret; + + if (!currdev) { + printf("First, set the PMIC device!\n"); + return CMD_RET_USAGE; + } + + dev = currdev; + priv = dev_get_uclass_priv(dev); + printf("Dump pmic: %s registers\n", dev->name); + + sprintf(fmt, "%%%d.%dx ", priv->trans_len * 2, + priv->trans_len * 2); + + for (reg = 0; reg < pmic_reg_count(dev); reg++) { + ret = pmic_reg_read(dev, reg); + if (ret < 0 && ret != -ENODATA) { + printf("Can't read register: %d\n", reg); + return failure(ret); + } + + if (!(reg % 16)) + printf("\n0x%02x: ", reg); + + if (ret == -ENODATA) { + int i; + + for (i = 0; i < priv->trans_len; i++) + puts("--"); + puts(" "); + } else { + printf(fmt, ret); + } + } + printf("\n"); + + return CMD_RET_SUCCESS; +} + +static int do_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct uc_pmic_priv *priv; + struct udevice *dev; + int regs, ret; + char fmt[24]; + uint reg; + + if (!currdev) { + printf("First, set the PMIC device!\n"); + return CMD_RET_USAGE; + } + + dev = currdev; + priv = dev_get_uclass_priv(dev); + + if (argc != 2) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[1], NULL, 0); + regs = pmic_reg_count(dev); + if (reg > regs) { + printf("PMIC max reg: %d\n", regs); + return failure(-EFAULT); + } + + ret = pmic_reg_read(dev, reg); + if (ret < 0) { + printf("Can't read PMIC register: %d!\n", reg); + return failure(ret); + } + + sprintf(fmt, "0x%%02x: 0x%%%d.%dx\n", priv->trans_len * 2, + priv->trans_len * 2); + printf(fmt, reg, ret); + + return CMD_RET_SUCCESS; +} + +static int do_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + uint reg, value; + int regs, ret; + + if (!currdev) { + printf("First, set the PMIC device!\n"); + return CMD_RET_USAGE; + } + + dev = currdev; + + if (argc != 3) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[1], NULL, 0); + regs = pmic_reg_count(dev); + if (reg > regs) { + printf("PMIC max reg: %d\n", regs); + return failure(-EFAULT); + } + + value = simple_strtoul(argv[2], NULL, 0); + + ret = pmic_reg_write(dev, reg, value); + if (ret) { + printf("Can't write PMIC register: %d!\n", reg); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl subcmd[] = { + U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""), + U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""), + U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""), + U_BOOT_CMD_MKENT(read, 2, 1, do_read, "", ""), + U_BOOT_CMD_MKENT(write, 3, 1, do_write, "", ""), +}; + +static int do_pmic(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cmd; + + argc--; + argv++; + + cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd)); + if (cmd == NULL || argc > cmd->maxargs) + return CMD_RET_USAGE; + + return cmd->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic, + "PMIC sub-system", + "list - list pmic devices\n" + "pmic dev [name] - show or [set] operating PMIC device\n" + "pmic dump - dump registers\n" + "pmic read address - read byte of register at address\n" + "pmic write address - write byte to register at address\n" +); diff --git a/roms/u-boot/cmd/pstore.c b/roms/u-boot/cmd/pstore.c new file mode 100644 index 000000000..5656bae6d --- /dev/null +++ b/roms/u-boot/cmd/pstore.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright © 2019 Collabora Ltd + */ + +#include <config.h> +#include <command.h> +#include <fdtdec.h> +#include <fs.h> +#include <log.h> +#include <mapmem.h> +#include <memalign.h> +#include <part.h> + +struct persistent_ram_buffer { + u32 sig; + u32 start; + u32 size; + u8 data[0]; +}; + +#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ +#define RAMOOPS_KERNMSG_HDR "====" + +#define PSTORE_TYPE_DMESG 0 +#define PSTORE_TYPE_CONSOLE 2 +#define PSTORE_TYPE_FTRACE 3 +#define PSTORE_TYPE_PMSG 7 +#define PSTORE_TYPE_ALL 255 + +static phys_addr_t pstore_addr = CONFIG_CMD_PSTORE_MEM_ADDR; +static phys_size_t pstore_length = CONFIG_CMD_PSTORE_MEM_SIZE; +static unsigned int pstore_record_size = CONFIG_CMD_PSTORE_RECORD_SIZE; +static unsigned int pstore_console_size = CONFIG_CMD_PSTORE_CONSOLE_SIZE; +static unsigned int pstore_ftrace_size = CONFIG_CMD_PSTORE_FTRACE_SIZE; +static unsigned int pstore_pmsg_size = CONFIG_CMD_PSTORE_PMSG_SIZE; +static unsigned int pstore_ecc_size = CONFIG_CMD_PSTORE_ECC_SIZE; +static unsigned int buffer_size; + + /** + * pstore_read_kmsg_hdr() - Check kernel header and get compression flag if + * available. + * @buffer: Kernel messages buffer. + * @compressed: Returns TRUE if kernel buffer is compressed, else FALSE. + * + * Check if buffer starts with a kernel header of the form: + * ====<secs>.<nsecs>[-<compression>]\n + * If <compression> is equal to 'C' then the buffer is compressed, else iter + * should be 'D'. + * + * Return: Length of kernel header. + */ +static int pstore_read_kmsg_hdr(char *buffer, bool *compressed) +{ + char *ptr = buffer; + *compressed = false; + + if (strncmp(RAMOOPS_KERNMSG_HDR, ptr, strlen(RAMOOPS_KERNMSG_HDR)) != 0) + return 0; + + ptr += strlen(RAMOOPS_KERNMSG_HDR); + + ptr = strchr(ptr, '\n'); + if (!ptr) + return 0; + + if (ptr[-2] == '-' && ptr[-1] == 'C') + *compressed = true; + + return ptr - buffer + 1; +} + +/** + * pstore_get_buffer() - Get unwrapped record buffer + * @sig: Signature to check + * @buffer: Buffer containing wrapped record + * @size: wrapped record size + * @dest: Buffer used to store unwrapped record + * + * The record starts with <signature><start><size> header. + * The signature is 'DBGC' for all records except for Ftrace's record(s) wich + * use LINUX_VERSION_CODE ^ 'DBGC'. + * Use 0 for @sig to prevent checking signature. + * Start and size are 4 bytes long. + * + * Return: record's length + */ +static u32 pstore_get_buffer(u32 sig, phys_addr_t buffer, u32 size, char *dest) +{ + struct persistent_ram_buffer *prb = + (struct persistent_ram_buffer *)map_sysmem(buffer, size); + u32 dest_size; + + if (sig == 0 || prb->sig == sig) { + if (prb->size == 0) { + log_debug("found existing empty buffer\n"); + return 0; + } + + if (prb->size > size) { + log_debug("found existing invalid buffer, size %u, start %u\n", + prb->size, prb->start); + return 0; + } + } else { + log_debug("no valid data in buffer (sig = 0x%08x)\n", prb->sig); + return 0; + } + + log_debug("found existing buffer, size %u, start %u\n", + prb->size, prb->start); + + memcpy(dest, &prb->data[prb->start], prb->size - prb->start); + memcpy(dest + prb->size - prb->start, &prb->data[0], prb->start); + + dest_size = prb->size; + unmap_sysmem(prb); + + return dest_size; +} + +/** + * pstore_init_buffer_size() - Init buffer size to largest record size + * + * Records, console, FTrace and user logs can use different buffer sizes. + * This function allows to retrieve the biggest one. + */ +static void pstore_init_buffer_size(void) +{ + if (pstore_record_size > buffer_size) + buffer_size = pstore_record_size; + + if (pstore_console_size > buffer_size) + buffer_size = pstore_console_size; + + if (pstore_ftrace_size > buffer_size) + buffer_size = pstore_ftrace_size; + + if (pstore_pmsg_size > buffer_size) + buffer_size = pstore_pmsg_size; +} + +/** + * pstore_set() - Initialize PStore settings from command line arguments + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Set pstore reserved memory info, starting at 'addr' for 'len' bytes. + * Default length for records is 4K. + * Mandatory arguments: + * - addr: ramoops starting address + * - len: ramoops total length + * Optional arguments: + * - record-size: size of one panic or oops record ('dump' type) + * - console-size: size of the kernel logs record + * - ftrace-size: size of the ftrace record(s), this can be a single record or + * divided in parts based on number of CPUs + * - pmsg-size: size of the user space logs record + * - ecc-size: enables/disables ECC support and specifies ECC buffer size in + * bytes (0 disables it, 1 is a special value, means 16 bytes ECC) + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_set(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc < 3) + return CMD_RET_USAGE; + + /* Address is specified since argc > 2 + */ + pstore_addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is specified since argc > 2 + */ + pstore_length = simple_strtoul(argv[2], NULL, 16); + + if (argc > 3) + pstore_record_size = simple_strtoul(argv[3], NULL, 16); + + if (argc > 4) + pstore_console_size = simple_strtoul(argv[4], NULL, 16); + + if (argc > 5) + pstore_ftrace_size = simple_strtoul(argv[5], NULL, 16); + + if (argc > 6) + pstore_pmsg_size = simple_strtoul(argv[6], NULL, 16); + + if (argc > 7) + pstore_ecc_size = simple_strtoul(argv[7], NULL, 16); + + if (pstore_length < (pstore_record_size + pstore_console_size + + pstore_ftrace_size + pstore_pmsg_size)) { + printf("pstore <len> should be larger than the sum of all records sizes\n"); + pstore_length = 0; + } + + log_debug("pstore set done: start 0x%08llx - length 0x%llx\n", + (unsigned long long)pstore_addr, + (unsigned long long)pstore_length); + + return 0; +} + +/** + * pstore_print_buffer() - Print buffer + * @type: buffer type + * @buffer: buffer to print + * @size: buffer size + * + * Print buffer type and content + */ +static void pstore_print_buffer(char *type, char *buffer, u32 size) +{ + u32 i = 0; + + printf("**** %s\n", type); + while (i < size && buffer[i] != 0) { + putc(buffer[i]); + i++; + } +} + +/** + * pstore_display() - Display existing records in pstore reserved memory + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * A 'record-type' can be given to only display records of this kind. + * If no 'record-type' is given, all valid records are dispayed. + * 'record-type' can be one of 'dump', 'console', 'ftrace' or 'user'. For 'dump' + * and 'ftrace' types, a 'nb' can be given to only display one record. + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_display(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + int type = PSTORE_TYPE_ALL; + phys_addr_t ptr; + char *buffer; + u32 size; + int header_len = 0; + bool compressed; + + if (argc > 1) { + if (!strcmp(argv[1], "dump")) + type = PSTORE_TYPE_DMESG; + else if (!strcmp(argv[1], "console")) + type = PSTORE_TYPE_CONSOLE; + else if (!strcmp(argv[1], "ftrace")) + type = PSTORE_TYPE_FTRACE; + else if (!strcmp(argv[1], "user")) + type = PSTORE_TYPE_PMSG; + else + return CMD_RET_USAGE; + } + + if (pstore_length == 0) { + printf("Please set PStore configuration\n"); + return CMD_RET_USAGE; + } + + if (buffer_size == 0) + pstore_init_buffer_size(); + + buffer = malloc_cache_aligned(buffer_size); + + if (type == PSTORE_TYPE_DMESG || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr; + phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size - pstore_console_size; + + if (argc > 2) { + ptr += simple_strtoul(argv[2], NULL, 10) + * pstore_record_size; + ptr_end = ptr + pstore_record_size; + } + + while (ptr < ptr_end) { + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_record_size, buffer); + ptr += pstore_record_size; + + if (size == 0) + continue; + + header_len = pstore_read_kmsg_hdr(buffer, &compressed); + if (header_len == 0) { + log_debug("no valid kernel header\n"); + continue; + } + + if (compressed) { + printf("Compressed buffer, display not available\n"); + continue; + } + + pstore_print_buffer("Dump", buffer + header_len, + size - header_len); + } + } + + if (type == PSTORE_TYPE_CONSOLE || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size - pstore_console_size; + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_console_size, buffer); + if (size != 0) + pstore_print_buffer("Console", buffer, size); + } + + if (type == PSTORE_TYPE_FTRACE || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size; + /* The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' + * signature, pass 0 to pstore_get_buffer to prevent + * checking it + */ + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); + if (size != 0) + pstore_print_buffer("FTrace", buffer, size); + } + + if (type == PSTORE_TYPE_PMSG || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size; + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_pmsg_size, buffer); + if (size != 0) + pstore_print_buffer("User", buffer, size); + } + + free(buffer); + + return 0; +} + +/** + * pstore_save() - Save existing records from pstore reserved memory + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * the records are saved under 'directory path', which should already exist, + * to partition 'part' on device type 'interface' instance 'dev' + * Filenames are automatically generated, depending on record type, like in + * /sys/fs/pstore under Linux + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_save(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + phys_addr_t ptr, ptr_end; + char *buffer; + char *save_argv[6]; + char addr[19], length[19]; + char path[256]; + u32 size; + unsigned int index; + int header_len = 0; + bool compressed; + + if (argc < 4) + return CMD_RET_USAGE; + + if (pstore_length == 0) { + printf("Please set PStore configuration\n"); + return CMD_RET_USAGE; + } + + if (buffer_size == 0) + pstore_init_buffer_size(); + + buffer = malloc_cache_aligned(buffer_size); + sprintf(addr, "0x%p", buffer); + + save_argv[0] = argv[0]; + save_argv[1] = argv[1]; + save_argv[2] = argv[2]; + save_argv[3] = addr; + save_argv[4] = path; + save_argv[5] = length; + + /* Save all Dump records */ + ptr = pstore_addr; + ptr_end = ptr + pstore_length - pstore_pmsg_size - pstore_ftrace_size + - pstore_console_size; + index = 0; + while (ptr < ptr_end) { + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_record_size, buffer); + ptr += pstore_record_size; + + if (size == 0) + continue; + + header_len = pstore_read_kmsg_hdr(buffer, &compressed); + if (header_len == 0) { + log_debug("no valid kernel header\n"); + continue; + } + + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer + header_len)); + sprintf(length, "0x%X", size - header_len); + sprintf(path, "%s/dmesg-ramoops-%u%s", argv[3], index, + compressed ? ".enc.z" : ""); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + index++; + } + + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer)); + + /* Save Console record */ + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_console_size, + buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/console-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + ptr += pstore_console_size; + + /* Save FTrace record(s) + * The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' signature, + * pass 0 to pstore_get_buffer to prevent checking it + */ + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/ftrace-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + ptr += pstore_ftrace_size; + + /* Save Console record */ + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_pmsg_size, + buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/pmsg-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + + free(buffer); + + return 0; +} + +static struct cmd_tbl cmd_pstore_sub[] = { + U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""), + U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""), + U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""), +}; + +static int do_pstore(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_pstore_sub, ARRAY_SIZE(cmd_pstore_sub)); + + if (!c) + return CMD_RET_USAGE; + + return c->cmd(cmdtp, flag, argc, argv); +} + +void fdt_fixup_pstore(void *blob) +{ + char node[32]; + int nodeoffset; /* node offset from libfdt */ + + nodeoffset = fdt_path_offset(blob, "/"); + if (nodeoffset < 0) { + /* Not found or something else bad happened. */ + log_err("fdt_path_offset() returned %s\n", fdt_strerror(nodeoffset)); + return; + } + + nodeoffset = fdt_add_subnode(blob, nodeoffset, "reserved-memory"); + if (nodeoffset < 0) { + log_err("Add 'reserved-memory' node failed: %s\n", + fdt_strerror(nodeoffset)); + return; + } + fdt_setprop_u32(blob, nodeoffset, "#address-cells", 2); + fdt_setprop_u32(blob, nodeoffset, "#size-cells", 2); + fdt_setprop_empty(blob, nodeoffset, "ranges"); + + sprintf(node, "ramoops@%llx", (unsigned long long)pstore_addr); + nodeoffset = fdt_add_subnode(blob, nodeoffset, node); + if (nodeoffset < 0) { + log_err("Add '%s' node failed: %s\n", node, fdt_strerror(nodeoffset)); + return; + } + fdt_setprop_string(blob, nodeoffset, "compatible", "ramoops"); + fdt_setprop_u64(blob, nodeoffset, "reg", pstore_addr); + fdt_appendprop_u64(blob, nodeoffset, "reg", pstore_length); + fdt_setprop_u32(blob, nodeoffset, "record-size", pstore_record_size); + fdt_setprop_u32(blob, nodeoffset, "console-size", pstore_console_size); + fdt_setprop_u32(blob, nodeoffset, "ftrace-size", pstore_ftrace_size); + fdt_setprop_u32(blob, nodeoffset, "pmsg-size", pstore_pmsg_size); + fdt_setprop_u32(blob, nodeoffset, "ecc-size", pstore_ecc_size); +} + +U_BOOT_CMD(pstore, 10, 0, do_pstore, + "Manage Linux Persistent Storage", + "set <addr> <len> [record-size] [console-size] [ftrace-size] [pmsg_size] [ecc-size]\n" + "- Set pstore reserved memory info, starting at 'addr' for 'len' bytes.\n" + " Default length for records is 4K.\n" + " 'record-size' is the size of one panic or oops record ('dump' type).\n" + " 'console-size' is the size of the kernel logs record.\n" + " 'ftrace-size' is the size of the ftrace record(s), this can be a single\n" + " record or divided in parts based on number of CPUs.\n" + " 'pmsg-size' is the size of the user space logs record.\n" + " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n" + " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n" + "pstore display [record-type] [nb]\n" + "- Display existing records in pstore reserved memory. A 'record-type' can\n" + " be given to only display records of this kind. 'record-type' can be one\n" + " of 'dump', 'console', 'ftrace' or 'user'. For 'dump' and 'ftrace' types,\n" + " a 'nb' can be given to only display one record.\n" + "pstore save <interface> <dev[:part]> <directory-path>\n" + "- Save existing records in pstore reserved memory under 'directory path'\n" + " to partition 'part' on device type 'interface' instance 'dev'.\n" + " Filenames are automatically generated, depending on record type, like\n" + " in /sys/fs/pstore under Linux.\n" + " The 'directory-path' should already exist.\n" +); diff --git a/roms/u-boot/cmd/pvblock.c b/roms/u-boot/cmd/pvblock.c new file mode 100644 index 000000000..4e99b0612 --- /dev/null +++ b/roms/u-boot/cmd/pvblock.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 EPAM Systems Inc. + * + * XEN para-virtualized block device support + */ + +#include <blk.h> +#include <common.h> +#include <command.h> + +/* Current I/O Device */ +static int pvblock_curr_device; + +int do_pvblock(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + return blk_common_cmd(argc, argv, IF_TYPE_PVBLOCK, + &pvblock_curr_device); +} + +U_BOOT_CMD(pvblock, 5, 1, do_pvblock, + "Xen para-virtualized block device", + "info - show available block devices\n" + "pvblock device [dev] - show or set current device\n" + "pvblock part [dev] - print partition table of one or all devices\n" + "pvblock read addr blk# cnt\n" + "pvblock write addr blk# cnt - read/write `cnt'" + " blocks starting at block `blk#'\n" + " to/from memory address `addr'"); + diff --git a/roms/u-boot/cmd/pwm.c b/roms/u-boot/cmd/pwm.c new file mode 100644 index 000000000..e1f97c759 --- /dev/null +++ b/roms/u-boot/cmd/pwm.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Control PWM channels + * + * Copyright (c) 2020 SiFive, Inc + * author: Pragnesh Patel <pragnesh.patel@sifive.com> + */ + +#include <command.h> +#include <dm.h> +#include <pwm.h> + +enum pwm_cmd { + PWM_SET_INVERT, + PWM_SET_CONFIG, + PWM_SET_ENABLE, + PWM_SET_DISABLE, +}; + +static int do_pwm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *str_cmd, *str_channel = NULL, *str_enable = NULL; + const char *str_pwm = NULL, *str_period = NULL, *str_duty = NULL; + enum pwm_cmd sub_cmd; + struct udevice *dev; + u32 channel, pwm_enable, pwm_dev, period_ns = 0, duty_ns = 0; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + + str_cmd = argv[1]; + argc -= 2; + argv += 2; + + str_pwm = *argv; + argc--; + argv++; + + if (!str_pwm) + return CMD_RET_USAGE; + + switch (*str_cmd) { + case 'i': + sub_cmd = PWM_SET_INVERT; + if (argc != 2) + return CMD_RET_USAGE; + break; + case 'c': + sub_cmd = PWM_SET_CONFIG; + if (argc != 3) + return CMD_RET_USAGE; + break; + case 'e': + sub_cmd = PWM_SET_ENABLE; + if (argc != 1) + return CMD_RET_USAGE; + break; + case 'd': + sub_cmd = PWM_SET_DISABLE; + if (argc != 1) + return CMD_RET_USAGE; + break; + default: + return CMD_RET_USAGE; + } + + pwm_dev = simple_strtoul(str_pwm, NULL, 10); + ret = uclass_get_device(UCLASS_PWM, pwm_dev, &dev); + if (ret) { + printf("pwm: '%s' not found\n", str_pwm); + return cmd_process_error(cmdtp, ret); + } + + str_channel = *argv; + channel = simple_strtoul(str_channel, NULL, 10); + argc--; + argv++; + + if (sub_cmd == PWM_SET_INVERT) { + str_enable = *argv; + pwm_enable = simple_strtoul(str_enable, NULL, 10); + ret = pwm_set_invert(dev, channel, pwm_enable); + } else if (sub_cmd == PWM_SET_CONFIG) { + str_period = *argv; + argc--; + argv++; + period_ns = simple_strtoul(str_period, NULL, 10); + + str_duty = *argv; + duty_ns = simple_strtoul(str_duty, NULL, 10); + + ret = pwm_set_config(dev, channel, period_ns, duty_ns); + } else if (sub_cmd == PWM_SET_ENABLE) { + ret = pwm_set_enable(dev, channel, 1); + } else if (sub_cmd == PWM_SET_DISABLE) { + ret = pwm_set_enable(dev, channel, 0); + } + + if (ret) { + printf("error(%d)\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(pwm, 6, 0, do_pwm, + "control pwm channels", + "pwm <invert> <pwm_dev_num> <channel> <polarity>\n" + "pwm <config> <pwm_dev_num> <channel> <period_ns> <duty_ns>\n" + "pwm <enable/disable> <pwm_dev_num> <channel>\n" + "Note: All input values are in decimal"); diff --git a/roms/u-boot/cmd/pxe.c b/roms/u-boot/cmd/pxe.c new file mode 100644 index 000000000..46ac08fa3 --- /dev/null +++ b/roms/u-boot/cmd/pxe.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2010-2011 Calxeda, Inc. + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + */ + +#include <common.h> +#include <command.h> +#include <fs.h> +#include <net.h> + +#include "pxe_utils.h" + +#ifdef CONFIG_CMD_NET +const char *pxe_default_paths[] = { +#ifdef CONFIG_SYS_SOC +#ifdef CONFIG_SYS_BOARD + "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC "-" CONFIG_SYS_BOARD, +#endif + "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC, +#endif + "default-" CONFIG_SYS_ARCH, + "default", + NULL +}; + +static int do_get_tftp(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr) +{ + char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; + + tftp_argv[1] = file_addr; + tftp_argv[2] = (void *)file_path; + + if (do_tftpb(cmdtp, 0, 3, tftp_argv)) + return -ENOENT; + + return 1; +} + +/* + * Looks for a pxe file with a name based on the pxeuuid environment variable. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_uuid_path(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r) +{ + char *uuid_str; + + uuid_str = from_env("pxeuuid"); + + if (!uuid_str) + return -ENOENT; + + return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r); +} + +/* + * Looks for a pxe file with a name based on the 'ethaddr' environment + * variable. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_mac_path(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r) +{ + char mac_str[21]; + int err; + + err = format_mac_pxe(mac_str, sizeof(mac_str)); + + if (err < 0) + return err; + + return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r); +} + +/* + * Looks for pxe files with names based on our IP address. See pxelinux + * documentation for details on what these file names look like. We match + * that exactly. + * + * Returns 1 on success or < 0 on error. + */ +static int pxe_ipaddr_paths(struct cmd_tbl *cmdtp, unsigned long pxefile_addr_r) +{ + char ip_addr[9]; + int mask_pos, err; + + sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr)); + + for (mask_pos = 7; mask_pos >= 0; mask_pos--) { + err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r); + + if (err > 0) + return err; + + ip_addr[mask_pos] = '\0'; + } + + return -ENOENT; +} +/* + * Entry point for the 'pxe get' command. + * This Follows pxelinux's rules to download a config file from a tftp server. + * The file is stored at the location given by the pxefile_addr_r environment + * variable, which must be set. + * + * UUID comes from pxeuuid env variable, if defined + * MAC addr comes from ethaddr env variable, if defined + * IP + * + * see http://syslinux.zytor.com/wiki/index.php/PXELINUX + * + * Returns 0 on success or 1 on error. + */ +static int +do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *pxefile_addr_str; + unsigned long pxefile_addr_r; + int err, i = 0; + + do_getfile = do_get_tftp; + + if (argc != 1) + return CMD_RET_USAGE; + + pxefile_addr_str = from_env("pxefile_addr_r"); + + if (!pxefile_addr_str) + return 1; + + err = strict_strtoul(pxefile_addr_str, 16, + (unsigned long *)&pxefile_addr_r); + if (err < 0) + return 1; + + /* + * Keep trying paths until we successfully get a file we're looking + * for. + */ + if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 || + pxe_mac_path(cmdtp, pxefile_addr_r) > 0 || + pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) { + printf("Config file found\n"); + + return 0; + } + + while (pxe_default_paths[i]) { + if (get_pxelinux_path(cmdtp, pxe_default_paths[i], + pxefile_addr_r) > 0) { + printf("Config file found\n"); + return 0; + } + i++; + } + + printf("Config file not found\n"); + + return 1; +} + +/* + * Boots a system using a pxe file + * + * Returns 0 on success, 1 on error. + */ +static int +do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long pxefile_addr_r; + struct pxe_menu *cfg; + char *pxefile_addr_str; + + do_getfile = do_get_tftp; + + if (argc == 1) { + pxefile_addr_str = from_env("pxefile_addr_r"); + if (!pxefile_addr_str) + return 1; + + } else if (argc == 2) { + pxefile_addr_str = argv[1]; + } else { + return CMD_RET_USAGE; + } + + if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { + printf("Invalid pxefile address: %s\n", pxefile_addr_str); + return 1; + } + + cfg = parse_pxefile(cmdtp, pxefile_addr_r); + + if (!cfg) { + printf("Error parsing config file\n"); + return 1; + } + + handle_pxe_menu(cmdtp, cfg); + + destroy_pxe_menu(cfg); + + copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name)); + + return 0; +} + +static struct cmd_tbl cmd_pxe_sub[] = { + U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""), + U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "") +}; + +static void __maybe_unused pxe_reloc(void) +{ + static int relocated_pxe; + + if (!relocated_pxe) { + fixup_cmdtable(cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub)); + relocated_pxe = 1; + } +} + +static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + pxe_reloc(); +#endif + + if (argc < 2) + return CMD_RET_USAGE; + + is_pxe = true; + + /* drop initial "pxe" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(pxe, 3, 1, do_pxe, + "commands to get and boot from pxe files", + "get - try to retrieve a pxe file using tftp\n" + "pxe boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n" +); +#endif diff --git a/roms/u-boot/cmd/pxe_utils.c b/roms/u-boot/cmd/pxe_utils.c new file mode 100644 index 000000000..9a30629e2 --- /dev/null +++ b/roms/u-boot/cmd/pxe_utils.c @@ -0,0 +1,1479 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2010-2011 Calxeda, Inc. + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <lcd.h> +#include <net.h> +#include <fdt_support.h> +#include <linux/libfdt.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <errno.h> +#include <linux/list.h> + +#include <splash.h> +#include <asm/io.h> + +#include "menu.h" +#include "cli.h" + +#include "pxe_utils.h" + +#define MAX_TFTP_PATH_LEN 512 + +bool is_pxe; + +/* + * Convert an ethaddr from the environment to the format used by pxelinux + * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to + * the beginning of the ethernet address to indicate a hardware type of + * Ethernet. Also converts uppercase hex characters into lowercase, to match + * pxelinux's behavior. + * + * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the + * environment, or some other value < 0 on error. + */ +int format_mac_pxe(char *outbuf, size_t outbuf_len) +{ + uchar ethaddr[6]; + + if (outbuf_len < 21) { + printf("outbuf is too small (%zd < 21)\n", outbuf_len); + + return -EINVAL; + } + + if (!eth_env_get_enetaddr_by_index("eth", eth_get_dev_index(), ethaddr)) + return -ENOENT; + + sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + + return 1; +} + +/* + * Returns the directory the file specified in the bootfile env variable is + * in. If bootfile isn't defined in the environment, return NULL, which should + * be interpreted as "don't prepend anything to paths". + */ +static int get_bootfile_path(const char *file_path, char *bootfile_path, + size_t bootfile_path_size) +{ + char *bootfile, *last_slash; + size_t path_len = 0; + + /* Only syslinux allows absolute paths */ + if (file_path[0] == '/' && !is_pxe) + goto ret; + + bootfile = from_env("bootfile"); + + if (!bootfile) + goto ret; + + last_slash = strrchr(bootfile, '/'); + + if (!last_slash) + goto ret; + + path_len = (last_slash - bootfile) + 1; + + if (bootfile_path_size < path_len) { + printf("bootfile_path too small. (%zd < %zd)\n", + bootfile_path_size, path_len); + + return -1; + } + + strncpy(bootfile_path, bootfile, path_len); + + ret: + bootfile_path[path_len] = '\0'; + + return 1; +} + +int (*do_getfile)(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr); + +/* + * As in pxelinux, paths to files referenced from files we retrieve are + * relative to the location of bootfile. get_relfile takes such a path and + * joins it with the bootfile path to get the full path to the target file. If + * the bootfile path is NULL, we use file_path as is. + * + * Returns 1 for success, or < 0 on error. + */ +static int get_relfile(struct cmd_tbl *cmdtp, const char *file_path, + unsigned long file_addr) +{ + size_t path_len; + char relfile[MAX_TFTP_PATH_LEN + 1]; + char addr_buf[18]; + int err; + + err = get_bootfile_path(file_path, relfile, sizeof(relfile)); + + if (err < 0) + return err; + + path_len = strlen(file_path); + path_len += strlen(relfile); + + if (path_len > MAX_TFTP_PATH_LEN) { + printf("Base path too long (%s%s)\n", relfile, file_path); + + return -ENAMETOOLONG; + } + + strcat(relfile, file_path); + + printf("Retrieving file: %s\n", relfile); + + sprintf(addr_buf, "%lx", file_addr); + + return do_getfile(cmdtp, relfile, addr_buf); +} + +/* + * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If + * 'bootfile' was specified in the environment, the path to bootfile will be + * prepended to 'file_path' and the resulting path will be used. + * + * Returns 1 on success, or < 0 for error. + */ +int get_pxe_file(struct cmd_tbl *cmdtp, const char *file_path, + unsigned long file_addr) +{ + unsigned long config_file_size; + char *tftp_filesize; + int err; + char *buf; + + err = get_relfile(cmdtp, file_path, file_addr); + + if (err < 0) + return err; + + /* + * the file comes without a NUL byte at the end, so find out its size + * and add the NUL byte. + */ + tftp_filesize = from_env("filesize"); + + if (!tftp_filesize) + return -ENOENT; + + if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0) + return -EINVAL; + + buf = map_sysmem(file_addr + config_file_size, 1); + *buf = '\0'; + unmap_sysmem(buf); + + return 1; +} + +#define PXELINUX_DIR "pxelinux.cfg/" + +/* + * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file + * to do the hard work, the location of the 'pxelinux.cfg' folder is generated + * from the bootfile path, as described above. + * + * Returns 1 on success or < 0 on error. + */ +int get_pxelinux_path(struct cmd_tbl *cmdtp, const char *file, + unsigned long pxefile_addr_r) +{ + size_t base_len = strlen(PXELINUX_DIR); + char path[MAX_TFTP_PATH_LEN + 1]; + + if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) { + printf("path (%s%s) too long, skipping\n", + PXELINUX_DIR, file); + return -ENAMETOOLONG; + } + + sprintf(path, PXELINUX_DIR "%s", file); + + return get_pxe_file(cmdtp, path, pxefile_addr_r); +} + +/* + * Wrapper to make it easier to store the file at file_path in the location + * specified by envaddr_name. file_path will be joined to the bootfile path, + * if any is specified. + * + * Returns 1 on success or < 0 on error. + */ +static int get_relfile_envaddr(struct cmd_tbl *cmdtp, const char *file_path, + const char *envaddr_name) +{ + unsigned long file_addr; + char *envaddr; + + envaddr = from_env(envaddr_name); + + if (!envaddr) + return -ENOENT; + + if (strict_strtoul(envaddr, 16, &file_addr) < 0) + return -EINVAL; + + return get_relfile(cmdtp, file_path, file_addr); +} + +/* + * Allocates memory for and initializes a pxe_label. This uses malloc, so the + * result must be free()'d to reclaim the memory. + * + * Returns NULL if malloc fails. + */ +static struct pxe_label *label_create(void) +{ + struct pxe_label *label; + + label = malloc(sizeof(struct pxe_label)); + + if (!label) + return NULL; + + memset(label, 0, sizeof(struct pxe_label)); + + return label; +} + +/* + * Free the memory used by a pxe_label, including that used by its name, + * kernel, append and initrd members, if they're non NULL. + * + * So - be sure to only use dynamically allocated memory for the members of + * the pxe_label struct, unless you want to clean it up first. These are + * currently only created by the pxe file parsing code. + */ +static void label_destroy(struct pxe_label *label) +{ + if (label->name) + free(label->name); + + if (label->kernel) + free(label->kernel); + + if (label->config) + free(label->config); + + if (label->append) + free(label->append); + + if (label->initrd) + free(label->initrd); + + if (label->fdt) + free(label->fdt); + + if (label->fdtdir) + free(label->fdtdir); + + if (label->fdtoverlays) + free(label->fdtoverlays); + + free(label); +} + +/* + * Print a label and its string members if they're defined. + * + * This is passed as a callback to the menu code for displaying each + * menu entry. + */ +static void label_print(void *data) +{ + struct pxe_label *label = data; + const char *c = label->menu ? label->menu : label->name; + + printf("%s:\t%s\n", label->num, c); +} + +/* + * Boot a label that specified 'localboot'. This requires that the 'localcmd' + * environment variable is defined. Its contents will be executed as U-Boot + * command. If the label specified an 'append' line, its contents will be + * used to overwrite the contents of the 'bootargs' environment variable prior + * to running 'localcmd'. + * + * Returns 1 on success or < 0 on error. + */ +static int label_localboot(struct pxe_label *label) +{ + char *localcmd; + + localcmd = from_env("localcmd"); + + if (!localcmd) + return -ENOENT; + + if (label->append) { + char bootargs[CONFIG_SYS_CBSIZE]; + + cli_simple_process_macros(label->append, bootargs, + sizeof(bootargs)); + env_set("bootargs", bootargs); + } + + debug("running: %s\n", localcmd); + + return run_command_list(localcmd, strlen(localcmd), 0); +} + +/* + * Loads fdt overlays specified in 'fdtoverlays'. + */ +#ifdef CONFIG_OF_LIBFDT_OVERLAY +static void label_boot_fdtoverlay(struct cmd_tbl *cmdtp, struct pxe_label *label) +{ + char *fdtoverlay = label->fdtoverlays; + struct fdt_header *working_fdt; + char *fdtoverlay_addr_env; + ulong fdtoverlay_addr; + ulong fdt_addr; + int err; + + /* Get the main fdt and map it */ + fdt_addr = simple_strtoul(env_get("fdt_addr_r"), NULL, 16); + working_fdt = map_sysmem(fdt_addr, 0); + err = fdt_check_header(working_fdt); + if (err) + return; + + /* Get the specific overlay loading address */ + fdtoverlay_addr_env = env_get("fdtoverlay_addr_r"); + if (!fdtoverlay_addr_env) { + printf("Invalid fdtoverlay_addr_r for loading overlays\n"); + return; + } + + fdtoverlay_addr = simple_strtoul(fdtoverlay_addr_env, NULL, 16); + + /* Cycle over the overlay files and apply them in order */ + do { + struct fdt_header *blob; + char *overlayfile; + char *end; + int len; + + /* Drop leading spaces */ + while (*fdtoverlay == ' ') + ++fdtoverlay; + + /* Copy a single filename if multiple provided */ + end = strstr(fdtoverlay, " "); + if (end) { + len = (int)(end - fdtoverlay); + overlayfile = malloc(len + 1); + strncpy(overlayfile, fdtoverlay, len); + overlayfile[len] = '\0'; + } else + overlayfile = fdtoverlay; + + if (!strlen(overlayfile)) + goto skip_overlay; + + /* Load overlay file */ + err = get_relfile_envaddr(cmdtp, overlayfile, + "fdtoverlay_addr_r"); + if (err < 0) { + printf("Failed loading overlay %s\n", overlayfile); + goto skip_overlay; + } + + /* Resize main fdt */ + fdt_shrink_to_minimum(working_fdt, 8192); + + blob = map_sysmem(fdtoverlay_addr, 0); + err = fdt_check_header(blob); + if (err) { + printf("Invalid overlay %s, skipping\n", + overlayfile); + goto skip_overlay; + } + + err = fdt_overlay_apply_verbose(working_fdt, blob); + if (err) { + printf("Failed to apply overlay %s, skipping\n", + overlayfile); + goto skip_overlay; + } + +skip_overlay: + if (end) + free(overlayfile); + } while ((fdtoverlay = strstr(fdtoverlay, " "))); +} +#endif + +/* + * Boot according to the contents of a pxe_label. + * + * If we can't boot for any reason, we return. A successful boot never + * returns. + * + * The kernel will be stored in the location given by the 'kernel_addr_r' + * environment variable. + * + * If the label specifies an initrd file, it will be stored in the location + * given by the 'ramdisk_addr_r' environment variable. + * + * If the label specifies an 'append' line, its contents will overwrite that + * of the 'bootargs' environment variable. + */ +static int label_boot(struct cmd_tbl *cmdtp, struct pxe_label *label) +{ + char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL }; + char initrd_str[28]; + char mac_str[29] = ""; + char ip_str[68] = ""; + char *fit_addr = NULL; + int bootm_argc = 2; + int len = 0; + ulong kernel_addr; + void *buf; + + label_print(label); + + label->attempted = 1; + + if (label->localboot) { + if (label->localboot_val >= 0) + label_localboot(label); + return 0; + } + + if (!label->kernel) { + printf("No kernel given, skipping %s\n", + label->name); + return 1; + } + + if (label->initrd) { + if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) { + printf("Skipping %s for failure retrieving initrd\n", + label->name); + return 1; + } + + bootm_argv[2] = initrd_str; + strncpy(bootm_argv[2], env_get("ramdisk_addr_r"), 18); + strcat(bootm_argv[2], ":"); + strncat(bootm_argv[2], env_get("filesize"), 9); + bootm_argc = 3; + } + + if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) { + printf("Skipping %s for failure retrieving kernel\n", + label->name); + return 1; + } + + if (label->ipappend & 0x1) { + sprintf(ip_str, " ip=%s:%s:%s:%s", + env_get("ipaddr"), env_get("serverip"), + env_get("gatewayip"), env_get("netmask")); + } + + if (IS_ENABLED(CONFIG_CMD_NET)) { + if (label->ipappend & 0x2) { + int err; + + strcpy(mac_str, " BOOTIF="); + err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8); + if (err < 0) + mac_str[0] = '\0'; + } + } + + if ((label->ipappend & 0x3) || label->append) { + char bootargs[CONFIG_SYS_CBSIZE] = ""; + char finalbootargs[CONFIG_SYS_CBSIZE]; + + if (strlen(label->append ?: "") + + strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) { + printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n", + strlen(label->append ?: ""), + strlen(ip_str), strlen(mac_str), + sizeof(bootargs)); + return 1; + } + + if (label->append) + strncpy(bootargs, label->append, sizeof(bootargs)); + + strcat(bootargs, ip_str); + strcat(bootargs, mac_str); + + cli_simple_process_macros(bootargs, finalbootargs, + sizeof(finalbootargs)); + env_set("bootargs", finalbootargs); + printf("append: %s\n", finalbootargs); + } + + bootm_argv[1] = env_get("kernel_addr_r"); + /* for FIT, append the configuration identifier */ + if (label->config) { + int len = strlen(bootm_argv[1]) + strlen(label->config) + 1; + + fit_addr = malloc(len); + if (!fit_addr) { + printf("malloc fail (FIT address)\n"); + return 1; + } + snprintf(fit_addr, len, "%s%s", bootm_argv[1], label->config); + bootm_argv[1] = fit_addr; + } + + /* + * fdt usage is optional: + * It handles the following scenarios. + * + * Scenario 1: If fdt_addr_r specified and "fdt" or "fdtdir" label is + * defined in pxe file, retrieve fdt blob from server. Pass fdt_addr_r to + * bootm, and adjust argc appropriately. + * + * If retrieve fails and no exact fdt blob is specified in pxe file with + * "fdt" label, try Scenario 2. + * + * Scenario 2: If there is an fdt_addr specified, pass it along to + * bootm, and adjust argc appropriately. + * + * Scenario 3: fdt blob is not available. + */ + bootm_argv[3] = env_get("fdt_addr_r"); + + /* if fdt label is defined then get fdt from server */ + if (bootm_argv[3]) { + char *fdtfile = NULL; + char *fdtfilefree = NULL; + + if (label->fdt) { + fdtfile = label->fdt; + } else if (label->fdtdir) { + char *f1, *f2, *f3, *f4, *slash; + + f1 = env_get("fdtfile"); + if (f1) { + f2 = ""; + f3 = ""; + f4 = ""; + } else { + /* + * For complex cases where this code doesn't + * generate the correct filename, the board + * code should set $fdtfile during early boot, + * or the boot scripts should set $fdtfile + * before invoking "pxe" or "sysboot". + */ + f1 = env_get("soc"); + f2 = "-"; + f3 = env_get("board"); + f4 = ".dtb"; + if (!f1) { + f1 = ""; + f2 = ""; + } + if (!f3) { + f2 = ""; + f3 = ""; + } + } + + len = strlen(label->fdtdir); + if (!len) + slash = "./"; + else if (label->fdtdir[len - 1] != '/') + slash = "/"; + else + slash = ""; + + len = strlen(label->fdtdir) + strlen(slash) + + strlen(f1) + strlen(f2) + strlen(f3) + + strlen(f4) + 1; + fdtfilefree = malloc(len); + if (!fdtfilefree) { + printf("malloc fail (FDT filename)\n"); + goto cleanup; + } + + snprintf(fdtfilefree, len, "%s%s%s%s%s%s", + label->fdtdir, slash, f1, f2, f3, f4); + fdtfile = fdtfilefree; + } + + if (fdtfile) { + int err = get_relfile_envaddr(cmdtp, fdtfile, + "fdt_addr_r"); + + free(fdtfilefree); + if (err < 0) { + bootm_argv[3] = NULL; + + if (label->fdt) { + printf("Skipping %s for failure retrieving FDT\n", + label->name); + goto cleanup; + } + } + +#ifdef CONFIG_OF_LIBFDT_OVERLAY + if (label->fdtoverlays) + label_boot_fdtoverlay(cmdtp, label); +#endif + } else { + bootm_argv[3] = NULL; + } + } + + if (!bootm_argv[3]) + bootm_argv[3] = env_get("fdt_addr"); + + if (bootm_argv[3]) { + if (!bootm_argv[2]) + bootm_argv[2] = "-"; + bootm_argc = 4; + } + + kernel_addr = genimg_get_kernel_addr(bootm_argv[1]); + buf = map_sysmem(kernel_addr, 0); + /* Try bootm for legacy and FIT format image */ + if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID) + do_bootm(cmdtp, 0, bootm_argc, bootm_argv); + /* Try booting an AArch64 Linux kernel image */ + else if (IS_ENABLED(CONFIG_CMD_BOOTI)) + do_booti(cmdtp, 0, bootm_argc, bootm_argv); + /* Try booting a Image */ + else if (IS_ENABLED(CONFIG_CMD_BOOTZ)) + do_bootz(cmdtp, 0, bootm_argc, bootm_argv); + /* Try booting an x86_64 Linux kernel image */ + else if (IS_ENABLED(CONFIG_CMD_ZBOOT)) + do_zboot_parent(cmdtp, 0, bootm_argc, bootm_argv, NULL); + + unmap_sysmem(buf); + +cleanup: + if (fit_addr) + free(fit_addr); + return 1; +} + +/* + * Tokens for the pxe file parser. + */ +enum token_type { + T_EOL, + T_STRING, + T_EOF, + T_MENU, + T_TITLE, + T_TIMEOUT, + T_LABEL, + T_KERNEL, + T_LINUX, + T_APPEND, + T_INITRD, + T_LOCALBOOT, + T_DEFAULT, + T_PROMPT, + T_INCLUDE, + T_FDT, + T_FDTDIR, + T_FDTOVERLAYS, + T_ONTIMEOUT, + T_IPAPPEND, + T_BACKGROUND, + T_INVALID +}; + +/* + * A token - given by a value and a type. + */ +struct token { + char *val; + enum token_type type; +}; + +/* + * Keywords recognized. + */ +static const struct token keywords[] = { + {"menu", T_MENU}, + {"title", T_TITLE}, + {"timeout", T_TIMEOUT}, + {"default", T_DEFAULT}, + {"prompt", T_PROMPT}, + {"label", T_LABEL}, + {"kernel", T_KERNEL}, + {"linux", T_LINUX}, + {"localboot", T_LOCALBOOT}, + {"append", T_APPEND}, + {"initrd", T_INITRD}, + {"include", T_INCLUDE}, + {"devicetree", T_FDT}, + {"fdt", T_FDT}, + {"devicetreedir", T_FDTDIR}, + {"fdtdir", T_FDTDIR}, + {"fdtoverlays", T_FDTOVERLAYS}, + {"ontimeout", T_ONTIMEOUT,}, + {"ipappend", T_IPAPPEND,}, + {"background", T_BACKGROUND,}, + {NULL, T_INVALID} +}; + +/* + * Since pxe(linux) files don't have a token to identify the start of a + * literal, we have to keep track of when we're in a state where a literal is + * expected vs when we're in a state a keyword is expected. + */ +enum lex_state { + L_NORMAL = 0, + L_KEYWORD, + L_SLITERAL +}; + +/* + * get_string retrieves a string from *p and stores it as a token in + * *t. + * + * get_string used for scanning both string literals and keywords. + * + * Characters from *p are copied into t-val until a character equal to + * delim is found, or a NUL byte is reached. If delim has the special value of + * ' ', any whitespace character will be used as a delimiter. + * + * If lower is unequal to 0, uppercase characters will be converted to + * lowercase in the result. This is useful to make keywords case + * insensitive. + * + * The location of *p is updated to point to the first character after the end + * of the token - the ending delimiter. + * + * On success, the new value of t->val is returned. Memory for t->val is + * allocated using malloc and must be free()'d to reclaim it. If insufficient + * memory is available, NULL is returned. + */ +static char *get_string(char **p, struct token *t, char delim, int lower) +{ + char *b, *e; + size_t len, i; + + /* + * b and e both start at the beginning of the input stream. + * + * e is incremented until we find the ending delimiter, or a NUL byte + * is reached. Then, we take e - b to find the length of the token. + */ + b = *p; + e = *p; + + while (*e) { + if ((delim == ' ' && isspace(*e)) || delim == *e) + break; + e++; + } + + len = e - b; + + /* + * Allocate memory to hold the string, and copy it in, converting + * characters to lowercase if lower is != 0. + */ + t->val = malloc(len + 1); + if (!t->val) + return NULL; + + for (i = 0; i < len; i++, b++) { + if (lower) + t->val[i] = tolower(*b); + else + t->val[i] = *b; + } + + t->val[len] = '\0'; + + /* + * Update *p so the caller knows where to continue scanning. + */ + *p = e; + + t->type = T_STRING; + + return t->val; +} + +/* + * Populate a keyword token with a type and value. + */ +static void get_keyword(struct token *t) +{ + int i; + + for (i = 0; keywords[i].val; i++) { + if (!strcmp(t->val, keywords[i].val)) { + t->type = keywords[i].type; + break; + } + } +} + +/* + * Get the next token. We have to keep track of which state we're in to know + * if we're looking to get a string literal or a keyword. + * + * *p is updated to point at the first character after the current token. + */ +static void get_token(char **p, struct token *t, enum lex_state state) +{ + char *c = *p; + + t->type = T_INVALID; + + /* eat non EOL whitespace */ + while (isblank(*c)) + c++; + + /* + * eat comments. note that string literals can't begin with #, but + * can contain a # after their first character. + */ + if (*c == '#') { + while (*c && *c != '\n') + c++; + } + + if (*c == '\n') { + t->type = T_EOL; + c++; + } else if (*c == '\0') { + t->type = T_EOF; + c++; + } else if (state == L_SLITERAL) { + get_string(&c, t, '\n', 0); + } else if (state == L_KEYWORD) { + /* + * when we expect a keyword, we first get the next string + * token delimited by whitespace, and then check if it + * matches a keyword in our keyword list. if it does, it's + * converted to a keyword token of the appropriate type, and + * if not, it remains a string token. + */ + get_string(&c, t, ' ', 1); + get_keyword(t); + } + + *p = c; +} + +/* + * Increment *c until we get to the end of the current line, or EOF. + */ +static void eol_or_eof(char **c) +{ + while (**c && **c != '\n') + (*c)++; +} + +/* + * All of these parse_* functions share some common behavior. + * + * They finish with *c pointing after the token they parse, and return 1 on + * success, or < 0 on error. + */ + +/* + * Parse a string literal and store a pointer it at *dst. String literals + * terminate at the end of the line. + */ +static int parse_sliteral(char **c, char **dst) +{ + struct token t; + char *s = *c; + + get_token(c, &t, L_SLITERAL); + + if (t.type != T_STRING) { + printf("Expected string literal: %.*s\n", (int)(*c - s), s); + return -EINVAL; + } + + *dst = t.val; + + return 1; +} + +/* + * Parse a base 10 (unsigned) integer and store it at *dst. + */ +static int parse_integer(char **c, int *dst) +{ + struct token t; + char *s = *c; + + get_token(c, &t, L_SLITERAL); + + if (t.type != T_STRING) { + printf("Expected string: %.*s\n", (int)(*c - s), s); + return -EINVAL; + } + + *dst = simple_strtol(t.val, NULL, 10); + + free(t.val); + + return 1; +} + +static int parse_pxefile_top(struct cmd_tbl *cmdtp, char *p, unsigned long base, + struct pxe_menu *cfg, int nest_level); + +/* + * Parse an include statement, and retrieve and parse the file it mentions. + * + * base should point to a location where it's safe to store the file, and + * nest_level should indicate how many nested includes have occurred. For this + * include, nest_level has already been incremented and doesn't need to be + * incremented here. + */ +static int handle_include(struct cmd_tbl *cmdtp, char **c, unsigned long base, + struct pxe_menu *cfg, int nest_level) +{ + char *include_path; + char *s = *c; + int err; + char *buf; + int ret; + + err = parse_sliteral(c, &include_path); + + if (err < 0) { + printf("Expected include path: %.*s\n", (int)(*c - s), s); + return err; + } + + err = get_pxe_file(cmdtp, include_path, base); + + if (err < 0) { + printf("Couldn't retrieve %s\n", include_path); + return err; + } + + buf = map_sysmem(base, 0); + ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level); + unmap_sysmem(buf); + + return ret; +} + +/* + * Parse lines that begin with 'menu'. + * + * base and nest are provided to handle the 'menu include' case. + * + * base should point to a location where it's safe to store the included file. + * + * nest_level should be 1 when parsing the top level pxe file, 2 when parsing + * a file it includes, 3 when parsing a file included by that file, and so on. + */ +static int parse_menu(struct cmd_tbl *cmdtp, char **c, struct pxe_menu *cfg, + unsigned long base, int nest_level) +{ + struct token t; + char *s = *c; + int err = 0; + + get_token(c, &t, L_KEYWORD); + + switch (t.type) { + case T_TITLE: + err = parse_sliteral(c, &cfg->title); + + break; + + case T_INCLUDE: + err = handle_include(cmdtp, c, base, cfg, nest_level + 1); + break; + + case T_BACKGROUND: + err = parse_sliteral(c, &cfg->bmp); + break; + + default: + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); + } + + if (err < 0) + return err; + + eol_or_eof(c); + + return 1; +} + +/* + * Handles parsing a 'menu line' when we're parsing a label. + */ +static int parse_label_menu(char **c, struct pxe_menu *cfg, + struct pxe_label *label) +{ + struct token t; + char *s; + + s = *c; + + get_token(c, &t, L_KEYWORD); + + switch (t.type) { + case T_DEFAULT: + if (!cfg->default_label) + cfg->default_label = strdup(label->name); + + if (!cfg->default_label) + return -ENOMEM; + + break; + case T_LABEL: + parse_sliteral(c, &label->menu); + break; + default: + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); + } + + eol_or_eof(c); + + return 0; +} + +/* + * Handles parsing a 'kernel' label. + * expecting "filename" or "<fit_filename>#cfg" + */ +static int parse_label_kernel(char **c, struct pxe_label *label) +{ + char *s; + int err; + + err = parse_sliteral(c, &label->kernel); + if (err < 0) + return err; + + s = strstr(label->kernel, "#"); + if (!s) + return 1; + + label->config = malloc(strlen(s) + 1); + if (!label->config) + return -ENOMEM; + + strcpy(label->config, s); + *s = 0; + + return 1; +} + +/* + * Parses a label and adds it to the list of labels for a menu. + * + * A label ends when we either get to the end of a file, or + * get some input we otherwise don't have a handler defined + * for. + * + */ +static int parse_label(char **c, struct pxe_menu *cfg) +{ + struct token t; + int len; + char *s = *c; + struct pxe_label *label; + int err; + + label = label_create(); + if (!label) + return -ENOMEM; + + err = parse_sliteral(c, &label->name); + if (err < 0) { + printf("Expected label name: %.*s\n", (int)(*c - s), s); + label_destroy(label); + return -EINVAL; + } + + list_add_tail(&label->list, &cfg->labels); + + while (1) { + s = *c; + get_token(c, &t, L_KEYWORD); + + err = 0; + switch (t.type) { + case T_MENU: + err = parse_label_menu(c, cfg, label); + break; + + case T_KERNEL: + case T_LINUX: + err = parse_label_kernel(c, label); + break; + + case T_APPEND: + err = parse_sliteral(c, &label->append); + if (label->initrd) + break; + s = strstr(label->append, "initrd="); + if (!s) + break; + s += 7; + len = (int)(strchr(s, ' ') - s); + label->initrd = malloc(len + 1); + strncpy(label->initrd, s, len); + label->initrd[len] = '\0'; + + break; + + case T_INITRD: + if (!label->initrd) + err = parse_sliteral(c, &label->initrd); + break; + + case T_FDT: + if (!label->fdt) + err = parse_sliteral(c, &label->fdt); + break; + + case T_FDTDIR: + if (!label->fdtdir) + err = parse_sliteral(c, &label->fdtdir); + break; + + case T_FDTOVERLAYS: + if (!label->fdtoverlays) + err = parse_sliteral(c, &label->fdtoverlays); + break; + + case T_LOCALBOOT: + label->localboot = 1; + err = parse_integer(c, &label->localboot_val); + break; + + case T_IPAPPEND: + err = parse_integer(c, &label->ipappend); + break; + + case T_EOL: + break; + + default: + /* + * put the token back! we don't want it - it's the end + * of a label and whatever token this is, it's + * something for the menu level context to handle. + */ + *c = s; + return 1; + } + + if (err < 0) + return err; + } +} + +/* + * This 16 comes from the limit pxelinux imposes on nested includes. + * + * There is no reason at all we couldn't do more, but some limit helps prevent + * infinite (until crash occurs) recursion if a file tries to include itself. + */ +#define MAX_NEST_LEVEL 16 + +/* + * Entry point for parsing a menu file. nest_level indicates how many times + * we've nested in includes. It will be 1 for the top level menu file. + * + * Returns 1 on success, < 0 on error. + */ +static int parse_pxefile_top(struct cmd_tbl *cmdtp, char *p, unsigned long base, + struct pxe_menu *cfg, int nest_level) +{ + struct token t; + char *s, *b, *label_name; + int err; + + b = p; + + if (nest_level > MAX_NEST_LEVEL) { + printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL); + return -EMLINK; + } + + while (1) { + s = p; + + get_token(&p, &t, L_KEYWORD); + + err = 0; + switch (t.type) { + case T_MENU: + cfg->prompt = 1; + err = parse_menu(cmdtp, &p, cfg, + base + ALIGN(strlen(b) + 1, 4), + nest_level); + break; + + case T_TIMEOUT: + err = parse_integer(&p, &cfg->timeout); + break; + + case T_LABEL: + err = parse_label(&p, cfg); + break; + + case T_DEFAULT: + case T_ONTIMEOUT: + err = parse_sliteral(&p, &label_name); + + if (label_name) { + if (cfg->default_label) + free(cfg->default_label); + + cfg->default_label = label_name; + } + + break; + + case T_INCLUDE: + err = handle_include(cmdtp, &p, + base + ALIGN(strlen(b), 4), cfg, + nest_level + 1); + break; + + case T_PROMPT: + eol_or_eof(&p); + break; + + case T_EOL: + break; + + case T_EOF: + return 1; + + default: + printf("Ignoring unknown command: %.*s\n", + (int)(p - s), s); + eol_or_eof(&p); + } + + if (err < 0) + return err; + } +} + +/* + * Free the memory used by a pxe_menu and its labels. + */ +void destroy_pxe_menu(struct pxe_menu *cfg) +{ + struct list_head *pos, *n; + struct pxe_label *label; + + if (cfg->title) + free(cfg->title); + + if (cfg->default_label) + free(cfg->default_label); + + list_for_each_safe(pos, n, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + label_destroy(label); + } + + free(cfg); +} + +/* + * Entry point for parsing a pxe file. This is only used for the top level + * file. + * + * Returns NULL if there is an error, otherwise, returns a pointer to a + * pxe_menu struct populated with the results of parsing the pxe file (and any + * files it includes). The resulting pxe_menu struct can be free()'d by using + * the destroy_pxe_menu() function. + */ +struct pxe_menu *parse_pxefile(struct cmd_tbl *cmdtp, unsigned long menucfg) +{ + struct pxe_menu *cfg; + char *buf; + int r; + + cfg = malloc(sizeof(struct pxe_menu)); + + if (!cfg) + return NULL; + + memset(cfg, 0, sizeof(struct pxe_menu)); + + INIT_LIST_HEAD(&cfg->labels); + + buf = map_sysmem(menucfg, 0); + r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1); + unmap_sysmem(buf); + + if (r < 0) { + destroy_pxe_menu(cfg); + return NULL; + } + + return cfg; +} + +/* + * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic + * menu code. + */ +static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) +{ + struct pxe_label *label; + struct list_head *pos; + struct menu *m; + int err; + int i = 1; + char *default_num = NULL; + + /* + * Create a menu and add items for all the labels. + */ + m = menu_create(cfg->title, DIV_ROUND_UP(cfg->timeout, 10), + cfg->prompt, NULL, label_print, NULL, NULL); + + if (!m) + return NULL; + + list_for_each(pos, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + sprintf(label->num, "%d", i++); + if (menu_item_add(m, label->num, label) != 1) { + menu_destroy(m); + return NULL; + } + if (cfg->default_label && + (strcmp(label->name, cfg->default_label) == 0)) + default_num = label->num; + } + + /* + * After we've created items for each label in the menu, set the + * menu's default label if one was specified. + */ + if (default_num) { + err = menu_default_set(m, default_num); + if (err != 1) { + if (err != -ENOENT) { + menu_destroy(m); + return NULL; + } + + printf("Missing default: %s\n", cfg->default_label); + } + } + + return m; +} + +/* + * Try to boot any labels we have yet to attempt to boot. + */ +static void boot_unattempted_labels(struct cmd_tbl *cmdtp, struct pxe_menu *cfg) +{ + struct list_head *pos; + struct pxe_label *label; + + list_for_each(pos, &cfg->labels) { + label = list_entry(pos, struct pxe_label, list); + + if (!label->attempted) + label_boot(cmdtp, label); + } +} + +/* + * Boot the system as prescribed by a pxe_menu. + * + * Use the menu system to either get the user's choice or the default, based + * on config or user input. If there is no default or user's choice, + * attempted to boot labels in the order they were given in pxe files. + * If the default or user's choice fails to boot, attempt to boot other + * labels in the order they were given in pxe files. + * + * If this function returns, there weren't any labels that successfully + * booted, or the user interrupted the menu selection via ctrl+c. + */ +void handle_pxe_menu(struct cmd_tbl *cmdtp, struct pxe_menu *cfg) +{ + void *choice; + struct menu *m; + int err; + + if (IS_ENABLED(CONFIG_CMD_BMP)) { + /* display BMP if available */ + if (cfg->bmp) { + if (get_relfile(cmdtp, cfg->bmp, image_load_addr)) { + if (CONFIG_IS_ENABLED(CMD_CLS)) + run_command("cls", 0); + bmp_display(image_load_addr, + BMP_ALIGN_CENTER, BMP_ALIGN_CENTER); + } else { + printf("Skipping background bmp %s for failure\n", + cfg->bmp); + } + } + } + + m = pxe_menu_to_menu(cfg); + if (!m) + return; + + err = menu_get_choice(m, &choice); + + menu_destroy(m); + + /* + * err == 1 means we got a choice back from menu_get_choice. + * + * err == -ENOENT if the menu was setup to select the default but no + * default was set. in that case, we should continue trying to boot + * labels that haven't been attempted yet. + * + * otherwise, the user interrupted or there was some other error and + * we give up. + */ + + if (err == 1) { + err = label_boot(cmdtp, choice); + if (!err) + return; + } else if (err != -ENOENT) { + return; + } + + boot_unattempted_labels(cmdtp, cfg); +} diff --git a/roms/u-boot/cmd/pxe_utils.h b/roms/u-boot/cmd/pxe_utils.h new file mode 100644 index 000000000..bf58e1534 --- /dev/null +++ b/roms/u-boot/cmd/pxe_utils.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __PXE_UTILS_H +#define __PXE_UTILS_H + +#include <linux/list.h> + +/* + * A note on the pxe file parser. + * + * We're parsing files that use syslinux grammar, which has a few quirks. + * String literals must be recognized based on context - there is no + * quoting or escaping support. There's also nothing to explicitly indicate + * when a label section completes. We deal with that by ending a label + * section whenever we see a line that doesn't include. + * + * As with the syslinux family, this same file format could be reused in the + * future for non pxe purposes. The only action it takes during parsing that + * would throw this off is handling of include files. It assumes we're using + * pxe, and does a tftp download of a file listed as an include file in the + * middle of the parsing operation. That could be handled by refactoring it to + * take a 'include file getter' function. + */ + +/* + * Describes a single label given in a pxe file. + * + * Create these with the 'label_create' function given below. + * + * name - the name of the menu as given on the 'menu label' line. + * kernel - the path to the kernel file to use for this label. + * append - kernel command line to use when booting this label + * initrd - path to the initrd to use for this label. + * attempted - 0 if we haven't tried to boot this label, 1 if we have. + * localboot - 1 if this label specified 'localboot', 0 otherwise. + * list - lets these form a list, which a pxe_menu struct will hold. + */ +struct pxe_label { + char num[4]; + char *name; + char *menu; + char *kernel; + char *config; + char *append; + char *initrd; + char *fdt; + char *fdtdir; + char *fdtoverlays; + int ipappend; + int attempted; + int localboot; + int localboot_val; + struct list_head list; +}; + +/* + * Describes a pxe menu as given via pxe files. + * + * title - the name of the menu as given by a 'menu title' line. + * default_label - the name of the default label, if any. + * bmp - the bmp file name which is displayed in background + * timeout - time in tenths of a second to wait for a user key-press before + * booting the default label. + * prompt - if 0, don't prompt for a choice unless the timeout period is + * interrupted. If 1, always prompt for a choice regardless of + * timeout. + * labels - a list of labels defined for the menu. + */ +struct pxe_menu { + char *title; + char *default_label; + char *bmp; + int timeout; + int prompt; + struct list_head labels; +}; + +extern bool is_pxe; + +extern int (*do_getfile)(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr); +void destroy_pxe_menu(struct pxe_menu *cfg); +int get_pxe_file(struct cmd_tbl *cmdtp, const char *file_path, + unsigned long file_addr); +int get_pxelinux_path(struct cmd_tbl *cmdtp, const char *file, + unsigned long pxefile_addr_r); +void handle_pxe_menu(struct cmd_tbl *cmdtp, struct pxe_menu *cfg); +struct pxe_menu *parse_pxefile(struct cmd_tbl *cmdtp, unsigned long menucfg); +int format_mac_pxe(char *outbuf, size_t outbuf_len); + +#endif /* __PXE_UTILS_H */ diff --git a/roms/u-boot/cmd/qfw.c b/roms/u-boot/cmd/qfw.c new file mode 100644 index 000000000..e6a9fdb2a --- /dev/null +++ b/roms/u-boot/cmd/qfw.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <errno.h> +#include <qfw.h> +#include <dm.h> + +static struct udevice *qfw_dev; + +/* + * This function prepares kernel for zboot. It loads kernel data + * to 'load_addr', initrd to 'initrd_addr' and kernel command + * line using qemu fw_cfg interface. + */ +static int qemu_fwcfg_cmd_setup_kernel(void *load_addr, void *initrd_addr) +{ + char *data_addr; + uint32_t setup_size, kernel_size, cmdline_size, initrd_size; + + qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size); + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size); + + if (setup_size == 0 || kernel_size == 0) { + printf("warning: no kernel available\n"); + return -1; + } + + data_addr = load_addr; + qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, + le32_to_cpu(setup_size), data_addr); + data_addr += le32_to_cpu(setup_size); + + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, + le32_to_cpu(kernel_size), data_addr); + data_addr += le32_to_cpu(kernel_size); + + data_addr = initrd_addr; + qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size); + if (initrd_size == 0) { + printf("warning: no initrd available\n"); + } else { + qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, + le32_to_cpu(initrd_size), data_addr); + data_addr += le32_to_cpu(initrd_size); + } + + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); + if (cmdline_size) { + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, + le32_to_cpu(cmdline_size), data_addr); + /* + * if kernel cmdline only contains '\0', (e.g. no -append + * when invoking qemu), do not update bootargs + */ + if (*data_addr != '\0') { + if (env_set("bootargs", data_addr) < 0) + printf("warning: unable to change bootargs\n"); + } + } + + printf("loading kernel to address %p size %x", load_addr, + le32_to_cpu(kernel_size)); + if (initrd_size) + printf(" initrd %p size %x\n", + initrd_addr, + le32_to_cpu(initrd_size)); + else + printf("\n"); + + return 0; +} + +static int qemu_fwcfg_cmd_list_firmware(void) +{ + int ret; + struct fw_cfg_file_iter iter; + struct fw_file *file; + + /* make sure fw_list is loaded */ + ret = qfw_read_firmware_list(qfw_dev); + if (ret) + return ret; + + for (file = qfw_file_iter_init(qfw_dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { + printf("%-56s\n", file->cfg.name); + } + + return 0; +} + +static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + if (qemu_fwcfg_cmd_list_firmware() < 0) + return CMD_RET_FAILURE; + + return 0; +} + +static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + printf("%d cpu(s) online\n", qfw_online_cpus(qfw_dev)); + return 0; +} + +static int qemu_fwcfg_do_load(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + char *env; + void *load_addr; + void *initrd_addr; + + env = env_get("loadaddr"); + load_addr = env ? + (void *)simple_strtoul(env, NULL, 16) : +#ifdef CONFIG_LOADADDR + (void *)CONFIG_LOADADDR; +#else + NULL; +#endif + + env = env_get("ramdiskaddr"); + initrd_addr = env ? + (void *)simple_strtoul(env, NULL, 16) : +#ifdef CONFIG_RAMDISK_ADDR + (void *)CONFIG_RAMDISK_ADDR; +#else + NULL; +#endif + + if (argc == 2) { + load_addr = (void *)simple_strtoul(argv[0], NULL, 16); + initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16); + } else if (argc == 1) { + load_addr = (void *)simple_strtoul(argv[0], NULL, 16); + } + + if (!load_addr || !initrd_addr) { + printf("missing load or initrd address\n"); + return CMD_RET_FAILURE; + } + + return qemu_fwcfg_cmd_setup_kernel(load_addr, initrd_addr); +} + +static struct cmd_tbl fwcfg_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""), + U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""), + U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""), +}; + +static int do_qemu_fw(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + struct cmd_tbl *fwcfg_cmd; + + ret = qfw_get_dev(&qfw_dev); + if (ret) { + printf("QEMU fw_cfg interface not found\n"); + return CMD_RET_USAGE; + } + + fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands, + ARRAY_SIZE(fwcfg_commands)); + argc -= 2; + argv += 2; + if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs) + return CMD_RET_USAGE; + + ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv); + + return cmd_process_error(fwcfg_cmd, ret); +} + +U_BOOT_CMD( + qfw, 4, 1, do_qemu_fw, + "QEMU firmware interface", + "<command>\n" + " - list : print firmware(s) currently loaded\n" + " - cpus : print online cpu number\n" + " - load <kernel addr> <initrd addr> : load kernel and initrd (if any), and setup for zboot\n" +) diff --git a/roms/u-boot/cmd/read.c b/roms/u-boot/cmd/read.c new file mode 100644 index 000000000..34f53f9a2 --- /dev/null +++ b/roms/u-boot/cmd/read.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 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 <command.h> +#include <part.h> + +int do_read(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *ep; + struct blk_desc *dev_desc = NULL; + int dev; + int part = 0; + struct disk_partition part_info; + ulong offset = 0u; + ulong limit = 0u; + void *addr; + uint blk; + uint cnt; + + if (argc != 6) { + cmd_usage(cmdtp); + return 1; + } + + dev = (int)simple_strtoul(argv[2], &ep, 16); + if (*ep) { + if (*ep != ':') { + printf("Invalid block device %s\n", argv[2]); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + dev_desc = blk_get_dev(argv[1], dev); + if (dev_desc == NULL) { + printf("Block device %s %d not supported\n", argv[1], dev); + return 1; + } + + addr = (void *)simple_strtoul(argv[3], NULL, 16); + blk = simple_strtoul(argv[4], NULL, 16); + cnt = simple_strtoul(argv[5], NULL, 16); + + if (part != 0) { + if (part_get_info(dev_desc, part, &part_info)) { + printf("Cannot find partition %d\n", part); + return 1; + } + offset = part_info.start; + limit = part_info.size; + } else { + /* Largest address not available in struct blk_desc. */ + limit = ~0; + } + + if (cnt + blk > limit) { + printf("Read out of range\n"); + return 1; + } + + if (blk_dread(dev_desc, offset + blk, cnt, addr) != cnt) { + printf("Error reading blocks\n"); + return 1; + } + + return 0; +} + +U_BOOT_CMD( + read, 6, 0, do_read, + "Load binary data from a partition", + "<interface> <dev[:part]> addr blk# cnt" +); diff --git a/roms/u-boot/cmd/reginfo.c b/roms/u-boot/cmd/reginfo.c new file mode 100644 index 000000000..c8a04b175 --- /dev/null +++ b/roms/u-boot/cmd/reginfo.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Subodh Nijsure, SkyStream Networks, snijsure@skystream.com + */ + +#include <common.h> +#include <command.h> +#include <asm/ppc.h> + +static int do_reginfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + print_reginfo(); + + return 0; +} + + /**************************************************/ + +U_BOOT_CMD( + reginfo, 2, 1, do_reginfo, + "print register information", + "" +); diff --git a/roms/u-boot/cmd/regulator.c b/roms/u-boot/cmd/regulator.c new file mode 100644 index 000000000..60a70036d --- /dev/null +++ b/roms/u-boot/cmd/regulator.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ +#include <common.h> +#include <command.h> +#include <errno.h> +#include <dm.h> +#include <dm/uclass-internal.h> +#include <power/regulator.h> + +#define LIMIT_DEVNAME 20 +#define LIMIT_OFNAME 32 +#define LIMIT_INFO 18 + +static struct udevice *currdev; + +static int failure(int ret) +{ + printf("Error: %d (%s)\n", ret, errno_str(ret)); + + return CMD_RET_FAILURE; +} + +static int do_dev(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct dm_regulator_uclass_plat *uc_pdata; + const char *name; + int ret = -ENXIO; + + switch (argc) { + case 2: + name = argv[1]; + ret = regulator_get_by_platname(name, &currdev); + if (ret) { + printf("Can't get the regulator: %s!\n", name); + return failure(ret); + } + case 1: + if (!currdev) { + printf("Regulator device is not set!\n\n"); + return CMD_RET_USAGE; + } + + uc_pdata = dev_get_uclass_plat(currdev); + if (!uc_pdata) { + printf("%s: no regulator platform data!\n", currdev->name); + return failure(ret); + } + + printf("dev: %s @ %s\n", uc_pdata->name, currdev->name); + } + + return CMD_RET_SUCCESS; +} + +static int curr_dev_and_plat(struct udevice **devp, + struct dm_regulator_uclass_plat **uc_pdata, + bool allow_type_fixed) +{ + *devp = NULL; + *uc_pdata = NULL; + + if (!currdev) { + printf("First, set the regulator device!\n"); + return CMD_RET_FAILURE; + } + + *devp = currdev; + + *uc_pdata = dev_get_uclass_plat(*devp); + if (!*uc_pdata) { + pr_err("Regulator: %s - missing platform data!\n", currdev->name); + return CMD_RET_FAILURE; + } + + if (!allow_type_fixed && (*uc_pdata)->type == REGULATOR_TYPE_FIXED) { + printf("Operation not allowed for fixed regulator!\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + int ret; + + printf("| %-*.*s| %-*.*s| %s\n", + LIMIT_DEVNAME, LIMIT_DEVNAME, "Device", + LIMIT_OFNAME, LIMIT_OFNAME, "regulator-name", + "Parent"); + + for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret) + continue; + + uc_pdata = dev_get_uclass_plat(dev); + printf("| %-*.*s| %-*.*s| %s\n", + LIMIT_DEVNAME, LIMIT_DEVNAME, dev->name, + LIMIT_OFNAME, LIMIT_OFNAME, uc_pdata->name, + dev->parent->name); + } + + return ret; +} + +static int constraint(const char *name, int val, const char *val_name) +{ + printf("%-*s", LIMIT_INFO, name); + if (val < 0) { + printf(" %s (err: %d)\n", errno_str(val), val); + return val; + } + + if (val_name) + printf(" %d (%s)\n", val, val_name); + else + printf(" %d\n", val); + + return 0; +} + +static const char *get_mode_name(struct dm_regulator_mode *mode, + int mode_count, + int mode_id) +{ + while (mode_count--) { + if (mode->id == mode_id) + return mode->name; + mode++; + } + + return NULL; +} + +static int do_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + struct dm_regulator_mode *modes; + const char *parent_uc; + int mode_count; + int ret; + int i; + + ret = curr_dev_and_plat(&dev, &uc_pdata, true); + if (ret) + return ret; + + parent_uc = dev_get_uclass_name(dev->parent); + + printf("%s\n%-*s %s\n%-*s %s\n%-*s %s\n%-*s %s\n%-*s\n", + "Regulator info:", + LIMIT_INFO, "* regulator-name:", uc_pdata->name, + LIMIT_INFO, "* device name:", dev->name, + LIMIT_INFO, "* parent name:", dev->parent->name, + LIMIT_INFO, "* parent uclass:", parent_uc, + LIMIT_INFO, "* constraints:"); + + constraint(" - min uV:", uc_pdata->min_uV, NULL); + constraint(" - max uV:", uc_pdata->max_uV, NULL); + constraint(" - min uA:", uc_pdata->min_uA, NULL); + constraint(" - max uA:", uc_pdata->max_uA, NULL); + constraint(" - always on:", uc_pdata->always_on, + uc_pdata->always_on ? "true" : "false"); + constraint(" - boot on:", uc_pdata->boot_on, + uc_pdata->boot_on ? "true" : "false"); + + mode_count = regulator_mode(dev, &modes); + constraint("* op modes:", mode_count, NULL); + + for (i = 0; i < mode_count; i++, modes++) + constraint(" - mode id:", modes->id, modes->name); + + return CMD_RET_SUCCESS; +} + +static void do_status_detail(struct udevice *dev, + struct dm_regulator_uclass_plat *uc_pdata) +{ + int current, value, mode; + const char *mode_name; + bool enabled; + + printf("Regulator %s status:\n", uc_pdata->name); + + enabled = regulator_get_enable(dev); + constraint(" * enable:", enabled, enabled ? "true" : "false"); + + value = regulator_get_value(dev); + constraint(" * value uV:", value, NULL); + + current = regulator_get_current(dev); + constraint(" * current uA:", current, NULL); + + mode = regulator_get_mode(dev); + mode_name = get_mode_name(uc_pdata->mode, uc_pdata->mode_count, mode); + constraint(" * mode id:", mode, mode_name); +} + +static void do_status_line(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *pdata; + int current, value, mode; + const char *mode_name; + bool enabled; + + pdata = dev_get_uclass_plat(dev); + enabled = regulator_get_enable(dev); + value = regulator_get_value(dev); + current = regulator_get_current(dev); + mode = regulator_get_mode(dev); + mode_name = get_mode_name(pdata->mode, pdata->mode_count, mode); + printf("%-20s %-10s ", pdata->name, enabled ? "enabled" : "disabled"); + if (value >= 0) + printf("%10d ", value); + else + printf("%10s ", "-"); + if (current >= 0) + printf("%10d ", current); + else + printf("%10s ", "-"); + if (mode >= 0) + printf("%-10s", mode_name); + else + printf("%-10s", "-"); + printf("\n"); +} + +static int do_status(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + int ret; + + if (currdev && (argc < 2 || strcmp(argv[1], "-a"))) { + ret = curr_dev_and_plat(&dev, &uc_pdata, true); + if (ret) + return CMD_RET_FAILURE; + do_status_detail(dev, uc_pdata); + return 0; + } + + /* Show all of them in a list, probing them as needed */ + printf("%-20s %-10s %10s %10s %-10s\n", "Name", "Enabled", "uV", "mA", + "Mode"); + for (ret = uclass_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_next_device(&dev)) + do_status_line(dev); + + return CMD_RET_SUCCESS; +} + +static int do_value(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + int value; + int force; + int ret; + + ret = curr_dev_and_plat(&dev, &uc_pdata, argc == 1); + if (ret) + return ret; + + if (argc == 1) { + ret = regulator_get_value(dev); + if (ret < 0) { + printf("Regulator: %s - can't get the Voltage!\n", + uc_pdata->name); + return failure(ret); + } + + printf("%d uV\n", ret); + return CMD_RET_SUCCESS; + } + + if (argc == 3) + force = !strcmp("-f", argv[2]); + else + force = 0; + + value = simple_strtoul(argv[1], NULL, 0); + if ((value < uc_pdata->min_uV || value > uc_pdata->max_uV) && !force) { + printf("Value exceeds regulator constraint limits %d..%d uV\n", + uc_pdata->min_uV, uc_pdata->max_uV); + return CMD_RET_FAILURE; + } + + if (!force) + ret = regulator_set_value(dev, value); + else + ret = regulator_set_value_force(dev, value); + if (ret) { + printf("Regulator: %s - can't set the Voltage!\n", + uc_pdata->name); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static int do_current(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + int current; + int ret; + + ret = curr_dev_and_plat(&dev, &uc_pdata, argc == 1); + if (ret) + return ret; + + if (argc == 1) { + ret = regulator_get_current(dev); + if (ret < 0) { + printf("Regulator: %s - can't get the Current!\n", + uc_pdata->name); + return failure(ret); + } + + printf("%d uA\n", ret); + return CMD_RET_SUCCESS; + } + + current = simple_strtoul(argv[1], NULL, 0); + if (current < uc_pdata->min_uA || current > uc_pdata->max_uA) { + printf("Current exceeds regulator constraint limits\n"); + return CMD_RET_FAILURE; + } + + ret = regulator_set_current(dev, current); + if (ret) { + printf("Regulator: %s - can't set the Current!\n", + uc_pdata->name); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static int do_mode(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + int mode; + int ret; + + ret = curr_dev_and_plat(&dev, &uc_pdata, false); + if (ret) + return ret; + + if (argc == 1) { + ret = regulator_get_mode(dev); + if (ret < 0) { + printf("Regulator: %s - can't get the operation mode!\n", + uc_pdata->name); + return failure(ret); + } + + printf("mode id: %d\n", ret); + return CMD_RET_SUCCESS; + } + + mode = simple_strtoul(argv[1], NULL, 0); + + ret = regulator_set_mode(dev, mode); + if (ret) { + printf("Regulator: %s - can't set the operation mode!\n", + uc_pdata->name); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static int do_enable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + int ret; + + ret = curr_dev_and_plat(&dev, &uc_pdata, true); + if (ret) + return ret; + + ret = regulator_set_enable(dev, true); + if (ret) { + printf("Regulator: %s - can't enable!\n", uc_pdata->name); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static int do_disable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct dm_regulator_uclass_plat *uc_pdata; + int ret; + + ret = curr_dev_and_plat(&dev, &uc_pdata, true); + if (ret) + return ret; + + ret = regulator_set_enable(dev, false); + if (ret) { + printf("Regulator: %s - can't disable!\n", uc_pdata->name); + return failure(ret); + } + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl subcmd[] = { + U_BOOT_CMD_MKENT(dev, 2, 1, do_dev, "", ""), + U_BOOT_CMD_MKENT(list, 1, 1, do_list, "", ""), + U_BOOT_CMD_MKENT(info, 2, 1, do_info, "", ""), + U_BOOT_CMD_MKENT(status, 2, 1, do_status, "", ""), + U_BOOT_CMD_MKENT(value, 3, 1, do_value, "", ""), + U_BOOT_CMD_MKENT(current, 3, 1, do_current, "", ""), + U_BOOT_CMD_MKENT(mode, 2, 1, do_mode, "", ""), + U_BOOT_CMD_MKENT(enable, 1, 1, do_enable, "", ""), + U_BOOT_CMD_MKENT(disable, 1, 1, do_disable, "", ""), +}; + +static int do_regulator(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cmd; + + argc--; + argv++; + + cmd = find_cmd_tbl(argv[0], subcmd, ARRAY_SIZE(subcmd)); + if (cmd == NULL || argc > cmd->maxargs) + return CMD_RET_USAGE; + + return cmd->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD(regulator, CONFIG_SYS_MAXARGS, 1, do_regulator, + "uclass operations", + "list - list UCLASS regulator devices\n" + "regulator dev [regulator-name] - show/[set] operating regulator device\n" + "regulator info - print constraints info\n" + "regulator status [-a] - print operating status [for all]\n" + "regulator value [val] [-f] - print/[set] voltage value [uV] (force)\n" + "regulator current [val] - print/[set] current value [uA]\n" + "regulator mode [id] - print/[set] operating mode id\n" + "regulator enable - enable the regulator output\n" + "regulator disable - disable the regulator output\n" +); diff --git a/roms/u-boot/cmd/reiser.c b/roms/u-boot/cmd/reiser.c new file mode 100644 index 000000000..3db926f80 --- /dev/null +++ b/roms/u-boot/cmd/reiser.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2003 - 2004 + * Sysgo Real-Time Solutions, AG <www.elinos.com> + * Pavel Bartusek <pba@sysgo.com> + */ + +/* + * Reiserfs support + */ +#include <common.h> +#include <config.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <reiserfs.h> +#include <part.h> + +#if !CONFIG_IS_ENABLED(DOS_PARTITION) +#error DOS partition support must be selected +#endif + +/* #define REISER_DEBUG */ + +#ifdef REISER_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +int do_reiserls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename = "/"; + int dev, part; + struct blk_desc *dev_desc = NULL; + struct disk_partition info; + + if (argc < 3) + return CMD_RET_USAGE; + + part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + if (argc == 4) { + filename = argv[3]; + } + + dev = dev_desc->devnum; + PRINTF("Using device %s %d:%d, directory: %s\n", argv[1], dev, part, filename); + + reiserfs_set_blk_dev(dev_desc, &info); + + if (!reiserfs_mount(info.size)) { + printf ("** Bad Reiserfs partition or disk - %s %d:%d **\n", argv[1], dev, part); + return 1; + } + + if (reiserfs_ls (filename)) { + printf ("** Error reiserfs_ls() **\n"); + return 1; + }; + + return 0; +} + +U_BOOT_CMD( + reiserls, 4, 1, do_reiserls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a 'directory'" +); + +/****************************************************************************** + * Reiserfs boot command intepreter. Derived from diskboot + */ +int do_reiserload(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename = NULL; + int dev, part; + ulong addr = 0, filelen; + struct disk_partition info; + struct blk_desc *dev_desc = NULL; + unsigned long count; + char *addr_str; + + switch (argc) { + case 3: + addr_str = env_get("loadaddr"); + if (addr_str != NULL) { + addr = simple_strtoul (addr_str, NULL, 16); + } else { + addr = CONFIG_SYS_LOAD_ADDR; + } + filename = env_get("bootfile"); + count = 0; + break; + case 4: + addr = simple_strtoul (argv[3], NULL, 16); + filename = env_get("bootfile"); + count = 0; + break; + case 5: + addr = simple_strtoul (argv[3], NULL, 16); + filename = argv[4]; + count = 0; + break; + case 6: + addr = simple_strtoul (argv[3], NULL, 16); + filename = argv[4]; + count = simple_strtoul (argv[5], NULL, 16); + break; + + default: + return CMD_RET_USAGE; + } + + if (!filename) { + puts ("\n** No boot file defined **\n"); + return 1; + } + + part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->devnum; + + printf("Loading file \"%s\" from %s device %d%c%c\n", + filename, argv[1], dev, + part ? ':' : ' ', part ? part + '0' : ' '); + + reiserfs_set_blk_dev(dev_desc, &info); + + if (!reiserfs_mount(info.size)) { + printf ("** Bad Reiserfs partition or disk - %s %d:%d **\n", argv[1], dev, part); + return 1; + } + + filelen = reiserfs_open(filename); + if (filelen < 0) { + printf("** File not found %s **\n", filename); + return 1; + } + if ((count < filelen) && (count != 0)) { + filelen = count; + } + + if (reiserfs_read((char *)addr, filelen) != filelen) { + printf("\n** Unable to read \"%s\" from %s %d:%d **\n", filename, argv[1], dev, part); + return 1; + } + + /* Loading ok, update default load address */ + image_load_addr = addr; + + printf ("\n%ld bytes read\n", filelen); + env_set_hex("filesize", filelen); + + return filelen; +} + +U_BOOT_CMD( + reiserload, 6, 0, do_reiserload, + "load binary file from a Reiser filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem" +); diff --git a/roms/u-boot/cmd/remoteproc.c b/roms/u-boot/cmd/remoteproc.c new file mode 100644 index 000000000..b3ddcebe3 --- /dev/null +++ b/roms/u-boot/cmd/remoteproc.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <remoteproc.h> + +/** + * print_remoteproc_list() - print all the remote processor devices + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int print_remoteproc_list(void) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + char *type; + + ret = uclass_get(UCLASS_REMOTEPROC, &uc); + if (ret) { + printf("Cannot find Remote processor class\n"); + return ret; + } + + uclass_foreach_dev(dev, uc) { + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops = rproc_get_ops(dev); + + uc_pdata = dev_get_uclass_plat(dev); + + /* Do not print if rproc is not probed */ + if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)) + continue; + + switch (uc_pdata->mem_type) { + case RPROC_INTERNAL_MEMORY_MAPPED: + type = "internal memory mapped"; + break; + default: + type = "unknown"; + break; + } + printf("%d - Name:'%s' type:'%s' supports: %s%s%s%s%s%s\n", + dev_seq(dev), + uc_pdata->name, + type, + ops->load ? "load " : "", + ops->start ? "start " : "", + ops->stop ? "stop " : "", + ops->reset ? "reset " : "", + ops->is_running ? "is_running " : "", + ops->ping ? "ping " : ""); + } + return 0; +} + +/** + * do_rproc_init() - do basic initialization + * @cmdtp: unused + * @flag: unused + * @argc: unused + * @argv: unused + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int do_rproc_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int id; + + if (rproc_is_initialized()) { + printf("\tRemote Processors are already initialized\n"); + return CMD_RET_FAILURE; + } + + if (argc == 1) { + if (!rproc_init()) + return 0; + printf("Few Remote Processors failed to be initialized\n"); + } else if (argc == 2) { + id = (int)simple_strtoul(argv[1], NULL, 10); + if (!rproc_dev_init(id)) + return 0; + printf("Remote Processor %d failed to be initialized\n", id); + } + + return CMD_RET_FAILURE; +} + +/** + * do_remoteproc_list() - print list of remote proc devices. + * @cmdtp: unused + * @flag: unused + * @argc: unused + * @argv: unused + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int do_remoteproc_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (print_remoteproc_list()) + return CMD_RET_FAILURE; + + return 0; +} + +/** + * do_remoteproc_load() - Load a remote processor with binary image + * @cmdtp: unused + * @flag: unused + * @argc: argument count for the load function + * @argv: arguments for the load function + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int do_remoteproc_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr, size; + int id, ret; + + if (argc != 4) + return CMD_RET_USAGE; + + id = (int)simple_strtoul(argv[1], NULL, 10); + addr = simple_strtoul(argv[2], NULL, 16); + + size = simple_strtoul(argv[3], NULL, 16); + + if (!size) { + printf("\t Expect some size??\n"); + return CMD_RET_USAGE; + } + + ret = rproc_load(id, addr, size); + printf("Load Remote Processor %d with data@addr=0x%08lx %lu bytes:%s\n", + id, addr, size, ret ? " Failed!" : " Success!"); + + return ret ? CMD_RET_FAILURE : 0; +} + +/** + * do_remoteproc_wrapper() - wrapper for various rproc commands + * @cmdtp: unused + * @flag: unused + * @argc: argument count for the rproc command + * @argv: arguments for the rproc command + * + * Most of the commands just take id as a parameter andinvoke various + * helper routines in remote processor core. by using a set of + * common checks, we can reduce the amount of code used for this. + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int do_remoteproc_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int id, ret = CMD_RET_USAGE; + + if (argc != 2) + return CMD_RET_USAGE; + + id = (int)simple_strtoul(argv[1], NULL, 10); + + if (!strcmp(argv[0], "start")) { + ret = rproc_start(id); + } else if (!strcmp(argv[0], "stop")) { + ret = rproc_stop(id); + } else if (!strcmp(argv[0], "reset")) { + ret = rproc_reset(id); + } else if (!strcmp(argv[0], "is_running")) { + ret = rproc_is_running(id); + if (!ret) { + printf("Remote processor is Running\n"); + } else if (ret == 1) { + printf("Remote processor is NOT Running\n"); + ret = 0; + } + /* Else error.. */ + } else if (!strcmp(argv[0], "ping")) { + ret = rproc_ping(id); + if (!ret) { + printf("Remote processor responds 'Pong'\n"); + } else if (ret == 1) { + printf("No response from Remote processor\n"); + ret = 0; + } + /* Else error.. */ + } + + if (ret < 0) + printf("Operation Failed with error (%d)\n", ret); + + return ret ? CMD_RET_FAILURE : 0; +} + +static struct cmd_tbl cmd_remoteproc_sub[] = { + U_BOOT_CMD_MKENT(init, 1, 1, do_rproc_init, + "Enumerate and initialize the remote processor(s)", + "id - ID of the remote processor\n" + "If id is not passed, initialize all the remote processors"), + U_BOOT_CMD_MKENT(list, 0, 1, do_remoteproc_list, + "list remote processors", ""), + U_BOOT_CMD_MKENT(load, 5, 1, do_remoteproc_load, + "Load remote processor with provided image", + "<id> [addr] [size]\n" + "- id: ID of the remote processor(see 'list' cmd)\n" + "- addr: Address in memory of the image to loadup\n" + "- size: Size of the image to loadup\n"), + U_BOOT_CMD_MKENT(start, 1, 1, do_remoteproc_wrapper, + "Start remote processor", + "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(stop, 1, 1, do_remoteproc_wrapper, + "Stop remote processor", + "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(reset, 1, 1, do_remoteproc_wrapper, + "Reset remote processor", + "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(is_running, 1, 1, do_remoteproc_wrapper, + "Check to see if remote processor is running\n", + "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(ping, 1, 1, do_remoteproc_wrapper, + "Ping to communicate with remote processor\n", + "id - ID of the remote processor (see 'list' cmd)\n"), +}; + +/** + * do_remoteproc() - (replace: short desc) + * @cmdtp: unused + * @flag: unused + * @argc: argument count + * @argv: argument list + * + * parses up the command table to invoke the correct command. + * + * Return: 0 if no error, else returns appropriate error value. + */ +static int do_remoteproc(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c = NULL; + + /* Strip off leading 'rproc' command argument */ + argc--; + argv++; + + if (argc) + c = find_cmd_tbl(argv[0], cmd_remoteproc_sub, + ARRAY_SIZE(cmd_remoteproc_sub)); + if (c) + return c->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(rproc, 5, 1, do_remoteproc, + "Control operation of remote processors in an SoC", + " [init|list|load|start|stop|reset|is_running|ping]\n" + "\t\t Where:\n" + "\t\t[addr] is a memory address\n" + "\t\t<id> is a numerical identifier for the remote processor\n" + "\t\t provided by 'list' command.\n" + "\t\tNote: Remote processors must be initalized prior to usage\n" + "\t\tNote: Services are dependent on the driver capability\n" + "\t\t 'list' command shows the capability of each device\n" + "\n\tSubcommands:\n" + "\tinit <id> - Enumerate and initalize the remote processor.\n" + "\t if id is not passed, initialize all the remote prcessors\n" + "\tlist - list available remote processors\n" + "\tload <id> [addr] [size]- Load the remote processor with binary\n" + "\t image stored at address [addr] in memory\n" + "\tstart <id> - Start the remote processor(must be loaded)\n" + "\tstop <id> - Stop the remote processor\n" + "\treset <id> - Reset the remote processor\n" + "\tis_running <id> - Reports if the remote processor is running\n" + "\tping <id> - Ping the remote processor for communication\n"); diff --git a/roms/u-boot/cmd/riscv/Makefile b/roms/u-boot/cmd/riscv/Makefile new file mode 100644 index 000000000..1e6ac364e --- /dev/null +++ b/roms/u-boot/cmd/riscv/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_CMD_EXCEPTION) += exception.o +obj-$(CONFIG_CMD_SBI) += sbi.o diff --git a/roms/u-boot/cmd/riscv/exception.c b/roms/u-boot/cmd/riscv/exception.c new file mode 100644 index 000000000..7a08061d1 --- /dev/null +++ b/roms/u-boot/cmd/riscv/exception.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_ebreak(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile ("ebreak\n"); + return CMD_RET_FAILURE; +} + +static int do_unaligned(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile ( + "auipc a1, 0\n" + "ori a1, a1, 3\n" + "lw a2, (0)(a1)\n" + ); + printf("The system supports unaligned access.\n"); + return CMD_RET_SUCCESS; +} + +static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile (".word 0xffffffff\n"); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_sub[] = { + U_BOOT_CMD_MKENT(ebreak, CONFIG_SYS_MAXARGS, 1, do_ebreak, + "", ""), + U_BOOT_CMD_MKENT(unaligned, CONFIG_SYS_MAXARGS, 1, do_unaligned, + "", ""), + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " ebreak - breakpoint\n" + " undefined - illegal instruction\n" + " unaligned - load address misaligned\n" + ; + +#include <exception.h> diff --git a/roms/u-boot/cmd/riscv/sbi.c b/roms/u-boot/cmd/riscv/sbi.c new file mode 100644 index 000000000..90c0811e1 --- /dev/null +++ b/roms/u-boot/cmd/riscv/sbi.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'sbi' command displays information about the SBI implementation. + * + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> +#include <asm/sbi.h> + +struct sbi_imp { + const long id; + const char *name; +}; + +struct sbi_ext { + const u32 id; + const char *name; +}; + +static struct sbi_imp implementations[] = { + { 0, "Berkeley Boot Loader (BBL)" }, + { 1, "OpenSBI" }, + { 2, "Xvisor" }, + { 3, "KVM" }, + { 4, "RustSBI" }, + { 5, "Diosix" }, +}; + +static struct sbi_ext extensions[] = { + { 0x00000000, "sbi_set_timer" }, + { 0x00000001, "sbi_console_putchar" }, + { 0x00000002, "sbi_console_getchar" }, + { 0x00000003, "sbi_clear_ipi" }, + { 0x00000004, "sbi_send_ipi" }, + { 0x00000005, "sbi_remote_fence_i" }, + { 0x00000006, "sbi_remote_sfence_vma" }, + { 0x00000007, "sbi_remote_sfence_vma_asid" }, + { 0x00000008, "sbi_shutdown" }, + { 0x00000010, "SBI Base Functionality" }, + { 0x54494D45, "Timer Extension" }, + { 0x00735049, "IPI Extension" }, + { 0x52464E43, "RFENCE Extension" }, + { 0x0048534D, "Hart State Management Extension" }, + { 0x53525354, "System Reset Extension" }, +}; + +static int do_sbi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + long ret; + + ret = sbi_get_spec_version(); + if (ret >= 0) + printf("SBI %ld.%ld\n", ret >> 24, ret & 0xffffff); + ret = sbi_get_impl_id(); + if (ret >= 0) { + for (i = 0; i < ARRAY_SIZE(implementations); ++i) { + if (ret == implementations[i].id) { + printf("%s\n", implementations[i].name); + break; + } + } + if (i == ARRAY_SIZE(implementations)) + printf("Unknown implementation ID %ld\n", ret); + } + printf("Extensions:\n"); + for (i = 0; i < ARRAY_SIZE(extensions); ++i) { + ret = sbi_probe_extension(extensions[i].id); + if (ret > 0) + printf(" %s\n", extensions[i].name); + } + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char sbi_help_text[] = + "- display SBI spec version, implementation, and available extensions"; + +#endif + +U_BOOT_CMD_COMPLETE( + sbi, 1, 0, do_sbi, + "display SBI information", + sbi_help_text, NULL +); diff --git a/roms/u-boot/cmd/rng.c b/roms/u-boot/cmd/rng.c new file mode 100644 index 000000000..9747c11be --- /dev/null +++ b/roms/u-boot/cmd/rng.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'rng' command prints bytes from the hardware random number generator. + * + * Copyright (c) 2019, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <hexdump.h> +#include <malloc.h> +#include <rng.h> + +static int do_rng(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + size_t n = 0x40; + struct udevice *dev; + void *buf; + int ret = CMD_RET_SUCCESS; + + if (uclass_get_device(UCLASS_RNG, 0, &dev) || !dev) { + printf("No RNG device\n"); + return CMD_RET_FAILURE; + } + + if (argc >= 2) + n = simple_strtoul(argv[1], NULL, 16); + + buf = malloc(n); + if (!buf) { + printf("Out of memory\n"); + return CMD_RET_FAILURE; + } + + if (dm_rng_read(dev, buf, n)) { + printf("Reading RNG failed\n"); + ret = CMD_RET_FAILURE; + } else { + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, n); + } + + free(buf); + + return ret; +} + +#ifdef CONFIG_SYS_LONGHELP +static char rng_help_text[] = + "[n]\n" + " - print n random bytes\n"; +#endif + +U_BOOT_CMD( + rng, 2, 0, do_rng, + "print bytes from the hardware random number generator", + rng_help_text +); diff --git a/roms/u-boot/cmd/rockusb.c b/roms/u-boot/cmd/rockusb.c new file mode 100644 index 000000000..f181ec611 --- /dev/null +++ b/roms/u-boot/cmd/rockusb.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Eddie Cai <eddie.cai.linux@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <g_dnl.h> +#include <usb.h> +#include <asm/arch-rockchip/f_rockusb.h> + +static int do_rockusb(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int controller_index, dev_index; + char *usb_controller; + char *devtype; + char *devnum; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + usb_controller = argv[1]; + controller_index = simple_strtoul(usb_controller, NULL, 0); + + if (argc >= 4) { + devtype = argv[2]; + devnum = argv[3]; + } else { + return CMD_RET_USAGE; + } + dev_index = simple_strtoul(devnum, NULL, 0); + rockusb_dev_init(devtype, dev_index); + + ret = usb_gadget_initialize(controller_index); + if (ret) { + printf("USB init failed: %d\n", ret); + return CMD_RET_FAILURE; + } + + g_dnl_clear_detach(); + ret = g_dnl_register("usb_dnl_rockusb"); + if (ret) + return CMD_RET_FAILURE; + + if (!g_dnl_board_usb_cable_connected()) { + puts("\rUSB cable not detected, Command exit.\n"); + ret = CMD_RET_FAILURE; + goto exit; + } + + while (1) { + if (g_dnl_detach()) + break; + if (ctrlc()) + break; + usb_gadget_handle_interrupts(controller_index); + } + ret = CMD_RET_SUCCESS; + +exit: + g_dnl_unregister(); + g_dnl_clear_detach(); + usb_gadget_release(controller_index); + + return ret; +} + +U_BOOT_CMD(rockusb, 4, 1, do_rockusb, + "use the rockusb protocol", + "<USB_controller> <devtype> <dev[:part]> e.g. rockusb 0 mmc 0\n" +); diff --git a/roms/u-boot/cmd/rtc.c b/roms/u-boot/cmd/rtc.c new file mode 100644 index 000000000..b4f61b2e8 --- /dev/null +++ b/roms/u-boot/cmd/rtc.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <hexdump.h> +#include <i2c.h> +#include <mapmem.h> +#include <rtc.h> + +#define MAX_RTC_BYTES 32 + +static int do_rtc_read(struct udevice *dev, int argc, char * const argv[]) +{ + u8 buf[MAX_RTC_BYTES]; + int reg, len, ret, r; + + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[0], NULL, 16); + len = simple_strtoul(argv[1], NULL, 16); + + if (argc == 3) { + u8 *addr; + + addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len); + ret = dm_rtc_read(dev, reg, addr, len); + unmap_sysmem(addr); + if (ret) { + printf("dm_rtc_read() failed: %d\n", ret); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; + } + + while (len) { + r = min_t(int, len, sizeof(buf)); + ret = dm_rtc_read(dev, reg, buf, r); + if (ret) { + printf("dm_rtc_read() failed: %d\n", ret); + return CMD_RET_FAILURE; + } + print_buffer(reg, buf, 1, r, 0); + len -= r; + reg += r; + } + + return CMD_RET_SUCCESS; +} + +static int do_rtc_write(struct udevice *dev, int argc, char * const argv[]) +{ + u8 buf[MAX_RTC_BYTES]; + int reg, len, ret; + const char *s; + int slen; + + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[0], NULL, 16); + + if (argc == 3) { + u8 *addr; + + len = simple_strtoul(argv[1], NULL, 16); + addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len); + ret = dm_rtc_write(dev, reg, addr, len); + unmap_sysmem(addr); + if (ret) { + printf("dm_rtc_write() failed: %d\n", ret); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; + } + + s = argv[1]; + slen = strlen(s); + + if (slen % 2) { + printf("invalid hex string\n"); + return CMD_RET_FAILURE; + } + + while (slen) { + len = min_t(int, slen / 2, sizeof(buf)); + if (hex2bin(buf, s, len)) { + printf("invalid hex string\n"); + return CMD_RET_FAILURE; + } + + ret = dm_rtc_write(dev, reg, buf, len); + if (ret) { + printf("dm_rtc_write() failed: %d\n", ret); + return CMD_RET_FAILURE; + } + s += 2 * len; + slen -= 2 * len; + } + + return CMD_RET_SUCCESS; +} + +int do_rtc(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + static int curr_rtc; + struct udevice *dev; + int ret, idx; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; + argv++; + + if (!strcmp(argv[0], "list")) { + struct uclass *uc; + + idx = 0; + uclass_id_foreach_dev(UCLASS_RTC, dev, uc) { + printf("RTC #%d - %s\n", idx++, dev->name); + } + if (!idx) { + printf("*** no RTC devices available ***\n"); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; + } + + idx = curr_rtc; + if (!strcmp(argv[0], "dev") && argc >= 2) + idx = simple_strtoul(argv[1], NULL, 10); + + ret = uclass_get_device(UCLASS_RTC, idx, &dev); + if (ret) { + printf("Cannot find RTC #%d: err=%d\n", idx, ret); + return CMD_RET_FAILURE; + } + + if (!strcmp(argv[0], "dev")) { + /* Show the existing or newly selected RTC */ + if (argc >= 2) + curr_rtc = idx; + printf("RTC #%d - %s\n", idx, dev->name); + return CMD_RET_SUCCESS; + } + + if (!strcmp(argv[0], "read")) + return do_rtc_read(dev, argc - 1, argv + 1); + + if (!strcmp(argv[0], "write")) + return do_rtc_write(dev, argc - 1, argv + 1); + + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + rtc, 5, 0, do_rtc, + "RTC subsystem", + "list - show available rtc devices\n" + "rtc dev [n] - show or set current rtc device\n" + "rtc read <reg> <count> - read and display 8-bit registers starting at <reg>\n" + "rtc read <reg> <count> <addr> - read 8-bit registers starting at <reg> to memory <addr>\n" + "rtc write <reg> <hexstring> - write 8-bit registers starting at <reg>\n" + "rtc write <reg> <count> <addr> - write from memory <addr> to 8-bit registers starting at <reg>\n" +); diff --git a/roms/u-boot/cmd/sandbox/Makefile b/roms/u-boot/cmd/sandbox/Makefile new file mode 100644 index 000000000..24df023ec --- /dev/null +++ b/roms/u-boot/cmd/sandbox/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_CMD_EXCEPTION) += exception.o diff --git a/roms/u-boot/cmd/sandbox/exception.c b/roms/u-boot/cmd/sandbox/exception.c new file mode 100644 index 000000000..1aa1d673a --- /dev/null +++ b/roms/u-boot/cmd/sandbox/exception.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_sigsegv(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u8 *ptr = NULL; + + *ptr = 0; + return CMD_RET_FAILURE; +} + +static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile (".word 0xffff\n"); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_sub[] = { + U_BOOT_CMD_MKENT(sigsegv, CONFIG_SYS_MAXARGS, 1, do_sigsegv, + "", ""), + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + " sigsegv - illegal memory access\n" + ; + +#include <exception.h> diff --git a/roms/u-boot/cmd/sata.c b/roms/u-boot/cmd/sata.c new file mode 100644 index 000000000..aa396c1bb --- /dev/null +++ b/roms/u-boot/cmd/sata.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2000-2005, DENX Software Engineering + * Wolfgang Denk <wd@denx.de> + * Copyright (C) Procsys. All rights reserved. + * Mushtaq Khan <mushtaq_k@procsys.com> + * <mushtaqk_921@yahoo.co.in> + * Copyright (C) 2008 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + */ + +#include <common.h> +#include <ahci.h> +#include <blk.h> +#include <dm.h> +#include <command.h> +#include <part.h> +#include <sata.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +static int sata_curr_device = -1; + +int sata_remove(int devnum) +{ +#ifdef CONFIG_AHCI + struct udevice *dev; + int rc; + + blk_unbind_all(IF_TYPE_SATA); + + rc = uclass_find_device(UCLASS_AHCI, devnum, &dev); + if (!rc && !dev) + rc = uclass_find_first_device(UCLASS_AHCI, &dev); + if (rc || !dev) { + printf("Cannot find SATA device %d (err=%d)\n", devnum, rc); + return CMD_RET_FAILURE; + } + + rc = device_remove(dev, DM_REMOVE_NORMAL); + if (rc) { + printf("Cannot remove SATA device '%s' (err=%d)\n", dev->name, + rc); + return CMD_RET_FAILURE; + } + + return 0; +#else + return sata_stop(); +#endif +} + +int sata_probe(int devnum) +{ +#ifdef CONFIG_AHCI + struct udevice *dev; + int rc; + + rc = uclass_get_device(UCLASS_AHCI, devnum, &dev); + if (rc) + rc = uclass_find_first_device(UCLASS_AHCI, &dev); + if (rc) { + printf("Cannot probe SATA device %d (err=%d)\n", devnum, rc); + return CMD_RET_FAILURE; + } + if (!dev) { + printf("No SATA device found!\n"); + return CMD_RET_FAILURE; + } + rc = sata_scan(dev); + if (rc) { + printf("Cannot scan SATA device %d (err=%d)\n", devnum, rc); + return CMD_RET_FAILURE; + } + + return 0; +#else + return sata_initialize() < 0 ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +#endif +} + +static int do_sata(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int rc = 0; + + if (argc >= 2) { + int devnum = 0; + + if (argc == 3) + devnum = (int)simple_strtoul(argv[2], NULL, 10); + if (!strcmp(argv[1], "stop")) + return sata_remove(devnum); + + if (!strcmp(argv[1], "init")) { + if (sata_curr_device != -1) { + rc = sata_remove(devnum); + if (rc) + return rc; + } + + return sata_probe(devnum); + } + } + + /* If the user has not yet run `sata init`, do it now */ + if (sata_curr_device == -1) { + rc = sata_probe(0); + if (rc) + return rc; + sata_curr_device = 0; + } + + return blk_common_cmd(argc, argv, IF_TYPE_SATA, &sata_curr_device); +} + +U_BOOT_CMD( + sata, 5, 1, do_sata, + "SATA sub system", + "init - init SATA sub system\n" + "sata stop [dev] - disable SATA sub system or device\n" + "sata info - show available SATA devices\n" + "sata device [dev] - show or set current device\n" + "sata part [dev] - print partition table\n" + "sata read addr blk# cnt\n" + "sata write addr blk# cnt" +); diff --git a/roms/u-boot/cmd/sb.c b/roms/u-boot/cmd/sb.c new file mode 100644 index 000000000..0d55818e3 --- /dev/null +++ b/roms/u-boot/cmd/sb.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018, Google Inc. + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/state.h> + +static int do_sb_handoff(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ +#if CONFIG_IS_ENABLED(HANDOFF) + if (gd->spl_handoff) + printf("SPL handoff magic %lx\n", gd->spl_handoff->arch.magic); + else + printf("SPL handoff info not received\n"); + + return 0; +#else + printf("Command not supported\n"); + + return CMD_RET_USAGE; +#endif +} + +static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct sandbox_state *state; + + state = state_get_current(); + state_show(state); + + return 0; +} + +static struct cmd_tbl cmd_sb_sub[] = { + U_BOOT_CMD_MKENT(handoff, 1, 0, do_sb_handoff, "", ""), + U_BOOT_CMD_MKENT(state, 1, 0, do_sb_state, "", ""), +}; + +static int do_sb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *c; + + /* Skip past 'sb' */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_sb_sub, ARRAY_SIZE(cmd_sb_sub)); + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + sb, 8, 1, do_sb, + "Sandbox status commands", + "handoff - Show handoff data received from SPL\n" + "sb state - Show sandbox state" +); diff --git a/roms/u-boot/cmd/scp03.c b/roms/u-boot/cmd/scp03.c new file mode 100644 index 000000000..655e0bba0 --- /dev/null +++ b/roms/u-boot/cmd/scp03.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021, Foundries.IO + * + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <scp03.h> + +int do_scp03_enable(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 1) + return CMD_RET_USAGE; + + if (tee_enable_scp03()) { + printf("TEE failed to enable SCP03\n"); + return CMD_RET_FAILURE; + } + + printf("SCP03 is enabled\n"); + + return CMD_RET_SUCCESS; +} + +int do_scp03_provision(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 1) + return CMD_RET_USAGE; + + if (tee_provision_scp03()) { + printf("TEE failed to provision SCP03 keys\n"); + return CMD_RET_FAILURE; + } + + printf("SCP03 is provisioned\n"); + + return CMD_RET_SUCCESS; +} + +static char text[] = + "provides a command to enable SCP03 and provision the SCP03 keys\n" + " enable - enable SCP03 on the TEE\n" + " provision - provision SCP03 on the TEE\n"; + +U_BOOT_CMD_WITH_SUBCMDS(scp03, "Secure Channel Protocol 03 control", text, + U_BOOT_SUBCMD_MKENT(enable, 1, 1, do_scp03_enable), + U_BOOT_SUBCMD_MKENT(provision, 1, 1, do_scp03_provision)); + diff --git a/roms/u-boot/cmd/scsi.c b/roms/u-boot/cmd/scsi.c new file mode 100644 index 000000000..5f710d289 --- /dev/null +++ b/roms/u-boot/cmd/scsi.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + */ + +/* + * SCSI support. + */ +#include <common.h> +#include <blk.h> +#include <command.h> +#include <scsi.h> + +static int scsi_curr_dev; /* current device */ + +/* + * scsi boot command intepreter. Derived from diskboot + */ +static int do_scsiboot(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return common_diskboot(cmdtp, "scsi", argc, argv); +} + +/* + * scsi command intepreter + */ +static int do_scsi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc == 2) { + if (strncmp(argv[1], "res", 3) == 0) { + printf("\nReset SCSI\n"); +#ifndef CONFIG_DM_SCSI + scsi_bus_reset(NULL); +#endif + ret = scsi_scan(true); + if (ret) + return CMD_RET_FAILURE; + return ret; + } + if (strncmp(argv[1], "scan", 4) == 0) { + ret = scsi_scan(true); + if (ret) + return CMD_RET_FAILURE; + return ret; + } + } + + return blk_common_cmd(argc, argv, IF_TYPE_SCSI, &scsi_curr_dev); +} + +U_BOOT_CMD( + scsi, 5, 1, do_scsi, + "SCSI sub-system", + "reset - reset SCSI controller\n" + "scsi info - show available SCSI devices\n" + "scsi scan - (re-)scan SCSI bus\n" + "scsi device [dev] - show or set current device\n" + "scsi part [dev] - print partition table of one or all SCSI devices\n" + "scsi read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" + " to memory address `addr'\n" + "scsi write addr blk# cnt - write `cnt' blocks starting at block\n" + " `blk#' from memory address `addr'" +); + +U_BOOT_CMD( + scsiboot, 3, 1, do_scsiboot, + "boot from SCSI device", + "loadAddr dev:part" +); diff --git a/roms/u-boot/cmd/setexpr.c b/roms/u-boot/cmd/setexpr.c new file mode 100644 index 000000000..e828be397 --- /dev/null +++ b/roms/u-boot/cmd/setexpr.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * Copyright 2013 Wolfgang Denk <wd@denx.de> + */ + +/* + * This file provides a shell like 'expr' function to return. + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <env.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <linux/sizes.h> + +/** + * struct expr_arg: Holds an argument to an expression + * + * @ival: Integer value (if width is not CMD_DATA_SIZE_STR) + * @sval: String value (if width is CMD_DATA_SIZE_STR) + */ +struct expr_arg { + union { + ulong ival; + char *sval; + }; +}; + +static int get_arg(char *s, int w, struct expr_arg *argp) +{ + struct expr_arg arg; + + /* + * If the parameter starts with a '*' then assume it is a pointer to + * the value we want. + */ + if (s[0] == '*') { + ulong *p; + ulong addr; + ulong val; + int len; + char *str; + + addr = simple_strtoul(&s[1], NULL, 16); + switch (w) { + case 1: + p = map_sysmem(addr, sizeof(uchar)); + val = (ulong)*(uchar *)p; + unmap_sysmem(p); + arg.ival = val; + break; + case 2: + p = map_sysmem(addr, sizeof(ushort)); + val = (ulong)*(ushort *)p; + unmap_sysmem(p); + arg.ival = val; + break; + case CMD_DATA_SIZE_STR: + p = map_sysmem(addr, SZ_64K); + + /* Maximum string length of 64KB plus terminator */ + len = strnlen((char *)p, SZ_64K) + 1; + str = malloc(len); + if (!str) { + printf("Out of memory\n"); + return -ENOMEM; + } + memcpy(str, p, len); + str[len - 1] = '\0'; + unmap_sysmem(p); + arg.sval = str; + break; + case 4: + p = map_sysmem(addr, sizeof(u32)); + val = *(u32 *)p; + unmap_sysmem(p); + arg.ival = val; + break; + default: + p = map_sysmem(addr, sizeof(ulong)); + val = *p; + unmap_sysmem(p); + arg.ival = val; + break; + } + } else { + if (w == CMD_DATA_SIZE_STR) + return -EINVAL; + arg.ival = simple_strtoul(s, NULL, 16); + } + *argp = arg; + + return 0; +} + +#ifdef CONFIG_REGEX + +#include <slre.h> + +/* + * memstr - Find the first substring in memory + * @s1: The string to be searched + * @s2: The string to search for + * + * Similar to and based on strstr(), + * but strings do not need to be NUL terminated. + */ +static char *memstr(const char *s1, int l1, const char *s2, int l2) +{ + if (!l2) + return (char *)s1; + + while (l1 >= l2) { + l1--; + if (!memcmp(s1, s2, l2)) + return (char *)s1; + s1++; + } + return NULL; +} + +/** + * substitute() - Substitute part of one string with another + * + * This updates @string so that the first occurrence of @old is replaced with + * @new + * + * @string: String buffer containing string to update at the start + * @slen: Pointer to current string length, updated on success + * @ssize: Size of string buffer + * @old: Old string to find in the buffer (no terminator needed) + * @olen: Length of @old excluding terminator + * @new: New string to replace @old with + * @nlen: Length of @new excluding terminator + * @return pointer to immediately after the copied @new in @string, or NULL if + * no replacement took place + */ +static char *substitute(char *string, int *slen, int ssize, + const char *old, int olen, const char *new, int nlen) +{ + char *p = memstr(string, *slen, old, olen); + + if (p == NULL) + return NULL; + + debug("## Match at pos %ld: match len %d, subst len %d\n", + (long)(p - string), olen, nlen); + + /* make sure replacement matches */ + if (*slen + nlen - olen > ssize) { + printf("## error: substitution buffer overflow\n"); + return NULL; + } + + /* move tail if needed */ + if (olen != nlen) { + int tail, len; + + len = (olen > nlen) ? olen : nlen; + + tail = ssize - (p + len - string); + + debug("## tail len %d\n", tail); + + memmove(p + nlen, p + olen, tail); + } + + /* insert substitute */ + memcpy(p, new, nlen); + + *slen += nlen - olen; + + return p + nlen; +} + +int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size, + const char *r, const char *s, bool global) +{ + struct slre slre; + char *datap = data; + int res, len, nlen, loop; + + if (slre_compile(&slre, r) == 0) { + printf("Error compiling regex: %s\n", slre.err_str); + return 1; + } + + len = strlen(data); + for (loop = 0;; loop++) { + struct cap caps[slre.num_caps + 2]; + const char *old; + char *np; + int i, olen; + + (void) memset(caps, 0, sizeof(caps)); + + res = slre_match(&slre, datap, len - (datap - data), caps); + + debug("Result: %d\n", res); + + for (i = 0; i <= slre.num_caps; i++) { + if (caps[i].len > 0) { + debug("Substring %d: [%.*s]\n", i, + caps[i].len, caps[i].ptr); + } + } + + if (res == 0) { + if (loop == 0) { + printf("%s: No match\n", data); + return 1; + } else { + break; + } + } + + debug("## MATCH ## %s\n", data); + + if (!s) + return 1; + + old = caps[0].ptr; + olen = caps[0].len; + nlen = strlen(s); + + if (nlen + 1 >= nbuf_size) { + printf("## error: pattern buffer overflow: have %d, need %d\n", + nbuf_size, nlen + 1); + return 1; + } + strcpy(nbuf, s); + + debug("## SUBST(1) ## %s\n", nbuf); + + /* + * Handle back references + * + * Support for \0 ... \9, where \0 is the + * whole matched pattern (similar to &). + * + * Implementation is a bit simpleminded as + * backrefs are substituted sequentially, one + * by one. This will lead to somewhat + * unexpected results if the replacement + * strings contain any \N strings then then + * may get substitued, too. We accept this + * restriction for the sake of simplicity. + */ + for (i = 0; i < 10; ++i) { + char backref[2] = { + '\\', + '0', + }; + + if (caps[i].len == 0) + break; + + backref[1] += i; + + debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n", + i, + 2, backref, + caps[i].len, caps[i].ptr, + nbuf); + + for (np = nbuf;;) { + char *p = memstr(np, nlen, backref, 2); + + if (p == NULL) + break; + + np = substitute(np, &nlen, + nbuf_size - (np - nbuf), + backref, 2, + caps[i].ptr, caps[i].len); + + if (np == NULL) + return 1; + } + } + debug("## SUBST(2) ## %s\n", nbuf); + + datap = substitute(datap, &len, data_size - (datap - data), + old, olen, nbuf, nlen); + + if (datap == NULL) + return 1; + + debug("## REMAINDER: %s\n", datap); + + debug("## RESULT: %s\n", data); + + if (!global) + break; + } + debug("## FINAL (now env_set()) : %s\n", data); + + return 0; +} + +#define SLRE_BUFSZ 16384 +#define SLRE_PATSZ 4096 + +/* + * Perform regex operations on a environment variable + * + * Returns 0 if OK, 1 in case of errors. + */ +static int regex_sub_var(const char *name, const char *r, const char *s, + const char *t, int global) +{ + struct slre slre; + char data[SLRE_BUFSZ]; + char nbuf[SLRE_PATSZ]; + const char *value; + int len; + int ret; + + if (!name) + return 1; + + if (slre_compile(&slre, r) == 0) { + printf("Error compiling regex: %s\n", slre.err_str); + return 1; + } + + if (!t) { + value = env_get(name); + if (!value) { + printf("## Error: variable \"%s\" not defined\n", name); + return 1; + } + t = value; + } + + debug("REGEX on %s=%s\n", name, t); + debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>", + global); + + len = strlen(t); + if (len + 1 > SLRE_BUFSZ) { + printf("## error: subst buffer overflow: have %d, need %d\n", + SLRE_BUFSZ, len + 1); + return 1; + } + + strcpy(data, t); + + ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s, + global); + if (ret) + return 1; + + printf("%s=%s\n", name, data); + + return env_set(name, data); +} +#endif + +static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct expr_arg aval, bval; + ulong value; + int ret = 0; + int w; + + /* + * We take 3, 5, or 6 arguments: + * 3 : setexpr name value + * 5 : setexpr name val1 op val2 + * setexpr name [g]sub r s + * 6 : setexpr name [g]sub r s t + */ + + /* > 6 already tested by max command args */ + if ((argc < 3) || (argc == 4)) + return CMD_RET_USAGE; + + w = cmd_get_data_size(argv[0], 4); + + if (get_arg(argv[2], w, &aval)) + return CMD_RET_FAILURE; + + /* plain assignment: "setexpr name value" */ + if (argc == 3) { + if (w == CMD_DATA_SIZE_STR) { + ret = env_set(argv[1], aval.sval); + free(aval.sval); + } else { + ret = env_set_hex(argv[1], aval.ival); + } + + return ret; + } + + /* 5 or 6 args (6 args only with [g]sub) */ +#ifdef CONFIG_REGEX + /* + * rexep handling: "setexpr name [g]sub r s [t]" + * with 5 args, "t" will be NULL + */ + if (strcmp(argv[2], "gsub") == 0) + return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1); + + if (strcmp(argv[2], "sub") == 0) + return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0); +#endif + + /* standard operators: "setexpr name val1 op val2" */ + if (argc != 5) + return CMD_RET_USAGE; + + if (strlen(argv[3]) != 1) + return CMD_RET_USAGE; + + if (get_arg(argv[4], w, &bval)) { + if (w == CMD_DATA_SIZE_STR) + free(aval.sval); + return CMD_RET_FAILURE; + } + + if (w == CMD_DATA_SIZE_STR) { + int len; + char *str; + + switch (argv[3][0]) { + case '+': + len = strlen(aval.sval) + strlen(bval.sval) + 1; + str = malloc(len); + if (!str) { + printf("Out of memory\n"); + ret = CMD_RET_FAILURE; + } else { + /* These were copied out and checked earlier */ + strcpy(str, aval.sval); + strcat(str, bval.sval); + ret = env_set(argv[1], str); + if (ret) + printf("Could not set var\n"); + free(str); + } + break; + default: + printf("invalid op\n"); + ret = 1; + } + } else { + ulong a = aval.ival; + ulong b = bval.ival; + + switch (argv[3][0]) { + case '|': + value = a | b; + break; + case '&': + value = a & b; + break; + case '+': + value = a + b; + break; + case '^': + value = a ^ b; + break; + case '-': + value = a - b; + break; + case '*': + value = a * b; + break; + case '/': + value = a / b; + break; + case '%': + value = a % b; + break; + default: + printf("invalid op\n"); + return 1; + } + + env_set_hex(argv[1], value); + } + + if (w == CMD_DATA_SIZE_STR) { + free(aval.sval); + free(bval.sval); + } + + return ret; +} + +U_BOOT_CMD( + setexpr, 6, 0, do_setexpr, + "set environment variable as the result of eval expression", + "[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n" + " - set environment variable 'name' to the result of the evaluated\n" + " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n" + " (for strings only + is supported)\n" + " size argument is only meaningful if value1 and/or value2 are\n" + " memory addresses (*)\n" + "setexpr[.b, .w, .l] name [*]value\n" + " - load a value into a variable" +#ifdef CONFIG_REGEX + "\n" + "setexpr name gsub r s [t]\n" + " - For each substring matching the regular expression <r> in the\n" + " string <t>, substitute the string <s>. The result is\n" + " assigned to <name>. If <t> is not supplied, use the old\n" + " value of <name>\n" + "setexpr name sub r s [t]\n" + " - Just like gsub(), but replace only the first matching substring" +#endif +); diff --git a/roms/u-boot/cmd/sf.c b/roms/u-boot/cmd/sf.c new file mode 100644 index 000000000..46346fb9d --- /dev/null +++ b/roms/u-boot/cmd/sf.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Command for accessing SPI flash. + * + * Copyright (C) 2008 Atmel Corporation + */ + +#include <common.h> +#include <command.h> +#include <div64.h> +#include <dm.h> +#include <flash.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <spi.h> +#include <spi_flash.h> +#include <asm/cache.h> +#include <jffs2/jffs2.h> +#include <linux/mtd/mtd.h> + +#include <asm/io.h> +#include <dm/device-internal.h> + +#include "legacy-mtd-utils.h" + +static struct spi_flash *flash; + +/* + * This function computes the length argument for the erase command. + * The length on which the command is to operate can be given in two forms: + * 1. <cmd> offset len - operate on <'offset', 'len') + * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') + * If the second form is used and the length doesn't fall on the + * sector boundary, than it will be adjusted to the next sector boundary. + * If it isn't in the flash, the function will fail (return -1). + * Input: + * arg: length specification (i.e. both command arguments) + * Output: + * len: computed length for operation + * Return: + * 1: success + * -1: failure (bad format, bad address). + */ +static int sf_parse_len_arg(char *arg, ulong *len) +{ + char *ep; + char round_up_len; /* indicates if the "+length" form used */ + ulong len_arg; + + round_up_len = 0; + if (*arg == '+') { + round_up_len = 1; + ++arg; + } + + len_arg = simple_strtoul(arg, &ep, 16); + if (ep == arg || *ep != '\0') + return -1; + + if (round_up_len && flash->sector_size > 0) + *len = ROUND(len_arg, flash->sector_size); + else + *len = len_arg; + + return 1; +} + +/** + * This function takes a byte length and a delta unit of time to compute the + * approximate bytes per second + * + * @param len amount of bytes currently processed + * @param start_ms start time of processing in ms + * @return bytes per second if OK, 0 on error + */ +static ulong bytes_per_second(unsigned int len, ulong start_ms) +{ + /* less accurate but avoids overflow */ + if (len >= ((unsigned int) -1) / 1024) + return len / (max(get_timer(start_ms) / 1024, 1UL)); + else + return 1024 * len / max(get_timer(start_ms), 1UL); +} + +static int do_spi_flash_probe(int argc, char *const argv[]) +{ + unsigned int bus = CONFIG_SF_DEFAULT_BUS; + unsigned int cs = CONFIG_SF_DEFAULT_CS; + /* In DM mode, defaults speed and mode will be taken from DT */ + unsigned int speed = CONFIG_SF_DEFAULT_SPEED; + unsigned int mode = CONFIG_SF_DEFAULT_MODE; + char *endp; +#if CONFIG_IS_ENABLED(DM_SPI_FLASH) + struct udevice *new, *bus_dev; + int ret; +#else + struct spi_flash *new; +#endif + + if (argc >= 2) { + cs = simple_strtoul(argv[1], &endp, 0); + if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) + return -1; + if (*endp == ':') { + if (endp[1] == 0) + return -1; + + bus = cs; + cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != 0) + return -1; + } + } + + if (argc >= 3) { + speed = simple_strtoul(argv[2], &endp, 0); + if (*argv[2] == 0 || *endp != 0) + return -1; + } + if (argc >= 4) { + mode = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return -1; + } + +#if CONFIG_IS_ENABLED(DM_SPI_FLASH) + /* Remove the old device, otherwise probe will just be a nop */ + ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); + if (!ret) { + device_remove(new, DM_REMOVE_NORMAL); + } + flash = NULL; + ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new); + if (ret) { + printf("Failed to initialize SPI flash at %u:%u (error %d)\n", + bus, cs, ret); + return 1; + } + + flash = dev_get_uclass_priv(new); +#else + if (flash) + spi_flash_free(flash); + + new = spi_flash_probe(bus, cs, speed, mode); + flash = new; + if (!new) { + printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); + return 1; + } +#endif + + return 0; +} + +/** + * Write a block of data to SPI flash, first checking if it is different from + * what is already there. + * + * If the data being written is the same, then *skipped is incremented by len. + * + * @param flash flash context pointer + * @param offset flash offset to write + * @param len number of bytes to write + * @param buf buffer to write from + * @param cmp_buf read buffer to use to compare data + * @param skipped Count of skipped data (incremented by this function) + * @return NULL if OK, else a string containing the stage which failed + */ +static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, + size_t len, const char *buf, char *cmp_buf, size_t *skipped) +{ + char *ptr = (char *)buf; + + debug("offset=%#x, sector_size=%#x, len=%#zx\n", + offset, flash->sector_size, len); + /* Read the entire sector so to allow for rewriting */ + if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) + return "read"; + /* Compare only what is meaningful (len) */ + if (memcmp(cmp_buf, buf, len) == 0) { + debug("Skip region %x size %zx: no change\n", + offset, len); + *skipped += len; + return NULL; + } + /* Erase the entire sector */ + if (spi_flash_erase(flash, offset, flash->sector_size)) + return "erase"; + /* If it's a partial sector, copy the data into the temp-buffer */ + if (len != flash->sector_size) { + memcpy(cmp_buf, buf, len); + ptr = cmp_buf; + } + /* Write one complete sector */ + if (spi_flash_write(flash, offset, flash->sector_size, ptr)) + return "write"; + + return NULL; +} + +/** + * Update an area of SPI flash by erasing and writing any blocks which need + * to change. Existing blocks with the correct data are left unchanged. + * + * @param flash flash context pointer + * @param offset flash offset to write + * @param len number of bytes to write + * @param buf buffer to write from + * @return 0 if ok, 1 on error + */ +static int spi_flash_update(struct spi_flash *flash, u32 offset, + size_t len, const char *buf) +{ + const char *err_oper = NULL; + char *cmp_buf; + const char *end = buf + len; + size_t todo; /* number of bytes to do in this pass */ + size_t skipped = 0; /* statistics */ + const ulong start_time = get_timer(0); + size_t scale = 1; + const char *start_buf = buf; + ulong delta; + + if (end - buf >= 200) + scale = (end - buf) / 100; + cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size); + if (cmp_buf) { + ulong last_update = get_timer(0); + + for (; buf < end && !err_oper; buf += todo, offset += todo) { + todo = min_t(size_t, end - buf, flash->sector_size); + if (get_timer(last_update) > 100) { + printf(" \rUpdating, %zu%% %lu B/s", + 100 - (end - buf) / scale, + bytes_per_second(buf - start_buf, + start_time)); + last_update = get_timer(0); + } + err_oper = spi_flash_update_block(flash, offset, todo, + buf, cmp_buf, &skipped); + } + } else { + err_oper = "malloc"; + } + free(cmp_buf); + putc('\r'); + if (err_oper) { + printf("SPI flash failed in %s step\n", err_oper); + return 1; + } + + delta = get_timer(start_time); + printf("%zu bytes written, %zu bytes skipped", len - skipped, + skipped); + printf(" in %ld.%lds, speed %ld B/s\n", + delta / 1000, delta % 1000, bytes_per_second(len, start_time)); + + return 0; +} + +static int do_spi_flash_read_write(int argc, char *const argv[]) +{ + unsigned long addr; + void *buf; + char *endp; + int ret = 1; + int dev = 0; + loff_t offset, len, maxsize; + + if (argc < 3) + return -1; + + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + + if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len, + &maxsize, MTD_DEV_TYPE_NOR, flash->size)) + return -1; + + /* Consistency checking */ + if (offset + len > flash->size) { + printf("ERROR: attempting %s past flash size (%#x)\n", + argv[0], flash->size); + return 1; + } + + buf = map_physmem(addr, len, MAP_WRBACK); + if (!buf && addr) { + puts("Failed to map physical memory\n"); + return 1; + } + + if (strcmp(argv[0], "update") == 0) { + ret = spi_flash_update(flash, offset, len, buf); + } else if (strncmp(argv[0], "read", 4) == 0 || + strncmp(argv[0], "write", 5) == 0) { + int read; + + read = strncmp(argv[0], "read", 4) == 0; + if (read) + ret = spi_flash_read(flash, offset, len, buf); + else + ret = spi_flash_write(flash, offset, len, buf); + + printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset, + read ? "Read" : "Written"); + if (ret) + printf("ERROR %d\n", ret); + else + printf("OK\n"); + } + + unmap_physmem(buf, len); + + return ret == 0 ? 0 : 1; +} + +static int do_spi_flash_erase(int argc, char *const argv[]) +{ + int ret; + int dev = 0; + loff_t offset, len, maxsize; + ulong size; + + if (argc < 3) + return -1; + + if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize, + MTD_DEV_TYPE_NOR, flash->size)) + return -1; + + ret = sf_parse_len_arg(argv[2], &size); + if (ret != 1) + return -1; + + /* Consistency checking */ + if (offset + size > flash->size) { + printf("ERROR: attempting %s past flash size (%#x)\n", + argv[0], flash->size); + return 1; + } + + ret = spi_flash_erase(flash, offset, size); + printf("SF: %zu bytes @ %#x Erased: ", (size_t)size, (u32)offset); + if (ret) + printf("ERROR %d\n", ret); + else + printf("OK\n"); + + return ret == 0 ? 0 : 1; +} + +static int do_spi_protect(int argc, char *const argv[]) +{ + int ret = 0; + loff_t start, len; + bool prot = false; + + if (argc != 4) + return -1; + + if (!str2off(argv[2], &start)) { + puts("start sector is not a valid number\n"); + return 1; + } + + if (!str2off(argv[3], &len)) { + puts("len is not a valid number\n"); + return 1; + } + + if (strcmp(argv[1], "lock") == 0) + prot = true; + else if (strcmp(argv[1], "unlock") == 0) + prot = false; + else + return -1; /* Unknown parameter */ + + ret = spi_flash_protect(flash, start, len, prot); + + return ret == 0 ? 0 : 1; +} + +#ifdef CONFIG_CMD_SF_TEST +enum { + STAGE_ERASE, + STAGE_CHECK, + STAGE_WRITE, + STAGE_READ, + + STAGE_COUNT, +}; + +static char *stage_name[STAGE_COUNT] = { + "erase", + "check", + "write", + "read", +}; + +struct test_info { + int stage; + int bytes; + unsigned base_ms; + unsigned time_ms[STAGE_COUNT]; +}; + +static void show_time(struct test_info *test, int stage) +{ + uint64_t speed; /* KiB/s */ + int bps; /* Bits per second */ + + speed = (long long)test->bytes * 1000; + if (test->time_ms[stage]) + do_div(speed, test->time_ms[stage] * 1024); + bps = speed * 8; + + printf("%d %s: %u ticks, %d KiB/s %d.%03d Mbps\n", stage, + stage_name[stage], test->time_ms[stage], + (int)speed, bps / 1000, bps % 1000); +} + +static void spi_test_next_stage(struct test_info *test) +{ + test->time_ms[test->stage] = get_timer(test->base_ms); + show_time(test, test->stage); + test->base_ms = get_timer(0); + test->stage++; +} + +/** + * Run a test on the SPI flash + * + * @param flash SPI flash to use + * @param buf Source buffer for data to write + * @param len Size of data to read/write + * @param offset Offset within flash to check + * @param vbuf Verification buffer + * @return 0 if ok, -1 on error + */ +static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, + ulong offset, uint8_t *vbuf) +{ + struct test_info test; + int err, i; + + printf("SPI flash test:\n"); + memset(&test, '\0', sizeof(test)); + test.base_ms = get_timer(0); + test.bytes = len; + err = spi_flash_erase(flash, offset, len); + if (err) { + printf("Erase failed (err = %d)\n", err); + return -1; + } + spi_test_next_stage(&test); + + err = spi_flash_read(flash, offset, len, vbuf); + if (err) { + printf("Check read failed (err = %d)\n", err); + return -1; + } + for (i = 0; i < len; i++) { + if (vbuf[i] != 0xff) { + printf("Check failed at %d\n", i); + print_buffer(i, vbuf + i, 1, + min_t(uint, len - i, 0x40), 0); + return -1; + } + } + spi_test_next_stage(&test); + + err = spi_flash_write(flash, offset, len, buf); + if (err) { + printf("Write failed (err = %d)\n", err); + return -1; + } + memset(vbuf, '\0', len); + spi_test_next_stage(&test); + + err = spi_flash_read(flash, offset, len, vbuf); + if (err) { + printf("Read failed (ret = %d)\n", err); + return -1; + } + spi_test_next_stage(&test); + + for (i = 0; i < len; i++) { + if (buf[i] != vbuf[i]) { + printf("Verify failed at %d, good data:\n", i); + print_buffer(i, buf + i, 1, + min_t(uint, len - i, 0x40), 0); + printf("Bad data:\n"); + print_buffer(i, vbuf + i, 1, + min_t(uint, len - i, 0x40), 0); + return -1; + } + } + printf("Test passed\n"); + for (i = 0; i < STAGE_COUNT; i++) + show_time(&test, i); + + return 0; +} + +static int do_spi_flash_test(int argc, char *const argv[]) +{ + unsigned long offset; + unsigned long len; + uint8_t *buf, *from; + char *endp; + uint8_t *vbuf; + int ret; + + if (argc < 3) + return -1; + offset = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + len = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + + vbuf = memalign(ARCH_DMA_MINALIGN, len); + if (!vbuf) { + printf("Cannot allocate memory (%lu bytes)\n", len); + return 1; + } + buf = memalign(ARCH_DMA_MINALIGN, len); + if (!buf) { + free(vbuf); + printf("Cannot allocate memory (%lu bytes)\n", len); + return 1; + } + + from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0); + memcpy(buf, from, len); + ret = spi_flash_test(flash, buf, len, offset, vbuf); + free(vbuf); + free(buf); + if (ret) { + printf("Test failed\n"); + return 1; + } + + return 0; +} +#endif /* CONFIG_CMD_SF_TEST */ + +static int do_spi_flash(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *cmd; + int ret; + + /* need at least two arguments */ + if (argc < 2) + goto usage; + + cmd = argv[1]; + --argc; + ++argv; + + if (strcmp(cmd, "probe") == 0) { + ret = do_spi_flash_probe(argc, argv); + goto done; + } + + /* The remaining commands require a selected device */ + if (!flash) { + puts("No SPI flash selected. Please run `sf probe'\n"); + return 1; + } + + if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || + strcmp(cmd, "update") == 0) + ret = do_spi_flash_read_write(argc, argv); + else if (strcmp(cmd, "erase") == 0) + ret = do_spi_flash_erase(argc, argv); + else if (strcmp(cmd, "protect") == 0) + ret = do_spi_protect(argc, argv); +#ifdef CONFIG_CMD_SF_TEST + else if (!strcmp(cmd, "test")) + ret = do_spi_flash_test(argc, argv); +#endif + else + ret = -1; + +done: + if (ret != -1) + return ret; + +usage: + return CMD_RET_USAGE; +} + +#ifdef CONFIG_CMD_SF_TEST +#define SF_TEST_HELP "\nsf test offset len " \ + "- run a very basic destructive test" +#else +#define SF_TEST_HELP +#endif + +U_BOOT_CMD( + sf, 5, 1, do_spi_flash, + "SPI flash sub-system", + "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" + " and chip select\n" + "sf read addr offset|partition len - read `len' bytes starting at\n" + " `offset' or from start of mtd\n" + " `partition'to memory at `addr'\n" + "sf write addr offset|partition len - write `len' bytes from memory\n" + " at `addr' to flash at `offset'\n" + " or to start of mtd `partition'\n" + "sf erase offset|partition [+]len - erase `len' bytes from `offset'\n" + " or from start of mtd `partition'\n" + " `+len' round up `len' to block size\n" + "sf update addr offset|partition len - erase and write `len' bytes from memory\n" + " at `addr' to flash at `offset'\n" + " or to start of mtd `partition'\n" + "sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n" + " at address 'sector'\n" + SF_TEST_HELP +); diff --git a/roms/u-boot/cmd/sha1sum.c b/roms/u-boot/cmd/sha1sum.c new file mode 100644 index 000000000..bcc665a5a --- /dev/null +++ b/roms/u-boot/cmd/sha1sum.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <hash.h> +#include <u-boot/sha1.h> + +int do_sha1sum(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int flags = HASH_FLAG_ENV; + int ac; + char * const *av; + + if (argc < 3) + return CMD_RET_USAGE; + + av = argv + 1; + ac = argc - 1; +#ifdef CONFIG_SHA1SUM_VERIFY + if (strcmp(*av, "-v") == 0) { + flags |= HASH_FLAG_VERIFY; + av++; + ac--; + } +#endif + + return hash_command("sha1", flags, cmdtp, flag, ac, av); +} + +#ifdef CONFIG_SHA1SUM_VERIFY +U_BOOT_CMD( + sha1sum, 5, 1, do_sha1sum, + "compute SHA1 message digest", + "address count [[*]sum]\n" + " - compute SHA1 message digest [save to sum]\n" + "sha1sum -v address count [*]sum\n" + " - verify sha1sum of memory area" +); +#else +U_BOOT_CMD( + sha1sum, 4, 1, do_sha1sum, + "compute SHA1 message digest", + "address count [[*]sum]\n" + " - compute SHA1 message digest [save to sum]" +); +#endif diff --git a/roms/u-boot/cmd/sleep.c b/roms/u-boot/cmd/sleep.c new file mode 100644 index 000000000..1fff400c7 --- /dev/null +++ b/roms/u-boot/cmd/sleep.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <linux/delay.h> + +static int do_sleep(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong start = get_timer(0); + ulong mdelay = 0; + ulong delay; + char *frpart; + + if (argc != 2) + return CMD_RET_USAGE; + + delay = simple_strtoul(argv[1], NULL, 10) * CONFIG_SYS_HZ; + + frpart = strchr(argv[1], '.'); + + if (frpart) { + uint mult = CONFIG_SYS_HZ / 10; + for (frpart++; *frpart != '\0' && mult > 0; frpart++) { + if (*frpart < '0' || *frpart > '9') { + mdelay = 0; + break; + } + mdelay += (*frpart - '0') * mult; + mult /= 10; + } + } + + delay += mdelay; + + while (get_timer(start) < delay) { + if (ctrlc()) + return CMD_RET_FAILURE; + + udelay(100); + } + + return 0; +} + +U_BOOT_CMD( + sleep , 2, 1, do_sleep, + "delay execution for some time", + "N\n" + " - delay execution for N seconds (N is _decimal_ and can be\n" + " fractional)" +); diff --git a/roms/u-boot/cmd/smccc.c b/roms/u-boot/cmd/smccc.c new file mode 100644 index 000000000..0b19f6c68 --- /dev/null +++ b/roms/u-boot/cmd/smccc.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 + * Michalis Pappas <mpappas@fastmail.fm> + */ +#include <asm/psci.h> +#include <common.h> +#include <command.h> +#include <linux/arm-smccc.h> +#include <linux/compiler.h> +#include <linux/psci.h> + +static int do_call(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct arm_smccc_res res; + + unsigned long fid; + + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + + if (argc < 2) + return CMD_RET_USAGE; + + fid = simple_strtoul(argv[1], NULL, 16); + + a1 = argc > 2 ? simple_strtoul(argv[2], NULL, 16) : 0; + a2 = argc > 3 ? simple_strtoul(argv[3], NULL, 16) : 0; + a3 = argc > 4 ? simple_strtoul(argv[4], NULL, 16) : 0; + a4 = argc > 5 ? simple_strtoul(argv[5], NULL, 16) : 0; + a5 = argc > 6 ? simple_strtoul(argv[6], NULL, 16) : 0; + a6 = argc > 7 ? simple_strtoul(argv[7], NULL, 16) : 0; + a7 = argc > 8 ? simple_strtoul(argv[8], NULL, 16) : 0; + + if (!strcmp(argv[0], "smc")) + arm_smccc_smc(fid, a1, a2, a3, a4, a5, a6, a7, &res); + else + arm_smccc_hvc(fid, a1, a2, a3, a4, a5, a6, a7, &res); + + printf("Res: %ld %ld %ld %ld\n", res.a0, res.a1, res.a2, res.a3); + + return 0; +} + +#ifdef CONFIG_CMD_SMC +U_BOOT_CMD( + smc, 8, 2, do_call, + "Issue a Secure Monitor Call", + "<fid> [arg1 ... arg6] [id]\n" + " - fid Function ID\n" + " - arg SMC arguments, passed to X1-X6 (default to zero)\n" + " - id Secure OS ID / Session ID, passed to W7 (defaults to zero)\n" +); +#endif + +#ifdef CONFIG_CMD_HVC +U_BOOT_CMD( + hvc, 8, 2, do_call, + "Issue a Hypervisor Call", + "<fid> [arg1...arg7] [id]\n" + " - fid Function ID\n" + " - arg HVC arguments, passed to X1-X6 (default to zero)\n" + " - id Session ID, passed to W7 (defaults to zero)\n" +); +#endif + diff --git a/roms/u-boot/cmd/sound.c b/roms/u-boot/cmd/sound.c new file mode 100644 index 000000000..fdcde3653 --- /dev/null +++ b/roms/u-boot/cmd/sound.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde <rajeshwari.s@samsung.com> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <fdtdec.h> +#include <sound.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Initilaise sound subsystem */ +static int do_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_SOUND, &dev); + if (!ret) + ret = sound_setup(dev); + if (ret) { + printf("Initialise Audio driver failed (ret=%d)\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +/* play sound from buffer */ +static int do_play(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int ret = 0; + int msec = 1000; + int freq = 400; + + if (argc > 1) + msec = simple_strtoul(argv[1], NULL, 10); + if (argc > 2) + freq = simple_strtoul(argv[2], NULL, 10); + + ret = uclass_first_device_err(UCLASS_SOUND, &dev); + if (!ret) + ret = sound_beep(dev, msec, freq); + if (ret) { + printf("Sound device failed to play (err=%d)\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +static struct cmd_tbl cmd_sound_sub[] = { + U_BOOT_CMD_MKENT(init, 0, 1, do_init, "", ""), + U_BOOT_CMD_MKENT(play, 2, 1, do_play, "", ""), +}; + +/* process sound command */ +static int do_sound(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + + if (argc < 1) + return CMD_RET_USAGE; + + /* Strip off leading 'sound' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_sound_sub[0], ARRAY_SIZE(cmd_sound_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + sound, 4, 1, do_sound, + "sound sub-system", + "init - initialise the sound driver\n" + "sound play [len] [freq] - play a sound for len ms at freq hz\n" +); diff --git a/roms/u-boot/cmd/source.c b/roms/u-boot/cmd/source.c new file mode 100644 index 000000000..71f71528a --- /dev/null +++ b/roms/u-boot/cmd/source.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Kyle Harris, kharris@nexus-tech.net + */ + +/* + * The "source" command allows to define "script images", i. e. files + * that contain command sequences that can be executed by the command + * interpreter. It returns the exit status of the last command + * executed from the script. This is very similar to running a shell + * script in a UNIX shell, hence the name for the command. + */ + +/* #define DEBUG */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <asm/byteorder.h> +#include <asm/io.h> + +#if defined(CONFIG_FIT) +/** + * get_default_image() - Return default property from /images + * + * Return: Pointer to value of default property (or NULL) + */ +static const char *get_default_image(const void *fit) +{ + int images_noffset; + + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) + return NULL; + + return fdt_getprop(fit, images_noffset, FIT_DEFAULT_PROP, NULL); +} +#endif + +int image_source_script(ulong addr, const char *fit_uname) +{ + ulong len; +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + const image_header_t *hdr; +#endif + u32 *data; + int verify; + void *buf; +#if defined(CONFIG_FIT) + const void* fit_hdr; + int noffset; + const void *fit_data; + size_t fit_len; +#endif + + verify = env_get_yesno("verify"); + + buf = map_sysmem(addr, 0); + switch (genimg_get_format(buf)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + hdr = buf; + + if (!image_check_magic (hdr)) { + puts ("Bad magic number\n"); + return 1; + } + + if (!image_check_hcrc (hdr)) { + puts ("Bad header crc\n"); + return 1; + } + + if (verify) { + if (!image_check_dcrc (hdr)) { + puts ("Bad data crc\n"); + return 1; + } + } + + if (!image_check_type (hdr, IH_TYPE_SCRIPT)) { + puts ("Bad image type\n"); + return 1; + } + + /* get length of script */ + data = (u32 *)image_get_data (hdr); + + if ((len = uimage_to_cpu (*data)) == 0) { + puts ("Empty Script\n"); + return 1; + } + + /* + * scripts are just multi-image files with one component, seek + * past the zero-terminated sequence of image lengths to get + * to the actual image data + */ + while (*data++); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + fit_hdr = buf; + if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) { + puts ("Bad FIT image format\n"); + return 1; + } + + if (!fit_uname) + fit_uname = get_default_image(fit_hdr); + + if (!fit_uname) { + puts("No FIT subimage unit name\n"); + return 1; + } + + /* get script component image node offset */ + noffset = fit_image_get_node (fit_hdr, fit_uname); + if (noffset < 0) { + printf ("Can't find '%s' FIT subimage\n", fit_uname); + return 1; + } + + if (!fit_image_check_type (fit_hdr, noffset, IH_TYPE_SCRIPT)) { + puts ("Not a image image\n"); + return 1; + } + + /* verify integrity */ + if (verify) { + if (!fit_image_verify(fit_hdr, noffset)) { + puts ("Bad Data Hash\n"); + return 1; + } + } + + /* get script subimage data address and length */ + if (fit_image_get_data (fit_hdr, noffset, &fit_data, &fit_len)) { + puts ("Could not find script subimage data\n"); + return 1; + } + + data = (u32 *)fit_data; + len = (ulong)fit_len; + break; +#endif + default: + puts ("Wrong image format for \"source\" command\n"); + return 1; + } + + debug("** Script length: %ld\n", len); + return run_command_list((char *)data, len, 0); +} + +/**************************************************/ +#if defined(CONFIG_CMD_SOURCE) +static int do_source(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr; + int rcode; + const char *fit_uname = NULL; + + /* Find script image */ + if (argc < 2) { + addr = CONFIG_SYS_LOAD_ADDR; + debug("* source: default load address = 0x%08lx\n", addr); +#if defined(CONFIG_FIT) + } else if (fit_parse_subimage(argv[1], image_load_addr, &addr, + &fit_uname)) { + debug("* source: subimage '%s' from FIT image at 0x%08lx\n", + fit_uname, addr); +#endif + } else { + addr = simple_strtoul(argv[1], NULL, 16); + debug("* source: cmdline image address = 0x%08lx\n", addr); + } + + printf ("## Executing script at %08lx\n", addr); + rcode = image_source_script(addr, fit_uname); + return rcode; +} + +#ifdef CONFIG_SYS_LONGHELP +static char source_help_text[] = + "[addr]\n" + "\t- run script starting at addr\n" + "\t- A valid image header must be present" +#if defined(CONFIG_FIT) + "\n" + "For FIT format uImage addr must include subimage\n" + "unit name in the form of addr:<subimg_uname>" +#endif + ""; +#endif + +U_BOOT_CMD( + source, 2, 0, do_source, + "run script from memory", source_help_text +); +#endif diff --git a/roms/u-boot/cmd/spi.c b/roms/u-boot/cmd/spi.c new file mode 100644 index 000000000..4aea19141 --- /dev/null +++ b/roms/u-boot/cmd/spi.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + */ + +/* + * SPI Read/Write Utilities + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <spi.h> + +/*----------------------------------------------------------------------- + * Definitions + */ + +#ifndef MAX_SPI_BYTES +# define MAX_SPI_BYTES 32 /* Maximum number of bytes we can handle */ +#endif + +/* + * Values from last command. + */ +static unsigned int bus; +static unsigned int cs; +static unsigned int mode; +static unsigned int freq; +static int bitlen; +static uchar dout[MAX_SPI_BYTES]; +static uchar din[MAX_SPI_BYTES]; + +static int do_spi_xfer(int bus, int cs) +{ + struct spi_slave *slave; + int ret = 0; + +#if CONFIG_IS_ENABLED(DM_SPI) + char name[30], *str; + struct udevice *dev; + + snprintf(name, sizeof(name), "generic_%d:%d", bus, cs); + str = strdup(name); + if (!str) + return -ENOMEM; + ret = spi_get_bus_and_cs(bus, cs, freq, mode, "spi_generic_drv", + str, &dev, &slave); + if (ret) + return ret; +#else + slave = spi_setup_slave(bus, cs, freq, mode); + if (!slave) { + printf("Invalid device %d:%d\n", bus, cs); + return -EINVAL; + } +#endif + + ret = spi_claim_bus(slave); + if (ret) + goto done; + ret = spi_xfer(slave, bitlen, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END); +#if !CONFIG_IS_ENABLED(DM_SPI) + /* We don't get an error code in this case */ + if (ret) + ret = -EIO; +#endif + if (ret) { + printf("Error %d during SPI transaction\n", ret); + } else { + int j; + + for (j = 0; j < ((bitlen + 7) / 8); j++) + printf("%02X", din[j]); + printf("\n"); + } +done: + spi_release_bus(slave); +#if !CONFIG_IS_ENABLED(DM_SPI) + spi_free_slave(slave); +#endif + + return ret; +} + +/* + * SPI read/write + * + * Syntax: + * spi {dev} {num_bits} {dout} + * {dev} is the device number for controlling chip select (see TBD) + * {num_bits} is the number of bits to send & receive (base 10) + * {dout} is a hexadecimal string of data to send + * The command prints out the hexadecimal string received via SPI. + */ + +int do_spi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *cp = 0; + uchar tmp; + int j; + + /* + * We use the last specified parameters, unless new ones are + * entered. + */ + if (freq == 0) + freq = 1000000; + + if ((flag & CMD_FLAG_REPEAT) == 0) + { + if (argc >= 2) { + mode = CONFIG_DEFAULT_SPI_MODE; + bus = simple_strtoul(argv[1], &cp, 10); + if (*cp == ':') { + cs = simple_strtoul(cp+1, &cp, 10); + } else { + cs = bus; + bus = CONFIG_DEFAULT_SPI_BUS; + } + if (*cp == '.') + mode = simple_strtoul(cp+1, &cp, 10); + if (*cp == '@') + freq = simple_strtoul(cp+1, &cp, 10); + } + if (argc >= 3) + bitlen = simple_strtoul(argv[2], NULL, 10); + if (argc >= 4) { + cp = argv[3]; + for(j = 0; *cp; j++, cp++) { + tmp = *cp - '0'; + if(tmp > 9) + tmp -= ('A' - '0') - 10; + if(tmp > 15) + tmp -= ('a' - 'A'); + if(tmp > 15) { + printf("Hex conversion error on %c\n", *cp); + return 1; + } + if((j % 2) == 0) + dout[j / 2] = (tmp << 4); + else + dout[j / 2] |= tmp; + } + } + } + + if ((bitlen < 0) || (bitlen > (MAX_SPI_BYTES * 8))) { + printf("Invalid bitlen %d\n", bitlen); + return 1; + } + + if (do_spi_xfer(bus, cs)) + return 1; + + return 0; +} + +/***************************************************/ + +U_BOOT_CMD( + sspi, 5, 1, do_spi, + "SPI utility command", + "[<bus>:]<cs>[.<mode>][@<freq>] <bit_len> <dout> - Send and receive bits\n" + "<bus> - Identifies the SPI bus\n" + "<cs> - Identifies the chip select\n" + "<mode> - Identifies the SPI mode to use\n" + "<freq> - Identifies the SPI bus frequency in Hz\n" + "<bit_len> - Number of bits to send (base 10)\n" + "<dout> - Hexadecimal string that gets sent" +); diff --git a/roms/u-boot/cmd/spl.c b/roms/u-boot/cmd/spl.c new file mode 100644 index 000000000..472703f8f --- /dev/null +++ b/roms/u-boot/cmd/spl.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + */ + +#include <common.h> +#include <command.h> +#include <cmd_spl.h> +#include <env.h> +#include <image.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char **subcmd_list[] = { + + [SPL_EXPORT_FDT] = (const char * []) { +#ifdef CONFIG_OF_LIBFDT + "start", + "loados", + #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + "ramdisk", + #endif + "fdt", + "cmdline", + "bdt", + "prep", +#endif + NULL, + }, + [SPL_EXPORT_ATAGS] = (const char * []) { +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) + "start", + "loados", +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + "ramdisk", +#endif + "cmdline", + "bdt", + "prep", +#endif + NULL, + }, + NULL +}; + +/* Calls bootm with the parameters given */ +static int call_bootm(int argc, char *const argv[], const char *subcommand[]) +{ + char *bootm_argv[5]; + + int i = 0; + int ret = 0; + int j; + + /* create paramter array */ + bootm_argv[0] = "do_bootm"; + switch (argc) { + case 3: + bootm_argv[4] = argv[2]; /* fdt addr */ + case 2: + bootm_argv[3] = argv[1]; /* initrd addr */ + case 1: + bootm_argv[2] = argv[0]; /* kernel addr */ + } + + + /* + * - do the work - + * exec subcommands of do_bootm to init the images + * data structure + */ + while (subcommand[i] != NULL) { + bootm_argv[1] = (char *)subcommand[i]; + debug("args %d: %s %s ", argc, bootm_argv[0], bootm_argv[1]); + for (j = 0; j < argc; j++) + debug("%s ", bootm_argv[j + 2]); + debug("\n"); + + ret = do_bootm(find_cmd("do_bootm"), 0, argc+2, + bootm_argv); + debug("Subcommand retcode: %d\n", ret); + i++; + } + + if (ret) { + printf("ERROR prep subcommand failed!\n"); + return -1; + } + + return 0; +} + +static struct cmd_tbl cmd_spl_export_sub[] = { + U_BOOT_CMD_MKENT(fdt, 0, 1, (void *)SPL_EXPORT_FDT, "", ""), + U_BOOT_CMD_MKENT(atags, 0, 1, (void *)SPL_EXPORT_ATAGS, "", ""), +}; + +static int spl_export(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct cmd_tbl *c; + + if (argc < 2) /* no subcommand */ + return cmd_usage(cmdtp); + + c = find_cmd_tbl(argv[1], &cmd_spl_export_sub[0], + ARRAY_SIZE(cmd_spl_export_sub)); + if ((c) && ((long)c->cmd <= SPL_EXPORT_LAST)) { + argc -= 2; + argv += 2; + if (call_bootm(argc, argv, subcmd_list[(long)c->cmd])) + return -1; + switch ((long)c->cmd) { +#ifdef CONFIG_OF_LIBFDT + case SPL_EXPORT_FDT: + printf("Argument image is now in RAM: 0x%p\n", + (void *)images.ft_addr); + env_set_addr("fdtargsaddr", images.ft_addr); + env_set_hex("fdtargslen", fdt_totalsize(images.ft_addr)); +#ifdef CONFIG_CMD_SPL_WRITE_SIZE + if (fdt_totalsize(images.ft_addr) > + CONFIG_CMD_SPL_WRITE_SIZE) + puts("WARN: FDT size > CMD_SPL_WRITE_SIZE\n"); +#endif + break; +#endif + case SPL_EXPORT_ATAGS: + printf("Argument image is now in RAM at: 0x%p\n", + (void *)gd->bd->bi_boot_params); + break; + } + } else { + /* Unrecognized command */ + return cmd_usage(cmdtp); + } + + return 0; +} + +static struct cmd_tbl cmd_spl_sub[] = { + U_BOOT_CMD_MKENT(export, 0, 1, (void *)SPL_EXPORT, "", ""), +}; + +static int do_spl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct cmd_tbl *c; + int cmd; + + if (argc < 2) /* no subcommand */ + return cmd_usage(cmdtp); + + c = find_cmd_tbl(argv[1], &cmd_spl_sub[0], ARRAY_SIZE(cmd_spl_sub)); + if (c) { + cmd = (long)c->cmd; + switch (cmd) { + case SPL_EXPORT: + argc--; + argv++; + if (spl_export(cmdtp, flag, argc, argv)) + printf("Subcommand failed\n"); + break; + default: + /* unrecognized command */ + return cmd_usage(cmdtp); + } + } else { + /* Unrecognized command */ + return cmd_usage(cmdtp); + } + return 0; +} + +U_BOOT_CMD( + spl, 6 , 1, do_spl, "SPL configuration", + "export <img=atags|fdt> [kernel_addr] [initrd_addr] [fdt_addr]\n" + "\timg\t\t\"atags\" or \"fdt\"\n" + "\tkernel_addr\taddress where a kernel image is stored.\n" + "\t\t\tkernel is loaded as part of the boot process, but it is not started.\n" + "\tinitrd_addr\taddress of initial ramdisk\n" + "\t\t\tcan be set to \"-\" if fdt_addr without initrd_addr is used.\n" + "\tfdt_addr\tin case of fdt, the address of the device tree.\n" + ); diff --git a/roms/u-boot/cmd/sqfs.c b/roms/u-boot/cmd/sqfs.c new file mode 100644 index 000000000..107038c4c --- /dev/null +++ b/roms/u-boot/cmd/sqfs.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Bootlin + * + * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com> + * + * squashfs.c: implements SquashFS related commands + */ + +#include <command.h> +#include <fs.h> +#include <squashfs.h> + +static int do_sqfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS); +} + +U_BOOT_CMD(sqfsls, 4, 1, do_sqfs_ls, + "List files in directory. Default: root (/).", + "<interface> [<dev[:part]>] [directory]\n" + " - list files from 'dev' on 'interface' in 'directory'\n" +); + +static int do_sqfs_load(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS); +} + +U_BOOT_CMD(sqfsload, 7, 0, do_sqfs_load, + "load binary file from a SquashFS filesystem", + "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n" + " - Load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from SquashFS filesystem.\n" + " 'pos' gives the file position to start loading from.\n" + " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n" + " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n" + " the load stops on end of file.\n" + " If either 'pos' or 'bytes' are not aligned to\n" + " ARCH_DMA_MINALIGN then a misaligned buffer warning will\n" + " be printed and performance will suffer for the load." +); diff --git a/roms/u-boot/cmd/stackprot_test.c b/roms/u-boot/cmd/stackprot_test.c new file mode 100644 index 000000000..1e26193e8 --- /dev/null +++ b/roms/u-boot/cmd/stackprot_test.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Broadcom + */ + +#include <common.h> +#include <command.h> + +static int do_test_stackprot_fail(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * In order to avoid having the compiler optimize away the stack smashing + * we need to do a little something here. + */ + char a[128]; + + memset(a, 0xa5, 512); + + printf("We have smashed our stack as this should not exceed 128: sizeof(a) = %ld\n", strlen(a)); + + return 0; +} + +U_BOOT_CMD(stackprot_test, 1, 1, do_test_stackprot_fail, + "test stack protector fail", ""); diff --git a/roms/u-boot/cmd/strings.c b/roms/u-boot/cmd/strings.c new file mode 100644 index 000000000..85fdc34f0 --- /dev/null +++ b/roms/u-boot/cmd/strings.c @@ -0,0 +1,46 @@ +/* + * cmd_strings.c - just like `strings` command + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <common.h> +#include <command.h> + +static char *start_addr, *last_addr; + +int do_strings(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc == 1) + return CMD_RET_USAGE; + + if ((flag & CMD_FLAG_REPEAT) == 0) { + start_addr = (char *)simple_strtoul(argv[1], NULL, 16); + if (argc > 2) + last_addr = (char *)simple_strtoul(argv[2], NULL, 16); + else + last_addr = (char *)-1; + } + + char *addr = start_addr; + do { + puts(addr); + puts("\n"); + addr += strlen(addr) + 1; + } while (addr[0] && addr < last_addr); + + last_addr = addr + (last_addr - start_addr); + start_addr = addr; + + return 0; +} + +U_BOOT_CMD( + strings, 3, 1, do_strings, + "display strings", + "<addr> [byte count]\n" + " - display strings at <addr> for at least [byte count] or first double NUL" +); diff --git a/roms/u-boot/cmd/sysboot.c b/roms/u-boot/cmd/sysboot.c new file mode 100644 index 000000000..af6a2f1b7 --- /dev/null +++ b/roms/u-boot/cmd/sysboot.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <fs.h> +#include "pxe_utils.h" + +static char *fs_argv[5]; + +static int do_get_ext2(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr) +{ +#ifdef CONFIG_CMD_EXT2 + fs_argv[0] = "ext2load"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_ext2load(cmdtp, 0, 5, fs_argv)) + return 1; +#endif + return -ENOENT; +} + +static int do_get_fat(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr) +{ +#ifdef CONFIG_CMD_FAT + fs_argv[0] = "fatload"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_fat_fsload(cmdtp, 0, 5, fs_argv)) + return 1; +#endif + return -ENOENT; +} + +static int do_get_any(struct cmd_tbl *cmdtp, const char *file_path, + char *file_addr) +{ +#ifdef CONFIG_CMD_FS_GENERIC + fs_argv[0] = "load"; + fs_argv[3] = file_addr; + fs_argv[4] = (void *)file_path; + + if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY)) + return 1; +#endif + return -ENOENT; +} + +/* + * Boots a system using a local disk syslinux/extlinux file + * + * Returns 0 on success, 1 on error. + */ +static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long pxefile_addr_r; + struct pxe_menu *cfg; + char *pxefile_addr_str; + char *filename; + int prompt = 0; + + is_pxe = false; + + if (argc > 1 && strstr(argv[1], "-p")) { + prompt = 1; + argc--; + argv++; + } + + if (argc < 4) + return cmd_usage(cmdtp); + + if (argc < 5) { + pxefile_addr_str = from_env("pxefile_addr_r"); + if (!pxefile_addr_str) + return 1; + } else { + pxefile_addr_str = argv[4]; + } + + if (argc < 6) { + filename = env_get("bootfile"); + } else { + filename = argv[5]; + env_set("bootfile", filename); + } + + if (strstr(argv[3], "ext2")) { + do_getfile = do_get_ext2; + } else if (strstr(argv[3], "fat")) { + do_getfile = do_get_fat; + } else if (strstr(argv[3], "any")) { + do_getfile = do_get_any; + } else { + printf("Invalid filesystem: %s\n", argv[3]); + return 1; + } + fs_argv[1] = argv[1]; + fs_argv[2] = argv[2]; + + if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { + printf("Invalid pxefile address: %s\n", pxefile_addr_str); + return 1; + } + + if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) { + printf("Error reading config file\n"); + return 1; + } + + cfg = parse_pxefile(cmdtp, pxefile_addr_r); + + if (!cfg) { + printf("Error parsing config file\n"); + return 1; + } + + if (prompt) + cfg->prompt = 1; + + handle_pxe_menu(cmdtp, cfg); + + destroy_pxe_menu(cfg); + + return 0; +} + +U_BOOT_CMD(sysboot, 7, 1, do_sysboot, + "command to get and boot from syslinux files", + "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n" + " - load and parse syslinux menu file 'filename' from ext2, fat\n" + " or any filesystem on 'dev' on 'interface' to address 'addr'" +); diff --git a/roms/u-boot/cmd/terminal.c b/roms/u-boot/cmd/terminal.c new file mode 100644 index 000000000..9e32a4191 --- /dev/null +++ b/roms/u-boot/cmd/terminal.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 OpenMoko, Inc. + * Written by Harald Welte <laforge@openmoko.org> + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <stdio_dev.h> +#include <serial.h> + +int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[]) +{ + int last_tilde = 0; + struct stdio_dev *dev = NULL; + + if (argc < 1) + return -1; + + /* Scan for selected output/input device */ + dev = stdio_get_by_name(argv[1]); + if (!dev) + return -1; + + if (IS_ENABLED(CONFIG_SERIAL)) + serial_reinit_all(); + + printf("Entering terminal mode for port %s\n", dev->name); + puts("Use '~.' to leave the terminal and get back to u-boot\n"); + + while (1) { + int c; + + /* read from console and display on serial port */ + if (stdio_devices[0]->tstc(stdio_devices[0])) { + c = stdio_devices[0]->getc(stdio_devices[0]); + if (last_tilde == 1) { + if (c == '.') { + putc(c); + putc('\n'); + break; + } else { + last_tilde = 0; + /* write the delayed tilde */ + dev->putc(dev, '~'); + /* fall-through to print current + * character */ + } + } + if (c == '~') { + last_tilde = 1; + puts("[u-boot]"); + putc(c); + } + dev->putc(dev, c); + } + + /* read from serial port and display on console */ + if (dev->tstc(dev)) { + c = dev->getc(dev); + putc(c); + } + } + return 0; +} + + +/***************************************************/ + +U_BOOT_CMD( + terminal, 3, 1, do_terminal, + "start terminal emulator", + "" +); diff --git a/roms/u-boot/cmd/test.c b/roms/u-boot/cmd/test.c new file mode 100644 index 000000000..fa7c48fb9 --- /dev/null +++ b/roms/u-boot/cmd/test.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <fs.h> +#include <log.h> + +#define OP_INVALID 0 +#define OP_NOT 1 +#define OP_OR 2 +#define OP_AND 3 +#define OP_STR_EMPTY 4 +#define OP_STR_NEMPTY 5 +#define OP_STR_EQ 6 +#define OP_STR_NEQ 7 +#define OP_STR_LT 8 +#define OP_STR_GT 9 +#define OP_INT_EQ 10 +#define OP_INT_NEQ 11 +#define OP_INT_LT 12 +#define OP_INT_LE 13 +#define OP_INT_GT 14 +#define OP_INT_GE 15 +#define OP_FILE_EXISTS 16 + +const struct { + int arg; + const char *str; + int op; + int adv; +} op_adv[] = { + {1, "=", OP_STR_EQ, 3}, + {1, "!=", OP_STR_NEQ, 3}, + {1, "<", OP_STR_LT, 3}, + {1, ">", OP_STR_GT, 3}, + {1, "-eq", OP_INT_EQ, 3}, + {1, "-ne", OP_INT_NEQ, 3}, + {1, "-lt", OP_INT_LT, 3}, + {1, "-le", OP_INT_LE, 3}, + {1, "-gt", OP_INT_GT, 3}, + {1, "-ge", OP_INT_GE, 3}, + {0, "!", OP_NOT, 1}, + {0, "-o", OP_OR, 1}, + {0, "-a", OP_AND, 1}, + {0, "-z", OP_STR_EMPTY, 2}, + {0, "-n", OP_STR_NEMPTY, 2}, + {0, "-e", OP_FILE_EXISTS, 4}, +}; + +static int do_test(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char * const *ap; + int i, op, left, adv, expr, last_expr, last_unop, last_binop; + + /* args? */ + if (argc < 3) + return 1; + +#ifdef DEBUG + { + debug("test(%d):", argc); + left = 1; + while (argv[left]) + debug(" '%s'", argv[left++]); + } +#endif + + left = argc - 1; + ap = argv + 1; + expr = 0; + last_unop = OP_INVALID; + last_binop = OP_INVALID; + last_expr = -1; + while (left > 0) { + for (i = 0; i < ARRAY_SIZE(op_adv); i++) { + if (left <= op_adv[i].arg) + continue; + if (!strcmp(ap[op_adv[i].arg], op_adv[i].str)) { + op = op_adv[i].op; + adv = op_adv[i].adv; + break; + } + } + if (i == ARRAY_SIZE(op_adv)) { + expr = 1; + break; + } + if (left < adv) { + expr = 1; + break; + } + + switch (op) { + case OP_STR_EMPTY: + expr = strlen(ap[1]) == 0 ? 1 : 0; + break; + case OP_STR_NEMPTY: + expr = strlen(ap[1]) == 0 ? 0 : 1; + break; + case OP_STR_EQ: + expr = strcmp(ap[0], ap[2]) == 0; + break; + case OP_STR_NEQ: + expr = strcmp(ap[0], ap[2]) != 0; + break; + case OP_STR_LT: + expr = strcmp(ap[0], ap[2]) < 0; + break; + case OP_STR_GT: + expr = strcmp(ap[0], ap[2]) > 0; + break; + case OP_INT_EQ: + expr = simple_strtol(ap[0], NULL, 0) == + simple_strtol(ap[2], NULL, 0); + break; + case OP_INT_NEQ: + expr = simple_strtol(ap[0], NULL, 0) != + simple_strtol(ap[2], NULL, 0); + break; + case OP_INT_LT: + expr = simple_strtol(ap[0], NULL, 0) < + simple_strtol(ap[2], NULL, 0); + break; + case OP_INT_LE: + expr = simple_strtol(ap[0], NULL, 0) <= + simple_strtol(ap[2], NULL, 0); + break; + case OP_INT_GT: + expr = simple_strtol(ap[0], NULL, 0) > + simple_strtol(ap[2], NULL, 0); + break; + case OP_INT_GE: + expr = simple_strtol(ap[0], NULL, 0) >= + simple_strtol(ap[2], NULL, 0); + break; + case OP_FILE_EXISTS: + expr = file_exists(ap[1], ap[2], ap[3], FS_TYPE_ANY); + break; + } + + switch (op) { + case OP_OR: + last_expr = expr; + last_binop = OP_OR; + break; + case OP_AND: + last_expr = expr; + last_binop = OP_AND; + break; + case OP_NOT: + if (last_unop == OP_NOT) + last_unop = OP_INVALID; + else + last_unop = OP_NOT; + break; + default: + if (last_unop == OP_NOT) { + expr = !expr; + last_unop = OP_INVALID; + } + + if (last_binop == OP_OR) + expr = last_expr || expr; + else if (last_binop == OP_AND) + expr = last_expr && expr; + last_binop = OP_INVALID; + + break; + } + + ap += adv; left -= adv; + } + + expr = !expr; + + debug (": returns %d\n", expr); + + return expr; +} + +#undef true +#undef false + +U_BOOT_CMD( + test, CONFIG_SYS_MAXARGS, 1, do_test, + "minimal test like /bin/sh", + "[args..]" +); + +static int do_false(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return 1; +} + +U_BOOT_CMD( + false, CONFIG_SYS_MAXARGS, 1, do_false, + "do nothing, unsuccessfully", + NULL +); + +static int do_true(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return 0; +} + +U_BOOT_CMD( + true, CONFIG_SYS_MAXARGS, 1, do_true, + "do nothing, successfully", + NULL +); diff --git a/roms/u-boot/cmd/thordown.c b/roms/u-boot/cmd/thordown.c new file mode 100644 index 000000000..838764cce --- /dev/null +++ b/roms/u-boot/cmd/thordown.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget + * + * Copyright (C) 2013 Lukasz Majewski <l.majewski@samsung.com> + * All rights reserved. + */ + +#include <common.h> +#include <command.h> +#include <thor.h> +#include <dfu.h> +#include <g_dnl.h> +#include <usb.h> + +int do_thor_down(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 4) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + char *interface = argv[2]; + char *devstring = argv[3]; + + int ret; + + puts("TIZEN \"THOR\" Downloader\n"); + + ret = dfu_init_env_entities(interface, devstring); + if (ret) + goto done; + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + ret = usb_gadget_initialize(controller_index); + if (ret) { + pr_err("USB init failed: %d\n", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + ret = g_dnl_register("usb_dnl_thor"); + if (ret) { + pr_err("g_dnl_register failed %d\n", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + ret = thor_init(); + if (ret) { + pr_err("THOR DOWNLOAD failed: %d\n", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + do { + ret = thor_handle(); + if (ret == THOR_DFU_REINIT_NEEDED) { + dfu_free_entities(); + ret = dfu_init_env_entities(interface, devstring); + } + if (ret) { + pr_err("THOR failed: %d\n", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + } while (ret == 0); +exit: + g_dnl_unregister(); + usb_gadget_release(controller_index); +done: + dfu_free_entities(); + + return ret; +} + +U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down, + "TIZEN \"THOR\" downloader", + "<USB_controller> <interface> <dev>\n" + " - device software upgrade via LTHOR TIZEN download\n" + " program via <USB_controller> on device <dev>,\n" + " attached to interface <interface>\n" +); diff --git a/roms/u-boot/cmd/ti/Kconfig b/roms/u-boot/cmd/ti/Kconfig new file mode 100644 index 000000000..efeff0d48 --- /dev/null +++ b/roms/u-boot/cmd/ti/Kconfig @@ -0,0 +1,10 @@ +menu "TI specific command line interface" + +config CMD_DDR3 + bool "command for verifying DDR features" + help + Support for testing ddr3 on TI platforms. This command + supports memory verification, memory comapre and ecc + verification if supported. + +endmenu diff --git a/roms/u-boot/cmd/ti/Makefile b/roms/u-boot/cmd/ti/Makefile new file mode 100644 index 000000000..16fbade9e --- /dev/null +++ b/roms/u-boot/cmd/ti/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + +obj- += dummy.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_CMD_DDR3) += ddr3.o +endif diff --git a/roms/u-boot/cmd/ti/ddr3.c b/roms/u-boot/cmd/ti/ddr3.c new file mode 100644 index 000000000..6b43a7386 --- /dev/null +++ b/roms/u-boot/cmd/ti/ddr3.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EMIF: DDR3 test commands + * + * Copyright (C) 2012-2017 Texas Instruments Incorporated, <www.ti.com> + */ + +#include <cpu_func.h> +#include <env.h> +#include <init.h> +#include <log.h> +#include <asm/arch/hardware.h> +#include <asm/cache.h> +#include <asm/emif.h> +#include <common.h> +#include <command.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_ARCH_KEYSTONE +#include <asm/arch/ddr3.h> +#define DDR_MIN_ADDR CONFIG_SYS_SDRAM_BASE +#define STACKSIZE (512 << 10) /* 512 KiB */ + +#define DDR_REMAP_ADDR 0x80000000 +#define ECC_START_ADDR1 ((DDR_MIN_ADDR - DDR_REMAP_ADDR) >> 17) + +#define ECC_END_ADDR1 (((gd->start_addr_sp - DDR_REMAP_ADDR - \ + STACKSIZE) >> 17) - 2) +#endif + +#define DDR_TEST_BURST_SIZE 1024 + +static int ddr_memory_test(u32 start_address, u32 end_address, int quick) +{ + u32 index_start, value, index; + + index_start = start_address; + + while (1) { + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) + __raw_writel(index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) { + value = __raw_readl(index); + if (value != index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readl(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + if (quick) + continue; + + /* Write a pattern for complementary values */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) + __raw_writel((u32)~index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) { + value = __raw_readl(index); + if (value != ~index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readl(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 2) + __raw_writew((u16)index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 2) { + value = __raw_readw(index); + if (value != (u16)index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readw(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 1) + __raw_writeb((u8)index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 1) { + value = __raw_readb(index); + if (value != (u8)index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readb(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + } + + puts("ddr memory test PASSED!\n"); + return 0; +} + +static int ddr_memory_compare(u32 address1, u32 address2, u32 size) +{ + u32 index, value, index2, value2; + + for (index = address1, index2 = address2; + index < address1 + size; + index += 4, index2 += 4) { + value = __raw_readl(index); + value2 = __raw_readl(index2); + + if (value != value2) { + printf("ddr_memory_test: Compare failed at address = 0x%x value = 0x%x, address2 = 0x%x value2 = 0x%x\n", + index, value, index2, value2); + + return -1; + } + } + + puts("ddr memory compare PASSED!\n"); + return 0; +} + +static void ddr_check_ecc_status(void) +{ + struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; + u32 err_1b = readl(&emif->emif_1b_ecc_err_cnt); + u32 int_status = readl(&emif->emif_irqstatus_raw_sys); + int ecc_test = 0; + char *env; + + env = env_get("ecc_test"); + if (env) + ecc_test = simple_strtol(env, NULL, 0); + + puts("ECC test Status:\n"); + if (int_status & EMIF_INT_WR_ECC_ERR_SYS_MASK) + puts("\tECC test: DDR ECC write error interrupted\n"); + + if (int_status & EMIF_INT_TWOBIT_ECC_ERR_SYS_MASK) + if (!ecc_test) + panic("\tECC test: DDR ECC 2-bit error interrupted"); + + if (int_status & EMIF_INT_ONEBIT_ECC_ERR_SYS_MASK) + puts("\tECC test: DDR ECC 1-bit error interrupted\n"); + + if (err_1b) + printf("\tECC test: 1-bit ECC err count: 0x%x\n", err_1b); +} + +static int ddr_memory_ecc_err(u32 addr, u32 ecc_err) +{ + struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; + u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); + u32 val1, val2, val3; + + debug("Disabling D-Cache before ECC test\n"); + dcache_disable(); + invalidate_dcache_all(); + + puts("Testing DDR ECC:\n"); + puts("\tECC test: Disabling DDR ECC ...\n"); + writel(0, &emif->emif_ecc_ctrl_reg); + + val1 = readl(addr); + val2 = val1 ^ ecc_err; + writel(val2, addr); + + val3 = readl(addr); +#ifdef CONFIG_ARCH_KEYSTONE + ecc_ctrl = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16); + writel(ecc_ctrl, EMIF1_BASE + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET); + ddr3_enable_ecc(EMIF1_BASE, 1); +#else + writel(ecc_ctrl, &emif->emif_ecc_ctrl_reg); +#endif + + printf("\tECC test: addr 0x%x, read data 0x%x, written data 0x%x, err pattern: 0x%x, read after write data 0x%x\n", + addr, val1, val2, ecc_err, val3); + + puts("\tECC test: Enabled DDR ECC ...\n"); + + val1 = readl(addr); + printf("\tECC test: addr 0x%x, read data 0x%x\n", addr, val1); + + ddr_check_ecc_status(); + + debug("Enabling D-cache back after ECC test\n"); + enable_caches(); + + return 0; +} + +static int is_addr_valid(u32 addr) +{ + struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; + u32 start_addr, end_addr, range, ecc_ctrl; + +#ifdef CONFIG_ARCH_KEYSTONE + ecc_ctrl = EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK; + range = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16); +#else + ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); + range = readl(&emif->emif_ecc_address_range_1); +#endif + + /* Check in ecc address range 1 */ + if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK) { + start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) + + CONFIG_SYS_SDRAM_BASE; + end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF + + CONFIG_SYS_SDRAM_BASE; + if ((addr >= start_addr) && (addr <= end_addr)) + /* addr within ecc address range 1 */ + return 1; + } + + /* Check in ecc address range 2 */ + if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_MASK) { + range = readl(&emif->emif_ecc_address_range_2); + start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) + + CONFIG_SYS_SDRAM_BASE; + end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF + + CONFIG_SYS_SDRAM_BASE; + if ((addr >= start_addr) && (addr <= end_addr)) + /* addr within ecc address range 2 */ + return 1; + } + + return 0; +} + +static int is_ecc_enabled(void) +{ + struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; + u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); + + return (ecc_ctrl & EMIF_ECC_CTRL_REG_ECC_EN_MASK) && + (ecc_ctrl & EMIF_ECC_REG_RMW_EN_MASK); +} + +static int do_ddr_test(struct cmd_tbl *cmdtp, + int flag, int argc, char *const argv[]) +{ + u32 start_addr, end_addr, size, ecc_err; + + if ((argc == 4) && (strncmp(argv[1], "ecc_err", 8) == 0)) { + if (!is_ecc_enabled()) { + puts("ECC not enabled. Please Enable ECC any try again\n"); + return CMD_RET_FAILURE; + } + + start_addr = simple_strtoul(argv[2], NULL, 16); + ecc_err = simple_strtoul(argv[3], NULL, 16); + + if (!is_addr_valid(start_addr)) { + puts("Invalid address. Please enter ECC supported address!\n"); + return CMD_RET_FAILURE; + } + + ddr_memory_ecc_err(start_addr, ecc_err); + return 0; + } + + if (!(((argc == 4) && (strncmp(argv[1], "test", 5) == 0)) || + ((argc == 5) && (strncmp(argv[1], "compare", 8) == 0)))) + return cmd_usage(cmdtp); + + start_addr = simple_strtoul(argv[2], NULL, 16); + end_addr = simple_strtoul(argv[3], NULL, 16); + + if ((start_addr < CONFIG_SYS_SDRAM_BASE) || + (start_addr > (CONFIG_SYS_SDRAM_BASE + + get_effective_memsize() - 1)) || + (end_addr < CONFIG_SYS_SDRAM_BASE) || + (end_addr > (CONFIG_SYS_SDRAM_BASE + + get_effective_memsize() - 1)) || (start_addr >= end_addr)) { + puts("Invalid start or end address!\n"); + return cmd_usage(cmdtp); + } + + puts("Please wait ...\n"); + if (argc == 5) { + size = simple_strtoul(argv[4], NULL, 16); + ddr_memory_compare(start_addr, end_addr, size); + } else { + ddr_memory_test(start_addr, end_addr, 0); + } + + return 0; +} + +U_BOOT_CMD(ddr, 5, 1, do_ddr_test, + "DDR3 test", + "test <start_addr in hex> <end_addr in hex> - test DDR from start\n" + " address to end address\n" + "ddr compare <start_addr in hex> <end_addr in hex> <size in hex> -\n" + " compare DDR data of (size) bytes from start address to end\n" + " address\n" + "ddr ecc_err <addr in hex> <bit_err in hex> - generate bit errors\n" + " in DDR data at <addr>, the command will read a 32-bit data\n" + " from <addr>, and write (data ^ bit_err) back to <addr>\n" +); diff --git a/roms/u-boot/cmd/time.c b/roms/u-boot/cmd/time.c new file mode 100644 index 000000000..db8c1892d --- /dev/null +++ b/roms/u-boot/cmd/time.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + */ + +#include <common.h> +#include <command.h> + +static void report_time(ulong cycles) +{ + ulong minutes, seconds, milliseconds; + ulong total_seconds, remainder; + + total_seconds = cycles / CONFIG_SYS_HZ; + remainder = cycles % CONFIG_SYS_HZ; + minutes = total_seconds / 60; + seconds = total_seconds % 60; + /* approximate millisecond value */ + milliseconds = (remainder * 1000 + CONFIG_SYS_HZ / 2) / CONFIG_SYS_HZ; + + printf("\ntime:"); + if (minutes) + printf(" %lu minutes,", minutes); + printf(" %lu.%03lu seconds\n", seconds, milliseconds); +} + +static int do_time(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong cycles = 0; + int retval = 0; + int repeatable = 0; + + if (argc == 1) + return CMD_RET_USAGE; + + retval = cmd_process(0, argc - 1, argv + 1, &repeatable, &cycles); + report_time(cycles); + + return retval; +} + +U_BOOT_CMD(time, CONFIG_SYS_MAXARGS, 0, do_time, + "run commands and summarize execution time", + "command [args...]\n"); diff --git a/roms/u-boot/cmd/timer.c b/roms/u-boot/cmd/timer.c new file mode 100644 index 000000000..551be5dd5 --- /dev/null +++ b/roms/u-boot/cmd/timer.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> + +static int do_timer(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + static ulong start; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "start")) + start = get_timer(0); + + if (!strcmp(argv[1], "get")) { + ulong msecs = get_timer(start) * 1000 / CONFIG_SYS_HZ; + printf("%ld.%03d\n", msecs / 1000, (int)(msecs % 1000)); + } + + return 0; +} + +U_BOOT_CMD( + timer, 2, 1, do_timer, + "access the system timer", + "start - Reset the timer reference.\n" + "timer get - Print the time since 'start'." +); diff --git a/roms/u-boot/cmd/tlv_eeprom.c b/roms/u-boot/cmd/tlv_eeprom.c new file mode 100644 index 000000000..5099cbdd9 --- /dev/null +++ b/roms/u-boot/cmd/tlv_eeprom.c @@ -0,0 +1,1108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * See file CREDITS for list of people who contributed to this + * project. + * + * Copyright (C) 2013 Curt Brune <curt@cumulusnetworks.com> + * Copyright (C) 2014 Srideep <srideep_devireddy@dell.com> + * Copyright (C) 2013 Miles Tseng <miles_tseng@accton.com> + * Copyright (C) 2014,2016 david_yang <david_yang@accton.com> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <i2c.h> +#include <i2c_eeprom.h> +#include <env.h> +#include <init.h> +#include <net.h> +#include <asm/global_data.h> +#include <linux/ctype.h> +#include <u-boot/crc.h> + +#include "tlv_eeprom.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define MAX_TLV_DEVICES 2 + +/* File scope function prototypes */ +static bool is_checksum_valid(u8 *eeprom); +static int read_eeprom(u8 *eeprom); +static void show_eeprom(u8 *eeprom); +static void decode_tlv(struct tlvinfo_tlv *tlv); +static void update_crc(u8 *eeprom); +static int prog_eeprom(u8 *eeprom); +static bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index); +static bool tlvinfo_delete_tlv(u8 *eeprom, u8 code); +static bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval); +static int set_mac(char *buf, const char *string); +static int set_date(char *buf, const char *string); +static int set_bytes(char *buf, const char *string, int *converted_accum); +static void show_tlv_devices(void); + +/* Set to 1 if we've read EEPROM into memory */ +static int has_been_read; +/* The EERPOM contents after being read into memory */ +static u8 eeprom[TLV_INFO_MAX_LEN]; + +static struct udevice *tlv_devices[MAX_TLV_DEVICES]; +static unsigned int current_dev; + +#define to_header(p) ((struct tlvinfo_header *)p) +#define to_entry(p) ((struct tlvinfo_tlv *)p) + +#define HDR_SIZE sizeof(struct tlvinfo_header) +#define ENT_SIZE sizeof(struct tlvinfo_tlv) + +static inline bool is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + +/** + * is_valid_tlv + * + * Perform basic sanity checks on a TLV field. The TLV is pointed to + * by the parameter provided. + * 1. The type code is not reserved (0x00 or 0xFF) + */ +static inline bool is_valid_tlv(struct tlvinfo_tlv *tlv) +{ + return((tlv->type != 0x00) && (tlv->type != 0xFF)); +} + +/** + * is_hex + * + * Tests if character is an ASCII hex digit + */ +static inline u8 is_hex(char p) +{ + return (((p >= '0') && (p <= '9')) || + ((p >= 'A') && (p <= 'F')) || + ((p >= 'a') && (p <= 'f'))); +} + +/** + * is_checksum_valid + * + * Validate the checksum in the provided TlvInfo EEPROM data. First, + * verify that the TlvInfo header is valid, then make sure the last + * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data + * and compare it to the value stored in the EEPROM CRC-32 TLV. + */ +static bool is_checksum_valid(u8 *eeprom) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_crc; + unsigned int calc_crc; + unsigned int stored_crc; + + // Is the eeprom header valid? + if (!is_valid_tlvinfo_header(eeprom_hdr)) + return false; + + // Is the last TLV a CRC? + eeprom_crc = to_entry(&eeprom[HDR_SIZE + + be16_to_cpu(eeprom_hdr->totallen) - (ENT_SIZE + 4)]); + if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4) + return false; + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, + HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); + stored_crc = (eeprom_crc->value[0] << 24) | + (eeprom_crc->value[1] << 16) | + (eeprom_crc->value[2] << 8) | + eeprom_crc->value[3]; + return calc_crc == stored_crc; +} + +/** + * read_eeprom + * + * Read the EEPROM into memory, if it hasn't already been read. + */ +static int read_eeprom(u8 *eeprom) +{ + int ret; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv = to_entry(&eeprom[HDR_SIZE]); + + if (has_been_read) + return 0; + + /* Read the header */ + ret = read_tlv_eeprom((void *)eeprom_hdr, 0, HDR_SIZE, current_dev); + /* If the header was successfully read, read the TLVs */ + if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr)) + ret = read_tlv_eeprom((void *)eeprom_tlv, HDR_SIZE, + be16_to_cpu(eeprom_hdr->totallen), + current_dev); + + // If the contents are invalid, start over with default contents + if (!is_valid_tlvinfo_header(eeprom_hdr) || + !is_checksum_valid(eeprom)) { + strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING); + eeprom_hdr->version = TLV_INFO_VERSION; + eeprom_hdr->totallen = cpu_to_be16(0); + update_crc(eeprom); + } + + has_been_read = 1; + +#ifdef DEBUG + show_eeprom(eeprom); +#endif + + return ret; +} + +/** + * show_eeprom + * + * Display the contents of the EEPROM + */ +static void show_eeprom(u8 *eeprom) +{ + int tlv_end; + int curr_tlv; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + + if (!is_valid_tlvinfo_header(eeprom_hdr)) { + printf("EEPROM does not contain data in a valid TlvInfo format.\n"); + return; + } + + printf("TLV: %u\n", current_dev); + printf("TlvInfo Header:\n"); + printf(" Id String: %s\n", eeprom_hdr->signature); + printf(" Version: %d\n", eeprom_hdr->version); + printf(" Total Length: %d\n", be16_to_cpu(eeprom_hdr->totallen)); + + printf("TLV Name Code Len Value\n"); + printf("-------------------- ---- --- -----\n"); + curr_tlv = HDR_SIZE; + tlv_end = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen); + while (curr_tlv < tlv_end) { + eeprom_tlv = to_entry(&eeprom[curr_tlv]); + if (!is_valid_tlv(eeprom_tlv)) { + printf("Invalid TLV field starting at EEPROM offset %d\n", + curr_tlv); + return; + } + decode_tlv(eeprom_tlv); + curr_tlv += ENT_SIZE + eeprom_tlv->length; + } + + printf("Checksum is %s.\n", + is_checksum_valid(eeprom) ? "valid" : "invalid"); + +#ifdef DEBUG + printf("EEPROM dump: (0x%x bytes)", TLV_INFO_MAX_LEN); + for (i = 0; i < TLV_INFO_MAX_LEN; i++) { + if ((i % 16) == 0) + printf("\n%02X: ", i); + printf("%02X ", eeprom[i]); + } + printf("\n"); +#endif +} + +/** + * Struct for displaying the TLV codes and names. + */ +struct tlv_code_desc { + u8 m_code; + char *m_name; +}; + +/** + * List of TLV codes and names. + */ +static struct tlv_code_desc tlv_code_list[] = { + { TLV_CODE_PRODUCT_NAME, "Product Name"}, + { TLV_CODE_PART_NUMBER, "Part Number"}, + { TLV_CODE_SERIAL_NUMBER, "Serial Number"}, + { TLV_CODE_MAC_BASE, "Base MAC Address"}, + { TLV_CODE_MANUF_DATE, "Manufacture Date"}, + { TLV_CODE_DEVICE_VERSION, "Device Version"}, + { TLV_CODE_LABEL_REVISION, "Label Revision"}, + { TLV_CODE_PLATFORM_NAME, "Platform Name"}, + { TLV_CODE_ONIE_VERSION, "ONIE Version"}, + { TLV_CODE_MAC_SIZE, "MAC Addresses"}, + { TLV_CODE_MANUF_NAME, "Manufacturer"}, + { TLV_CODE_MANUF_COUNTRY, "Country Code"}, + { TLV_CODE_VENDOR_NAME, "Vendor Name"}, + { TLV_CODE_DIAG_VERSION, "Diag Version"}, + { TLV_CODE_SERVICE_TAG, "Service Tag"}, + { TLV_CODE_VENDOR_EXT, "Vendor Extension"}, + { TLV_CODE_CRC_32, "CRC-32"}, +}; + +/** + * Look up a TLV name by its type. + */ +static inline const char *tlv_type2name(u8 type) +{ + char *name = "Unknown"; + int i; + + for (i = 0; i < ARRAY_SIZE(tlv_code_list); i++) { + if (tlv_code_list[i].m_code == type) { + name = tlv_code_list[i].m_name; + break; + } + } + + return name; +} + +/* + * decode_tlv + * + * Print a string representing the contents of the TLV field. The format of + * the string is: + * 1. The name of the field left justified in 20 characters + * 2. The type code in hex right justified in 5 characters + * 3. The length in decimal right justified in 4 characters + * 4. The value, left justified in however many characters it takes + * The validity of EEPROM contents and the TLV field have been verified + * prior to calling this function. + */ +#define DECODE_NAME_MAX 20 + +/* + * The max decode value is currently for the 'raw' type or the 'vendor + * extension' type, both of which have the same decode format. The + * max decode string size is computed as follows: + * + * strlen(" 0xFF") * TLV_VALUE_MAX_LEN + 1 + * + */ +#define DECODE_VALUE_MAX ((5 * TLV_VALUE_MAX_LEN) + 1) + +static void decode_tlv(struct tlvinfo_tlv *tlv) +{ + char name[DECODE_NAME_MAX]; + char value[DECODE_VALUE_MAX]; + int i; + + strncpy(name, tlv_type2name(tlv->type), DECODE_NAME_MAX); + + switch (tlv->type) { + case TLV_CODE_PRODUCT_NAME: + case TLV_CODE_PART_NUMBER: + case TLV_CODE_SERIAL_NUMBER: + case TLV_CODE_MANUF_DATE: + case TLV_CODE_LABEL_REVISION: + case TLV_CODE_PLATFORM_NAME: + case TLV_CODE_ONIE_VERSION: + case TLV_CODE_MANUF_NAME: + case TLV_CODE_MANUF_COUNTRY: + case TLV_CODE_VENDOR_NAME: + case TLV_CODE_DIAG_VERSION: + case TLV_CODE_SERVICE_TAG: + memcpy(value, tlv->value, tlv->length); + value[tlv->length] = 0; + break; + case TLV_CODE_MAC_BASE: + sprintf(value, "%02X:%02X:%02X:%02X:%02X:%02X", + tlv->value[0], tlv->value[1], tlv->value[2], + tlv->value[3], tlv->value[4], tlv->value[5]); + break; + case TLV_CODE_DEVICE_VERSION: + sprintf(value, "%u", tlv->value[0]); + break; + case TLV_CODE_MAC_SIZE: + sprintf(value, "%u", (tlv->value[0] << 8) | tlv->value[1]); + break; + case TLV_CODE_VENDOR_EXT: + value[0] = 0; + for (i = 0; (i < (DECODE_VALUE_MAX / 5)) && (i < tlv->length); + i++) { + sprintf(value, "%s 0x%02X", value, tlv->value[i]); + } + break; + case TLV_CODE_CRC_32: + sprintf(value, "0x%02X%02X%02X%02X", + tlv->value[0], tlv->value[1], + tlv->value[2], tlv->value[3]); + break; + default: + value[0] = 0; + for (i = 0; (i < (DECODE_VALUE_MAX / 5)) && (i < tlv->length); + i++) { + sprintf(value, "%s 0x%02X", value, tlv->value[i]); + } + break; + } + + name[DECODE_NAME_MAX - 1] = 0; + printf("%-20s 0x%02X %3d %s\n", name, tlv->type, tlv->length, value); +} + +/** + * update_crc + * + * This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then + * one is added. This function should be called after each update to the + * EEPROM structure, to make sure the CRC is always correct. + */ +static void update_crc(u8 *eeprom) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_crc; + unsigned int calc_crc; + int eeprom_index; + + // Discover the CRC TLV + if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) { + unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen); + + if ((totallen + ENT_SIZE + 4) > TLV_TOTAL_LEN_MAX) + return; + eeprom_index = HDR_SIZE + totallen; + eeprom_hdr->totallen = cpu_to_be16(totallen + ENT_SIZE + 4); + } + eeprom_crc = to_entry(&eeprom[eeprom_index]); + eeprom_crc->type = TLV_CODE_CRC_32; + eeprom_crc->length = 4; + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, + HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); + eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF; + eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF; + eeprom_crc->value[2] = (calc_crc >> 8) & 0xFF; + eeprom_crc->value[3] = (calc_crc >> 0) & 0xFF; +} + +/** + * prog_eeprom + * + * Write the EEPROM data from CPU memory to the hardware. + */ +static int prog_eeprom(u8 *eeprom) +{ + int ret = 0; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + int eeprom_len; + + update_crc(eeprom); + + eeprom_len = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen); + ret = write_tlv_eeprom(eeprom, eeprom_len); + if (ret) { + printf("Programming failed.\n"); + return -1; + } + + printf("Programming passed.\n"); + return 0; +} + +/** + * show_tlv_code_list - Display the list of TLV codes and names + */ +void show_tlv_code_list(void) +{ + int i; + + printf("TLV Code TLV Name\n"); + printf("======== =================\n"); + for (i = 0; i < ARRAY_SIZE(tlv_code_list); i++) { + printf("0x%02X %s\n", + tlv_code_list[i].m_code, + tlv_code_list[i].m_name); + } +} + +/** + * do_tlv_eeprom + * + * This function implements the tlv_eeprom command. + */ +int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char cmd; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + + // If no arguments, read the EERPOM and display its contents + if (argc == 1) { + read_eeprom(eeprom); + show_eeprom(eeprom); + return 0; + } + + // We only look at the first character to the command, so "read" and + // "reset" will both be treated as "read". + cmd = argv[1][0]; + + // Read the EEPROM contents + if (cmd == 'r') { + has_been_read = 0; + if (!read_eeprom(eeprom)) + printf("EEPROM data loaded from device to memory.\n"); + return 0; + } + + // Subsequent commands require that the EEPROM has already been read. + if (!has_been_read) { + printf("Please read the EEPROM data first, using the 'tlv_eeprom read' command.\n"); + return 0; + } + + // Handle the commands that don't take parameters + if (argc == 2) { + switch (cmd) { + case 'w': /* write */ + prog_eeprom(eeprom); + break; + case 'e': /* erase */ + strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING); + eeprom_hdr->version = TLV_INFO_VERSION; + eeprom_hdr->totallen = cpu_to_be16(0); + update_crc(eeprom); + printf("EEPROM data in memory reset.\n"); + break; + case 'l': /* list */ + show_tlv_code_list(); + break; + case 'd': /* dev */ + show_tlv_devices(); + break; + default: + cmd_usage(cmdtp); + break; + } + return 0; + } + + // The set command takes one or two args. + if (argc > 4) { + cmd_usage(cmdtp); + return 0; + } + + // Set command. If the TLV exists in the EEPROM, delete it. Then if + // data was supplied for this TLV add the TLV with the new contents at + // the end. + if (cmd == 's') { + int tcode; + + tcode = simple_strtoul(argv[2], NULL, 0); + tlvinfo_delete_tlv(eeprom, tcode); + if (argc == 4) + tlvinfo_add_tlv(eeprom, tcode, argv[3]); + } else if (cmd == 'd') { /* 'dev' command */ + unsigned int devnum; + + devnum = simple_strtoul(argv[2], NULL, 0); + if (devnum > MAX_TLV_DEVICES || !tlv_devices[devnum]) { + printf("Invalid device number\n"); + return 0; + } + current_dev = devnum; + has_been_read = 0; + } else { + cmd_usage(cmdtp); + } + + return 0; +} + +/** + * This macro defines the tlv_eeprom command line command. + */ +U_BOOT_CMD(tlv_eeprom, 4, 1, do_tlv_eeprom, + "Display and program the system EEPROM data block.", + "[read|write|set <type_code> <string_value>|erase|list]\n" + "tlv_eeprom\n" + " - With no arguments display the current contents.\n" + "tlv_eeprom dev [dev]\n" + " - List devices or set current EEPROM device.\n" + "tlv_eeprom read\n" + " - Load EEPROM data from device to memory.\n" + "tlv_eeprom write\n" + " - Write the EEPROM data to persistent storage.\n" + "tlv_eeprom set <type_code> <string_value>\n" + " - Set a field to a value.\n" + " - If no string_value, field is deleted.\n" + " - Use 'tlv_eeprom write' to make changes permanent.\n" + "tlv_eeprom erase\n" + " - Reset the in memory EEPROM data.\n" + " - Use 'tlv_eeprom read' to refresh the in memory EEPROM data.\n" + " - Use 'tlv_eeprom write' to make changes permanent.\n" + "tlv_eeprom list\n" + " - List the understood TLV codes and names.\n" + ); + +/** + * tlvinfo_find_tlv + * + * This function finds the TLV with the supplied code in the EERPOM. + * An offset from the beginning of the EEPROM is returned in the + * eeprom_index parameter if the TLV is found. + */ +static bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + int eeprom_end; + + // Search through the TLVs, looking for the first one which matches the + // supplied type code. + *eeprom_index = HDR_SIZE; + eeprom_end = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen); + while (*eeprom_index < eeprom_end) { + eeprom_tlv = to_entry(&eeprom[*eeprom_index]); + if (!is_valid_tlv(eeprom_tlv)) + return false; + if (eeprom_tlv->type == tcode) + return true; + *eeprom_index += ENT_SIZE + eeprom_tlv->length; + } + return(false); +} + +/** + * tlvinfo_delete_tlv + * + * This function deletes the TLV with the specified type code from the + * EEPROM. + */ +static bool tlvinfo_delete_tlv(u8 *eeprom, u8 code) +{ + int eeprom_index; + int tlength; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + + // Find the TLV and then move all following TLVs "forward" + if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + tlength = ENT_SIZE + eeprom_tlv->length; + memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength], + HDR_SIZE + + be16_to_cpu(eeprom_hdr->totallen) - eeprom_index - + tlength); + eeprom_hdr->totallen = + cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - + tlength); + update_crc(eeprom); + return true; + } + return false; +} + +/** + * tlvinfo_add_tlv + * + * This function adds a TLV to the EEPROM, converting the value (a string) to + * the format in which it will be stored in the EEPROM. + */ +#define MAX_TLV_VALUE_LEN 256 +static bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + int new_tlv_len = 0; + u32 value; + char data[MAX_TLV_VALUE_LEN]; + int eeprom_index; + + // Encode each TLV type into the format to be stored in the EERPOM + switch (tcode) { + case TLV_CODE_PRODUCT_NAME: + case TLV_CODE_PART_NUMBER: + case TLV_CODE_SERIAL_NUMBER: + case TLV_CODE_LABEL_REVISION: + case TLV_CODE_PLATFORM_NAME: + case TLV_CODE_ONIE_VERSION: + case TLV_CODE_MANUF_NAME: + case TLV_CODE_MANUF_COUNTRY: + case TLV_CODE_VENDOR_NAME: + case TLV_CODE_DIAG_VERSION: + case TLV_CODE_SERVICE_TAG: + strncpy(data, strval, MAX_TLV_VALUE_LEN); + new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval)); + break; + case TLV_CODE_DEVICE_VERSION: + value = simple_strtoul(strval, NULL, 0); + if (value >= 256) { + printf("ERROR: Device version must be 255 or less. Value supplied: %u", + value); + return false; + } + data[0] = value & 0xFF; + new_tlv_len = 1; + break; + case TLV_CODE_MAC_SIZE: + value = simple_strtoul(strval, NULL, 0); + if (value >= 65536) { + printf("ERROR: MAC Size must be 65535 or less. Value supplied: %u", + value); + return false; + } + data[0] = (value >> 8) & 0xFF; + data[1] = value & 0xFF; + new_tlv_len = 2; + break; + case TLV_CODE_MANUF_DATE: + if (set_date(data, strval) != 0) + return false; + new_tlv_len = 19; + break; + case TLV_CODE_MAC_BASE: + if (set_mac(data, strval) != 0) + return false; + new_tlv_len = 6; + break; + case TLV_CODE_CRC_32: + printf("WARNING: The CRC TLV is set automatically and cannot be set manually.\n"); + return false; + case TLV_CODE_VENDOR_EXT: + default: + if (set_bytes(data, strval, &new_tlv_len) != 0) + return false; + break; + } + + // Is there room for this TLV? + if ((be16_to_cpu(eeprom_hdr->totallen) + ENT_SIZE + new_tlv_len) > + TLV_TOTAL_LEN_MAX) { + printf("ERROR: There is not enough room in the EERPOM to save data.\n"); + return false; + } + + // Add TLV at the end, overwriting CRC TLV if it exists + if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) + eeprom_hdr->totallen = + cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - + ENT_SIZE - 4); + else + eeprom_index = HDR_SIZE + be16_to_cpu(eeprom_hdr->totallen); + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + eeprom_tlv->type = tcode; + eeprom_tlv->length = new_tlv_len; + memcpy(eeprom_tlv->value, data, new_tlv_len); + + // Update the total length and calculate (add) a new CRC-32 TLV + eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) + + ENT_SIZE + new_tlv_len); + update_crc(eeprom); + + return true; +} + +/** + * set_mac + * + * Converts a string MAC address into a binary buffer. + * + * This function takes a pointer to a MAC address string + * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number). + * The string format is verified and then converted to binary and + * stored in a buffer. + */ +static int set_mac(char *buf, const char *string) +{ + char *p = (char *)string; + int i; + int err = 0; + char *end; + + if (!p) { + printf("ERROR: NULL mac addr string passed in.\n"); + return -1; + } + + if (strlen(p) != 17) { + printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p)); + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + for (i = 0; i < 17; i++) { + if ((i % 3) == 2) { + if (p[i] != ':') { + err++; + printf("ERROR: mac: p[%i] != :, found: `%c'\n", + i, p[i]); + break; + } + continue; + } else if (!is_hex(p[i])) { + err++; + printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n", + i, p[i]); + break; + } + } + + if (err != 0) { + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + /* Convert string to binary */ + for (i = 0, p = (char *)string; i < 6; i++) { + buf[i] = p ? simple_strtoul(p, &end, 16) : 0; + if (p) + p = (*end) ? end + 1 : end; + } + + if (!is_valid_ethaddr((u8 *)buf)) { + printf("ERROR: MAC address must not be 00:00:00:00:00:00, a multicast address or FF:FF:FF:FF:FF:FF.\n"); + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + return 0; +} + +/** + * set_date + * + * Validates the format of the data string + * + * This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss) + * and validates that the format is correct. If so the string is copied + * to the supplied buffer. + */ +static int set_date(char *buf, const char *string) +{ + int i; + + if (!string) { + printf("ERROR: NULL date string passed in.\n"); + return -1; + } + + if (strlen(string) != 19) { + printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string)); + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + + for (i = 0; string[i] != 0; i++) { + switch (i) { + case 2: + case 5: + if (string[i] != '/') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + case 10: + if (string[i] != ' ') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + case 13: + case 16: + if (string[i] != ':') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + default: + if (!is_digit(string[i])) { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + } + } + + strcpy(buf, string); + return 0; +} + +/** + * set_bytes + * + * Converts a space-separated string of decimal numbers into a + * buffer of bytes. + * + * This function takes a pointer to a space-separated string of decimal + * numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers + * and converts them to an array of bytes. + */ +static int set_bytes(char *buf, const char *string, int *converted_accum) +{ + char *p = (char *)string; + int i; + uint byte; + + if (!p) { + printf("ERROR: NULL string passed in.\n"); + return -1; + } + + /* Convert string to bytes */ + for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0); + i++) { + while ((*p == ' ') || (*p == '\t') || (*p == ',') || + (*p == ';')) { + p++; + } + if (*p != 0) { + if (!is_digit(*p)) { + printf("ERROR: Non-digit found in byte string: (%s)\n", + string); + return -1; + } + byte = simple_strtoul(p, &p, 0); + if (byte >= 256) { + printf("ERROR: The value specified is greater than 255: (%u) in string: %s\n", + byte, string); + return -1; + } + buf[i] = byte & 0xFF; + } + } + + if (i == TLV_VALUE_MAX_LEN && (*p != 0)) { + printf("ERROR: Trying to assign too many bytes (max: %d) in string: %s\n", + TLV_VALUE_MAX_LEN, string); + return -1; + } + + *converted_accum = i; + return 0; +} + +static void show_tlv_devices(void) +{ + unsigned int dev; + + for (dev = 0; dev < MAX_TLV_DEVICES; dev++) + if (tlv_devices[dev]) + printf("TLV: %u%s\n", dev, + (dev == current_dev) ? " (*)" : ""); +} + +static int find_tlv_devices(struct udevice **tlv_devices_p) +{ + int ret; + int count_dev = 0; + struct udevice *dev; + + for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev); + dev; + ret = uclass_next_device_check(&dev)) { + if (ret == 0) + tlv_devices_p[count_dev++] = dev; + if (count_dev >= MAX_TLV_DEVICES) + break; + } + + return (count_dev == 0) ? -ENODEV : 0; +} + +static struct udevice *find_tlv_device_by_index(int dev_num) +{ + struct udevice *local_tlv_devices[MAX_TLV_DEVICES] = {}; + struct udevice **tlv_devices_p; + int ret; + + if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) { + /* Assume BSS is initialized; use static data */ + if (tlv_devices[dev_num]) + return tlv_devices[dev_num]; + tlv_devices_p = tlv_devices; + } else { + tlv_devices_p = local_tlv_devices; + } + + ret = find_tlv_devices(tlv_devices_p); + if (ret == 0 && tlv_devices_p[dev_num]) + return tlv_devices_p[dev_num]; + + return NULL; +} + +/** + * read_tlv_eeprom - read the hwinfo from i2c EEPROM + */ +int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num) +{ + struct udevice *dev; + + if (dev_num >= MAX_TLV_DEVICES) + return -EINVAL; + + dev = find_tlv_device_by_index(dev_num); + if (!dev) + return -ENODEV; + + return i2c_eeprom_read(dev, offset, eeprom, len); +} + +/** + * write_tlv_eeprom - write the hwinfo to i2c EEPROM + */ +int write_tlv_eeprom(void *eeprom, int len) +{ + if (!(gd->flags & GD_FLG_RELOC)) + return -ENODEV; + if (!tlv_devices[current_dev]) + return -ENODEV; + + return i2c_eeprom_write(tlv_devices[current_dev], 0, eeprom, len); +} + +int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr, + struct tlvinfo_tlv **first_entry, int dev_num) +{ + int ret; + struct tlvinfo_header *tlv_hdr; + struct tlvinfo_tlv *tlv_ent; + + /* Read TLV header */ + ret = read_tlv_eeprom(eeprom, 0, HDR_SIZE, dev_num); + if (ret < 0) + return ret; + + tlv_hdr = eeprom; + if (!is_valid_tlvinfo_header(tlv_hdr)) + return -EINVAL; + + /* Read TLV entries */ + tlv_ent = to_entry(&tlv_hdr[1]); + ret = read_tlv_eeprom(tlv_ent, HDR_SIZE, + be16_to_cpu(tlv_hdr->totallen), dev_num); + if (ret < 0) + return ret; + if (!is_checksum_valid(eeprom)) + return -EINVAL; + + *hdr = tlv_hdr; + *first_entry = tlv_ent; + + return 0; +} + +/** + * mac_read_from_eeprom + * + * Read the MAC addresses from EEPROM + * + * This function reads the MAC addresses from EEPROM and sets the + * appropriate environment variables for each one read. + * + * The environment variables are only set if they haven't been set already. + * This ensures that any user-saved variables are never overwritten. + * + * This function must be called after relocation. + */ +int mac_read_from_eeprom(void) +{ + unsigned int i; + int eeprom_index; + struct tlvinfo_tlv *eeprom_tlv; + int maccount; + u8 macbase[6]; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + + puts("EEPROM: "); + + if (read_eeprom(eeprom)) { + printf("Read failed.\n"); + return -1; + } + + maccount = 1; + if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1]; + } + + memcpy(macbase, "\0\0\0\0\0\0", 6); + if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + memcpy(macbase, eeprom_tlv->value, 6); + } + + for (i = 0; i < maccount; i++) { + if (is_valid_ethaddr(macbase)) { + char ethaddr[18]; + char enetvar[11]; + + sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", + macbase[0], macbase[1], macbase[2], + macbase[3], macbase[4], macbase[5]); + sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i); + /* Only initialize environment variables that are blank + * (i.e. have not yet been set) + */ + if (!env_get(enetvar)) + env_set(enetvar, ethaddr); + + macbase[5]++; + if (macbase[5] == 0) { + macbase[4]++; + if (macbase[4] == 0) { + macbase[3]++; + if (macbase[3] == 0) { + macbase[0] = 0; + macbase[1] = 0; + macbase[2] = 0; + } + } + } + } + } + + printf("%s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version, + be16_to_cpu(eeprom_hdr->totallen)); + + return 0; +} + +/** + * populate_serial_number - read the serial number from EEPROM + * + * This function reads the serial number from the EEPROM and sets the + * appropriate environment variable. + * + * The environment variable is only set if it has not been set + * already. This ensures that any user-saved variables are never + * overwritten. + * + * This function must be called after relocation. + */ +int populate_serial_number(void) +{ + char serialstr[257]; + int eeprom_index; + struct tlvinfo_tlv *eeprom_tlv; + + if (env_get("serial#")) + return 0; + + if (read_eeprom(eeprom)) { + printf("Read failed.\n"); + return -1; + } + + if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length); + serialstr[eeprom_tlv->length] = 0; + env_set("serial#", serialstr); + } + + return 0; +} diff --git a/roms/u-boot/cmd/tpm-common.c b/roms/u-boot/cmd/tpm-common.c new file mode 100644 index 000000000..88c9e08e3 --- /dev/null +++ b/roms/u-boot/cmd/tpm-common.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 The Chromium OS Authors. + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <env.h> +#include <malloc.h> +#include <asm/unaligned.h> +#include <linux/string.h> +#include <tpm-common.h> +#include "tpm-user-utils.h" + +static struct udevice *tpm_dev; + +/** + * Print a byte string in hexdecimal format, 16-bytes per line. + * + * @param data byte string to be printed + * @param count number of bytes to be printed + */ +void print_byte_string(u8 *data, size_t count) +{ + int i, print_newline = 0; + + for (i = 0; i < count; i++) { + printf(" %02x", data[i]); + print_newline = (i % 16 == 15); + if (print_newline) + putc('\n'); + } + /* Avoid duplicated newline at the end */ + if (!print_newline) + putc('\n'); +} + +/** + * Convert a text string of hexdecimal values into a byte string. + * + * @param bytes text string of hexdecimal values with no space + * between them + * @param data output buffer for byte string. The caller has to make + * sure it is large enough for storing the output. If + * NULL is passed, a large enough buffer will be allocated, + * and the caller must free it. + * @param count_ptr output variable for the length of byte string + * @return pointer to output buffer + */ +void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr) +{ + char byte[3]; + size_t count, length; + int i; + + if (!bytes) + return NULL; + length = strlen(bytes); + count = length / 2; + + if (!data) + data = malloc(count); + if (!data) + return NULL; + + byte[2] = '\0'; + for (i = 0; i < length; i += 2) { + byte[0] = bytes[i]; + byte[1] = bytes[i + 1]; + data[i / 2] = (u8)simple_strtoul(byte, NULL, 16); + } + + if (count_ptr) + *count_ptr = count; + + return data; +} + +/** + * report_return_code() - Report any error and return failure or success + * + * @param return_code TPM command return code + * @return value of enum command_ret_t + */ +int report_return_code(int return_code) +{ + if (return_code) { + printf("Error: %d\n", return_code); + return CMD_RET_FAILURE; + } else { + return CMD_RET_SUCCESS; + } +} + +/** + * Return number of values defined by a type string. + * + * @param type_str type string + * @return number of values of type string + */ +int type_string_get_num_values(const char *type_str) +{ + return strlen(type_str); +} + +/** + * Return total size of values defined by a type string. + * + * @param type_str type string + * @return total size of values of type string, or 0 if type string + * contains illegal type character. + */ +size_t type_string_get_space_size(const char *type_str) +{ + size_t size; + + for (size = 0; *type_str; type_str++) { + switch (*type_str) { + case 'b': + size += 1; + break; + case 'w': + size += 2; + break; + case 'd': + size += 4; + break; + default: + return 0; + } + } + + return size; +} + +/** + * Allocate a buffer large enough to hold values defined by a type + * string. The caller has to free the buffer. + * + * @param type_str type string + * @param count pointer for storing size of buffer + * @return pointer to buffer or NULL on error + */ +void *type_string_alloc(const char *type_str, u32 *count) +{ + void *data; + size_t size; + + size = type_string_get_space_size(type_str); + if (!size) + return NULL; + data = malloc(size); + if (data) + *count = size; + + return data; +} + +/** + * Pack values defined by a type string into a buffer. The buffer must have + * large enough space. + * + * @param type_str type string + * @param values text strings of values to be packed + * @param data output buffer of values + * @return 0 on success, non-0 on error + */ +int type_string_pack(const char *type_str, char * const values[], + u8 *data) +{ + size_t offset; + u32 value; + + for (offset = 0; *type_str; type_str++, values++) { + value = simple_strtoul(values[0], NULL, 0); + switch (*type_str) { + case 'b': + data[offset] = value; + offset += 1; + break; + case 'w': + put_unaligned_be16(value, data + offset); + offset += 2; + break; + case 'd': + put_unaligned_be32(value, data + offset); + offset += 4; + break; + default: + return -1; + } + } + + return 0; +} + +/** + * Read values defined by a type string from a buffer, and write these values + * to environment variables. + * + * @param type_str type string + * @param data input buffer of values + * @param vars names of environment variables + * @return 0 on success, non-0 on error + */ +int type_string_write_vars(const char *type_str, u8 *data, + char * const vars[]) +{ + size_t offset; + u32 value; + + for (offset = 0; *type_str; type_str++, vars++) { + switch (*type_str) { + case 'b': + value = data[offset]; + offset += 1; + break; + case 'w': + value = get_unaligned_be16(data + offset); + offset += 2; + break; + case 'd': + value = get_unaligned_be32(data + offset); + offset += 4; + break; + default: + return -1; + } + if (env_set_ulong(*vars, value)) + return -1; + } + + return 0; +} + +static int tpm_show_device(void) +{ + struct udevice *dev; + char buf[80]; + int n = 0, rc; + + for_each_tpm_device(dev) { + rc = tpm_get_desc(dev, buf, sizeof(buf)); + if (rc < 0) + printf("device %d: can't get info\n", n); + else + printf("device %d: %s\n", n, buf); + + n++; + }; + + return 0; +} + +static int tpm_set_device(unsigned long num) +{ + struct udevice *dev; + unsigned long n = 0; + int rc = CMD_RET_FAILURE; + + for_each_tpm_device(dev) { + if (n == num) { + rc = 0; + break; + } + + n++; + } + + if (!rc) + tpm_dev = dev; + + return rc; +} + +int get_tpm(struct udevice **devp) +{ + int rc; + + /* + * To keep a backward compatibility with previous code, + * if a tpm device is not explicitly set, we set the first one. + */ + if (!tpm_dev) { + rc = tpm_set_device(0); + if (rc) { + printf("Couldn't set TPM 0 (rc = %d)\n", rc); + return CMD_RET_FAILURE; + } + } + + if (devp) + *devp = tpm_dev; + + return 0; +} + +int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long num; + int rc; + + if (argc == 2) { + num = simple_strtoul(argv[1], NULL, 10); + + rc = tpm_set_device(num); + if (rc) + printf("Couldn't set TPM %lu (rc = %d)\n", num, rc); + } else { + rc = tpm_show_device(); + } + + return rc; +} + +int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev; + char buf[80]; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + rc = tpm_get_desc(dev, buf, sizeof(buf)); + if (rc < 0) { + printf("Couldn't get TPM info (%d)\n", rc); + return CMD_RET_FAILURE; + } + printf("%s\n", buf); + + return 0; +} + +int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev; + int rc; + + if (argc != 1) + return CMD_RET_USAGE; + rc = get_tpm(&dev); + if (rc) + return rc; + + return report_return_code(tpm_init(dev)); +} + +int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *tpm_commands, *cmd; + struct tpm_chip_priv *priv; + struct udevice *dev; + unsigned int size; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = get_tpm(&dev); + if (ret) + return ret; + + priv = dev_get_uclass_priv(dev); + + /* Below getters return NULL if the desired stack is not built */ + switch (priv->version) { + case TPM_V1: + tpm_commands = get_tpm1_commands(&size); + break; + case TPM_V2: + tpm_commands = get_tpm2_commands(&size); + break; + default: + tpm_commands = NULL; + } + + if (!tpm_commands) + return CMD_RET_USAGE; + + cmd = find_cmd_tbl(argv[1], tpm_commands, size); + if (!cmd) + return CMD_RET_USAGE; + + return cmd->cmd(cmdtp, flag, argc - 1, argv + 1); +} diff --git a/roms/u-boot/cmd/tpm-user-utils.h b/roms/u-boot/cmd/tpm-user-utils.h new file mode 100644 index 000000000..358ddff57 --- /dev/null +++ b/roms/u-boot/cmd/tpm-user-utils.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2013 The Chromium OS Authors. + * Coypright (c) 2013 Guntermann & Drunck GmbH + */ + +#ifndef __TPM_USER_UTILS_H +#define __TPM_USER_UTILS_H + +void print_byte_string(u8 *data, size_t count); +void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr); +int report_return_code(int return_code); +int type_string_get_num_values(const char *type_str); +size_t type_string_get_space_size(const char *type_str); +void *type_string_alloc(const char *type_str, u32 *count); +int type_string_pack(const char *type_str, char * const values[], u8 *data); +int type_string_write_vars(const char *type_str, u8 *data, char * const vars[]); +int get_tpm(struct udevice **devp); + +int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); +int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); + +#endif /* __TPM_USER_UTILS_H */ diff --git a/roms/u-boot/cmd/tpm-v1.c b/roms/u-boot/cmd/tpm-v1.c new file mode 100644 index 000000000..3a7e35d52 --- /dev/null +++ b/roms/u-boot/cmd/tpm-v1.c @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 The Chromium OS Authors. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <malloc.h> +#include <asm/unaligned.h> +#include <tpm-common.h> +#include <tpm-v1.h> +#include "tpm-user-utils.h" +#include <tpm_api.h> + +static int do_tpm_startup(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum tpm_startup_type mode; + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + if (argc != 2) + return CMD_RET_USAGE; + if (!strcasecmp("TPM_ST_CLEAR", argv[1])) { + mode = TPM_ST_CLEAR; + } else if (!strcasecmp("TPM_ST_STATE", argv[1])) { + mode = TPM_ST_STATE; + } else if (!strcasecmp("TPM_ST_DEACTIVATED", argv[1])) { + mode = TPM_ST_DEACTIVATED; + } else { + printf("Couldn't recognize mode string: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + return report_return_code(tpm_startup(dev, mode)); +} + +static int do_tpm_nv_define_space(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, perm, size; + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + perm = simple_strtoul(argv[2], NULL, 0); + size = simple_strtoul(argv[3], NULL, 0); + + return report_return_code(tpm1_nv_define_space(dev, index, perm, size)); +} + +static int do_tpm_nv_read_value(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, count, rc; + struct udevice *dev; + void *data; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = (void *)simple_strtoul(argv[2], NULL, 0); + count = simple_strtoul(argv[3], NULL, 0); + + rc = tpm_nv_read_value(dev, index, data, count); + if (!rc) { + puts("area content:\n"); + print_byte_string(data, count); + } + + return report_return_code(rc); +} + +static int do_tpm_nv_write_value(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + u32 index, rc; + size_t count; + void *data; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = parse_byte_string(argv[2], NULL, &count); + if (!data) { + printf("Couldn't parse byte string %s\n", argv[2]); + return CMD_RET_FAILURE; + } + + rc = tpm_nv_write_value(dev, index, data, count); + free(data); + + return report_return_code(rc); +} + +static int do_tpm_extend(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u8 in_digest[20], out_digest[20]; + struct udevice *dev; + u32 index, rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + if (!parse_byte_string(argv[2], in_digest, NULL)) { + printf("Couldn't parse byte string %s\n", argv[2]); + return CMD_RET_FAILURE; + } + + rc = tpm_pcr_extend(dev, index, in_digest, out_digest); + if (!rc) { + puts("PCR value after execution of the command:\n"); + print_byte_string(out_digest, sizeof(out_digest)); + } + + return report_return_code(rc); +} + +static int do_tpm_pcr_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, count, rc; + struct udevice *dev; + void *data; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 4) + return CMD_RET_USAGE; + index = simple_strtoul(argv[1], NULL, 0); + data = (void *)simple_strtoul(argv[2], NULL, 0); + count = simple_strtoul(argv[3], NULL, 0); + + rc = tpm_pcr_read(dev, index, data, count); + if (!rc) { + puts("Named PCR content:\n"); + print_byte_string(data, count); + } + + return report_return_code(rc); +} + +static int do_tpm_tsc_physical_presence(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + u16 presence; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 2) + return CMD_RET_USAGE; + presence = (u16)simple_strtoul(argv[1], NULL, 0); + + return report_return_code(tpm_tsc_physical_presence(dev, presence)); +} + +static int do_tpm_read_pubek(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + u32 count, rc; + void *data; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 3) + return CMD_RET_USAGE; + data = (void *)simple_strtoul(argv[1], NULL, 0); + count = simple_strtoul(argv[2], NULL, 0); + + rc = tpm_read_pubek(dev, data, count); + if (!rc) { + puts("pubek value:\n"); + print_byte_string(data, count); + } + + return report_return_code(rc); +} + +static int do_tpm_physical_set_deactivated(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + u8 state; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 2) + return CMD_RET_USAGE; + state = (u8)simple_strtoul(argv[1], NULL, 0); + + return report_return_code(tpm_physical_set_deactivated(dev, state)); +} + +static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 cap_area, sub_cap, rc; + void *cap; + size_t count; + struct udevice *dev; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 5) + return CMD_RET_USAGE; + cap_area = simple_strtoul(argv[1], NULL, 0); + sub_cap = simple_strtoul(argv[2], NULL, 0); + cap = (void *)simple_strtoul(argv[3], NULL, 0); + count = simple_strtoul(argv[4], NULL, 0); + + rc = tpm_get_capability(dev, cap_area, sub_cap, cap, count); + if (!rc) { + puts("capability information:\n"); + print_byte_string(cap, count); + } + + return report_return_code(rc); +} + +static int do_tpm_raw_transfer(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + void *command; + u8 response[1024]; + size_t count, response_length = sizeof(response); + u32 rc; + + command = parse_byte_string(argv[1], NULL, &count); + if (!command) { + printf("Couldn't parse byte string %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + rc = get_tpm(&dev); + if (rc) + return rc; + + rc = tpm_xfer(dev, command, count, response, &response_length); + free(command); + if (!rc) { + puts("tpm response:\n"); + print_byte_string(response, response_length); + } + + return report_return_code(rc); +} + +static int do_tpm_nv_define(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, perm, size; + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 4) + return CMD_RET_USAGE; + size = type_string_get_space_size(argv[1]); + if (!size) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + index = simple_strtoul(argv[2], NULL, 0); + perm = simple_strtoul(argv[3], NULL, 0); + + return report_return_code(tpm1_nv_define_space(dev, index, perm, size)); +} + +static int do_tpm_nv_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, count, err; + struct udevice *dev; + void *data; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc < 3) + return CMD_RET_USAGE; + if (argc != 3 + type_string_get_num_values(argv[1])) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], NULL, 0); + data = type_string_alloc(argv[1], &count); + if (!data) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + + err = tpm_nv_read_value(dev, index, data, count); + if (!err) { + if (type_string_write_vars(argv[1], data, argv + 3)) { + printf("Couldn't write to variables\n"); + err = ~0; + } + } + free(data); + + return report_return_code(err); +} + +static int do_tpm_nv_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index, count, err; + struct udevice *dev; + void *data; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc < 3) + return CMD_RET_USAGE; + if (argc != 3 + type_string_get_num_values(argv[1])) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], NULL, 0); + data = type_string_alloc(argv[1], &count); + if (!data) { + printf("Couldn't parse arguments\n"); + return CMD_RET_USAGE; + } + if (type_string_pack(argv[1], argv + 3, data)) { + printf("Couldn't parse arguments\n"); + free(data); + return CMD_RET_USAGE; + } + + err = tpm_nv_write_value(dev, index, data, count); + free(data); + + return report_return_code(err); +} + +#ifdef CONFIG_TPM_AUTH_SESSIONS + +static int do_tpm_oiap(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 auth_handle, err; + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + err = tpm1_oiap(dev, &auth_handle); + + return report_return_code(err); +} + +#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1 +static int do_tpm_load_key_by_sha1(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 parent_handle = 0; + u32 key_len, key_handle, err; + u8 usage_auth[DIGEST_LENGTH]; + u8 parent_hash[DIGEST_LENGTH]; + void *key; + struct udevice *dev; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc < 5) + return CMD_RET_USAGE; + + parse_byte_string(argv[1], parent_hash, NULL); + key = (void *)simple_strtoul(argv[2], NULL, 0); + key_len = simple_strtoul(argv[3], NULL, 0); + if (strlen(argv[4]) != 2 * DIGEST_LENGTH) + return CMD_RET_FAILURE; + parse_byte_string(argv[4], usage_auth, NULL); + + err = tpm_find_key_sha1(usage_auth, parent_hash, &parent_handle); + if (err) { + printf("Could not find matching parent key (err = %d)\n", err); + return CMD_RET_FAILURE; + } + + printf("Found parent key %08x\n", parent_handle); + + err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth, + &key_handle); + if (!err) { + printf("Key handle is 0x%x\n", key_handle); + env_set_hex("key_handle", key_handle); + } + + return report_return_code(err); +} +#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ + +static int do_tpm_load_key2_oiap(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 parent_handle, key_len, key_handle, err; + u8 usage_auth[DIGEST_LENGTH]; + void *key; + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc < 5) + return CMD_RET_USAGE; + + parent_handle = simple_strtoul(argv[1], NULL, 0); + key = (void *)simple_strtoul(argv[2], NULL, 0); + key_len = simple_strtoul(argv[3], NULL, 0); + if (strlen(argv[4]) != 2 * DIGEST_LENGTH) + return CMD_RET_FAILURE; + parse_byte_string(argv[4], usage_auth, NULL); + + err = tpm1_load_key2_oiap(dev, parent_handle, key, key_len, usage_auth, + &key_handle); + if (!err) + printf("Key handle is 0x%x\n", key_handle); + + return report_return_code(err); +} + +static int do_tpm_get_pub_key_oiap(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 key_handle, err; + u8 usage_auth[DIGEST_LENGTH]; + u8 pub_key_buffer[TPM_PUBKEY_MAX_LENGTH]; + size_t pub_key_len = sizeof(pub_key_buffer); + struct udevice *dev; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc < 3) + return CMD_RET_USAGE; + + key_handle = simple_strtoul(argv[1], NULL, 0); + if (strlen(argv[2]) != 2 * DIGEST_LENGTH) + return CMD_RET_FAILURE; + parse_byte_string(argv[2], usage_auth, NULL); + + err = tpm1_get_pub_key_oiap(dev, key_handle, usage_auth, pub_key_buffer, + &pub_key_len); + if (!err) { + printf("dump of received pub key structure:\n"); + print_byte_string(pub_key_buffer, pub_key_len); + } + return report_return_code(err); +} + +TPM_COMMAND_NO_ARG(tpm1_end_oiap) + +#endif /* CONFIG_TPM_AUTH_SESSIONS */ + +#ifdef CONFIG_TPM_FLUSH_RESOURCES +static int do_tpm_flush(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + int type = 0; + int rc; + + rc = get_tpm(&dev); + if (rc) + return rc; + + if (argc != 3) + return CMD_RET_USAGE; + + if (!strcasecmp(argv[1], "key")) + type = TPM_RT_KEY; + else if (!strcasecmp(argv[1], "auth")) + type = TPM_RT_AUTH; + else if (!strcasecmp(argv[1], "hash")) + type = TPM_RT_HASH; + else if (!strcasecmp(argv[1], "trans")) + type = TPM_RT_TRANS; + else if (!strcasecmp(argv[1], "context")) + type = TPM_RT_CONTEXT; + else if (!strcasecmp(argv[1], "counter")) + type = TPM_RT_COUNTER; + else if (!strcasecmp(argv[1], "delegate")) + type = TPM_RT_DELEGATE; + else if (!strcasecmp(argv[1], "daa_tpm")) + type = TPM_RT_DAA_TPM; + else if (!strcasecmp(argv[1], "daa_v0")) + type = TPM_RT_DAA_V0; + else if (!strcasecmp(argv[1], "daa_v1")) + type = TPM_RT_DAA_V1; + + if (!type) { + printf("Resource type %s unknown.\n", argv[1]); + return -1; + } + + if (!strcasecmp(argv[2], "all")) { + u16 res_count; + u8 buf[288]; + u8 *ptr; + int err; + uint i; + + /* fetch list of already loaded resources in the TPM */ + err = tpm_get_capability(dev, TPM_CAP_HANDLE, type, buf, + sizeof(buf)); + if (err) { + printf("tpm_get_capability returned error %d.\n", err); + return -1; + } + res_count = get_unaligned_be16(buf); + ptr = buf + 2; + for (i = 0; i < res_count; ++i, ptr += 4) + tpm1_flush_specific(dev, get_unaligned_be32(ptr), type); + } else { + u32 handle = simple_strtoul(argv[2], NULL, 0); + + if (!handle) { + printf("Illegal resource handle %s\n", argv[2]); + return -1; + } + tpm1_flush_specific(dev, cpu_to_be32(handle), type); + } + + return 0; +} +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ + +#ifdef CONFIG_TPM_LIST_RESOURCES +static int do_tpm_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int type = 0; + u16 res_count; + u8 buf[288]; + u8 *ptr; + int err; + uint i; + + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcasecmp(argv[1], "key")) + type = TPM_RT_KEY; + else if (!strcasecmp(argv[1], "auth")) + type = TPM_RT_AUTH; + else if (!strcasecmp(argv[1], "hash")) + type = TPM_RT_HASH; + else if (!strcasecmp(argv[1], "trans")) + type = TPM_RT_TRANS; + else if (!strcasecmp(argv[1], "context")) + type = TPM_RT_CONTEXT; + else if (!strcasecmp(argv[1], "counter")) + type = TPM_RT_COUNTER; + else if (!strcasecmp(argv[1], "delegate")) + type = TPM_RT_DELEGATE; + else if (!strcasecmp(argv[1], "daa_tpm")) + type = TPM_RT_DAA_TPM; + else if (!strcasecmp(argv[1], "daa_v0")) + type = TPM_RT_DAA_V0; + else if (!strcasecmp(argv[1], "daa_v1")) + type = TPM_RT_DAA_V1; + + if (!type) { + printf("Resource type %s unknown.\n", argv[1]); + return -1; + } + + /* fetch list of already loaded resources in the TPM */ + err = tpm_get_capability(TPM_CAP_HANDLE, type, buf, + sizeof(buf)); + if (err) { + printf("tpm_get_capability returned error %d.\n", err); + return -1; + } + res_count = get_unaligned_be16(buf); + ptr = buf + 2; + + printf("Resources of type %s (%02x):\n", argv[1], type); + if (!res_count) { + puts("None\n"); + } else { + for (i = 0; i < res_count; ++i, ptr += 4) + printf("Index %d: %08x\n", i, get_unaligned_be32(ptr)); + } + + return 0; +} +#endif /* CONFIG_TPM_LIST_RESOURCES */ + +TPM_COMMAND_NO_ARG(tpm_self_test_full) +TPM_COMMAND_NO_ARG(tpm_continue_self_test) +TPM_COMMAND_NO_ARG(tpm_force_clear) +TPM_COMMAND_NO_ARG(tpm_physical_enable) +TPM_COMMAND_NO_ARG(tpm_physical_disable) + +static struct cmd_tbl tpm1_commands[] = { + U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""), + U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""), + U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""), + U_BOOT_CMD_MKENT(startup, 0, 1, + do_tpm_startup, "", ""), + U_BOOT_CMD_MKENT(self_test_full, 0, 1, + do_tpm_self_test_full, "", ""), + U_BOOT_CMD_MKENT(continue_self_test, 0, 1, + do_tpm_continue_self_test, "", ""), + U_BOOT_CMD_MKENT(force_clear, 0, 1, + do_tpm_force_clear, "", ""), + U_BOOT_CMD_MKENT(physical_enable, 0, 1, + do_tpm_physical_enable, "", ""), + U_BOOT_CMD_MKENT(physical_disable, 0, 1, + do_tpm_physical_disable, "", ""), + U_BOOT_CMD_MKENT(nv_define_space, 0, 1, + do_tpm_nv_define_space, "", ""), + U_BOOT_CMD_MKENT(nv_read_value, 0, 1, + do_tpm_nv_read_value, "", ""), + U_BOOT_CMD_MKENT(nv_write_value, 0, 1, + do_tpm_nv_write_value, "", ""), + U_BOOT_CMD_MKENT(extend, 0, 1, + do_tpm_extend, "", ""), + U_BOOT_CMD_MKENT(pcr_read, 0, 1, + do_tpm_pcr_read, "", ""), + U_BOOT_CMD_MKENT(tsc_physical_presence, 0, 1, + do_tpm_tsc_physical_presence, "", ""), + U_BOOT_CMD_MKENT(read_pubek, 0, 1, + do_tpm_read_pubek, "", ""), + U_BOOT_CMD_MKENT(physical_set_deactivated, 0, 1, + do_tpm_physical_set_deactivated, "", ""), + U_BOOT_CMD_MKENT(get_capability, 0, 1, + do_tpm_get_capability, "", ""), + U_BOOT_CMD_MKENT(raw_transfer, 0, 1, + do_tpm_raw_transfer, "", ""), + U_BOOT_CMD_MKENT(nv_define, 0, 1, + do_tpm_nv_define, "", ""), + U_BOOT_CMD_MKENT(nv_read, 0, 1, + do_tpm_nv_read, "", ""), + U_BOOT_CMD_MKENT(nv_write, 0, 1, + do_tpm_nv_write, "", ""), +#ifdef CONFIG_TPM_AUTH_SESSIONS + U_BOOT_CMD_MKENT(oiap, 0, 1, + do_tpm_oiap, "", ""), + U_BOOT_CMD_MKENT(end_oiap, 0, 1, + do_tpm1_end_oiap, "", ""), + U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1, + do_tpm_load_key2_oiap, "", ""), +#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1 + U_BOOT_CMD_MKENT(load_key_by_sha1, 0, 1, + do_tpm_load_key_by_sha1, "", ""), +#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ + U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, + do_tpm_get_pub_key_oiap, "", ""), +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +#ifdef CONFIG_TPM_FLUSH_RESOURCES + U_BOOT_CMD_MKENT(flush, 0, 1, + do_tpm_flush, "", ""), +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ +#ifdef CONFIG_TPM_LIST_RESOURCES + U_BOOT_CMD_MKENT(list, 0, 1, + do_tpm_list, "", ""), +#endif /* CONFIG_TPM_LIST_RESOURCES */ +}; + +struct cmd_tbl *get_tpm1_commands(unsigned int *size) +{ + *size = ARRAY_SIZE(tpm1_commands); + + return tpm1_commands; +} + +U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, +"Issue a TPMv1.x command", +"cmd args...\n" +" - Issue TPM command <cmd> with arguments <args...>.\n" +"Admin Startup and State Commands:\n" +" device [num device]\n" +" - Show all devices or set the specified device\n" +" info - Show information about the TPM\n" +" init\n" +" - Put TPM into a state where it waits for 'startup' command.\n" +" startup mode\n" +" - Issue TPM_Starup command. <mode> is one of TPM_ST_CLEAR,\n" +" TPM_ST_STATE, and TPM_ST_DEACTIVATED.\n" +"Admin Testing Commands:\n" +" self_test_full\n" +" - Test all of the TPM capabilities.\n" +" continue_self_test\n" +" - Inform TPM that it should complete the self-test.\n" +"Admin Opt-in Commands:\n" +" physical_enable\n" +" - Set the PERMANENT disable flag to FALSE using physical presence as\n" +" authorization.\n" +" physical_disable\n" +" - Set the PERMANENT disable flag to TRUE using physical presence as\n" +" authorization.\n" +" physical_set_deactivated 0|1\n" +" - Set deactivated flag.\n" +"Admin Ownership Commands:\n" +" force_clear\n" +" - Issue TPM_ForceClear command.\n" +" tsc_physical_presence flags\n" +" - Set TPM device's Physical Presence flags to <flags>.\n" +"The Capability Commands:\n" +" get_capability cap_area sub_cap addr count\n" +" - Read <count> bytes of TPM capability indexed by <cap_area> and\n" +" <sub_cap> to memory address <addr>.\n" +#if defined(CONFIG_TPM_FLUSH_RESOURCES) || defined(CONFIG_TPM_LIST_RESOURCES) +"Resource management functions\n" +#endif +#ifdef CONFIG_TPM_FLUSH_RESOURCES +" flush resource_type id\n" +" - flushes a resource of type <resource_type> (may be one of key, auth,\n" +" hash, trans, context, counter, delegate, daa_tpm, daa_v0, daa_v1),\n" +" and id <id> from the TPM. Use an <id> of \"all\" to flush all\n" +" resources of that type.\n" +#endif /* CONFIG_TPM_FLUSH_RESOURCES */ +#ifdef CONFIG_TPM_LIST_RESOURCES +" list resource_type\n" +" - lists resources of type <resource_type> (may be one of key, auth,\n" +" hash, trans, context, counter, delegate, daa_tpm, daa_v0, daa_v1),\n" +" contained in the TPM.\n" +#endif /* CONFIG_TPM_LIST_RESOURCES */ +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Storage functions\n" +" loadkey2_oiap parent_handle key_addr key_len usage_auth\n" +" - loads a key data from memory address <key_addr>, <key_len> bytes\n" +" into TPM using the parent key <parent_handle> with authorization\n" +" <usage_auth> (20 bytes hex string).\n" +#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1 +" load_key_by_sha1 parent_hash key_addr key_len usage_auth\n" +" - loads a key data from memory address <key_addr>, <key_len> bytes\n" +" into TPM using the parent hash <parent_hash> (20 bytes hex string)\n" +" with authorization <usage_auth> (20 bytes hex string).\n" +#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ +" get_pub_key_oiap key_handle usage_auth\n" +" - get the public key portion of a loaded key <key_handle> using\n" +" authorization <usage auth> (20 bytes hex string)\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +"Endorsement Key Handling Commands:\n" +" read_pubek addr count\n" +" - Read <count> bytes of the public endorsement key to memory\n" +" address <addr>\n" +"Integrity Collection and Reporting Commands:\n" +" extend index digest_hex_string\n" +" - Add a new measurement to a PCR. Update PCR <index> with the 20-bytes\n" +" <digest_hex_string>\n" +" pcr_read index addr count\n" +" - Read <count> bytes from PCR <index> to memory address <addr>.\n" +#ifdef CONFIG_TPM_AUTH_SESSIONS +"Authorization Sessions\n" +" oiap\n" +" - setup an OIAP session\n" +" end_oiap\n" +" - terminates an active OIAP session\n" +#endif /* CONFIG_TPM_AUTH_SESSIONS */ +"Non-volatile Storage Commands:\n" +" nv_define_space index permission size\n" +" - Establish a space at index <index> with <permission> of <size> bytes.\n" +" nv_read_value index addr count\n" +" - Read <count> bytes from space <index> to memory address <addr>.\n" +" nv_write_value index addr count\n" +" - Write <count> bytes from memory address <addr> to space <index>.\n" +"Miscellaneous helper functions:\n" +" raw_transfer byte_string\n" +" - Send a byte string <byte_string> to TPM and print the response.\n" +" Non-volatile storage helper functions:\n" +" These helper functions treat a non-volatile space as a non-padded\n" +" sequence of integer values. These integer values are defined by a type\n" +" string, which is a text string of 'bwd' characters: 'b' means a 8-bit\n" +" value, 'w' 16-bit value, 'd' 32-bit value. All helper functions take\n" +" a type string as their first argument.\n" +" nv_define type_string index perm\n" +" - Define a space <index> with permission <perm>.\n" +" nv_read types_string index vars...\n" +" - Read from space <index> to environment variables <vars...>.\n" +" nv_write types_string index values...\n" +" - Write to space <index> from values <values...>.\n" +); diff --git a/roms/u-boot/cmd/tpm-v2.c b/roms/u-boot/cmd/tpm-v2.c new file mode 100644 index 000000000..daae91100 --- /dev/null +++ b/roms/u-boot/cmd/tpm-v2.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Bootlin + * Author: Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <log.h> +#include <mapmem.h> +#include <tpm-common.h> +#include <tpm-v2.h> +#include "tpm-user-utils.h" + +static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum tpm2_startup_types mode; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcasecmp("TPM2_SU_CLEAR", argv[1])) { + mode = TPM2_SU_CLEAR; + } else if (!strcasecmp("TPM2_SU_STATE", argv[1])) { + mode = TPM2_SU_STATE; + } else { + printf("Couldn't recognize mode string: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + return report_return_code(tpm2_startup(dev, mode)); +} + +static int do_tpm2_self_test(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + enum tpm2_yes_no full_test; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + if (argc != 2) + return CMD_RET_USAGE; + + if (!strcasecmp("full", argv[1])) { + full_test = TPMI_YES; + } else if (!strcasecmp("continue", argv[1])) { + full_test = TPMI_NO; + } else { + printf("Couldn't recognize test mode: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + + return report_return_code(tpm2_self_test(dev, full_test)); +} + +static int do_tpm2_clear(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 handle = 0; + const char *pw = (argc < 3) ? NULL : argv[2]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + + if (pw_sz > TPM2_DIGEST_LEN) + return -EINVAL; + + if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1])) + handle = TPM2_RH_LOCKOUT; + else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1])) + handle = TPM2_RH_PLATFORM; + else + return CMD_RET_USAGE; + + return report_return_code(tpm2_clear(dev, handle, pw, pw_sz)); +} + +static int do_tpm2_pcr_extend(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct tpm_chip_priv *priv; + u32 index = simple_strtoul(argv[1], NULL, 0); + void *digest = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0); + int ret; + u32 rc; + + if (argc != 3) + return CMD_RET_USAGE; + + ret = get_tpm(&dev); + if (ret) + return ret; + + priv = dev_get_uclass_priv(dev); + if (!priv) + return -EINVAL; + + if (index >= priv->pcr_count) + return -EINVAL; + + rc = tpm2_pcr_extend(dev, index, TPM2_ALG_SHA256, digest, + TPM2_DIGEST_LEN); + + unmap_sysmem(digest); + + return report_return_code(rc); +} + +static int do_tpm_pcr_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct tpm_chip_priv *priv; + u32 index, rc; + unsigned int updates; + void *data; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + ret = get_tpm(&dev); + if (ret) + return ret; + + priv = dev_get_uclass_priv(dev); + if (!priv) + return -EINVAL; + + index = simple_strtoul(argv[1], NULL, 0); + if (index >= priv->pcr_count) + return -EINVAL; + + data = map_sysmem(simple_strtoul(argv[2], NULL, 0), 0); + + rc = tpm2_pcr_read(dev, index, priv->pcr_select_min, data, &updates); + if (!rc) { + printf("PCR #%u content (%u known updates):\n", index, updates); + print_byte_string(data, TPM2_DIGEST_LEN); + } + + unmap_sysmem(data); + + return report_return_code(rc); +} + +static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 capability, property, rc; + u8 *data; + size_t count; + int i, j; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (argc != 5) + return CMD_RET_USAGE; + + capability = simple_strtoul(argv[1], NULL, 0); + property = simple_strtoul(argv[2], NULL, 0); + data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0); + count = simple_strtoul(argv[4], NULL, 0); + + rc = tpm2_get_capability(dev, capability, property, data, count); + if (rc) + goto unmap_data; + + printf("Capabilities read from TPM:\n"); + for (i = 0; i < count; i++) { + printf("Property 0x"); + for (j = 0; j < 4; j++) + printf("%02x", data[(i * 8) + j + sizeof(u32)]); + printf(": 0x"); + for (j = 4; j < 8; j++) + printf("%02x", data[(i * 8) + j + sizeof(u32)]); + printf("\n"); + } + +unmap_data: + unmap_sysmem(data); + + return report_return_code(rc); +} + +static int do_tpm_dam_reset(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *pw = (argc < 2) ? NULL : argv[1]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (argc > 2) + return CMD_RET_USAGE; + + if (pw_sz > TPM2_DIGEST_LEN) + return -EINVAL; + + return report_return_code(tpm2_dam_reset(dev, pw, pw_sz)); +} + +static int do_tpm_dam_parameters(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *pw = (argc < 5) ? NULL : argv[4]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + /* + * No Dictionary Attack Mitigation (DAM) means: + * maxtries = 0xFFFFFFFF, recovery_time = 1, lockout_recovery = 0 + */ + unsigned long int max_tries; + unsigned long int recovery_time; + unsigned long int lockout_recovery; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (argc < 4 || argc > 5) + return CMD_RET_USAGE; + + if (pw_sz > TPM2_DIGEST_LEN) + return -EINVAL; + + if (strict_strtoul(argv[1], 0, &max_tries)) + return CMD_RET_USAGE; + + if (strict_strtoul(argv[2], 0, &recovery_time)) + return CMD_RET_USAGE; + + if (strict_strtoul(argv[3], 0, &lockout_recovery)) + return CMD_RET_USAGE; + + log(LOGC_NONE, LOGL_INFO, "Changing dictionary attack parameters:\n"); + log(LOGC_NONE, LOGL_INFO, "- maxTries: %lu", max_tries); + log(LOGC_NONE, LOGL_INFO, "- recoveryTime: %lu\n", recovery_time); + log(LOGC_NONE, LOGL_INFO, "- lockoutRecovery: %lu\n", lockout_recovery); + + return report_return_code(tpm2_dam_parameters(dev, pw, pw_sz, max_tries, + recovery_time, + lockout_recovery)); +} + +static int do_tpm_change_auth(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 handle; + const char *newpw = argv[2]; + const char *oldpw = (argc == 3) ? NULL : argv[3]; + const ssize_t newpw_sz = strlen(newpw); + const ssize_t oldpw_sz = oldpw ? strlen(oldpw) : 0; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + if (newpw_sz > TPM2_DIGEST_LEN || oldpw_sz > TPM2_DIGEST_LEN) + return -EINVAL; + + if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1])) + handle = TPM2_RH_LOCKOUT; + else if (!strcasecmp("TPM2_RH_ENDORSEMENT", argv[1])) + handle = TPM2_RH_ENDORSEMENT; + else if (!strcasecmp("TPM2_RH_OWNER", argv[1])) + handle = TPM2_RH_OWNER; + else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1])) + handle = TPM2_RH_PLATFORM; + else + return CMD_RET_USAGE; + + return report_return_code(tpm2_change_auth(dev, handle, newpw, newpw_sz, + oldpw, oldpw_sz)); +} + +static int do_tpm_pcr_setauthpolicy(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 index = simple_strtoul(argv[1], NULL, 0); + char *key = argv[2]; + const char *pw = (argc < 4) ? NULL : argv[3]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (strlen(key) != TPM2_DIGEST_LEN) + return -EINVAL; + + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + return report_return_code(tpm2_pcr_setauthpolicy(dev, pw, pw_sz, index, + key)); +} + +static int do_tpm_pcr_setauthvalue(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + u32 index = simple_strtoul(argv[1], NULL, 0); + char *key = argv[2]; + const ssize_t key_sz = strlen(key); + const char *pw = (argc < 4) ? NULL : argv[3]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + struct udevice *dev; + int ret; + + ret = get_tpm(&dev); + if (ret) + return ret; + + if (strlen(key) != TPM2_DIGEST_LEN) + return -EINVAL; + + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + return report_return_code(tpm2_pcr_setauthvalue(dev, pw, pw_sz, index, + key, key_sz)); +} + +static struct cmd_tbl tpm2_commands[] = { + U_BOOT_CMD_MKENT(device, 0, 1, do_tpm_device, "", ""), + U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""), + U_BOOT_CMD_MKENT(init, 0, 1, do_tpm_init, "", ""), + U_BOOT_CMD_MKENT(startup, 0, 1, do_tpm2_startup, "", ""), + U_BOOT_CMD_MKENT(self_test, 0, 1, do_tpm2_self_test, "", ""), + U_BOOT_CMD_MKENT(clear, 0, 1, do_tpm2_clear, "", ""), + U_BOOT_CMD_MKENT(pcr_extend, 0, 1, do_tpm2_pcr_extend, "", ""), + U_BOOT_CMD_MKENT(pcr_read, 0, 1, do_tpm_pcr_read, "", ""), + U_BOOT_CMD_MKENT(get_capability, 0, 1, do_tpm_get_capability, "", ""), + U_BOOT_CMD_MKENT(dam_reset, 0, 1, do_tpm_dam_reset, "", ""), + U_BOOT_CMD_MKENT(dam_parameters, 0, 1, do_tpm_dam_parameters, "", ""), + U_BOOT_CMD_MKENT(change_auth, 0, 1, do_tpm_change_auth, "", ""), + U_BOOT_CMD_MKENT(pcr_setauthpolicy, 0, 1, + do_tpm_pcr_setauthpolicy, "", ""), + U_BOOT_CMD_MKENT(pcr_setauthvalue, 0, 1, + do_tpm_pcr_setauthvalue, "", ""), +}; + +struct cmd_tbl *get_tpm2_commands(unsigned int *size) +{ + *size = ARRAY_SIZE(tpm2_commands); + + return tpm2_commands; +} + +U_BOOT_CMD(tpm2, CONFIG_SYS_MAXARGS, 1, do_tpm, "Issue a TPMv2.x command", +"<command> [<arguments>]\n" +"\n" +"device [num device]\n" +" Show all devices or set the specified device\n" +"info\n" +" Show information about the TPM.\n" +"init\n" +" Initialize the software stack. Always the first command to issue.\n" +"startup <mode>\n" +" Issue a TPM2_Startup command.\n" +" <mode> is one of:\n" +" * TPM2_SU_CLEAR (reset state)\n" +" * TPM2_SU_STATE (preserved state)\n" +"self_test <type>\n" +" Test the TPM capabilities.\n" +" <type> is one of:\n" +" * full (perform all tests)\n" +" * continue (only check untested tests)\n" +"clear <hierarchy>\n" +" Issue a TPM2_Clear command.\n" +" <hierarchy> is one of:\n" +" * TPM2_RH_LOCKOUT\n" +" * TPM2_RH_PLATFORM\n" +"pcr_extend <pcr> <digest_addr>\n" +" Extend PCR #<pcr> with digest at <digest_addr>.\n" +" <pcr>: index of the PCR\n" +" <digest_addr>: address of a 32-byte SHA256 digest\n" +"pcr_read <pcr> <digest_addr>\n" +" Read PCR #<pcr> to memory address <digest_addr>.\n" +" <pcr>: index of the PCR\n" +" <digest_addr>: address to store the a 32-byte SHA256 digest\n" +"get_capability <capability> <property> <addr> <count>\n" +" Read and display <count> entries indexed by <capability>/<property>.\n" +" Values are 4 bytes long and are written at <addr>.\n" +" <capability>: capability\n" +" <property>: property\n" +" <addr>: address to store <count> entries of 4 bytes\n" +" <count>: number of entries to retrieve\n" +"dam_reset [<password>]\n" +" If the TPM is not in a LOCKOUT state, reset the internal error counter.\n" +" <password>: optional password\n" +"dam_parameters <max_tries> <recovery_time> <lockout_recovery> [<password>]\n" +" If the TPM is not in a LOCKOUT state, set the DAM parameters\n" +" <maxTries>: maximum number of failures before lockout,\n" +" 0 means always locking\n" +" <recoveryTime>: time before decrement of the error counter,\n" +" 0 means no lockout\n" +" <lockoutRecovery>: time of a lockout (before the next try),\n" +" 0 means a reboot is needed\n" +" <password>: optional password of the LOCKOUT hierarchy\n" +"change_auth <hierarchy> <new_pw> [<old_pw>]\n" +" <hierarchy>: the hierarchy\n" +" <new_pw>: new password for <hierarchy>\n" +" <old_pw>: optional previous password of <hierarchy>\n" +"pcr_setauthpolicy|pcr_setauthvalue <pcr> <key> [<password>]\n" +" Change the <key> to access PCR #<pcr>.\n" +" hierarchy and may be empty.\n" +" /!\\WARNING: untested function, use at your own risks !\n" +" <pcr>: index of the PCR\n" +" <key>: secret to protect the access of PCR #<pcr>\n" +" <password>: optional password of the PLATFORM hierarchy\n" +); diff --git a/roms/u-boot/cmd/tpm_test.c b/roms/u-boot/cmd/tpm_test.c new file mode 100644 index 000000000..a3ccb12f5 --- /dev/null +++ b/roms/u-boot/cmd/tpm_test.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <command.h> +#include <cpu_func.h> +#include <log.h> +#include <tpm-v1.h> +#include "tpm-user-utils.h" +#include <tpm_api.h> + +/* Prints error and returns on failure */ +#define TPM_CHECK(tpm_command) do { \ + uint32_t result; \ + \ + result = (tpm_command); \ + if (result != TPM_SUCCESS) { \ + printf("TEST FAILED: line %d: " #tpm_command ": 0x%x\n", \ + __LINE__, result); \ + return result; \ + } \ +} while (0) + +#define INDEX0 0xda70 +#define INDEX1 0xda71 +#define INDEX2 0xda72 +#define INDEX3 0xda73 +#define INDEX_INITIALISED 0xda80 +#define PHYS_PRESENCE 4 +#define PRESENCE 8 + +static uint32_t TlclStartupIfNeeded(struct udevice *dev) +{ + uint32_t result = tpm_startup(dev, TPM_ST_CLEAR); + + return result == TPM_INVALID_POSTINIT ? TPM_SUCCESS : result; +} + +static int test_timer(struct udevice *dev) +{ + printf("get_timer(0) = %lu\n", get_timer(0)); + return 0; +} + +static uint32_t tpm_get_flags(struct udevice *dev, uint8_t *disable, + uint8_t *deactivated, uint8_t *nvlocked) +{ + struct tpm_permanent_flags pflags; + uint32_t result; + + result = tpm1_get_permanent_flags(dev, &pflags); + if (result) + return result; + if (disable) + *disable = pflags.disable; + if (deactivated) + *deactivated = pflags.deactivated; + if (nvlocked) + *nvlocked = pflags.nv_locked; + debug("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n", + pflags.disable, pflags.deactivated, pflags.nv_locked); + + return 0; +} + +static uint32_t tpm_nv_write_value_lock(struct udevice *dev, uint32_t index) +{ + debug("TPM: Write lock 0x%x\n", index); + + return tpm_nv_write_value(dev, index, NULL, 0); +} + +static int tpm_is_owned(struct udevice *dev) +{ + uint8_t response[TPM_PUBEK_SIZE]; + uint32_t result; + + result = tpm_read_pubek(dev, response, sizeof(response)); + + return result != TPM_SUCCESS; +} + +static int test_early_extend(struct udevice *dev) +{ + uint8_t value_in[20]; + uint8_t value_out[20]; + + printf("Testing earlyextend ..."); + tpm_init(dev); + TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test(dev)); + TPM_CHECK(tpm_pcr_extend(dev, 1, value_in, value_out)); + printf("done\n"); + return 0; +} + +static int test_early_nvram(struct udevice *dev) +{ + uint32_t x; + + printf("Testing earlynvram ..."); + tpm_init(dev); + TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x))); + printf("done\n"); + return 0; +} + +static int test_early_nvram2(struct udevice *dev) +{ + uint32_t x; + + printf("Testing earlynvram2 ..."); + tpm_init(dev); + TPM_CHECK(tpm_startup(dev, TPM_ST_CLEAR)); + TPM_CHECK(tpm_continue_self_test(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x))); + printf("done\n"); + return 0; +} + +static int test_enable(struct udevice *dev) +{ + uint8_t disable = 0, deactivated = 0; + + printf("Testing enable ...\n"); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_self_test_full(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + TPM_CHECK(tpm_physical_enable(dev)); + TPM_CHECK(tpm_physical_set_deactivated(dev, 0)); + TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + if (disable == 1 || deactivated == 1) + printf("\tfailed to enable or activate\n"); + printf("\tdone\n"); + return 0; +} + +#define reboot() do { \ + printf("\trebooting...\n"); \ + reset_cpu(); \ +} while (0) + +static int test_fast_enable(struct udevice *dev) +{ + uint8_t disable = 0, deactivated = 0; + int i; + + printf("Testing fastenable ...\n"); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_self_test_full(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, deactivated); + for (i = 0; i < 2; i++) { + TPM_CHECK(tpm_force_clear(dev)); + TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, + deactivated); + assert(disable == 1 && deactivated == 1); + TPM_CHECK(tpm_physical_enable(dev)); + TPM_CHECK(tpm_physical_set_deactivated(dev, 0)); + TPM_CHECK(tpm_get_flags(dev, &disable, &deactivated, NULL)); + printf("\tdisable is %d, deactivated is %d\n", disable, + deactivated); + assert(disable == 0 && deactivated == 0); + } + printf("\tdone\n"); + return 0; +} + +static int test_global_lock(struct udevice *dev) +{ + uint32_t zero = 0; + uint32_t result; + uint32_t x; + + printf("Testing globallock ...\n"); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_self_test_full(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero, + sizeof(uint32_t))); + TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero, + sizeof(uint32_t))); + TPM_CHECK(tpm_set_global_lock(dev)); + /* Verifies that write to index0 fails */ + x = 1; + result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)); + assert(result == TPM_AREA_LOCKED); + TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x))); + assert(x == 0); + /* Verifies that write to index1 is still possible */ + x = 2; + TPM_CHECK(tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x))); + assert(x == 2); + /* Turns off PP */ + tpm_tsc_physical_presence(dev, PHYS_PRESENCE); + /* Verifies that write to index1 fails */ + x = 3; + result = tpm_nv_write_value(dev, INDEX1, (uint8_t *)&x, sizeof(x)); + assert(result == TPM_BAD_PRESENCE); + TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x))); + assert(x == 2); + printf("\tdone\n"); + return 0; +} + +static int test_lock(struct udevice *dev) +{ + printf("Testing lock ...\n"); + tpm_init(dev); + tpm_startup(dev, TPM_ST_CLEAR); + tpm_self_test_full(dev); + tpm_tsc_physical_presence(dev, PRESENCE); + tpm_nv_write_value_lock(dev, INDEX0); + printf("\tLocked 0x%x\n", INDEX0); + printf("\tdone\n"); + return 0; +} + +static void initialise_spaces(struct udevice *dev) +{ + uint32_t zero = 0; + uint32_t perm = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_PPWRITE; + + printf("\tInitialising spaces\n"); + tpm1_nv_set_locked(dev); /* useful only the first time */ + tpm1_nv_define_space(dev, INDEX0, perm, 4); + tpm_nv_write_value(dev, INDEX0, (uint8_t *)&zero, 4); + tpm1_nv_define_space(dev, INDEX1, perm, 4); + tpm_nv_write_value(dev, INDEX1, (uint8_t *)&zero, 4); + tpm1_nv_define_space(dev, INDEX2, perm, 4); + tpm_nv_write_value(dev, INDEX2, (uint8_t *)&zero, 4); + tpm1_nv_define_space(dev, INDEX3, perm, 4); + tpm_nv_write_value(dev, INDEX3, (uint8_t *)&zero, 4); + perm = TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_WRITE_STCLEAR | + TPM_NV_PER_PPWRITE; + tpm1_nv_define_space(dev, INDEX_INITIALISED, perm, 1); +} + +static int test_readonly(struct udevice *dev) +{ + uint8_t c; + uint32_t index_0, index_1, index_2, index_3; + int read0, read1, read2, read3; + + printf("Testing readonly ...\n"); + tpm_init(dev); + tpm_startup(dev, TPM_ST_CLEAR); + tpm_self_test_full(dev); + tpm_tsc_physical_presence(dev, PRESENCE); + /* + * Checks if initialisation has completed by trying to read-lock a + * space that's created at the end of initialisation + */ + if (tpm_nv_read_value(dev, INDEX_INITIALISED, &c, 0) == TPM_BADINDEX) { + /* The initialisation did not complete */ + initialise_spaces(dev); + } + + /* Checks if spaces are OK or messed up */ + read0 = tpm_nv_read_value(dev, INDEX0, (uint8_t *)&index_0, + sizeof(index_0)); + read1 = tpm_nv_read_value(dev, INDEX1, (uint8_t *)&index_1, + sizeof(index_1)); + read2 = tpm_nv_read_value(dev, INDEX2, (uint8_t *)&index_2, + sizeof(index_2)); + read3 = tpm_nv_read_value(dev, INDEX3, (uint8_t *)&index_3, + sizeof(index_3)); + if (read0 || read1 || read2 || read3) { + printf("Invalid contents\n"); + return 0; + } + + /* + * Writes space, and locks it. Then attempts to write again. + * I really wish I could use the imperative. + */ + index_0 += 1; + if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0, + sizeof(index_0) != + TPM_SUCCESS)) { + pr_err("\tcould not write index 0\n"); + } + tpm_nv_write_value_lock(dev, INDEX0); + if (tpm_nv_write_value(dev, INDEX0, (uint8_t *)&index_0, + sizeof(index_0)) == + TPM_SUCCESS) + pr_err("\tindex 0 is not locked\n"); + + printf("\tdone\n"); + return 0; +} + +static int test_redefine_unowned(struct udevice *dev) +{ + uint32_t perm; + uint32_t result; + uint32_t x; + + printf("Testing redefine_unowned ..."); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_self_test_full(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + assert(!tpm_is_owned(dev)); + + /* Ensures spaces exist. */ + TPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x))); + TPM_CHECK(tpm_nv_read_value(dev, INDEX1, (uint8_t *)&x, sizeof(x))); + + /* Redefines spaces a couple of times. */ + perm = TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK; + TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm, + 2 * sizeof(uint32_t))); + TPM_CHECK(tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t))); + perm = TPM_NV_PER_PPWRITE; + TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, + 2 * sizeof(uint32_t))); + TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t))); + + /* Sets the global lock */ + tpm_set_global_lock(dev); + + /* Verifies that index0 cannot be redefined */ + result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t)); + assert(result == TPM_AREA_LOCKED); + + /* Checks that index1 can */ + TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, + 2 * sizeof(uint32_t))); + TPM_CHECK(tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t))); + + /* Turns off PP */ + tpm_tsc_physical_presence(dev, PHYS_PRESENCE); + + /* Verifies that neither index0 nor index1 can be redefined */ + result = tpm1_nv_define_space(dev, INDEX0, perm, sizeof(uint32_t)); + assert(result == TPM_BAD_PRESENCE); + result = tpm1_nv_define_space(dev, INDEX1, perm, sizeof(uint32_t)); + assert(result == TPM_BAD_PRESENCE); + + printf("done\n"); + return 0; +} + +#define PERMPPGL (TPM_NV_PER_PPWRITE | TPM_NV_PER_GLOBALLOCK) +#define PERMPP TPM_NV_PER_PPWRITE + +static int test_space_perm(struct udevice *dev) +{ + uint32_t perm; + + printf("Testing spaceperm ..."); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_continue_self_test(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_get_permissions(dev, INDEX0, &perm)); + assert((perm & PERMPPGL) == PERMPPGL); + TPM_CHECK(tpm_get_permissions(dev, INDEX1, &perm)); + assert((perm & PERMPP) == PERMPP); + printf("done\n"); + return 0; +} + +static int test_startup(struct udevice *dev) +{ + uint32_t result; + + printf("Testing startup ...\n"); + + tpm_init(dev); + result = tpm_startup(dev, TPM_ST_CLEAR); + if (result != 0 && result != TPM_INVALID_POSTINIT) + printf("\ttpm startup failed with 0x%x\n", result); + result = tpm_get_flags(dev, NULL, NULL, NULL); + if (result != 0) + printf("\ttpm getflags failed with 0x%x\n", result); + printf("\texecuting SelfTestFull\n"); + tpm_self_test_full(dev); + result = tpm_get_flags(dev, NULL, NULL, NULL); + if (result != 0) + printf("\ttpm getflags failed with 0x%x\n", result); + printf("\tdone\n"); + return 0; +} + +/* + * Runs [op] and ensures it returns success and doesn't run longer than + * [time_limit] in milliseconds. + */ +#define TTPM_CHECK(op, time_limit) do { \ + ulong start, time; \ + uint32_t __result; \ + \ + start = get_timer(0); \ + __result = op; \ + if (__result != TPM_SUCCESS) { \ + printf("\t" #op ": error 0x%x\n", __result); \ + return -1; \ + } \ + time = get_timer(start); \ + printf("\t" #op ": %lu ms\n", time); \ + if (time > (ulong)time_limit) { \ + printf("\t" #op " exceeded " #time_limit " ms\n"); \ + } \ +} while (0) + + +static int test_timing(struct udevice *dev) +{ + uint8_t in[20], out[20]; + uint32_t x; + + printf("Testing timing ..."); + tpm_init(dev); + TTPM_CHECK(TlclStartupIfNeeded(dev), 50); + TTPM_CHECK(tpm_continue_self_test(dev), 100); + TTPM_CHECK(tpm_self_test_full(dev), 1000); + TTPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE), 100); + TTPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)), + 100); + TTPM_CHECK(tpm_nv_read_value(dev, INDEX0, (uint8_t *)&x, sizeof(x)), + 100); + TTPM_CHECK(tpm_pcr_extend(dev, 0, in, out), 200); + TTPM_CHECK(tpm_set_global_lock(dev), 50); + TTPM_CHECK(tpm_tsc_physical_presence(dev, PHYS_PRESENCE), 100); + printf("done\n"); + return 0; +} + +#define TPM_MAX_NV_WRITES_NOOWNER 64 + +static int test_write_limit(struct udevice *dev) +{ + uint32_t result; + int i; + + printf("Testing writelimit ...\n"); + tpm_init(dev); + TPM_CHECK(TlclStartupIfNeeded(dev)); + TPM_CHECK(tpm_self_test_full(dev)); + TPM_CHECK(tpm_tsc_physical_presence(dev, PRESENCE)); + TPM_CHECK(tpm_force_clear(dev)); + TPM_CHECK(tpm_physical_enable(dev)); + TPM_CHECK(tpm_physical_set_deactivated(dev, 0)); + + for (i = 0; i < TPM_MAX_NV_WRITES_NOOWNER + 2; i++) { + printf("\twriting %d\n", i); + result = tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i, + sizeof(i)); + switch (result) { + case TPM_SUCCESS: + break; + case TPM_MAXNVWRITES: + assert(i >= TPM_MAX_NV_WRITES_NOOWNER); + default: + pr_err("\tunexpected error code %d (0x%x)\n", + result, result); + } + } + + /* Reset write count */ + TPM_CHECK(tpm_force_clear(dev)); + TPM_CHECK(tpm_physical_enable(dev)); + TPM_CHECK(tpm_physical_set_deactivated(dev, 0)); + + /* Try writing again. */ + TPM_CHECK(tpm_nv_write_value(dev, INDEX0, (uint8_t *)&i, sizeof(i))); + printf("\tdone\n"); + return 0; +} + +#define VOIDTEST(XFUNC) \ + int do_test_##XFUNC(struct cmd_tbl *cmd_tbl, int flag, int argc, \ + char *const argv[]) \ + { \ + struct udevice *dev; \ + int ret; \ +\ + ret = get_tpm(&dev); \ + if (ret) \ + return ret; \ + return test_##XFUNC(dev); \ + } + +#define VOIDENT(XNAME) \ + U_BOOT_CMD_MKENT(XNAME, 0, 1, do_test_##XNAME, "", ""), + +VOIDTEST(early_extend) +VOIDTEST(early_nvram) +VOIDTEST(early_nvram2) +VOIDTEST(enable) +VOIDTEST(fast_enable) +VOIDTEST(global_lock) +VOIDTEST(lock) +VOIDTEST(readonly) +VOIDTEST(redefine_unowned) +VOIDTEST(space_perm) +VOIDTEST(startup) +VOIDTEST(timing) +VOIDTEST(write_limit) +VOIDTEST(timer) + +static struct cmd_tbl cmd_cros_tpm_sub[] = { + VOIDENT(early_extend) + VOIDENT(early_nvram) + VOIDENT(early_nvram2) + VOIDENT(enable) + VOIDENT(fast_enable) + VOIDENT(global_lock) + VOIDENT(lock) + VOIDENT(readonly) + VOIDENT(redefine_unowned) + VOIDENT(space_perm) + VOIDENT(startup) + VOIDENT(timing) + VOIDENT(write_limit) + VOIDENT(timer) +}; + +static int do_tpmtest(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + int i; + + printf("argc = %d, argv = ", argc); + + for (i = 0; i < argc; i++) + printf(" %s", argv[i]); + + printf("\n------\n"); + + argc--; + argv++; + c = find_cmd_tbl(argv[0], cmd_cros_tpm_sub, + ARRAY_SIZE(cmd_cros_tpm_sub)); + return c ? c->cmd(cmdtp, flag, argc, argv) : cmd_usage(cmdtp); +} + +U_BOOT_CMD(tpmtest, 2, 1, do_tpmtest, "TPM tests", + "\n\tearly_extend\n" + "\tearly_nvram\n" + "\tearly_nvram2\n" + "\tenable\n" + "\tfast_enable\n" + "\tglobal_lock\n" + "\tlock\n" + "\treadonly\n" + "\tredefine_unowned\n" + "\tspace_perm\n" + "\tstartup\n" + "\ttiming\n" + "\twrite_limit\n"); diff --git a/roms/u-boot/cmd/trace.c b/roms/u-boot/cmd/trace.c new file mode 100644 index 000000000..4ce47c762 --- /dev/null +++ b/roms/u-boot/cmd/trace.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <mapmem.h> +#include <trace.h> +#include <asm/io.h> + +static int get_args(int argc, char *const argv[], char **buff, + size_t *buff_ptr, size_t *buff_size) +{ + if (argc < 2) + return -1; + if (argc < 4) { + *buff_size = env_get_ulong("profsize", 16, 0); + *buff = map_sysmem(env_get_ulong("profbase", 16, 0), + *buff_size); + *buff_ptr = env_get_ulong("profoffset", 16, 0); + } else { + *buff_size = simple_strtoul(argv[3], NULL, 16); + *buff = map_sysmem(simple_strtoul(argv[2], NULL, 16), + *buff_size); + *buff_ptr = 0; + }; + return 0; +} + +static int create_func_list(int argc, char *const argv[]) +{ + size_t buff_size, avail, buff_ptr, needed, used; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_functions(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#zx bytes needed)\n", needed); + used = min(avail, (size_t)needed); + printf("Function trace dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + env_set_hex("profbase", map_to_sysmem(buff)); + env_set_hex("profsize", buff_size); + env_set_hex("profoffset", buff_ptr + used); + + return 0; +} + +static int create_call_list(int argc, char *const argv[]) +{ + size_t buff_size, avail, buff_ptr, needed, used; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_calls(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#zx bytes needed)\n", needed); + used = min(avail, (size_t)needed); + printf("Call list dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + + env_set_hex("profbase", map_to_sysmem(buff)); + env_set_hex("profsize", buff_size); + env_set_hex("profoffset", buff_ptr + used); + + return 0; +} + +int do_trace(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *cmd = argc < 2 ? NULL : argv[1]; + + if (!cmd) + return cmd_usage(cmdtp); + switch (*cmd) { + case 'p': + trace_set_enabled(0); + break; + case 'c': + if (create_call_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 'r': + trace_set_enabled(1); + break; + case 'f': + if (create_func_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 's': + trace_print_stats(); + break; + default: + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + trace, 4, 1, do_trace, + "trace utility commands", + "stats - display tracing statistics\n" + "trace pause - pause tracing\n" + "trace resume - resume tracing\n" + "trace funclist [<addr> <size>] - dump function list into buffer\n" + "trace calls [<addr> <size>] " + "- dump function call trace into buffer" +); diff --git a/roms/u-boot/cmd/tsi148.c b/roms/u-boot/cmd/tsi148.c new file mode 100644 index 000000000..2eae14f87 --- /dev/null +++ b/roms/u-boot/cmd/tsi148.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 Reinhard Arlt, reinhard.arlt@esd-electronics.com + * + * base on universe.h by + * + * (C) Copyright 2003 Stefan Roese, stefan.roese@esd-electronics.com + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <malloc.h> +#include <asm/io.h> +#include <pci.h> + +#include <tsi148.h> + +#define LPCI_VENDOR PCI_VENDOR_ID_TUNDRA +#define LPCI_DEVICE PCI_DEVICE_ID_TUNDRA_TSI148 + +typedef struct _TSI148_DEV TSI148_DEV; + +struct _TSI148_DEV { + int bus; + pci_dev_t busdevfn; + TSI148 *uregs; + unsigned int pci_bs; +}; + +static TSI148_DEV *dev; + +/* + * Most of the TSI148 register are BIGENDIAN + * This is the reason for the __raw_writel(htonl(x), x) usage! + */ + +int tsi148_init(void) +{ + int j, result; + pci_dev_t busdevfn; + unsigned int val; + + busdevfn = pci_find_device(LPCI_VENDOR, LPCI_DEVICE, 0); + if (busdevfn == -1) { + puts("Tsi148: No Tundra Tsi148 found!\n"); + return -1; + } + + /* Lets turn Latency off */ + pci_write_config_dword(busdevfn, 0x0c, 0); + + dev = malloc(sizeof(*dev)); + if (NULL == dev) { + puts("Tsi148: No memory!\n"); + return -1; + } + + memset(dev, 0, sizeof(*dev)); + dev->busdevfn = busdevfn; + + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_0, &val); + val &= ~0xf; + dev->uregs = (TSI148 *)val; + + debug("Tsi148: Base : %p\n", dev->uregs); + + /* check mapping */ + debug("Tsi148: Read via mapping, PCI_ID = %08X\n", + readl(&dev->uregs->pci_id)); + if (((LPCI_DEVICE << 16) | LPCI_VENDOR) != readl(&dev->uregs->pci_id)) { + printf("Tsi148: Cannot read PCI-ID via Mapping: %08x\n", + readl(&dev->uregs->pci_id)); + result = -1; + goto break_30; + } + + debug("Tsi148: PCI_BS = %08X\n", readl(&dev->uregs->pci_mbarl)); + + dev->pci_bs = readl(&dev->uregs->pci_mbarl); + + /* turn off windows */ + for (j = 0; j < 8; j++) { + __raw_writel(htonl(0x00000000), &dev->uregs->outbound[j].otat); + __raw_writel(htonl(0x00000000), &dev->uregs->inbound[j].itat); + } + + /* Tsi148 VME timeout etc */ + __raw_writel(htonl(0x00000084), &dev->uregs->vctrl); + +#ifdef DEBUG + if ((__raw_readl(&dev->uregs->vstat) & 0x00000100) != 0) + printf("Tsi148: System Controller!\n"); + else + printf("Tsi148: Not System Controller!\n"); +#endif + + /* + * Lets turn off interrupts + */ + /* Disable interrupts in Tsi148 first */ + __raw_writel(htonl(0x00000000), &dev->uregs->inten); + /* Disable interrupt out */ + __raw_writel(htonl(0x00000000), &dev->uregs->inteo); + eieio(); + /* Reset all IRQ's */ + __raw_writel(htonl(0x03ff3f00), &dev->uregs->intc); + /* Map all ints to 0 */ + __raw_writel(htonl(0x00000000), &dev->uregs->intm1); + __raw_writel(htonl(0x00000000), &dev->uregs->intm2); + eieio(); + + val = __raw_readl(&dev->uregs->vstat); + val &= ~(0x00004000); + __raw_writel(val, &dev->uregs->vstat); + eieio(); + + debug("Tsi148: register struct size %08x\n", sizeof(TSI148)); + + return 0; + + break_30: + free(dev); + dev = NULL; + + return result; +} + +/* + * Create pci slave window (access: pci -> vme) + */ +int tsi148_pci_slave_window(unsigned int pciAddr, unsigned int vmeAddr, + int size, int vam, int vdw) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 8; i++) { + if (0x00000000 == readl(&dev->uregs->outbound[i].otat)) + break; + } + + if (i > 7) { + printf("Tsi148: No Image available\n"); + result = -1; + goto exit_10; + } + + debug("Tsi148: Using image %d\n", i); + + printf("Tsi148: Pci addr %08x\n", pciAddr); + + __raw_writel(htonl(pciAddr), &dev->uregs->outbound[i].otsal); + __raw_writel(0x00000000, &dev->uregs->outbound[i].otsau); + __raw_writel(htonl(pciAddr + size), &dev->uregs->outbound[i].oteal); + __raw_writel(0x00000000, &dev->uregs->outbound[i].oteau); + __raw_writel(htonl(vmeAddr - pciAddr), &dev->uregs->outbound[i].otofl); + __raw_writel(0x00000000, &dev->uregs->outbound[i].otofu); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00000001; + break; + case VME_AM_A32: + ctl = 0x00000002; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00000010; + break; + } + + if (vam & VME_AM_SUP) + ctl |= 0x00000020; + + switch (vdw & VME_FLAG_Dxx) { + case VME_FLAG_D16: + ctl |= 0x00000000; + break; + case VME_FLAG_D32: + ctl |= 0x00000040; + break; + } + + ctl |= 0x80040000; /* enable, no prefetch */ + + __raw_writel(htonl(ctl), &dev->uregs->outbound[i].otat); + + debug("Tsi148: window-addr =%p\n", + &dev->uregs->outbound[i].otsau); + debug("Tsi148: pci slave window[%d] attr =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otat))); + debug("Tsi148: pci slave window[%d] start =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otsal))); + debug("Tsi148: pci slave window[%d] end =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].oteal))); + debug("Tsi148: pci slave window[%d] offset=%08x\n", + i, ntohl(__raw_readl(&dev->uregs->outbound[i].otofl))); + + return 0; + + exit_10: + return -result; +} + +unsigned int tsi148_eval_vam(int vam) +{ + unsigned int ctl = 0; + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00000010; + break; + case VME_AM_A32: + ctl = 0x00000020; + break; + } + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000001; + break; + case VME_AM_PROG: + ctl |= 0x00000002; + break; + case (VME_AM_PROG | VME_AM_DATA): + ctl |= 0x00000003; + break; + } + + if (vam & VME_AM_SUP) + ctl |= 0x00000008; + if (vam & VME_AM_USR) + ctl |= 0x00000004; + + return ctl; +} + +/* + * Create vme slave window (access: vme -> pci) + */ +int tsi148_vme_slave_window(unsigned int vmeAddr, unsigned int pciAddr, + int size, int vam) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 8; i++) { + if (0x00000000 == readl(&dev->uregs->inbound[i].itat)) + break; + } + + if (i > 7) { + printf("Tsi148: No Image available\n"); + result = -1; + goto exit_10; + } + + debug("Tsi148: Using image %d\n", i); + + __raw_writel(htonl(vmeAddr), &dev->uregs->inbound[i].itsal); + __raw_writel(0x00000000, &dev->uregs->inbound[i].itsau); + __raw_writel(htonl(vmeAddr + size), &dev->uregs->inbound[i].iteal); + __raw_writel(0x00000000, &dev->uregs->inbound[i].iteau); + __raw_writel(htonl(pciAddr - vmeAddr), &dev->uregs->inbound[i].itofl); + if (vmeAddr > pciAddr) + __raw_writel(0xffffffff, &dev->uregs->inbound[i].itofu); + else + __raw_writel(0x00000000, &dev->uregs->inbound[i].itofu); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x80000000; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->inbound[i].itat); + + debug("Tsi148: window-addr =%p\n", + &dev->uregs->inbound[i].itsau); + debug("Tsi148: vme slave window[%d] attr =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itat))); + debug("Tsi148: vme slave window[%d] start =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itsal))); + debug("Tsi148: vme slave window[%d] end =%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].iteal))); + debug("Tsi148: vme slave window[%d] offset=%08x\n", + i, ntohl(__raw_readl(&dev->uregs->inbound[i].itofl))); + + return 0; + + exit_10: + return -result; +} + +/* + * Create vme slave window (access: vme -> gcsr) + */ +int tsi148_vme_gcsr_window(unsigned int vmeAddr, int vam) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->gbal); + __raw_writel(0x00000000, &dev->uregs->gbau); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->gcsrat); + } + + return result; +} + +/* + * Create vme slave window (access: vme -> crcsr) + */ +int tsi148_vme_crcsr_window(unsigned int vmeAddr) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->crol); + __raw_writel(0x00000000, &dev->uregs->crou); + + ctl = 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->crat); + } + + return result; +} + +/* + * Create vme slave window (access: vme -> crg) + */ +int tsi148_vme_crg_window(unsigned int vmeAddr, int vam) +{ + int result; + unsigned int ctl; + + result = 0; + + if (NULL == dev) { + result = 1; + } else { + __raw_writel(htonl(vmeAddr), &dev->uregs->cbal); + __raw_writel(0x00000000, &dev->uregs->cbau); + + ctl = tsi148_eval_vam(vam); + ctl |= 0x00000080; /* enable */ + __raw_writel(htonl(ctl), &dev->uregs->crgat); + } + + return result; +} + +/* + * Tundra Tsi148 configuration + */ +int do_tsi148(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr1 = 0, addr2 = 0, size = 0, vam = 0, vdw = 0; + char cmd = 'x'; + + /* get parameter */ + if (argc > 1) + cmd = argv[1][0]; + if (argc > 2) + addr1 = simple_strtoul(argv[2], NULL, 16); + if (argc > 3) + addr2 = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + size = simple_strtoul(argv[4], NULL, 16); + if (argc > 5) + vam = simple_strtoul(argv[5], NULL, 16); + if (argc > 6) + vdw = simple_strtoul(argv[6], NULL, 16); + + switch (cmd) { + case 'c': + if (strcmp(argv[1], "crg") == 0) { + vam = addr2; + printf("Tsi148: Configuring VME CRG Window " + "(VME->CRG):\n"); + printf(" vme=%08lx vam=%02lx\n", addr1, vam); + tsi148_vme_crg_window(addr1, vam); + } else { + printf("Tsi148: Configuring VME CR/CSR Window " + "(VME->CR/CSR):\n"); + printf(" pci=%08lx\n", addr1); + tsi148_vme_crcsr_window(addr1); + } + break; + case 'i': /* init */ + tsi148_init(); + break; + case 'g': + vam = addr2; + printf("Tsi148: Configuring VME GCSR Window (VME->GCSR):\n"); + printf(" vme=%08lx vam=%02lx\n", addr1, vam); + tsi148_vme_gcsr_window(addr1, vam); + break; + case 'v': /* vme */ + printf("Tsi148: Configuring VME Slave Window (VME->PCI):\n"); + printf(" vme=%08lx pci=%08lx size=%08lx vam=%02lx\n", + addr1, addr2, size, vam); + tsi148_vme_slave_window(addr1, addr2, size, vam); + break; + case 'p': /* pci */ + printf("Tsi148: Configuring PCI Slave Window (PCI->VME):\n"); + printf(" pci=%08lx vme=%08lx size=%08lx vam=%02lx vdw=%02lx\n", + addr1, addr2, size, vam, vdw); + tsi148_pci_slave_window(addr1, addr2, size, vam, vdw); + break; + default: + printf("Tsi148: Command %s not supported!\n", argv[1]); + } + + return 0; +} + +U_BOOT_CMD( + tsi148, 7, 1, do_tsi148, + "initialize and configure Turndra Tsi148\n", + "init\n" + " - initialize tsi148\n" + "tsi148 vme [vme_addr] [pci_addr] [size] [vam]\n" + " - create vme slave window (access: vme->pci)\n" + "tsi148 pci [pci_addr] [vme_addr] [size] [vam] [vdw]\n" + " - create pci slave window (access: pci->vme)\n" + "tsi148 crg [vme_addr] [vam]\n" + " - create vme slave window: (access vme->CRG\n" + "tsi148 crcsr [pci_addr]\n" + " - create vme slave window: (access vme->CR/CSR\n" + "tsi148 gcsr [vme_addr] [vam]\n" + " - create vme slave window: (access vme->GCSR\n" + " [vam] = VMEbus Address-Modifier: 01 -> A16 Address Space\n" + " 02 -> A24 Address Space\n" + " 03 -> A32 Address Space\n" + " 04 -> Usr AM Code\n" + " 08 -> Supervisor AM Code\n" + " 10 -> Data AM Code\n" + " 20 -> Program AM Code\n" + " [vdw] = VMEbus Maximum Datawidth: 02 -> D16 Data Width\n" + " 03 -> D32 Data Width\n" +); diff --git a/roms/u-boot/cmd/ubi.c b/roms/u-boot/cmd/ubi.c new file mode 100644 index 000000000..cb14e3e1e --- /dev/null +++ b/roms/u-boot/cmd/ubi.c @@ -0,0 +1,744 @@ +/* + * Unsorted Block Image commands + * + * Copyright (C) 2008 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * Copyright 2008-2009 Stefan Roese <sr@denx.de>, DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <exports.h> +#include <malloc.h> +#include <memalign.h> +#include <mtd.h> +#include <nand.h> +#include <onenand_uboot.h> +#include <dm/devres.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/err.h> +#include <ubi_uboot.h> +#include <linux/errno.h> +#include <jffs2/load_kernel.h> + +#undef ubi_msg +#define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__) + +/* Private own data */ +static struct ubi_device *ubi; + +#ifdef CONFIG_CMD_UBIFS +#include <ubifs_uboot.h> +#endif + +static void display_volume_info(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < (ubi->vtbl_slots + 1); i++) { + if (!ubi->volumes[i]) + continue; /* Empty record */ + ubi_dump_vol_info(ubi->volumes[i]); + } +} + +static void display_ubi_info(struct ubi_device *ubi) +{ + ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); +} + +static int ubi_info(int layout) +{ + if (layout) + display_volume_info(ubi); + else + display_ubi_info(ubi); + + return 0; +} + +static int ubi_check_volumename(const struct ubi_volume *vol, char *name) +{ + return strcmp(vol->name, name); +} + +static int ubi_check(char *name) +{ + int i; + + for (i = 0; i < (ubi->vtbl_slots + 1); i++) { + if (!ubi->volumes[i]) + continue; /* Empty record */ + + if (!ubi_check_volumename(ubi->volumes[i], name)) + return 0; + } + + return 1; +} + +static int verify_mkvol_req(const struct ubi_device *ubi, + const struct ubi_mkvol_req *req) +{ + int n, err = EINVAL; + + if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 || + req->name_len < 0) + goto bad; + + if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) && + req->vol_id != UBI_VOL_NUM_AUTO) + goto bad; + + if (req->alignment == 0) + goto bad; + + if (req->bytes == 0) { + printf("No space left in UBI device!\n"); + err = ENOMEM; + goto bad; + } + + if (req->vol_type != UBI_DYNAMIC_VOLUME && + req->vol_type != UBI_STATIC_VOLUME) + goto bad; + + if (req->alignment > ubi->leb_size) + goto bad; + + n = req->alignment % ubi->min_io_size; + if (req->alignment != 1 && n) + goto bad; + + if (req->name_len > UBI_VOL_NAME_MAX) { + printf("Name too long!\n"); + err = ENAMETOOLONG; + goto bad; + } + + return 0; +bad: + return err; +} + +static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id, + bool skipcheck) +{ + struct ubi_mkvol_req req; + int err; + + if (dynamic) + req.vol_type = UBI_DYNAMIC_VOLUME; + else + req.vol_type = UBI_STATIC_VOLUME; + + req.vol_id = vol_id; + req.alignment = 1; + req.bytes = size; + + strcpy(req.name, volume); + req.name_len = strlen(volume); + req.name[req.name_len] = '\0'; + req.flags = 0; + if (skipcheck) + req.flags |= UBI_VOL_SKIP_CRC_CHECK_FLG; + + /* It's duplicated at drivers/mtd/ubi/cdev.c */ + err = verify_mkvol_req(ubi, &req); + if (err) { + printf("verify_mkvol_req failed %d\n", err); + return err; + } + printf("Creating %s volume %s of size %lld\n", + dynamic ? "dynamic" : "static", volume, size); + /* Call real ubi create volume */ + return ubi_create_volume(ubi, &req); +} + +static struct ubi_volume *ubi_find_volume(char *volume) +{ + struct ubi_volume *vol = NULL; + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) { + vol = ubi->volumes[i]; + if (vol && !strcmp(vol->name, volume)) + return vol; + } + + printf("Volume %s not found!\n", volume); + return NULL; +} + +static int ubi_remove_vol(char *volume) +{ + int err, reserved_pebs, i; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + printf("Remove UBI volume %s (id %d)\n", vol->name, vol->vol_id); + + if (ubi->ro_mode) { + printf("It's read-only mode\n"); + err = EROFS; + goto out_err; + } + + err = ubi_change_vtbl_record(ubi, vol->vol_id, NULL); + if (err) { + printf("Error changing Vol tabel record err=%x\n", err); + goto out_err; + } + reserved_pebs = vol->reserved_pebs; + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + goto out_err; + } + + kfree(vol->eba_tbl); + ubi->volumes[vol->vol_id]->eba_tbl = NULL; + ubi->volumes[vol->vol_id] = NULL; + + ubi->rsvd_pebs -= reserved_pebs; + ubi->avail_pebs += reserved_pebs; + i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (i > 0) { + i = ubi->avail_pebs >= i ? i : ubi->avail_pebs; + ubi->avail_pebs -= i; + ubi->rsvd_pebs += i; + ubi->beb_rsvd_pebs += i; + if (i > 0) + ubi_msg("reserve more %d PEBs", i); + } + ubi->vol_count -= 1; + + return 0; +out_err: + ubi_err(ubi, "cannot remove volume %s, error %d", volume, err); + if (err < 0) + err = -err; + return err; +} + +static int ubi_rename_vol(char *oldname, char *newname) +{ + struct ubi_volume *vol; + struct ubi_rename_entry rename; + struct ubi_volume_desc desc; + struct list_head list; + + vol = ubi_find_volume(oldname); + if (!vol) { + printf("%s: volume %s doesn't exist\n", __func__, oldname); + return ENODEV; + } + + if (!ubi_check(newname)) { + printf("%s: volume %s already exist\n", __func__, newname); + return EINVAL; + } + + printf("Rename UBI volume %s to %s\n", oldname, newname); + + if (ubi->ro_mode) { + printf("%s: ubi device is in read-only mode\n", __func__); + return EROFS; + } + + rename.new_name_len = strlen(newname); + strcpy(rename.new_name, newname); + rename.remove = 0; + desc.vol = vol; + desc.mode = 0; + rename.desc = &desc; + INIT_LIST_HEAD(&rename.list); + INIT_LIST_HEAD(&list); + list_add(&rename.list, &list); + + return ubi_rename_volumes(ubi, &list); +} + +static int ubi_volume_continue_write(char *volume, void *buf, size_t size) +{ + int err = 1; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + err = ubi_more_update_data(ubi, vol, buf, size); + if (err < 0) { + printf("Couldnt or partially wrote data\n"); + return -err; + } + + if (err) { + size = err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) + return -err; + + if (err) { + ubi_warn(ubi, "volume %d on UBI device %d is corrupt", + vol->vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + + vol->checked = 1; + ubi_gluebi_updated(vol); + } + + return 0; +} + +int ubi_volume_begin_write(char *volume, void *buf, size_t size, + size_t full_size) +{ + int err = 1; + int rsvd_bytes = 0; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad); + if (size > rsvd_bytes) { + printf("size > volume size! Aborting!\n"); + return EINVAL; + } + + err = ubi_start_update(ubi, vol, full_size); + if (err < 0) { + printf("Cannot start volume update\n"); + return -err; + } + + return ubi_volume_continue_write(volume, buf, size); +} + +int ubi_volume_write(char *volume, void *buf, size_t size) +{ + return ubi_volume_begin_write(volume, buf, size, size); +} + +int ubi_volume_read(char *volume, char *buf, size_t size) +{ + int err, lnum, off, len, tbuf_size; + void *tbuf; + unsigned long long tmp; + struct ubi_volume *vol; + loff_t offp = 0; + size_t len_read; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + if (vol->updating) { + printf("updating"); + return EBUSY; + } + if (vol->upd_marker) { + printf("damaged volume, update marker is set"); + return EBADF; + } + if (offp == vol->used_bytes) + return 0; + + if (size == 0) { + printf("No size specified -> Using max size (%lld)\n", vol->used_bytes); + size = vol->used_bytes; + } + + printf("Read %zu bytes from volume %s to %p\n", size, volume, buf); + + if (vol->corrupted) + printf("read from corrupted volume %d", vol->vol_id); + if (offp + size > vol->used_bytes) + size = vol->used_bytes - offp; + + tbuf_size = vol->usable_leb_size; + if (size < tbuf_size) + tbuf_size = ALIGN(size, ubi->min_io_size); + tbuf = malloc_cache_aligned(tbuf_size); + if (!tbuf) { + printf("NO MEM\n"); + return ENOMEM; + } + len = size > tbuf_size ? tbuf_size : size; + + tmp = offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + len_read = size; + do { + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); + if (err) { + printf("read err %x\n", err); + err = -err; + break; + } + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + size -= len; + offp += len; + + memcpy(buf, tbuf, len); + + buf += len; + len = size > tbuf_size ? tbuf_size : size; + } while (size); + + if (!size) + env_set_hex("filesize", len_read); + + free(tbuf); + return err; +} + +static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset) +{ + char ubi_mtd_param_buffer[80]; + int err; + + if (!vid_header_offset) + sprintf(ubi_mtd_param_buffer, "%s", info->name); + else + sprintf(ubi_mtd_param_buffer, "%s,%s", info->name, + vid_header_offset); + + err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL); + if (err) + return -err; + + err = ubi_init(); + if (err) + return -err; + + return 0; +} + +static int ubi_set_skip_check(char *volume, bool skip_check) +{ + struct ubi_vtbl_record vtbl_rec; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (!vol) + return ENODEV; + + printf("%sing skip_check on volume %s\n", + skip_check ? "Sett" : "Clear", volume); + + vtbl_rec = ubi->vtbl[vol->vol_id]; + if (skip_check) { + vtbl_rec.flags |= UBI_VTBL_SKIP_CRC_CHECK_FLG; + vol->skip_check = 1; + } else { + vtbl_rec.flags &= ~UBI_VTBL_SKIP_CRC_CHECK_FLG; + vol->skip_check = 0; + } + + return ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); +} + +static int ubi_detach(void) +{ +#ifdef CONFIG_CMD_UBIFS + /* + * Automatically unmount UBIFS partition when user + * changes the UBI device. Otherwise the following + * UBIFS commands will crash. + */ + if (ubifs_is_mounted()) + cmd_ubifs_umount(); +#endif + + /* + * Call ubi_exit() before re-initializing the UBI subsystem + */ + if (ubi) + ubi_exit(); + + ubi = NULL; + + return 0; +} + +int ubi_part(char *part_name, const char *vid_header_offset) +{ + struct mtd_info *mtd; + int err = 0; + + ubi_detach(); + + mtd_probe_devices(); + mtd = get_mtd_device_nm(part_name); + if (IS_ERR(mtd)) { + printf("Partition %s not found!\n", part_name); + return 1; + } + put_mtd_device(mtd); + + err = ubi_dev_scan(mtd, vid_header_offset); + if (err) { + printf("UBI init error %d\n", err); + printf("Please check, if the correct MTD partition is used (size big enough?)\n"); + return err; + } + + ubi = ubi_devices[0]; + + return 0; +} + +static int do_ubi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int64_t size = 0; + ulong addr = 0; + bool skipcheck = false; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "detach") == 0) + return ubi_detach(); + + if (strcmp(argv[1], "part") == 0) { + const char *vid_header_offset = NULL; + + /* Print current partition */ + if (argc == 2) { + if (!ubi) { + printf("Error, no UBI device selected!\n"); + return 1; + } + + printf("Device %d: %s, MTD partition %s\n", + ubi->ubi_num, ubi->ubi_name, ubi->mtd->name); + return 0; + } + + if (argc < 3) + return CMD_RET_USAGE; + + if (argc > 3) + vid_header_offset = argv[3]; + + return ubi_part(argv[2], vid_header_offset); + } + + if ((strcmp(argv[1], "part") != 0) && !ubi) { + printf("Error, no UBI device selected!\n"); + return 1; + } + + if (strcmp(argv[1], "info") == 0) { + int layout = 0; + if (argc > 2 && !strncmp(argv[2], "l", 1)) + layout = 1; + return ubi_info(layout); + } + + if (strcmp(argv[1], "check") == 0) { + if (argc > 2) + return ubi_check(argv[2]); + + printf("Error, no volume name passed\n"); + return 1; + } + + if (strncmp(argv[1], "create", 6) == 0) { + int dynamic = 1; /* default: dynamic volume */ + int id = UBI_VOL_NUM_AUTO; + + /* Use maximum available size */ + size = 0; + + /* E.g., create volume with "skipcheck" bit set */ + if (argc == 7) { + skipcheck = strncmp(argv[6], "--skipcheck", 11) == 0; + argc--; + } + + /* E.g., create volume size type vol_id */ + if (argc == 6) { + id = simple_strtoull(argv[5], NULL, 16); + argc--; + } + + /* E.g., create volume size type */ + if (argc == 5) { + if (strncmp(argv[4], "s", 1) == 0) + dynamic = 0; + else if (strncmp(argv[4], "d", 1) != 0) { + printf("Incorrect type\n"); + return 1; + } + argc--; + } + /* E.g., create volume size */ + if (argc == 4) { + if (argv[3][0] != '-') + size = simple_strtoull(argv[3], NULL, 16); + argc--; + } + /* Use maximum available size */ + if (!size) { + size = (int64_t)ubi->avail_pebs * ubi->leb_size; + printf("No size specified -> Using max size (%lld)\n", size); + } + /* E.g., create volume */ + if (argc == 3) { + return ubi_create_vol(argv[2], size, dynamic, id, + skipcheck); + } + } + + if (strncmp(argv[1], "remove", 6) == 0) { + /* E.g., remove volume */ + if (argc == 3) + return ubi_remove_vol(argv[2]); + } + + if (IS_ENABLED(CONFIG_CMD_UBI_RENAME) && !strncmp(argv[1], "rename", 6)) + return ubi_rename_vol(argv[2], argv[3]); + + if (strncmp(argv[1], "skipcheck", 9) == 0) { + /* E.g., change skip_check flag */ + if (argc == 4) { + skipcheck = strncmp(argv[3], "on", 2) == 0; + return ubi_set_skip_check(argv[2], skipcheck); + } + } + + if (strncmp(argv[1], "write", 5) == 0) { + int ret; + + if (argc < 5) { + printf("Please see usage\n"); + return 1; + } + + addr = simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[4], NULL, 16); + + if (strlen(argv[1]) == 10 && + strncmp(argv[1] + 5, ".part", 5) == 0) { + if (argc < 6) { + ret = ubi_volume_continue_write(argv[3], + (void *)addr, size); + } else { + size_t full_size; + full_size = simple_strtoul(argv[5], NULL, 16); + ret = ubi_volume_begin_write(argv[3], + (void *)addr, size, full_size); + } + } else { + ret = ubi_volume_write(argv[3], (void *)addr, size); + } + if (!ret) { + printf("%lld bytes written to volume %s\n", size, + argv[3]); + } + + return ret; + } + + if (strncmp(argv[1], "read", 4) == 0) { + size = 0; + + /* E.g., read volume size */ + if (argc == 5) { + size = simple_strtoul(argv[4], NULL, 16); + argc--; + } + + /* E.g., read volume */ + if (argc == 4) { + addr = simple_strtoul(argv[2], NULL, 16); + argc--; + } + + if (argc == 3) { + return ubi_volume_read(argv[3], (char *)addr, size); + } + } + + printf("Please see usage\n"); + return 1; +} + +U_BOOT_CMD( + ubi, 7, 1, do_ubi, + "ubi commands", + "detach" + " - detach ubi from a mtd partition\n" + "ubi part [part] [offset]\n" + " - Show or set current partition (with optional VID" + " header offset)\n" + "ubi info [l[ayout]]" + " - Display volume and ubi layout information\n" + "ubi check volumename" + " - check if volumename exists\n" + "ubi create[vol] volume [size] [type] [id] [--skipcheck]\n" + " - create volume name with size ('-' for maximum" + " available size)\n" + "ubi write[vol] address volume size" + " - Write volume from address with size\n" + "ubi write.part address volume size [fullsize]\n" + " - Write part of a volume from address\n" + "ubi read[vol] address volume [size]" + " - Read volume to address with size\n" + "ubi remove[vol] volume" + " - Remove volume\n" +#if IS_ENABLED(CONFIG_CMD_UBI_RENAME) + "ubi rename oldname newname\n" +#endif + "ubi skipcheck volume on/off - Set or clear skip_check flag in volume header\n" + "[Legends]\n" + " volume: character name\n" + " size: specified in bytes\n" + " type: s[tatic] or d[ynamic] (default=dynamic)" +); diff --git a/roms/u-boot/cmd/ubifs.c b/roms/u-boot/cmd/ubifs.c new file mode 100644 index 000000000..a26b653d6 --- /dev/null +++ b/roms/u-boot/cmd/ubifs.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + */ + + +/* + * UBIFS command support + */ + +#undef DEBUG + +#include <common.h> +#include <config.h> +#include <command.h> +#include <log.h> +#include <ubifs_uboot.h> + +static int ubifs_initialized; +static int ubifs_mounted; + +int cmd_ubifs_mount(char *vol_name) +{ + int ret; + + debug("Using volume %s\n", vol_name); + + if (ubifs_initialized == 0) { + ubifs_init(); + ubifs_initialized = 1; + } + + ret = uboot_ubifs_mount(vol_name); + if (ret) + return -1; + + ubifs_mounted = 1; + + return ret; +} + +static int do_ubifs_mount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *vol_name; + + if (argc != 2) + return CMD_RET_USAGE; + + vol_name = argv[1]; + + return cmd_ubifs_mount(vol_name); +} + +int ubifs_is_mounted(void) +{ + return ubifs_mounted; +} + +int cmd_ubifs_umount(void) +{ + if (ubifs_initialized == 0) { + printf("No UBIFS volume mounted!\n"); + return -1; + } + + uboot_ubifs_umount(); + ubifs_mounted = 0; + ubifs_initialized = 0; + + return 0; +} + +static int do_ubifs_umount(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc != 1) + return CMD_RET_USAGE; + + return cmd_ubifs_umount(); +} + +static int do_ubifs_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *filename = "/"; + int ret; + + if (!ubifs_mounted) { + printf("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } + + if (argc == 2) + filename = argv[1]; + debug("Using filename %s\n", filename); + + ret = ubifs_ls(filename); + if (ret) { + printf("** File not found %s **\n", filename); + ret = CMD_RET_FAILURE; + } + + return ret; +} + +static int do_ubifs_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *filename; + char *endp; + int ret; + u32 addr; + u32 size = 0; + + if (!ubifs_mounted) { + printf("UBIFS not mounted, use ubifs mount to mount volume first!\n"); + return -1; + } + + if (argc < 3) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) + return CMD_RET_USAGE; + + filename = argv[2]; + + if (argc == 4) { + size = simple_strtoul(argv[3], &endp, 16); + if (endp == argv[3]) + return CMD_RET_USAGE; + } + debug("Loading file '%s' to address 0x%08x (size %d)\n", filename, addr, size); + + ret = ubifs_load(filename, addr, size); + if (ret) { + printf("** File not found %s **\n", filename); + ret = CMD_RET_FAILURE; + } + + return ret; +} + +U_BOOT_CMD( + ubifsmount, 2, 0, do_ubifs_mount, + "mount UBIFS volume", + "<volume-name>\n" + " - mount 'volume-name' volume" +); + +U_BOOT_CMD( + ubifsumount, 1, 0, do_ubifs_umount, + "unmount UBIFS volume", + " - unmount current volume" +); + +U_BOOT_CMD( + ubifsls, 2, 0, do_ubifs_ls, + "list files in a directory", + "[directory]\n" + " - list files in a 'directory' (default '/')" +); + +U_BOOT_CMD( + ubifsload, 4, 0, do_ubifs_load, + "load file from an UBIFS filesystem", + "<addr> <filename> [bytes]\n" + " - load file 'filename' to address 'addr'" +); diff --git a/roms/u-boot/cmd/ufs.c b/roms/u-boot/cmd/ufs.c new file mode 100644 index 000000000..858cd49b5 --- /dev/null +++ b/roms/u-boot/cmd/ufs.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/** + * ufs.c - UFS specific U-boot commands + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * + */ +#include <common.h> +#include <command.h> +#include <ufs.h> + +static int do_ufs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int dev, ret; + + if (argc >= 2) { + if (!strcmp(argv[1], "init")) { + if (argc == 3) { + dev = simple_strtoul(argv[2], NULL, 10); + ret = ufs_probe_dev(dev); + if (ret) + return CMD_RET_FAILURE; + } else { + ufs_probe(); + } + + return CMD_RET_SUCCESS; + } + } + + return CMD_RET_USAGE; +} + +U_BOOT_CMD(ufs, 3, 1, do_ufs, + "UFS sub system", + "init [dev] - init UFS subsystem\n" +); diff --git a/roms/u-boot/cmd/universe.c b/roms/u-boot/cmd/universe.c new file mode 100644 index 000000000..5ff47dde4 --- /dev/null +++ b/roms/u-boot/cmd/universe.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2003 Stefan Roese, stefan.roese@esd-electronics.com + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <asm/io.h> +#include <pci.h> + +#include <universe.h> + +#define PCI_VENDOR PCI_VENDOR_ID_TUNDRA +#define PCI_DEVICE PCI_DEVICE_ID_TUNDRA_CA91C042 + + +typedef struct _UNI_DEV UNI_DEV; + +struct _UNI_DEV { + int bus; + pci_dev_t busdevfn; + UNIVERSE *uregs; + unsigned int pci_bs; +}; + +static UNI_DEV *dev; + + +int universe_init(void) +{ + int j, result; + pci_dev_t busdevfn; + unsigned int val; + + busdevfn = pci_find_device(PCI_VENDOR, PCI_DEVICE, 0); + if (busdevfn == -1) { + puts("No Tundra Universe found!\n"); + return -1; + } + + /* Lets turn Latency off */ + pci_write_config_dword(busdevfn, 0x0c, 0); + + dev = malloc(sizeof(*dev)); + if (NULL == dev) { + puts("UNIVERSE: No memory!\n"); + result = -1; + goto break_20; + } + + memset(dev, 0, sizeof(*dev)); + dev->busdevfn = busdevfn; + + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_1, &val); + if (val & 1) { + pci_read_config_dword(busdevfn, PCI_BASE_ADDRESS_0, &val); + } + val &= ~0xf; + dev->uregs = (UNIVERSE *)val; + + debug ("UNIVERSE-Base : %p\n", dev->uregs); + + /* check mapping */ + debug (" Read via mapping, PCI_ID = %08X\n", readl(&dev->uregs->pci_id)); + if (((PCI_DEVICE <<16) | PCI_VENDOR) != readl(&dev->uregs->pci_id)) { + printf ("UNIVERSE: Cannot read PCI-ID via Mapping: %08x\n", + readl(&dev->uregs->pci_id)); + result = -1; + goto break_30; + } + + debug ("PCI_BS = %08X\n", readl(&dev->uregs->pci_bs)); + + dev->pci_bs = readl(&dev->uregs->pci_bs); + + /* turn off windows */ + for (j=0; j <4; j ++) { + writel(0x00800000, &dev->uregs->lsi[j].ctl); + writel(0x00800000, &dev->uregs->vsi[j].ctl); + } + + /* + * Write to Misc Register + * Set VME Bus Time-out + * Arbitration Mode + * DTACK Enable + */ + writel(0x15040000 | (readl(&dev->uregs->misc_ctl) & 0x00020000), &dev->uregs->misc_ctl); + + if (readl(&dev->uregs->misc_ctl) & 0x00020000) { + debug ("System Controller!\n"); /* test-only */ + } else { + debug ("Not System Controller!\n"); /* test-only */ + } + + /* + * Lets turn off interrupts + */ + writel(0x00000000,&dev->uregs->lint_en); /* Disable interrupts in the Universe first */ + writel(0x0000FFFF,&dev->uregs->lint_stat); /* Clear Any Pending Interrupts */ + eieio(); + writel(0x0000, &dev->uregs->lint_map0); /* Map all ints to 0 */ + writel(0x0000, &dev->uregs->lint_map1); /* Map all ints to 0 */ + eieio(); + + return 0; + + break_30: + free(dev); + break_20: + return result; +} + + +/* + * Create pci slave window (access: pci -> vme) + */ +int universe_pci_slave_window(unsigned int pciAddr, unsigned int vmeAddr, int size, int vam, int pms, int vdw) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 4; i++) { + if (0x00800000 == readl(&dev->uregs->lsi[i].ctl)) + break; + } + + if (i == 4) { + printf ("universe: No Image available\n"); + result = -1; + goto exit_10; + } + + debug ("universe: Using image %d\n", i); + + writel(pciAddr , &dev->uregs->lsi[i].bs); + writel((pciAddr + size), &dev->uregs->lsi[i].bd); + writel((vmeAddr - pciAddr), &dev->uregs->lsi[i].to); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00010000; + break; + case VME_AM_A32: + ctl = 0x00020000; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00008000; + break; + } + + if (vam & VME_AM_SUP) { + ctl |= 0x00001000; + + } + + switch (vdw & VME_FLAG_Dxx) { + case VME_FLAG_D8: + ctl |= 0x00000000; + break; + case VME_FLAG_D16: + ctl |= 0x00400000; + break; + case VME_FLAG_D32: + ctl |= 0x00800000; + break; + } + + switch (pms & PCI_MS_Mxx) { + case PCI_MS_MEM: + ctl |= 0x00000000; + break; + case PCI_MS_IO: + ctl |= 0x00000001; + break; + case PCI_MS_CONFIG: + ctl |= 0x00000002; + break; + } + + ctl |= 0x80000000; /* enable */ + + writel(ctl, &dev->uregs->lsi[i].ctl); + + debug ("universe: window-addr=%p\n", &dev->uregs->lsi[i].ctl); + debug ("universe: pci slave window[%d] ctl=%08x\n", i, readl(&dev->uregs->lsi[i].ctl)); + debug ("universe: pci slave window[%d] bs=%08x\n", i, readl(&dev->uregs->lsi[i].bs)); + debug ("universe: pci slave window[%d] bd=%08x\n", i, readl(&dev->uregs->lsi[i].bd)); + debug ("universe: pci slave window[%d] to=%08x\n", i, readl(&dev->uregs->lsi[i].to)); + + return 0; + + exit_10: + return -result; +} + + +/* + * Create vme slave window (access: vme -> pci) + */ +int universe_vme_slave_window(unsigned int vmeAddr, unsigned int pciAddr, int size, int vam, int pms) +{ + int result, i; + unsigned int ctl = 0; + + if (NULL == dev) { + result = -1; + goto exit_10; + } + + for (i = 0; i < 4; i++) { + if (0x00800000 == readl(&dev->uregs->vsi[i].ctl)) + break; + } + + if (i == 4) { + printf ("universe: No Image available\n"); + result = -1; + goto exit_10; + } + + debug ("universe: Using image %d\n", i); + + writel(vmeAddr , &dev->uregs->vsi[i].bs); + writel((vmeAddr + size), &dev->uregs->vsi[i].bd); + writel((pciAddr - vmeAddr), &dev->uregs->vsi[i].to); + + switch (vam & VME_AM_Axx) { + case VME_AM_A16: + ctl = 0x00000000; + break; + case VME_AM_A24: + ctl = 0x00010000; + break; + case VME_AM_A32: + ctl = 0x00020000; + break; + } + + switch (vam & VME_AM_Mxx) { + case VME_AM_DATA: + ctl |= 0x00000000; + break; + case VME_AM_PROG: + ctl |= 0x00800000; + break; + } + + if (vam & VME_AM_SUP) { + ctl |= 0x00100000; + + } + + switch (pms & PCI_MS_Mxx) { + case PCI_MS_MEM: + ctl |= 0x00000000; + break; + case PCI_MS_IO: + ctl |= 0x00000001; + break; + case PCI_MS_CONFIG: + ctl |= 0x00000002; + break; + } + + ctl |= 0x80f00000; /* enable */ + + writel(ctl, &dev->uregs->vsi[i].ctl); + + debug ("universe: window-addr=%p\n", &dev->uregs->vsi[i].ctl); + debug ("universe: vme slave window[%d] ctl=%08x\n", i, readl(&dev->uregs->vsi[i].ctl)); + debug ("universe: vme slave window[%d] bs=%08x\n", i, readl(&dev->uregs->vsi[i].bs)); + debug ("universe: vme slave window[%d] bd=%08x\n", i, readl(&dev->uregs->vsi[i].bd)); + debug ("universe: vme slave window[%d] to=%08x\n", i, readl(&dev->uregs->vsi[i].to)); + + return 0; + + exit_10: + return -result; +} + + +/* + * Tundra Universe configuration + */ +int do_universe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr1 = 0, addr2 = 0, size = 0, vam = 0, pms = 0, vdw = 0; + char cmd = 'x'; + + /* get parameter */ + if (argc > 1) + cmd = argv[1][0]; + if (argc > 2) + addr1 = simple_strtoul(argv[2], NULL, 16); + if (argc > 3) + addr2 = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + size = simple_strtoul(argv[4], NULL, 16); + if (argc > 5) + vam = simple_strtoul(argv[5], NULL, 16); + if (argc > 6) + pms = simple_strtoul(argv[6], NULL, 16); + if (argc > 7) + vdw = simple_strtoul(argv[7], NULL, 16); + + switch (cmd) { + case 'i': /* init */ + universe_init(); + break; + case 'v': /* vme */ + printf("Configuring Universe VME Slave Window (VME->PCI):\n"); + printf(" vme=%08lx pci=%08lx size=%08lx vam=%02lx pms=%02lx\n", + addr1, addr2, size, vam, pms); + universe_vme_slave_window(addr1, addr2, size, vam, pms); + break; + case 'p': /* pci */ + printf("Configuring Universe PCI Slave Window (PCI->VME):\n"); + printf(" pci=%08lx vme=%08lx size=%08lx vam=%02lx pms=%02lx vdw=%02lx\n", + addr1, addr2, size, vam, pms, vdw); + universe_pci_slave_window(addr1, addr2, size, vam, pms, vdw); + break; + default: + printf("Universe command %s not supported!\n", argv[1]); + } + + return 0; +} + + +U_BOOT_CMD( + universe, 8, 1, do_universe, + "initialize and configure Turndra Universe", + "init\n" + " - initialize universe\n" + "universe vme [vme_addr] [pci_addr] [size] [vam] [pms]\n" + " - create vme slave window (access: vme->pci)\n" + "universe pci [pci_addr] [vme_addr] [size] [vam] [pms] [vdw]\n" + " - create pci slave window (access: pci->vme)\n" + " [vam] = VMEbus Address-Modifier: 01 -> A16 Address Space\n" + " 02 -> A24 Address Space\n" + " 03 -> A32 Address Space\n" + " 04 -> Supervisor AM Code\n" + " 10 -> Data AM Code\n" + " 20 -> Program AM Code\n" + " [pms] = PCI Memory Space: 01 -> Memory Space\n" + " 02 -> I/O Space\n" + " 03 -> Configuration Space\n" + " [vdw] = VMEbus Maximum Datawidth: 01 -> D8 Data Width\n" + " 02 -> D16 Data Width\n" + " 03 -> D32 Data Width" +); diff --git a/roms/u-boot/cmd/unlz4.c b/roms/u-boot/cmd/unlz4.c new file mode 100644 index 000000000..4ae7f34d3 --- /dev/null +++ b/roms/u-boot/cmd/unlz4.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 + * FUJITSU COMPUTERTECHNOLOGIES LIMITED. All rights reserved. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <lz4.h> + +static int do_unlz4(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long src, dst; + size_t src_len = ~0UL, dst_len = ~0UL; + int ret; + + switch (argc) { + case 4: + src = simple_strtoul(argv[1], NULL, 16); + dst = simple_strtoul(argv[2], NULL, 16); + dst_len = simple_strtoul(argv[3], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + ret = ulz4fn((void *)src, src_len, (void *)dst, &dst_len); + if (ret) { + printf("Uncompressed err :%d\n", ret); + return 1; + } + + printf("Uncompressed size: %zd = 0x%zX\n", dst_len, dst_len); + env_set_hex("filesize", dst_len); + + return 0; +} + +U_BOOT_CMD(unlz4, 4, 1, do_unlz4, + "lz4 uncompress a memory region", + "srcaddr dstaddr dstsize\n" + "NOTE: Specify the destination size that is sufficiently larger\n" + " than the source size.\n" +); diff --git a/roms/u-boot/cmd/unzip.c b/roms/u-boot/cmd/unzip.c new file mode 100644 index 000000000..9b28328a1 --- /dev/null +++ b/roms/u-boot/cmd/unzip.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <gzip.h> +#include <part.h> + +static int do_unzip(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long src, dst; + unsigned long src_len = ~0UL, dst_len = ~0UL; + + switch (argc) { + case 4: + dst_len = simple_strtoul(argv[3], NULL, 16); + /* fall through */ + case 3: + src = simple_strtoul(argv[1], NULL, 16); + dst = simple_strtoul(argv[2], NULL, 16); + break; + default: + return CMD_RET_USAGE; + } + + if (gunzip((void *) dst, dst_len, (void *) src, &src_len) != 0) + return 1; + + printf("Uncompressed size: %lu = 0x%lX\n", src_len, src_len); + env_set_hex("filesize", src_len); + + return 0; +} + +U_BOOT_CMD( + unzip, 4, 1, do_unzip, + "unzip a memory region", + "srcaddr dstaddr [dstsize]" +); + +static int do_gzwrite(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct blk_desc *bdev; + int ret; + unsigned char *addr; + unsigned long length; + unsigned long writebuf = 1<<20; + u64 startoffs = 0; + u64 szexpected = 0; + + if (argc < 5) + return CMD_RET_USAGE; + ret = blk_get_device_by_str(argv[1], argv[2], &bdev); + if (ret < 0) + return CMD_RET_FAILURE; + + addr = (unsigned char *)simple_strtoul(argv[3], NULL, 16); + length = simple_strtoul(argv[4], NULL, 16); + + if (5 < argc) { + writebuf = simple_strtoul(argv[5], NULL, 16); + if (6 < argc) { + startoffs = simple_strtoull(argv[6], NULL, 16); + if (7 < argc) + szexpected = simple_strtoull(argv[7], + NULL, 16); + } + } + + ret = gzwrite(addr, length, bdev, writebuf, startoffs, szexpected); + + return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + gzwrite, 8, 0, do_gzwrite, + "unzip and write memory to block device", + "<interface> <dev> <addr> length [wbuf=1M [offs=0 [outsize=0]]]\n" + "\twbuf is the size in bytes (hex) of write buffer\n" + "\t\tand should be padded to erase size for SSDs\n" + "\toffs is the output start offset in bytes (hex)\n" + "\toutsize is the size of the expected output (hex bytes)\n" + "\t\tand is required for files with uncompressed lengths\n" + "\t\t4 GiB or larger\n" +); diff --git a/roms/u-boot/cmd/usb.c b/roms/u-boot/cmd/usb.c new file mode 100644 index 000000000..b9ec29a84 --- /dev/null +++ b/roms/u-boot/cmd/usb.c @@ -0,0 +1,758 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Adapted for U-Boot driver model + * (C) Copyright 2015 Google, Inc + * + * Most of this source has been derived from the Linux USB + * project. + */ + +#include <common.h> +#include <blk.h> +#include <bootstage.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <dm/uclass-internal.h> +#include <memalign.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <part.h> +#include <usb.h> + +#ifdef CONFIG_USB_STORAGE +static int usb_stor_curr_dev = -1; /* current device */ +#endif +#if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH) +static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */ +#endif + +/* some display routines (info command) */ +static char *usb_get_class_desc(unsigned char dclass) +{ + switch (dclass) { + case USB_CLASS_PER_INTERFACE: + return "See Interface"; + case USB_CLASS_AUDIO: + return "Audio"; + case USB_CLASS_COMM: + return "Communication"; + case USB_CLASS_HID: + return "Human Interface"; + case USB_CLASS_PRINTER: + return "Printer"; + case USB_CLASS_MASS_STORAGE: + return "Mass Storage"; + case USB_CLASS_HUB: + return "Hub"; + case USB_CLASS_DATA: + return "CDC Data"; + case USB_CLASS_VENDOR_SPEC: + return "Vendor specific"; + default: + return ""; + } +} + +static void usb_display_class_sub(unsigned char dclass, unsigned char subclass, + unsigned char proto) +{ + switch (dclass) { + case USB_CLASS_PER_INTERFACE: + printf("See Interface"); + break; + case USB_CLASS_HID: + printf("Human Interface, Subclass: "); + switch (subclass) { + case USB_SUB_HID_NONE: + printf("None"); + break; + case USB_SUB_HID_BOOT: + printf("Boot "); + switch (proto) { + case USB_PROT_HID_NONE: + printf("None"); + break; + case USB_PROT_HID_KEYBOARD: + printf("Keyboard"); + break; + case USB_PROT_HID_MOUSE: + printf("Mouse"); + break; + default: + printf("reserved"); + break; + } + break; + default: + printf("reserved"); + break; + } + break; + case USB_CLASS_MASS_STORAGE: + printf("Mass Storage, "); + switch (subclass) { + case US_SC_RBC: + printf("RBC "); + break; + case US_SC_8020: + printf("SFF-8020i (ATAPI)"); + break; + case US_SC_QIC: + printf("QIC-157 (Tape)"); + break; + case US_SC_UFI: + printf("UFI"); + break; + case US_SC_8070: + printf("SFF-8070"); + break; + case US_SC_SCSI: + printf("Transp. SCSI"); + break; + default: + printf("reserved"); + break; + } + printf(", "); + switch (proto) { + case US_PR_CB: + printf("Command/Bulk"); + break; + case US_PR_CBI: + printf("Command/Bulk/Int"); + break; + case US_PR_BULK: + printf("Bulk only"); + break; + default: + printf("reserved"); + break; + } + break; + default: + printf("%s", usb_get_class_desc(dclass)); + break; + } +} + +static void usb_display_string(struct usb_device *dev, int index) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256); + + if (index != 0) { + if (usb_string(dev, index, &buffer[0], 256) > 0) + printf("String: \"%s\"", buffer); + } +} + +static void usb_display_desc(struct usb_device *dev) +{ + uint packet_size = dev->descriptor.bMaxPacketSize0; + + if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) { + printf("%d: %s, USB Revision %x.%x\n", dev->devnum, + usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass), + (dev->descriptor.bcdUSB>>8) & 0xff, + dev->descriptor.bcdUSB & 0xff); + + if (strlen(dev->mf) || strlen(dev->prod) || + strlen(dev->serial)) + printf(" - %s %s %s\n", dev->mf, dev->prod, + dev->serial); + if (dev->descriptor.bDeviceClass) { + printf(" - Class: "); + usb_display_class_sub(dev->descriptor.bDeviceClass, + dev->descriptor.bDeviceSubClass, + dev->descriptor.bDeviceProtocol); + printf("\n"); + } else { + printf(" - Class: (from Interface) %s\n", + usb_get_class_desc( + dev->config.if_desc[0].desc.bInterfaceClass)); + } + if (dev->descriptor.bcdUSB >= cpu_to_le16(0x0300)) + packet_size = 1 << packet_size; + printf(" - PacketSize: %d Configurations: %d\n", + packet_size, dev->descriptor.bNumConfigurations); + printf(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\n", + dev->descriptor.idVendor, dev->descriptor.idProduct, + (dev->descriptor.bcdDevice>>8) & 0xff, + dev->descriptor.bcdDevice & 0xff); + } + +} + +static void usb_display_conf_desc(struct usb_config_descriptor *config, + struct usb_device *dev) +{ + printf(" Configuration: %d\n", config->bConfigurationValue); + printf(" - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces, + (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ", + (config->bmAttributes & 0x20) ? "Remote Wakeup " : "", + config->bMaxPower*2); + if (config->iConfiguration) { + printf(" - "); + usb_display_string(dev, config->iConfiguration); + printf("\n"); + } +} + +static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc, + struct usb_device *dev) +{ + printf(" Interface: %d\n", ifdesc->bInterfaceNumber); + printf(" - Alternate Setting %d, Endpoints: %d\n", + ifdesc->bAlternateSetting, ifdesc->bNumEndpoints); + printf(" - Class "); + usb_display_class_sub(ifdesc->bInterfaceClass, + ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol); + printf("\n"); + if (ifdesc->iInterface) { + printf(" - "); + usb_display_string(dev, ifdesc->iInterface); + printf("\n"); + } +} + +static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc) +{ + printf(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf, + (epdesc->bEndpointAddress & 0x80) ? "In" : "Out"); + switch ((epdesc->bmAttributes & 0x03)) { + case 0: + printf("Control"); + break; + case 1: + printf("Isochronous"); + break; + case 2: + printf("Bulk"); + break; + case 3: + printf("Interrupt"); + break; + } + printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize)); + if ((epdesc->bmAttributes & 0x03) == 0x3) + printf(" Interval %dms", epdesc->bInterval); + printf("\n"); +} + +/* main routine to diasplay the configs, interfaces and endpoints */ +static void usb_display_config(struct usb_device *dev) +{ + struct usb_config *config; + struct usb_interface *ifdesc; + struct usb_endpoint_descriptor *epdesc; + int i, ii; + + config = &dev->config; + usb_display_conf_desc(&config->desc, dev); + for (i = 0; i < config->no_of_if; i++) { + ifdesc = &config->if_desc[i]; + usb_display_if_desc(&ifdesc->desc, dev); + for (ii = 0; ii < ifdesc->no_of_ep; ii++) { + epdesc = &ifdesc->ep_desc[ii]; + usb_display_ep_desc(epdesc); + } + } + printf("\n"); +} + +/* + * With driver model this isn't right since we can have multiple controllers + * and the device numbering starts at 1 on each bus. + * TODO(sjg@chromium.org): Add a way to specify the controller/bus. + */ +static struct usb_device *usb_find_device(int devnum) +{ +#ifdef CONFIG_DM_USB + struct usb_device *udev; + struct udevice *hub; + struct uclass *uc; + int ret; + + /* Device addresses start at 1 */ + devnum++; + ret = uclass_get(UCLASS_USB_HUB, &uc); + if (ret) + return NULL; + + uclass_foreach_dev(hub, uc) { + struct udevice *dev; + + if (!device_active(hub)) + continue; + udev = dev_get_parent_priv(hub); + if (udev->devnum == devnum) + return udev; + + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + if (!device_active(hub)) + continue; + + udev = dev_get_parent_priv(dev); + if (udev->devnum == devnum) + return udev; + } + } +#else + struct usb_device *udev; + int d; + + for (d = 0; d < USB_MAX_DEVICE; d++) { + udev = usb_get_dev_index(d); + if (udev == NULL) + return NULL; + if (udev->devnum == devnum) + return udev; + } +#endif + + return NULL; +} + +static inline const char *portspeed(int speed) +{ + switch (speed) { + case USB_SPEED_SUPER: + return "5 Gb/s"; + case USB_SPEED_HIGH: + return "480 Mb/s"; + case USB_SPEED_LOW: + return "1.5 Mb/s"; + default: + return "12 Mb/s"; + } +} + +/* shows the device tree recursively */ +static void usb_show_tree_graph(struct usb_device *dev, char *pre) +{ + int index; + int has_child, last_child; + + index = strlen(pre); + printf(" %s", pre); +#ifdef CONFIG_DM_USB + has_child = device_has_active_children(dev->dev); + if (device_get_uclass_id(dev->dev) == UCLASS_MASS_STORAGE) { + struct udevice *child; + + for (device_find_first_child(dev->dev, &child); + child; + device_find_next_child(&child)) { + if (device_get_uclass_id(child) == UCLASS_BLK) + has_child = 0; + } + } +#else + /* check if the device has connected children */ + int i; + + has_child = 0; + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) + has_child = 1; + } +#endif + /* check if we are the last one */ +#ifdef CONFIG_DM_USB + /* Not the root of the usb tree? */ + if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) { + last_child = device_is_last_sibling(dev->dev); +#else + if (dev->parent != NULL) { /* not root? */ + last_child = 1; + for (i = 0; i < dev->parent->maxchild; i++) { + /* search for children */ + if (dev->parent->children[i] == dev) { + /* found our pointer, see if we have a + * little sister + */ + while (i++ < dev->parent->maxchild) { + if (dev->parent->children[i] != NULL) { + /* found a sister */ + last_child = 0; + break; + } /* if */ + } /* while */ + } /* device found */ + } /* for all children of the parent */ +#endif + printf("\b+-"); + /* correct last child */ + if (last_child && index) + pre[index-1] = ' '; + } /* if not root hub */ + else + printf(" "); + printf("%d ", dev->devnum); + pre[index++] = ' '; + pre[index++] = has_child ? '|' : ' '; + pre[index] = 0; + printf(" %s (%s, %dmA)\n", usb_get_class_desc( + dev->config.if_desc[0].desc.bInterfaceClass), + portspeed(dev->speed), + dev->config.desc.bMaxPower * 2); + if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) + printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); + printf(" %s\n", pre); +#ifdef CONFIG_DM_USB + struct udevice *child; + + for (device_find_first_child(dev->dev, &child); + child; + device_find_next_child(&child)) { + struct usb_device *udev; + + if (!device_active(child)) + continue; + + udev = dev_get_parent_priv(child); + + /* + * Ignore emulators and block child devices, we only want + * real devices + */ + if ((device_get_uclass_id(child) != UCLASS_USB_EMUL) && + (device_get_uclass_id(child) != UCLASS_BLK)) { + usb_show_tree_graph(udev, pre); + pre[index] = 0; + } + } +#else + if (dev->maxchild > 0) { + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) { + usb_show_tree_graph(dev->children[i], pre); + pre[index] = 0; + } + } + } +#endif +} + +/* main routine for the tree command */ +static void usb_show_subtree(struct usb_device *dev) +{ + char preamble[32]; + + memset(preamble, '\0', sizeof(preamble)); + usb_show_tree_graph(dev, &preamble[0]); +} + +#ifdef CONFIG_DM_USB +typedef void (*usb_dev_func_t)(struct usb_device *udev); + +static void usb_for_each_root_dev(usb_dev_func_t func) +{ + struct udevice *bus; + + for (uclass_find_first_device(UCLASS_USB, &bus); + bus; + uclass_find_next_device(&bus)) { + struct usb_device *udev; + struct udevice *dev; + + if (!device_active(bus)) + continue; + + device_find_first_child(bus, &dev); + if (dev && device_active(dev)) { + udev = dev_get_parent_priv(dev); + func(udev); + } + } +} +#endif + +void usb_show_tree(void) +{ +#ifdef CONFIG_DM_USB + usb_for_each_root_dev(usb_show_subtree); +#else + struct usb_device *udev; + int i; + + for (i = 0; i < USB_MAX_DEVICE; i++) { + udev = usb_get_dev_index(i); + if (udev == NULL) + break; + if (udev->parent == NULL) + usb_show_subtree(udev); + } +#endif +} + +static int usb_test(struct usb_device *dev, int port, char* arg) +{ + int mode; + + if (port > dev->maxchild) { + printf("Device is no hub or does not have %d ports.\n", port); + return 1; + } + + switch (arg[0]) { + case 'J': + case 'j': + printf("Setting Test_J mode"); + mode = USB_TEST_MODE_J; + break; + case 'K': + case 'k': + printf("Setting Test_K mode"); + mode = USB_TEST_MODE_K; + break; + case 'S': + case 's': + printf("Setting Test_SE0_NAK mode"); + mode = USB_TEST_MODE_SE0_NAK; + break; + case 'P': + case 'p': + printf("Setting Test_Packet mode"); + mode = USB_TEST_MODE_PACKET; + break; + case 'F': + case 'f': + printf("Setting Test_Force_Enable mode"); + mode = USB_TEST_MODE_FORCE_ENABLE; + break; + default: + printf("Unrecognized test mode: %s\nAvailable modes: " + "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg); + return 1; + } + + if (port) + printf(" on downstream facing port %d...\n", port); + else + printf(" on upstream facing port...\n"); + + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE, + port ? USB_RT_PORT : USB_RECIP_DEVICE, + port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST, + (mode << 8) | port, + NULL, 0, USB_CNTL_TIMEOUT) == -1) { + printf("Error during SET_FEATURE.\n"); + return 1; + } else { + printf("Test mode successfully set. Use 'usb start' " + "to return to normal operation.\n"); + return 0; + } +} + + +/****************************************************************************** + * usb boot command intepreter. Derived from diskboot + */ +#ifdef CONFIG_USB_STORAGE +static int do_usbboot(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return common_diskboot(cmdtp, "usb", argc, argv); +} +#endif /* CONFIG_USB_STORAGE */ + +static int do_usb_stop_keyboard(int force) +{ +#if !defined CONFIG_DM_USB && defined CONFIG_USB_KEYBOARD + if (usb_kbd_deregister(force) != 0) { + printf("USB not stopped: usbkbd still using USB\n"); + return 1; + } +#endif + return 0; +} + +static void do_usb_start(void) +{ + bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); + + if (usb_init() < 0) + return; + + /* Driver model will probe the devices as they are found */ +# ifdef CONFIG_USB_STORAGE + /* try to recognize storage devices immediately */ + usb_stor_curr_dev = usb_stor_scan(1); +# endif +#ifndef CONFIG_DM_USB +# ifdef CONFIG_USB_KEYBOARD + drv_usb_kbd_init(); +# endif +#endif /* !CONFIG_DM_USB */ +#ifdef CONFIG_USB_HOST_ETHER +# ifdef CONFIG_DM_ETH +# ifndef CONFIG_DM_USB +# error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" +# endif +# else + /* try to recognize ethernet devices immediately */ + usb_ether_curr_dev = usb_host_eth_scan(1); +# endif +#endif +} + +#ifdef CONFIG_DM_USB +static void usb_show_info(struct usb_device *udev) +{ + struct udevice *child; + + usb_display_desc(udev); + usb_display_config(udev); + for (device_find_first_child(udev->dev, &child); + child; + device_find_next_child(&child)) { + if (device_active(child) && + (device_get_uclass_id(child) != UCLASS_USB_EMUL) && + (device_get_uclass_id(child) != UCLASS_BLK)) { + udev = dev_get_parent_priv(child); + usb_show_info(udev); + } + } +} +#endif + +/****************************************************************************** + * usb command intepreter + */ +static int do_usb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct usb_device *udev = NULL; + int i; + extern char usb_started; + + if (argc < 2) + return CMD_RET_USAGE; + + if (strncmp(argv[1], "start", 5) == 0) { + if (usb_started) + return 0; /* Already started */ + printf("starting USB...\n"); + do_usb_start(); + return 0; + } + + if (strncmp(argv[1], "reset", 5) == 0) { + printf("resetting USB...\n"); + if (do_usb_stop_keyboard(1) != 0) + return 1; + usb_stop(); + do_usb_start(); + return 0; + } + if (strncmp(argv[1], "stop", 4) == 0) { + if (argc != 2) + console_assign(stdin, "serial"); + if (do_usb_stop_keyboard(0) != 0) + return 1; + printf("stopping USB..\n"); + usb_stop(); + return 0; + } + if (!usb_started) { + printf("USB is stopped. Please issue 'usb start' first.\n"); + return 1; + } + if (strncmp(argv[1], "tree", 4) == 0) { + puts("USB device tree:\n"); + usb_show_tree(); + return 0; + } + if (strncmp(argv[1], "inf", 3) == 0) { + if (argc == 2) { +#ifdef CONFIG_DM_USB + usb_for_each_root_dev(usb_show_info); +#else + int d; + for (d = 0; d < USB_MAX_DEVICE; d++) { + udev = usb_get_dev_index(d); + if (udev == NULL) + break; + usb_display_desc(udev); + usb_display_config(udev); + } +#endif + return 0; + } else { + /* + * With driver model this isn't right since we can + * have multiple controllers and the device numbering + * starts at 1 on each bus. + */ + i = simple_strtoul(argv[2], NULL, 10); + printf("config for device %d\n", i); + udev = usb_find_device(i); + if (udev == NULL) { + printf("*** No device available ***\n"); + return 0; + } else { + usb_display_desc(udev); + usb_display_config(udev); + } + } + return 0; + } + if (strncmp(argv[1], "test", 4) == 0) { + if (argc < 5) + return CMD_RET_USAGE; + i = simple_strtoul(argv[2], NULL, 10); + udev = usb_find_device(i); + if (udev == NULL) { + printf("Device %d does not exist.\n", i); + return 1; + } + i = simple_strtoul(argv[3], NULL, 10); + return usb_test(udev, i, argv[4]); + } +#ifdef CONFIG_USB_STORAGE + if (strncmp(argv[1], "stor", 4) == 0) + return usb_stor_info(); + + return blk_common_cmd(argc, argv, IF_TYPE_USB, &usb_stor_curr_dev); +#else + return CMD_RET_USAGE; +#endif /* CONFIG_USB_STORAGE */ +} + +U_BOOT_CMD( + usb, 5, 1, do_usb, + "USB sub-system", + "start - start (scan) USB controller\n" + "usb reset - reset (rescan) USB controller\n" + "usb stop [f] - stop USB [f]=force stop\n" + "usb tree - show USB device tree\n" + "usb info [dev] - show available USB devices\n" + "usb test [dev] [port] [mode] - set USB 2.0 test mode\n" + " (specify port 0 to indicate the device's upstream port)\n" + " Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n" +#ifdef CONFIG_USB_STORAGE + "usb storage - show details of USB storage devices\n" + "usb dev [dev] - show or set current USB storage device\n" + "usb part [dev] - print partition table of one or all USB storage" + " devices\n" + "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" + " to memory address `addr'\n" + "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n" + " from memory address `addr'" +#endif /* CONFIG_USB_STORAGE */ +); + + +#ifdef CONFIG_USB_STORAGE +U_BOOT_CMD( + usbboot, 3, 1, do_usbboot, + "boot from USB device", + "loadAddr dev:part" +); +#endif /* CONFIG_USB_STORAGE */ diff --git a/roms/u-boot/cmd/usb_gadget_sdp.c b/roms/u-boot/cmd/usb_gadget_sdp.c new file mode 100644 index 000000000..1af82e113 --- /dev/null +++ b/roms/u-boot/cmd/usb_gadget_sdp.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * cmd_sdp.c -- sdp command + * + * Copyright (C) 2016 Toradex + * Author: Stefan Agner <stefan.agner@toradex.com> + */ + +#include <common.h> +#include <command.h> +#include <g_dnl.h> +#include <sdp.h> +#include <usb.h> + +static int do_sdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + int controller_index = simple_strtoul(usb_controller, NULL, 0); + usb_gadget_initialize(controller_index); + + g_dnl_clear_detach(); + ret = g_dnl_register("usb_dnl_sdp"); + if (ret) { + pr_err("SDP dnl register failed: %d\n", ret); + goto exit_register; + } + + ret = sdp_init(controller_index); + if (ret) { + pr_err("SDP init failed: %d\n", ret); + goto exit; + } + + /* This command typically does not return but jumps to an image */ + sdp_handle(controller_index); + pr_err("SDP ended\n"); + +exit: + g_dnl_unregister(); +exit_register: + usb_gadget_release(controller_index); + + return CMD_RET_FAILURE; +} + +U_BOOT_CMD(sdp, 2, 1, do_sdp, + "Serial Downloader Protocol", + "<USB_controller>\n" + " - serial downloader protocol via <USB_controller>\n" +); diff --git a/roms/u-boot/cmd/usb_mass_storage.c b/roms/u-boot/cmd/usb_mass_storage.c new file mode 100644 index 000000000..14fa7233c --- /dev/null +++ b/roms/u-boot/cmd/usb_mass_storage.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <console.h> +#include <errno.h> +#include <g_dnl.h> +#include <malloc.h> +#include <part.h> +#include <usb.h> +#include <usb_mass_storage.h> +#include <watchdog.h> +#include <linux/delay.h> + +static int ums_read_sector(struct ums *ums_dev, + ulong start, lbaint_t blkcnt, void *buf) +{ + struct blk_desc *block_dev = &ums_dev->block_dev; + lbaint_t blkstart = start + ums_dev->start_sector; + + return blk_dread(block_dev, blkstart, blkcnt, buf); +} + +static int ums_write_sector(struct ums *ums_dev, + ulong start, lbaint_t blkcnt, const void *buf) +{ + struct blk_desc *block_dev = &ums_dev->block_dev; + lbaint_t blkstart = start + ums_dev->start_sector; + + return blk_dwrite(block_dev, blkstart, blkcnt, buf); +} + +static struct ums *ums; +static int ums_count; + +static void ums_fini(void) +{ + int i; + + for (i = 0; i < ums_count; i++) + free((void *)ums[i].name); + free(ums); + ums = NULL; + ums_count = 0; +} + +#define UMS_NAME_LEN 16 + +static int ums_init(const char *devtype, const char *devnums_part_str) +{ + char *s, *t, *devnum_part_str, *name; + struct blk_desc *block_dev; + struct disk_partition info; + int partnum; + int ret = -1; + struct ums *ums_new; + + s = strdup(devnums_part_str); + if (!s) + return -1; + + t = s; + ums_count = 0; + + for (;;) { + devnum_part_str = strsep(&t, ","); + if (!devnum_part_str) + break; + + partnum = blk_get_device_part_str(devtype, devnum_part_str, + &block_dev, &info, 1); + + if (partnum < 0) + goto cleanup; + + /* Check if the argument is in legacy format. If yes, + * expose all partitions by setting the partnum = 0 + * e.g. ums 0 mmc 0 + */ + if (!strchr(devnum_part_str, ':')) + partnum = 0; + + /* f_mass_storage.c assumes SECTOR_SIZE sectors */ + if (block_dev->blksz != SECTOR_SIZE) + goto cleanup; + + ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums)); + if (!ums_new) + goto cleanup; + ums = ums_new; + + /* if partnum = 0, expose all partitions */ + if (partnum == 0) { + ums[ums_count].start_sector = 0; + ums[ums_count].num_sectors = block_dev->lba; + } else { + ums[ums_count].start_sector = info.start; + ums[ums_count].num_sectors = info.size; + } + + ums[ums_count].read_sector = ums_read_sector; + ums[ums_count].write_sector = ums_write_sector; + + name = malloc(UMS_NAME_LEN); + if (!name) + goto cleanup; + snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count); + ums[ums_count].name = name; + ums[ums_count].block_dev = *block_dev; + + printf("UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x\n", + ums_count, devtype, ums[ums_count].block_dev.devnum, + ums[ums_count].block_dev.hwpart, + ums[ums_count].start_sector, + ums[ums_count].num_sectors); + + ums_count++; + } + + if (ums_count) + ret = 0; + +cleanup: + free(s); + + if (ret < 0) + ums_fini(); + + return ret; +} + +static int do_usb_mass_storage(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + const char *usb_controller; + const char *devtype; + const char *devnum; + unsigned int controller_index; + int rc; + int cable_ready_timeout __maybe_unused; + + if (argc < 3) + return CMD_RET_USAGE; + + usb_controller = argv[1]; + if (argc >= 4) { + devtype = argv[2]; + devnum = argv[3]; + } else { + devtype = "mmc"; + devnum = argv[2]; + } + + rc = ums_init(devtype, devnum); + if (rc < 0) + return CMD_RET_FAILURE; + + controller_index = (unsigned int)(simple_strtoul( + usb_controller, NULL, 0)); + if (usb_gadget_initialize(controller_index)) { + pr_err("Couldn't init USB controller.\n"); + rc = CMD_RET_FAILURE; + goto cleanup_ums_init; + } + + rc = fsg_init(ums, ums_count, controller_index); + if (rc) { + pr_err("fsg_init failed\n"); + rc = CMD_RET_FAILURE; + goto cleanup_board; + } + + rc = g_dnl_register("usb_dnl_ums"); + if (rc) { + pr_err("g_dnl_register failed\n"); + rc = CMD_RET_FAILURE; + goto cleanup_board; + } + + /* Timeout unit: seconds */ + cable_ready_timeout = UMS_CABLE_READY_TIMEOUT; + + if (!g_dnl_board_usb_cable_connected()) { + /* + * Won't execute if we don't know whether the cable is + * connected. + */ + puts("Please connect USB cable.\n"); + + while (!g_dnl_board_usb_cable_connected()) { + if (ctrlc()) { + puts("\rCTRL+C - Operation aborted.\n"); + rc = CMD_RET_SUCCESS; + goto cleanup_register; + } + if (!cable_ready_timeout) { + puts("\rUSB cable not detected.\n" \ + "Command exit.\n"); + rc = CMD_RET_SUCCESS; + goto cleanup_register; + } + + printf("\rAuto exit in: %.2d s.", cable_ready_timeout); + mdelay(1000); + cable_ready_timeout--; + } + puts("\r\n"); + } + + while (1) { + usb_gadget_handle_interrupts(controller_index); + + rc = fsg_main_thread(NULL); + if (rc) { + /* Check I/O error */ + if (rc == -EIO) + printf("\rCheck USB cable connection\n"); + + /* Check CTRL+C */ + if (rc == -EPIPE) + printf("\rCTRL+C - Operation aborted\n"); + + rc = CMD_RET_SUCCESS; + goto cleanup_register; + } + + WATCHDOG_RESET(); + } + +cleanup_register: + g_dnl_unregister(); +cleanup_board: + usb_gadget_release(controller_index); +cleanup_ums_init: + ums_fini(); + + return rc; +} + +U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage, + "Use the UMS [USB Mass Storage]", + "<USB_controller> [<devtype>] <dev[:part]> e.g. ums 0 mmc 0\n" + " devtype defaults to mmc" +); diff --git a/roms/u-boot/cmd/version.c b/roms/u-boot/cmd/version.c new file mode 100644 index 000000000..685b458ce --- /dev/null +++ b/roms/u-boot/cmd/version.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <command.h> +#include <version.h> +#include <linux/compiler.h> +#ifdef CONFIG_SYS_COREBOOT +#include <asm/cb_sysinfo.h> +#endif + +const char __weak version_string[] = U_BOOT_VERSION_STRING; + +static int do_version(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char buf[DISPLAY_OPTIONS_BANNER_LENGTH]; + + printf(display_options_get_banner(false, buf, sizeof(buf))); +#ifdef CC_VERSION_STRING + puts(CC_VERSION_STRING "\n"); +#endif +#ifdef LD_VERSION_STRING + puts(LD_VERSION_STRING "\n"); +#endif +#ifdef CONFIG_SYS_COREBOOT + printf("coreboot-%s (%s)\n", lib_sysinfo.version, lib_sysinfo.build); +#endif + return 0; +} + +U_BOOT_CMD( + version, 1, 1, do_version, + "print monitor, compiler and linker version", + "" +); diff --git a/roms/u-boot/cmd/virtio.c b/roms/u-boot/cmd/virtio.c new file mode 100644 index 000000000..3dace5344 --- /dev/null +++ b/roms/u-boot/cmd/virtio.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <blk.h> +#include <command.h> +#include <dm.h> +#include <virtio_types.h> +#include <virtio.h> + +static int virtio_curr_dev; + +static int do_virtio(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc == 2 && !strcmp(argv[1], "scan")) { + /* make sure all virtio devices are enumerated */ + virtio_init(); + + return CMD_RET_SUCCESS; + } + + return blk_common_cmd(argc, argv, IF_TYPE_VIRTIO, &virtio_curr_dev); +} + +U_BOOT_CMD( + virtio, 8, 1, do_virtio, + "virtio block devices sub-system", + "scan - initialize virtio bus\n" + "virtio info - show all available virtio block devices\n" + "virtio device [dev] - show or set current virtio block device\n" + "virtio part [dev] - print partition table of one or all virtio block devices\n" + "virtio read addr blk# cnt - read `cnt' blocks starting at block\n" + " `blk#' to memory address `addr'\n" + "virtio write addr blk# cnt - write `cnt' blocks starting at block\n" + " `blk#' from memory address `addr'" +); diff --git a/roms/u-boot/cmd/w1.c b/roms/u-boot/cmd/w1.c new file mode 100644 index 000000000..d0f0ee123 --- /dev/null +++ b/roms/u-boot/cmd/w1.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2018 + * Microchip Technology, Inc. + * Eugen Hristev <eugen.hristev@microchip.com> + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <w1.h> +#include <w1-eeprom.h> +#include <dm/device-internal.h> + +static int w1_bus(void) +{ + struct udevice *bus, *dev; + int ret; + + ret = w1_get_bus(0, &bus); + if (ret) { + printf("one wire interface not found\n"); + return CMD_RET_FAILURE; + } + printf("Bus %d:\t%s", dev_seq(bus), bus->name); + if (device_active(bus)) + printf(" (active)"); + printf("\n"); + + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + ret = device_probe(dev); + + printf("\t%s (%d) uclass %s : ", dev->name, dev_seq(dev), + dev->uclass->uc_drv->name); + + if (ret) + printf("device error\n"); + else + printf("family 0x%x\n", w1_get_device_family(dev)); + } + return CMD_RET_SUCCESS; +} + +static int w1_read(int argc, char *const argv[]) +{ + int bus_n = 0, dev_n = 0, offset = 0, len = 512; + int i; + struct udevice *bus, *dev; + int ret; + u8 buf[512]; + + if (argc > 2) + bus_n = simple_strtoul(argv[2], NULL, 10); + + if (argc > 3) + dev_n = simple_strtoul(argv[3], NULL, 10); + + if (argc > 4) + offset = simple_strtoul(argv[4], NULL, 10); + + if (argc > 5) + len = simple_strtoul(argv[5], NULL, 10); + + if (len > 512) { + printf("len needs to be <= 512\n"); + return CMD_RET_FAILURE; + } + + ret = w1_get_bus(bus_n, &bus); + if (ret) { + printf("one wire interface not found\n"); + return CMD_RET_FAILURE; + } + + for (device_find_first_child(bus, &dev), i = 0; + dev && i <= dev_n; + device_find_next_child(&dev), i++) { + ret = device_probe(dev); + if (!ret && i == dev_n) + break; + } + + if (i != dev_n || ret || !dev) { + printf("invalid dev\n"); + return CMD_RET_FAILURE; + } + + if (strcmp(dev->uclass->uc_drv->name, "w1_eeprom")) { + printf("the device present on the interface is of unknown device class\n"); + return CMD_RET_FAILURE; + } + + ret = w1_eeprom_read_buf(dev, offset, (u8 *)buf, len); + if (ret) { + printf("error reading device %s\n", dev->name); + return CMD_RET_FAILURE; + } + + for (i = 0; i < len; i++) + printf("%x", buf[i]); + printf("\n"); + + return CMD_RET_SUCCESS; +} + +int do_w1(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 2) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "bus")) + return w1_bus(); + + if (!strcmp(argv[1], "read")) + return w1_read(argc, argv); + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD(w1, 6, 0, do_w1, + "onewire interface utility commands", + "bus - show onewire bus info (all)\n" + "w1 read [<bus> [<dev> [offset [length]]]]" + " - read from onewire device 'dev' on onewire bus 'bus'" + " starting from offset 'offset' and length 'length'\n" + " defaults: bus 0, dev 0, offset 0, length 512 bytes."); diff --git a/roms/u-boot/cmd/wdt.c b/roms/u-boot/cmd/wdt.c new file mode 100644 index 000000000..27410981e --- /dev/null +++ b/roms/u-boot/cmd/wdt.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog commands + * + * Copyright (c) 2019 Michael Walle <michael@walle.cc> + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <wdt.h> + +static struct udevice *currdev; + +static int do_wdt_list(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_WDT, &uc); + if (ret) + return CMD_RET_FAILURE; + + uclass_foreach_dev(dev, uc) + printf("%s (%s)\n", dev->name, dev->driver->name); + + return CMD_RET_SUCCESS; +} + +static int do_wdt_dev(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + if (argc > 1) { + ret = uclass_get_device_by_name(UCLASS_WDT, argv[1], &currdev); + if (ret) { + printf("Can't get the watchdog timer: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + } else { + if (!currdev) { + printf("No watchdog timer device set!\n"); + return CMD_RET_FAILURE; + } + printf("dev: %s\n", currdev->name); + } + + return CMD_RET_SUCCESS; +} + +static int check_currdev(void) +{ + if (!currdev) { + printf("No device set, use 'wdt dev' first\n"); + return CMD_RET_FAILURE; + } + return 0; +} + +static int do_wdt_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + u64 timeout; + ulong flags = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + ret = check_currdev(); + if (ret) + return ret; + + timeout = simple_strtoull(argv[1], NULL, 0); + if (argc > 2) + flags = simple_strtoul(argv[2], NULL, 0); + + ret = wdt_start(currdev, timeout, flags); + if (ret == -ENOSYS) { + printf("Starting watchdog timer not supported.\n"); + return CMD_RET_FAILURE; + } else if (ret) { + printf("Starting watchdog timer failed (%d)\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_wdt_stop(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + ret = check_currdev(); + if (ret) + return ret; + + ret = wdt_stop(currdev); + if (ret == -ENOSYS) { + printf("Stopping watchdog timer not supported.\n"); + return CMD_RET_FAILURE; + } else if (ret) { + printf("Stopping watchdog timer failed (%d)\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_wdt_reset(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + ret = check_currdev(); + if (ret) + return ret; + + ret = wdt_reset(currdev); + if (ret == -ENOSYS) { + printf("Resetting watchdog timer not supported.\n"); + return CMD_RET_FAILURE; + } else if (ret) { + printf("Resetting watchdog timer failed (%d)\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_wdt_expire(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + ulong flags = 0; + + ret = check_currdev(); + if (ret) + return ret; + + if (argc > 1) + flags = simple_strtoul(argv[1], NULL, 0); + + ret = wdt_expire_now(currdev, flags); + if (ret == -ENOSYS) { + printf("Expiring watchdog timer not supported.\n"); + return CMD_RET_FAILURE; + } else if (ret) { + printf("Expiring watchdog timer failed (%d)\n", ret); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static char wdt_help_text[] = + "list - list watchdog devices\n" + "wdt dev [<name>] - get/set current watchdog device\n" + "wdt start <timeout ms> [flags] - start watchdog timer\n" + "wdt stop - stop watchdog timer\n" + "wdt reset - reset watchdog timer\n" + "wdt expire [flags] - expire watchdog timer immediately\n"; + +U_BOOT_CMD_WITH_SUBCMDS(wdt, "Watchdog sub-system", wdt_help_text, + U_BOOT_SUBCMD_MKENT(list, 1, 1, do_wdt_list), + U_BOOT_SUBCMD_MKENT(dev, 2, 1, do_wdt_dev), + U_BOOT_SUBCMD_MKENT(start, 3, 1, do_wdt_start), + U_BOOT_SUBCMD_MKENT(stop, 1, 1, do_wdt_stop), + U_BOOT_SUBCMD_MKENT(reset, 1, 1, do_wdt_reset), + U_BOOT_SUBCMD_MKENT(expire, 2, 1, do_wdt_expire)); diff --git a/roms/u-boot/cmd/wol.c b/roms/u-boot/cmd/wol.c new file mode 100644 index 000000000..f0d634322 --- /dev/null +++ b/roms/u-boot/cmd/wol.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Lothar Felte, lothar.felten@gmail.com + */ + +/* + * Wake-on-LAN support + */ +#include <common.h> +#include <command.h> +#include <net.h> + +#if defined(CONFIG_CMD_WOL) +void wol_set_timeout(ulong); + +int do_wol(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + /* Validate arguments */ + if (argc < 2) + return CMD_RET_USAGE; + wol_set_timeout(simple_strtol(argv[1], NULL, 10) * 1000); + if (net_loop(WOL) < 0) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + wol, 2, 1, do_wol, + "wait for an incoming wake-on-lan packet", + "Timeout" +); +#endif diff --git a/roms/u-boot/cmd/x86/Makefile b/roms/u-boot/cmd/x86/Makefile new file mode 100644 index 000000000..5f82204c8 --- /dev/null +++ b/roms/u-boot/cmd/x86/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o +obj-y += mtrr.o +obj-$(CONFIG_CMD_EXCEPTION) += exception.o +obj-$(CONFIG_USE_HOB) += hob.o +obj-$(CONFIG_HAVE_FSP) += fsp.o diff --git a/roms/u-boot/cmd/x86/cbsysinfo.c b/roms/u-boot/cmd/x86/cbsysinfo.c new file mode 100644 index 000000000..34fdaf5b1 --- /dev/null +++ b/roms/u-boot/cmd/x86/cbsysinfo.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <asm/cb_sysinfo.h> +#include <command.h> +#include <console.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void cbprompt(const char *name) +{ + for (; *name == '>'; name++) + puts(" "); + printf("%-12s: ", name); +} + +static void print_dec(const char *name, int value) +{ + cbprompt(name); + printf(value > 9 ? "0d%d\n" : "%d\n", value); +} + +static void print_hex(const char *name, int value) +{ + cbprompt(name); + printf("%x\n", value); +} + +static void print_addr(const char *name, ulong value) +{ + cbprompt(name); + printf("%08lx\n", value); +} + +static void print_addr64(const char *name, u64 value) +{ + cbprompt(name); + printf("%16llx\n", value); +} + +static void print_ptr(const char *name, const void *value) +{ + cbprompt(name); + printf("%p\n", value); +} + +static void print_str(const char *name, const char *value) +{ + if (value) { + cbprompt(name); + printf("%s\n", value); + } +} + +static void print_idx(const char *name, uint idx, const u8 *strings) +{ + const char *ptr; + + cbprompt(name); + ptr = (char *)strings + idx; + printf("%d: %s\n", idx, ptr ? ptr : "(unknown)"); +} + +static const char *const cb_mem_name[] = { + NULL, + "ram", + "reserved", + "acpi", + "nvs", + "unusable", + "vendor", +}; + +static const char *get_mem_name(int tag) +{ + if (tag >= CB_MEM_RAM && tag <= CB_MEM_VENDOR_RSVD) + return cb_mem_name[tag]; + + if (tag == CB_MEM_TABLE) + return "table"; + + return "(unknown)"; +} + +static const struct timestamp_id_to_name { + uint id; + const char *name; +} timestamp_ids[] = { + /* Marker to report base_time */ + { 0, "1st timestamp" }, + { TS_START_ROMSTAGE, "start of romstage" }, + { TS_BEFORE_INITRAM, "before ram initialization" }, + { TS_AFTER_INITRAM, "after ram initialization" }, + { TS_END_ROMSTAGE, "end of romstage" }, + { TS_START_VBOOT, "start of verified boot" }, + { TS_END_VBOOT, "end of verified boot" }, + { TS_START_COPYRAM, "starting to load ramstage" }, + { TS_END_COPYRAM, "finished loading ramstage" }, + { TS_START_RAMSTAGE, "start of ramstage" }, + { TS_START_BOOTBLOCK, "start of bootblock" }, + { TS_END_BOOTBLOCK, "end of bootblock" }, + { TS_START_COPYROM, "starting to load romstage" }, + { TS_END_COPYROM, "finished loading romstage" }, + { TS_START_ULZMA, "starting LZMA decompress (ignore for x86)" }, + { TS_END_ULZMA, "finished LZMA decompress (ignore for x86)" }, + { TS_START_ULZ4F, "starting LZ4 decompress (ignore for x86)" }, + { TS_END_ULZ4F, "finished LZ4 decompress (ignore for x86)" }, + { TS_DEVICE_ENUMERATE, "device enumeration" }, + { TS_DEVICE_CONFIGURE, "device configuration" }, + { TS_DEVICE_ENABLE, "device enable" }, + { TS_DEVICE_INITIALIZE, "device initialization" }, + { TS_DEVICE_DONE, "device setup done" }, + { TS_CBMEM_POST, "cbmem post" }, + { TS_WRITE_TABLES, "write tables" }, + { TS_FINALIZE_CHIPS, "finalize chips" }, + { TS_LOAD_PAYLOAD, "load payload" }, + { TS_ACPI_WAKE_JUMP, "ACPI wake jump" }, + { TS_SELFBOOT_JUMP, "selfboot jump" }, + + { TS_START_COPYVER, "starting to load verstage" }, + { TS_END_COPYVER, "finished loading verstage" }, + { TS_START_TPMINIT, "starting to initialize TPM" }, + { TS_END_TPMINIT, "finished TPM initialization" }, + { TS_START_VERIFY_SLOT, "starting to verify keyblock/preamble (RSA)" }, + { TS_END_VERIFY_SLOT, "finished verifying keyblock/preamble (RSA)" }, + { TS_START_HASH_BODY, "starting to verify body (load+SHA2+RSA) " }, + { TS_DONE_LOADING, "finished loading body (ignore for x86)" }, + { TS_DONE_HASHING, "finished calculating body hash (SHA2)" }, + { TS_END_HASH_BODY, "finished verifying body signature (RSA)" }, + + { TS_START_COPYVPD, "starting to load Chrome OS VPD" }, + { TS_END_COPYVPD_RO, "finished loading Chrome OS VPD (RO)" }, + { TS_END_COPYVPD_RW, "finished loading Chrome OS VPD (RW)" }, + + { TS_U_BOOT_INITTED, "U-Boot start" }, + { TS_RO_PARAMS_INIT, "RO parameter init" }, + { TS_RO_VB_INIT, "RO vboot init" }, + { TS_RO_VB_SELECT_FIRMWARE, "RO vboot select firmware" }, + { TS_RO_VB_SELECT_AND_LOAD_KERNEL, "RO vboot select&load kernel" }, + { TS_RW_VB_SELECT_AND_LOAD_KERNEL, "RW vboot select&load kernel" }, + { TS_VB_SELECT_AND_LOAD_KERNEL, "vboot select&load kernel" }, + { TS_VB_EC_VBOOT_DONE, "finished EC verification" }, + { TS_VB_STORAGE_INIT_DONE, "finished storage device initialization" }, + { TS_VB_READ_KERNEL_DONE, "finished reading kernel from disk" }, + { TS_VB_VBOOT_DONE, "finished vboot kernel verification" }, + { TS_KERNEL_DECOMPRESSION, "starting kernel decompression/relocation" }, + { TS_START_KERNEL, "jumping to kernel" }, + { TS_U_BOOT_START_KERNEL, "just before jump to kernel" }, + + /* Intel ME-related timestamps */ + { TS_ME_INFORM_DRAM_WAIT, "waiting for ME acknowledgment of raminit"}, + { TS_ME_INFORM_DRAM_DONE, "finished waiting for ME response"}, + + /* FSP-related timestamps */ + { TS_FSP_MEMORY_INIT_START, "calling FspMemoryInit" }, + { TS_FSP_MEMORY_INIT_END, "returning from FspMemoryInit" }, + { TS_FSP_TEMP_RAM_EXIT_START, "calling FspTempRamExit" }, + { TS_FSP_TEMP_RAM_EXIT_END, "returning from FspTempRamExit" }, + { TS_FSP_SILICON_INIT_START, "calling FspSiliconInit" }, + { TS_FSP_SILICON_INIT_END, "returning from FspSiliconInit" }, + { TS_FSP_BEFORE_ENUMERATE, "calling FspNotify(AfterPciEnumeration)" }, + { TS_FSP_AFTER_ENUMERATE, + "returning from FspNotify(AfterPciEnumeration)" }, + { TS_FSP_BEFORE_FINALIZE, "calling FspNotify(ReadyToBoot)" }, + { TS_FSP_AFTER_FINALIZE, "returning from FspNotify(ReadyToBoot)" }, + { TS_FSP_BEFORE_END_OF_FIRMWARE, "calling FspNotify(EndOfFirmware)" }, + { TS_FSP_AFTER_END_OF_FIRMWARE, + "returning from FspNotify(EndOfFirmware)" }, +}; + +static const char *timestamp_name(uint32_t id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) { + if (timestamp_ids[i].id == id) + return timestamp_ids[i].name; + } + + return "<unknown>"; +} + +static void show_table(struct sysinfo_t *info, bool verbose) +{ + struct cb_serial *ser = info->serial; + int i; + + printf("Coreboot table at %lx, decoded to %p", + gd->arch.coreboot_table, info); + if (info->header) + printf(", forwarded to %p\n", info->header); + printf("\n"); + + print_dec("CPU KHz", info->cpu_khz); + + print_addr("Serial I/O port", info->ser_ioport); + print_addr(">base", info->ser_base); + print_ptr(">pointer", ser); + if (ser) { + print_hex(">type", ser->type); + print_addr(">base", ser->baseaddr); + print_dec(">baud", ser->baud); + print_hex(">regwidth", ser->regwidth); + print_dec(">input_hz", ser->input_hertz); + print_addr(">PCI addr", ser->uart_pci_addr); + } + + print_dec("Mem ranges", info->n_memranges); + printf("%12s: %-11s || base || size\n", "id", "type"); + for (i = 0; i < info->n_memranges; i++) { + const struct memrange *mr = &info->memrange[i]; + + printf("%12d: %02x:%-8s %016llx %016llx\n", i, mr->type, + get_mem_name(mr->type), mr->base, mr->size); + } + print_ptr("option_table", info->option_table); + + print_hex("CMOS start", info->cmos_range_start); + if (info->cmos_range_start) { + print_hex(">CMOS end", info->cmos_range_end); + print_hex(">CMOS csum loc", info->cmos_checksum_location); + } + + print_hex("VBNV start", info->vbnv_start); + print_hex("VBNV size", info->vbnv_size); + + print_str("CB version", info->cb_version); + print_str(">Extra", info->extra_version); + print_str(">Build", info->build); + print_str(">Time", info->compile_time); + print_str(">By", info->compile_by); + print_str(">Host", info->compile_host); + print_str(">Domain", info->compile_domain); + print_str(">Compiler", info->compiler); + print_str(">Linker", info->linker); + print_str(">Assembler", info->assembler); + + print_ptr("Framebuffer", info->framebuffer); + if (info->framebuffer) { + struct cb_framebuffer *fb = info->framebuffer; + + print_addr64(">Phys addr", fb->physical_address); + print_dec(">X res", fb->x_resolution); + print_dec(">X res", fb->y_resolution); + print_hex(">Bytes / line", fb->bytes_per_line); + print_dec(">Bpp", fb->bits_per_pixel); + printf(" %-12s red %d/%d, green %d/%d, blue %d/%d, reserved %d/%d\n", + "pos/size", fb->red_mask_pos, fb->red_mask_size, + fb->green_mask_pos, fb->green_mask_size, + fb->blue_mask_pos, fb->blue_mask_size, + fb->reserved_mask_pos, fb->reserved_mask_size); + } + + print_dec("GPIOs", info->num_gpios); + printf("%12s: %4s %12s %3s %s\n", "id", "port", "polarity", "val", + "name"); + for (i = 0; i < info->num_gpios; i++) { + const struct cb_gpio *gpio = &info->gpios[i]; + char portstr[4]; + + if (gpio->port == 0xffffffff) + strcpy(portstr, "-"); + else + sprintf(portstr, "%x", gpio->port); + printf("%12d: %4s %12s %3d %s\n", i, portstr, + gpio->polarity == CB_GPIO_ACTIVE_LOW ? "active-low" : + "active-high", gpio->value, gpio->name); + } + print_dec("MACs", info->num_macs); + for (i = 0; i < info->num_macs; i++) { + const struct mac_address *mac = &info->macs[i]; + int j; + + printf("%12d: ", i); + for (j = 0; j < sizeof(mac->mac_addr); j++) + printf("%s%02x", j ? ":" : "", mac->mac_addr[j]); + printf("\n"); + } + print_str(">Serial #", info->serialno); + print_ptr("Multiboot tab", info->mbtable); + print_ptr("CB header", info->header); + print_ptr("CB mainboard", info->mainboard); + if (info->mainboard) { + struct cb_mainboard *mb = info->mainboard; + + print_idx(">vendor", mb->vendor_idx, mb->strings); + print_idx(">part_number", mb->part_number_idx, mb->strings); + } + print_ptr("vboot handoff", info->vboot_handoff); + print_hex(">size", info->vboot_handoff_size); + print_ptr(">vdat addr", info->vdat_addr); + print_hex(">size", info->vdat_size); + + print_addr64("SMBIOS", info->smbios_start); + print_hex(">size", info->smbios_size); + print_hex("ROM MTRR", info->x86_rom_var_mtrr_index); + + print_ptr("Tstamp table", info->tstamp_table); + if (verbose && info->tstamp_table) { + struct timestamp_table *ts = info->tstamp_table; + + printf("%-12s", "Base_time"); + print_grouped_ull(ts->base_time, 12); + printf("\n"); + print_dec("Tick MHz", ts->tick_freq_mhz); + for (i = 0; i < ts->num_entries; i++) { + const struct timestamp_entry *tse; + + tse = &ts->entries[i]; + printf(" "); + print_grouped_ull(tse->entry_stamp, 12); + printf(" %s\n", timestamp_name(tse->entry_id)); + } + } + + print_ptr("CBmem cons", info->cbmem_cons); + if (info->cbmem_cons) { + struct cbmem_console *cons = info->cbmem_cons; + int i; + + print_hex("Size", cons->size); + print_hex("Cursor", cons->cursor); + if (verbose) { + for (i = 0; i < cons->cursor; i++) { + int ch = cons->body[i]; + + putc(ch); + + if (ch == '\n') { + /* check for ctrl-c to abort... */ + if (ctrlc()) { + puts("Abort\n"); + return; + } + printf(" "); + } + } + printf("\n"); + } + } + + print_ptr("MRC cache", info->mrc_cache); + print_ptr("ACPI GNVS", info->acpi_gnvs); + print_hex("Board ID", info->board_id); + print_hex("RAM code", info->ram_code); + print_ptr("WiFi calib", info->wifi_calibration); + print_addr64("Ramoops buff", info->ramoops_buffer); + print_hex(">size", info->ramoops_buffer_size); + print_hex("SF size", info->spi_flash.size); + print_hex("SF sector", info->spi_flash.sector_size); + print_hex("SF erase cmd", info->spi_flash.erase_cmd); + + print_addr64("FMAP offset", info->fmap_offset); + print_addr64("CBFS offset", info->cbfs_offset); + print_addr64("CBFS size", info->cbfs_size); + print_addr64("Boot media size", info->boot_media_size); + print_addr64("MTC start", info->mtc_start); + print_hex("MTC size", info->mtc_size); + + print_ptr("Chrome OS VPD", info->chromeos_vpd); +} + +static int do_cbsysinfo(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool verbose = false; + + if (argc > 1) { + if (!strcmp("-v", argv[1])) + verbose = true; + else + return CMD_RET_USAGE; + } + + if (!gd->arch.coreboot_table) { + printf("No coreboot sysinfo table found\n"); + return CMD_RET_FAILURE; + } + show_table(&lib_sysinfo, verbose); + + return 0; +} + +U_BOOT_CMD( + cbsysinfo, 2, 1, do_cbsysinfo, + "Show coreboot sysinfo table", + "[-v] Dumps out the contents of the sysinfo table. This only\n" + "works if U-Boot is booted from coreboot" +); diff --git a/roms/u-boot/cmd/x86/exception.c b/roms/u-boot/cmd/x86/exception.c new file mode 100644 index 000000000..82faaa913 --- /dev/null +++ b/roms/u-boot/cmd/x86/exception.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'exception' command can be used for testing exception handling. + * + * Copyright (c) 2018, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> + +static int do_undefined(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + asm volatile (".word 0xffff\n"); + return CMD_RET_FAILURE; +} + +static struct cmd_tbl cmd_sub[] = { + U_BOOT_CMD_MKENT(undefined, CONFIG_SYS_MAXARGS, 1, do_undefined, + "", ""), +}; + +static char exception_help_text[] = + "<ex>\n" + " The following exceptions are available:\n" + " undefined - undefined instruction\n" + ; + +#include <exception.h> diff --git a/roms/u-boot/cmd/x86/fsp.c b/roms/u-boot/cmd/x86/fsp.c new file mode 100644 index 000000000..82e4415b1 --- /dev/null +++ b/roms/u-boot/cmd/x86/fsp.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014-2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <asm/fsp/fsp_support.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int do_hdr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct fsp_header *hdr; + u32 img_addr; + char *sign; + uint addr; + int i; + +#ifdef CONFIG_FSP_VERSION2 + /* + * Only FSP-S is displayed. FSP-M was used in SPL but may not still be + * around, and we didn't keep a pointer to it. + */ + hdr = gd->arch.fsp_s_hdr; + img_addr = hdr->img_base; + addr = img_addr; +#else + addr = CONFIG_FSP_ADDR; + hdr = fsp_find_header(); + img_addr = hdr->img_base; +#endif + sign = (char *)&hdr->sign; + + printf("FSP : binary %08x, header %08x\n", addr, (int)hdr); + printf("Header : sign "); + for (i = 0; i < sizeof(hdr->sign); i++) + printf("%c", *sign++); + printf(", size %x, rev %d\n", hdr->hdr_len, hdr->hdr_rev); + printf("Image : rev "); + if (hdr->hdr_rev == FSP_HEADER_REVISION_1) { + printf("%d.%d", + (hdr->img_rev >> 8) & 0xff, hdr->img_rev & 0xff); + } else { + printf("%d.%d.%d.%d", + (hdr->img_rev >> 24) & 0xff, (hdr->img_rev >> 16) & 0xff, + (hdr->img_rev >> 8) & 0xff, hdr->img_rev & 0xff); + } + printf(", id "); + for (i = 0; i < ARRAY_SIZE(hdr->img_id); i++) + printf("%c", hdr->img_id[i]); + printf(", addr %08x, size %x\n", img_addr, hdr->img_size); + if (hdr->hdr_rev >= FSP_HEADER_REVISION_1) { + printf("GFX :%ssupported\n", + hdr->img_attr & FSP_ATTR_GRAPHICS_SUPPORT ? " " : " un"); + } + printf("VPD : addr %08x, size %x\n", + hdr->cfg_region_off + img_addr, hdr->cfg_region_size); + if (hdr->hdr_rev <= FSP_HEADER_REVISION_2) + printf("\nNumber of APIs Supported : %d\n", hdr->api_num); + if (hdr->fsp_tempram_init) + printf("\tTempRamInit : %08x\n", + hdr->fsp_tempram_init + img_addr); + if (hdr->fsp_init) + printf("\tFspInit : %08x\n", hdr->fsp_init + img_addr); + if (hdr->fsp_notify) + printf("\tFspNotify : %08x\n", hdr->fsp_notify + img_addr); + if (hdr->hdr_rev >= FSP_HEADER_REVISION_1) { + if (hdr->fsp_mem_init) + printf("\tMemoryInit : %08x\n", + hdr->fsp_mem_init + img_addr); + if (hdr->fsp_tempram_exit) + printf("\tTempRamExit : %08x\n", + hdr->fsp_tempram_exit + img_addr); + if (hdr->fsp_silicon_init) + printf("\tSiliconInit : %08x\n", + hdr->fsp_silicon_init + img_addr); + } + + return 0; +} + +static struct cmd_tbl fsp_commands[] = { + U_BOOT_CMD_MKENT(hdr, 0, 1, do_hdr, "", ""), +}; + +static int do_fsp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *fsp_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + fsp_cmd = find_cmd_tbl(argv[1], fsp_commands, ARRAY_SIZE(fsp_commands)); + argc -= 2; + argv += 2; + if (!fsp_cmd || argc > fsp_cmd->maxargs) + return CMD_RET_USAGE; + + ret = fsp_cmd->cmd(fsp_cmd, flag, argc, argv); + + return cmd_process_error(fsp_cmd, ret); +} + +U_BOOT_CMD( + fsp, 2, 1, do_fsp, + "Show Intel Firmware Support Package (FSP) related information", + "hdr - Print FSP header information" +); diff --git a/roms/u-boot/cmd/x86/hob.c b/roms/u-boot/cmd/x86/hob.c new file mode 100644 index 000000000..01db93eb3 --- /dev/null +++ b/roms/u-boot/cmd/x86/hob.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014-2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <command.h> +#include <efi.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <asm/hob.h> +#include <asm/fsp/fsp_hob.h> + +DECLARE_GLOBAL_DATA_PTR; + +static char *hob_type[] = { + "reserved", + "Hand-off", + "Mem Alloc", + "Res Desc", + "GUID Ext", + "FV", + "CPU", + "Mem Pool", + "reserved", + "FV2", + "Load PEIM", + "Capsule", +}; + +static char *res_type[] = { + "System", + "Memory-mapped I/O", + "I/O", + "Firmware device", + "Memory-mapped I/O port", + "Reserved", + "I/O reserved", +}; + +static struct guid_name { + efi_guid_t guid; + const char *name; +} guid_name[] = { + { FSP_HOB_RESOURCE_OWNER_TSEG_GUID, "TSEG" }, + { FSP_HOB_RESOURCE_OWNER_FSP_GUID, "FSP" }, + { FSP_HOB_RESOURCE_OWNER_SMM_PEI_SMRAM_GUID, "SMM PEI SMRAM" }, + { FSP_NON_VOLATILE_STORAGE_HOB_GUID, "NVS" }, + { FSP_VARIABLE_NV_DATA_HOB_GUID, "Variable NVS" }, + { FSP_GRAPHICS_INFO_HOB_GUID, "Graphics info" }, + { FSP_HOB_RESOURCE_OWNER_PCD_DATABASE_GUID1, "PCD database ea" }, + { FSP_HOB_RESOURCE_OWNER_PCD_DATABASE_GUID2, "PCD database 9b" }, + { FSP_HOB_RESOURCE_OWNER_PEIM_DXE_GUID, "PEIM Init DXE" }, + { FSP_HOB_RESOURCE_OWNER_ALLOC_STACK_GUID, "Alloc stack" }, + { FSP_HOB_RESOURCE_OWNER_SMBIOS_MEMORY_GUID, "SMBIOS memory" }, + { {}, "zero-guid" }, + {} +}; + +static const char *guid_to_name(const efi_guid_t *guid) +{ + struct guid_name *entry; + + for (entry = guid_name; entry->name; entry++) { + if (!guidcmp(guid, &entry->guid)) + return entry->name; + } + + return NULL; +} + +static void show_hob_details(const struct hob_header *hdr) +{ + const void *ptr = hdr; + + switch (hdr->type) { + case HOB_TYPE_RES_DESC: { + const struct hob_res_desc *res = ptr; + const char *typename; + + typename = res->type > 0 && res->type <= RES_MAX_MEM_TYPE ? + res_type[res->type] : "unknown"; + + printf(" base = %08llx, len = %08llx, end = %08llx, type = %d (%s)\n\n", + res->phys_start, res->len, res->phys_start + res->len, + res->type, typename); + break; + } + } +} + +static int do_hob(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const struct hob_header *hdr; + uint type; + char *desc; + int i = 0; + efi_guid_t *guid; + char uuid[UUID_STR_LEN + 1]; + bool verbose = false; + int seq = -1; /* Show all by default */ + + argc--; + argv++; + if (argc) { + if (!strcmp("-v", *argv)) { + verbose = true; + argc--; + argv++; + } + if (argc) + seq = simple_strtol(*argv, NULL, 16); + } + hdr = gd->arch.hob_list; + + printf("HOB list address: 0x%08x\n\n", (unsigned int)hdr); + + printf("# | Address | Type | Len | "); + printf("%36s\n", "GUID"); + printf("---|----------|-----------|------|-"); + printf("------------------------------------\n"); + for (i = 0; !end_of_hob(hdr); i++, hdr = get_next_hob(hdr)) { + if (seq != -1 && seq != i) + continue; + printf("%02x | %08x | ", i, (unsigned int)hdr); + type = hdr->type; + if (type == HOB_TYPE_UNUSED) + desc = "*Unused*"; + else if (type == HOB_TYPE_EOH) + desc = "*EOH*"; + else if (type >= 0 && type <= ARRAY_SIZE(hob_type)) + desc = hob_type[type]; + else + desc = "*Invalid*"; + printf("%-9s | %04x | ", desc, hdr->len); + + if (type == HOB_TYPE_MEM_ALLOC || type == HOB_TYPE_RES_DESC || + type == HOB_TYPE_GUID_EXT) { + const char *name; + + guid = (efi_guid_t *)(hdr + 1); + name = guid_to_name(guid); + if (!name) { + uuid_bin_to_str(guid->b, uuid, + UUID_STR_FORMAT_GUID); + name = uuid; + } + printf("%36s", name); + } else { + printf("%36s", "Not Available"); + } + printf("\n"); + if (verbose) + show_hob_details(hdr); + } + + return 0; +} + +U_BOOT_CMD(hob, 3, 1, do_hob, + "[-v] [seq] Print Hand-Off Block (HOB) information" + " -v - Show detailed HOB information where available" + " seq - Record # to show (all by default)", + "" +); diff --git a/roms/u-boot/cmd/x86/mtrr.c b/roms/u-boot/cmd/x86/mtrr.c new file mode 100644 index 000000000..fc61a549b --- /dev/null +++ b/roms/u-boot/cmd/x86/mtrr.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 Google, Inc + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <asm/msr.h> +#include <asm/mp.h> +#include <asm/mtrr.h> + +static const char *const mtrr_type_name[MTRR_TYPE_COUNT] = { + "Uncacheable", + "Combine", + "2", + "3", + "Through", + "Protect", + "Back", +}; + +static void read_mtrrs(void *arg) +{ + struct mtrr_info *info = arg; + + mtrr_read_all(info); +} + +static int do_mtrr_list(int reg_count, int cpu_select) +{ + struct mtrr_info info; + int ret; + int i; + + printf("Reg Valid Write-type %-16s %-16s %-16s\n", "Base ||", + "Mask ||", "Size ||"); + memset(&info, '\0', sizeof(info)); + ret = mp_run_on_cpus(cpu_select, read_mtrrs, &info); + if (ret) + return log_msg_ret("run", ret); + for (i = 0; i < reg_count; i++) { + const char *type = "Invalid"; + uint64_t base, mask, size; + bool valid; + + base = info.mtrr[i].base; + mask = info.mtrr[i].mask; + size = ~mask & ((1ULL << CONFIG_CPU_ADDR_BITS) - 1); + size |= (1 << 12) - 1; + size += 1; + valid = mask & MTRR_PHYS_MASK_VALID; + type = mtrr_type_name[base & MTRR_BASE_TYPE_MASK]; + printf("%d %-5s %-12s %016llx %016llx %016llx\n", i, + valid ? "Y" : "N", type, base & ~MTRR_BASE_TYPE_MASK, + mask & ~MTRR_PHYS_MASK_VALID, size); + } + + return 0; +} + +static int do_mtrr_set(int cpu_select, uint reg, int argc, char *const argv[]) +{ + const char *typename = argv[0]; + uint32_t start, size; + uint64_t base, mask; + int i, type = -1; + bool valid; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + for (i = 0; i < MTRR_TYPE_COUNT; i++) { + if (*typename == *mtrr_type_name[i]) + type = i; + } + if (type == -1) { + printf("Invalid type name %s\n", typename); + return CMD_RET_USAGE; + } + start = simple_strtoul(argv[1], NULL, 16); + size = simple_strtoul(argv[2], NULL, 16); + + base = start | type; + valid = native_read_msr(MTRR_PHYS_MASK_MSR(reg)) & MTRR_PHYS_MASK_VALID; + mask = ~((uint64_t)size - 1); + mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1; + if (valid) + mask |= MTRR_PHYS_MASK_VALID; + + ret = mtrr_set(cpu_select, reg, base, mask); + if (ret) + return CMD_RET_FAILURE; + + return 0; +} + +static int do_mtrr(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int reg_count = mtrr_get_var_count(); + int cmd; + int cpu_select; + uint reg; + int ret; + + cpu_select = MP_SELECT_BSP; + if (argc >= 3 && !strcmp("-c", argv[1])) { + const char *cpustr; + + cpustr = argv[2]; + if (*cpustr == 'a') + cpu_select = MP_SELECT_ALL; + else + cpu_select = simple_strtol(cpustr, NULL, 16); + argc -= 2; + argv += 2; + } + argc--; + argv++; + cmd = argv[0] ? *argv[0] : 0; + if (argc < 1 || !cmd) { + cmd = 'l'; + reg = 0; + } + if (cmd != 'l') { + if (argc < 2) + return CMD_RET_USAGE; + reg = simple_strtoul(argv[1], NULL, 16); + if (reg >= reg_count) { + printf("Invalid register number\n"); + return CMD_RET_USAGE; + } + } + if (cmd == 'l') { + bool first; + int i; + + i = mp_first_cpu(cpu_select); + if (i < 0) { + printf("Invalid CPU (err=%d)\n", i); + return CMD_RET_FAILURE; + } + first = true; + for (; i >= 0; i = mp_next_cpu(cpu_select, i)) { + if (!first) + printf("\n"); + printf("CPU %d:\n", i); + ret = do_mtrr_list(reg_count, i); + if (ret) { + printf("Failed to read CPU %d (err=%d)\n", i, + ret); + return CMD_RET_FAILURE; + } + first = false; + } + } else { + switch (cmd) { + case 'e': + ret = mtrr_set_valid(cpu_select, reg, true); + break; + case 'd': + ret = mtrr_set_valid(cpu_select, reg, false); + break; + case 's': + ret = do_mtrr_set(cpu_select, reg, argc - 2, argv + 2); + break; + default: + return CMD_RET_USAGE; + } + if (ret) { + printf("Operation failed (err=%d)\n", ret); + return CMD_RET_FAILURE; + } + } + + return 0; +} + +U_BOOT_CMD( + mtrr, 8, 1, do_mtrr, + "Use x86 memory type range registers (32-bit only)", + "[list] - list current registers\n" + "set <reg> <type> <start> <size> - set a register\n" + "\t<type> is Uncacheable, Combine, Through, Protect, Back\n" + "disable <reg> - disable a register\n" + "enable <reg> - enable a register\n" + "\n" + "Precede command with '-c <n>|all' to access a particular hex CPU, e.g.\n" + " mtrr -c all list; mtrr -c 2e list" +); diff --git a/roms/u-boot/cmd/ximg.c b/roms/u-boot/cmd/ximg.c new file mode 100644 index 000000000..ef738ebfa --- /dev/null +++ b/roms/u-boot/cmd/ximg.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2003 + * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de> + */ + + +/* + * Multi Image extract + */ +#include <common.h> +#include <command.h> +#include <cpu_func.h> +#include <env.h> +#include <gzip.h> +#include <image.h> +#include <malloc.h> +#include <mapmem.h> +#include <watchdog.h> +#if defined(CONFIG_BZIP2) +#include <bzlib.h> +#endif +#include <asm/byteorder.h> +#include <asm/cache.h> +#include <asm/io.h> + +#ifndef CONFIG_SYS_XIMG_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_XIMG_LEN 0x800000 +#endif + +static int +do_imgextract(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr = image_load_addr; + ulong dest = 0; + ulong data, len; + int verify; + int part = 0; +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + ulong count; + image_header_t *hdr = NULL; +#endif +#if defined(CONFIG_FIT) + const char *uname = NULL; + const void* fit_hdr; + int noffset; + const void *fit_data; + size_t fit_len; +#endif +#ifdef CONFIG_GZIP + uint unc_len = CONFIG_SYS_XIMG_LEN; +#endif + uint8_t comp; + + verify = env_get_yesno("verify"); + + if (argc > 1) { + addr = simple_strtoul(argv[1], NULL, 16); + } + if (argc > 2) { + part = simple_strtoul(argv[2], NULL, 16); +#if defined(CONFIG_FIT) + uname = argv[2]; +#endif + } + if (argc > 3) { + dest = simple_strtoul(argv[3], NULL, 16); + } + + switch (genimg_get_format((void *)addr)) { +#if defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IMAGE_FORMAT_LEGACY: + + printf("## Copying part %d from legacy image " + "at %08lx ...\n", part, addr); + + hdr = (image_header_t *)addr; + if (!image_check_magic(hdr)) { + printf("Bad Magic Number\n"); + return 1; + } + + if (!image_check_hcrc(hdr)) { + printf("Bad Header Checksum\n"); + return 1; + } +#ifdef DEBUG + image_print_contents(hdr); +#endif + + if (!image_check_type(hdr, IH_TYPE_MULTI) && + !image_check_type(hdr, IH_TYPE_SCRIPT)) { + printf("Wrong Image Type for %s command\n", + cmdtp->name); + return 1; + } + + comp = image_get_comp(hdr); + if ((comp != IH_COMP_NONE) && (argc < 4)) { + printf("Must specify load address for %s command " + "with compressed image\n", + cmdtp->name); + return 1; + } + + if (verify) { + printf(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + return 1; + } + printf("OK\n"); + } + + count = image_multi_count(hdr); + if (part >= count) { + printf("Bad Image Part\n"); + return 1; + } + + image_multi_getimg(hdr, part, &data, &len); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (uname == NULL) { + puts("No FIT subimage unit name\n"); + return 1; + } + + printf("## Copying '%s' subimage from FIT image " + "at %08lx ...\n", uname, addr); + + fit_hdr = (const void *)addr; + if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) { + puts("Bad FIT image format\n"); + return 1; + } + + /* get subimage node offset */ + noffset = fit_image_get_node(fit_hdr, uname); + if (noffset < 0) { + printf("Can't find '%s' FIT subimage\n", uname); + return 1; + } + + if (!fit_image_check_comp(fit_hdr, noffset, IH_COMP_NONE) + && (argc < 4)) { + printf("Must specify load address for %s command " + "with compressed image\n", + cmdtp->name); + return 1; + } + + /* verify integrity */ + if (verify) { + if (!fit_image_verify(fit_hdr, noffset)) { + puts("Bad Data Hash\n"); + return 1; + } + } + + /* get subimage/external data address and length */ + if (fit_image_get_data_and_size(fit_hdr, noffset, + &fit_data, &fit_len)) { + puts("Could not find script subimage data\n"); + return 1; + } + + if (fit_image_get_comp(fit_hdr, noffset, &comp)) { + puts("Could not find script subimage " + "compression type\n"); + return 1; + } + + data = (ulong)fit_data; + len = (ulong)fit_len; + break; +#endif + default: + puts("Invalid image type for imxtract\n"); + return 1; + } + + if (argc > 3) { + switch (comp) { + case IH_COMP_NONE: +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + { + size_t l = len; + size_t tail; + void *to = (void *) dest; + void *from = (void *)data; + + printf(" Loading part %d ... ", part); + + while (l > 0) { + tail = (l > CHUNKSZ) ? CHUNKSZ : l; + WATCHDOG_RESET(); + memmove(to, from, tail); + to += tail; + from += tail; + l -= tail; + } + } +#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ + printf(" Loading part %d ... ", part); + memmove((char *) dest, (char *)data, len); +#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ + break; +#ifdef CONFIG_GZIP + case IH_COMP_GZIP: + printf(" Uncompressing part %d ... ", part); + if (gunzip((void *) dest, unc_len, + (uchar *) data, &len) != 0) { + puts("GUNZIP ERROR - image not loaded\n"); + return 1; + } + break; +#endif +#if defined(CONFIG_BZIP2) && defined(CONFIG_LEGACY_IMAGE_FORMAT) + case IH_COMP_BZIP2: + { + int i; + + printf(" Uncompressing part %d ... ", part); + /* + * If we've got less than 4 MB of malloc() + * space, use slower decompression algorithm + * which requires at most 2300 KB of memory. + */ + i = BZ2_bzBuffToBuffDecompress( + map_sysmem(ntohl(hdr->ih_load), 0), + &unc_len, (char *)data, len, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), + 0); + if (i != BZ_OK) { + printf("BUNZIP2 ERROR %d - " + "image not loaded\n", i); + return 1; + } + } + break; +#endif /* CONFIG_BZIP2 */ + default: + printf("Unimplemented compression type %d\n", comp); + return 1; + } + puts("OK\n"); + } + + flush_cache(dest, ALIGN(len, ARCH_DMA_MINALIGN)); + + env_set_hex("fileaddr", data); + env_set_hex("filesize", len); + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char imgextract_help_text[] = + "addr part [dest]\n" + " - extract <part> from legacy image at <addr> and copy to <dest>" +#if defined(CONFIG_FIT) + "\n" + "addr uname [dest]\n" + " - extract <uname> subimage from FIT image at <addr> and copy to <dest>" +#endif + ""; +#endif + +U_BOOT_CMD( + imxtract, 4, 1, do_imgextract, + "extract a part of a multi-image", imgextract_help_text +); diff --git a/roms/u-boot/cmd/yaffs2.c b/roms/u-boot/cmd/yaffs2.c new file mode 100644 index 000000000..f29ebcc02 --- /dev/null +++ b/roms/u-boot/cmd/yaffs2.c @@ -0,0 +1,326 @@ +/* Yaffs commands. + * Modified by Charles Manning by adding ydevconfig command. + * + * Use ydevconfig to configure a mountpoint before use. + * For example: + * # Configure mountpt xxx using nand device 0 using blocks 100-500 + * ydevconfig xxx 0 100 500 + * # Mount it + * ymount xxx + * # yls, yrdm etc + * yls -l xxx + * yrdm xxx/boot-image 82000000 + * ... + */ + +#include <common.h> + +#include <config.h> +#include <command.h> + +#ifdef YAFFS2_DEBUG +#define PRINTF(fmt, args...) printf(fmt, ##args) +#else +#define PRINTF(fmt, args...) do { } while (0) +#endif + +extern void cmd_yaffs_dev_ls(void); +extern void cmd_yaffs_tracemask(unsigned set, unsigned mask); +extern void cmd_yaffs_devconfig(char *mp, int flash_dev, + int start_block, int end_block); +extern void cmd_yaffs_mount(char *mp); +extern void cmd_yaffs_umount(char *mp); +extern void cmd_yaffs_read_file(char *fn); +extern void cmd_yaffs_write_file(char *fn, char bval, int sizeOfFile); +extern void cmd_yaffs_ls(const char *mountpt, int longlist); +extern void cmd_yaffs_mwrite_file(char *fn, char *addr, int size); +extern void cmd_yaffs_mread_file(char *fn, char *addr); +extern void cmd_yaffs_mkdir(const char *dir); +extern void cmd_yaffs_rmdir(const char *dir); +extern void cmd_yaffs_rm(const char *path); +extern void cmd_yaffs_mv(const char *oldPath, const char *newPath); + +extern int yaffs_dump_dev(const char *path); + +/* ytrace - show/set yaffs trace mask */ +int do_ytrace(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc > 1) + cmd_yaffs_tracemask(1, simple_strtol(argv[1], NULL, 16)); + else + cmd_yaffs_tracemask(0, 0); + + return 0; +} + +/* ydevls - lists yaffs mount points. */ +int do_ydevls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + cmd_yaffs_dev_ls(); + + return 0; +} + +/* ydevconfig mount_pt mtd_dev_num start_block end_block */ +int do_ydevconfig(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + int mtd_dev; + int start_block; + int end_block; + + if (argc != 5) { + printf + ("Bad arguments: ydevconfig mount_pt mtd_dev start_block end_block\n"); + return -1; + } + + mtpoint = argv[1]; + mtd_dev = simple_strtol(argv[2], NULL, 16); + start_block = simple_strtol(argv[3], NULL, 16); + end_block = simple_strtol(argv[4], NULL, 16); + + cmd_yaffs_devconfig(mtpoint, mtd_dev, start_block, end_block); + + return 0; +} + +int do_ymount(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + + if (argc != 2) { + printf("Bad arguments: ymount mount_pt\n"); + return -1; + } + + mtpoint = argv[1]; + printf("Mounting yaffs2 mount point %s\n", mtpoint); + + cmd_yaffs_mount(mtpoint); + + return 0; +} + +int do_yumount(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *mtpoint; + + if (argc != 2) { + printf("Bad arguments: yumount mount_pt\n"); + return -1; + } + + mtpoint = argv[1]; + printf("Unmounting yaffs2 mount point %s\n", mtpoint); + cmd_yaffs_umount(mtpoint); + + return 0; +} + +int do_yls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc < 2 || argc > 3 || (argc == 3 && strcmp(argv[1], "-l"))) { + printf("Bad arguments: yls [-l] dir\n"); + return -1; + } + + dirname = argv[argc - 1]; + + cmd_yaffs_ls(dirname, (argc > 2) ? 1 : 0); + + return 0; +} + +int do_yrd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + + if (argc != 2) { + printf("Bad arguments: yrd file_name\n"); + return -1; + } + + filename = argv[1]; + + printf("Reading file %s ", filename); + + cmd_yaffs_read_file(filename); + + printf("done\n"); + return 0; +} + +int do_ywr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong value; + ulong numValues; + + if (argc != 4) { + printf("Bad arguments: ywr file_name value n_values\n"); + return -1; + } + + filename = argv[1]; + value = simple_strtoul(argv[2], NULL, 16); + numValues = simple_strtoul(argv[3], NULL, 16); + + printf("Writing value (%lx) %lx times to %s... ", value, numValues, + filename); + + cmd_yaffs_write_file(filename, value, numValues); + + printf("done\n"); + return 0; +} + +int do_yrdm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong addr; + + if (argc != 3) { + printf("Bad arguments: yrdm file_name addr\n"); + return -1; + } + + filename = argv[1]; + addr = simple_strtoul(argv[2], NULL, 16); + + cmd_yaffs_mread_file(filename, (char *)addr); + + return 0; +} + +int do_ywrm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *filename; + ulong addr; + ulong size; + + if (argc != 4) { + printf("Bad arguments: ywrm file_name addr size\n"); + return -1; + } + + filename = argv[1]; + addr = simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[3], NULL, 16); + + cmd_yaffs_mwrite_file(filename, (char *)addr, size); + + return 0; +} + +int do_ymkdir(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc != 2) { + printf("Bad arguments: ymkdir dir_name\n"); + return -1; + } + + dirname = argv[1]; + cmd_yaffs_mkdir(dirname); + + return 0; +} + +int do_yrmdir(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *dirname; + + if (argc != 2) { + printf("Bad arguments: yrmdir dir_name\n"); + return -1; + } + + dirname = argv[1]; + cmd_yaffs_rmdir(dirname); + + return 0; +} + +int do_yrm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *name; + + if (argc != 2) { + printf("Bad arguments: yrm name\n"); + return -1; + } + + name = argv[1]; + + cmd_yaffs_rm(name); + + return 0; +} + +int do_ymv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *oldPath; + char *newPath; + + if (argc != 3) { + printf("Bad arguments: ymv old_path new_path\n"); + return -1; + } + + oldPath = argv[1]; + newPath = argv[2]; + + cmd_yaffs_mv(newPath, oldPath); + + return 0; +} + +U_BOOT_CMD(ytrace, 2, 0, do_ytrace, + "show/set yaffs trace mask", + "[new_mask] show/set yaffs trace mask"); + +U_BOOT_CMD(ydevls, 1, 0, do_ydevls, + "list yaffs mount points", "list yaffs mount points"); + +U_BOOT_CMD(ydevconfig, 5, 0, do_ydevconfig, + "configure yaffs mount point", + "mtpoint mtd_id start_block end_block configures a yaffs2 mount point"); + +U_BOOT_CMD(ymount, 2, 0, do_ymount, + "mount yaffs", "mtpoint mounts a yaffs2 mount point"); + +U_BOOT_CMD(yumount, 2, 0, do_yumount, + "unmount yaffs", "mtpoint unmounts a yaffs2 mount point"); + +U_BOOT_CMD(yls, 3, 0, do_yls, "yaffs ls", "[-l] dirname"); + +U_BOOT_CMD(yrd, 2, 0, do_yrd, + "read file from yaffs", "path read file from yaffs"); + +U_BOOT_CMD(ywr, 4, 0, do_ywr, + "write file to yaffs", + "filename value num_vlues write values to yaffs file"); + +U_BOOT_CMD(yrdm, 3, 0, do_yrdm, + "read file to memory from yaffs", + "filename offset reads yaffs file into memory"); + +U_BOOT_CMD(ywrm, 4, 0, do_ywrm, + "write file from memory to yaffs", + "filename offset size writes memory to yaffs file"); + +U_BOOT_CMD(ymkdir, 2, 0, do_ymkdir, + "YAFFS mkdir", "dir create a yaffs directory"); + +U_BOOT_CMD(yrmdir, 2, 0, do_yrmdir, + "YAFFS rmdir", "dirname removes a yaffs directory"); + +U_BOOT_CMD(yrm, 2, 0, do_yrm, "YAFFS rm", "path removes a yaffs file"); + +U_BOOT_CMD(ymv, 4, 0, do_ymv, + "YAFFS mv", + "old_path new_path moves/rename files within a yaffs mount point"); diff --git a/roms/u-boot/cmd/zfs.c b/roms/u-boot/cmd/zfs.c new file mode 100644 index 000000000..e429ac88d --- /dev/null +++ b/roms/u-boot/cmd/zfs.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * + * ZFS filesystem porting to Uboot by + * Jorgen Lundman <lundman at lundman.net> + * + * zfsfs support + * made from existing GRUB Sources by Sun, GNU and others. + */ + +#include <common.h> +#include <part.h> +#include <config.h> +#include <command.h> +#include <env.h> +#include <image.h> +#include <linux/ctype.h> +#include <asm/byteorder.h> +#include <zfs_common.h> +#include <linux/stat.h> +#include <malloc.h> + +#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE) +#include <usb.h> +#endif + +#if !CONFIG_IS_ENABLED(DOS_PARTITION) && !CONFIG_IS_ENABLED(EFI_PARTITION) +#error DOS or EFI partition support must be selected +#endif + +#define DOS_PART_MAGIC_OFFSET 0x1fe +#define DOS_FS_TYPE_OFFSET 0x36 +#define DOS_FS32_TYPE_OFFSET 0x52 + +static int do_zfs_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char *filename = NULL; + int dev; + int part; + ulong addr = 0; + struct disk_partition info; + struct blk_desc *dev_desc; + unsigned long count; + const char *addr_str; + struct zfs_file zfile; + struct device_s vdev; + + if (argc < 3) + return CMD_RET_USAGE; + + count = 0; + addr = simple_strtoul(argv[3], NULL, 16); + filename = env_get("bootfile"); + switch (argc) { + case 3: + addr_str = env_get("loadaddr"); + if (addr_str != NULL) + addr = simple_strtoul(addr_str, NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + + break; + case 4: + break; + case 5: + filename = argv[4]; + break; + case 6: + filename = argv[4]; + count = simple_strtoul(argv[5], NULL, 16); + break; + + default: + return cmd_usage(cmdtp); + } + + if (!filename) { + puts("** No boot file defined **\n"); + return 1; + } + + part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->devnum; + printf("Loading file \"%s\" from %s device %d%c%c\n", + filename, argv[1], dev, + part ? ':' : ' ', part ? part + '0' : ' '); + + zfs_set_blk_dev(dev_desc, &info); + vdev.part_length = info.size; + + memset(&zfile, 0, sizeof(zfile)); + zfile.device = &vdev; + if (zfs_open(&zfile, filename)) { + printf("** File not found %s **\n", filename); + return 1; + } + + if ((count < zfile.size) && (count != 0)) + zfile.size = (uint64_t)count; + + if (zfs_read(&zfile, (char *)addr, zfile.size) != zfile.size) { + printf("** Unable to read \"%s\" from %s %d:%d **\n", + filename, argv[1], dev, part); + zfs_close(&zfile); + return 1; + } + + zfs_close(&zfile); + + /* Loading ok, update default load address */ + image_load_addr = addr; + + printf("%llu bytes read\n", zfile.size); + env_set_hex("filesize", zfile.size); + + return 0; +} + + +int zfs_print(const char *entry, const struct zfs_dirhook_info *data) +{ + printf("%s %s\n", + data->dir ? "<DIR> " : " ", + entry); + return 0; /* 0 continue, 1 stop */ +} + + +static int do_zfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *filename = "/"; + int part; + struct blk_desc *dev_desc; + struct disk_partition info; + struct device_s vdev; + + if (argc < 2) + return cmd_usage(cmdtp); + + if (argc == 4) + filename = argv[3]; + + part = blk_get_device_part_str(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + zfs_set_blk_dev(dev_desc, &info); + vdev.part_length = info.size; + + zfs_ls(&vdev, filename, + zfs_print); + + return 0; +} + + +U_BOOT_CMD(zfsls, 4, 1, do_zfs_ls, + "list files in a directory (default /)", + "<interface> <dev[:part]> [directory]\n" + " - list files from 'dev' on 'interface' in a '/DATASET/@/$dir/'"); + +U_BOOT_CMD(zfsload, 6, 0, do_zfs_load, + "load binary file from a ZFS filesystem", + "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + " - load binary file '/DATASET/@/$dir/$file' from 'dev' on 'interface'\n" + " to address 'addr' from ZFS filesystem"); diff --git a/roms/u-boot/cmd/zip.c b/roms/u-boot/cmd/zip.c new file mode 100644 index 000000000..8ad376846 --- /dev/null +++ b/roms/u-boot/cmd/zip.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2012 + * Lei Wen <leiwen@marvell.com>, Marvell Inc. + */ + +#include <common.h> +#include <command.h> +#include <env.h> +#include <gzip.h> + +static int do_zip(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + unsigned long src, dst; + unsigned long src_len, dst_len = ~0UL; + + switch (argc) { + case 5: + dst_len = simple_strtoul(argv[4], NULL, 16); + /* fall through */ + case 4: + src = simple_strtoul(argv[1], NULL, 16); + src_len = simple_strtoul(argv[2], NULL, 16); + dst = simple_strtoul(argv[3], NULL, 16); + break; + default: + return cmd_usage(cmdtp); + } + + if (gzip((void *) dst, &dst_len, (void *) src, src_len) != 0) + return 1; + + printf("Compressed size: %lu = 0x%lX\n", dst_len, dst_len); + env_set_hex("filesize", dst_len); + + return 0; +} + +U_BOOT_CMD( + zip, 5, 1, do_zip, + "zip a memory region", + "srcaddr srcsize dstaddr [dstsize]" +); |